如何找到两组非连续时间的交集?

时间:2012-01-16 16:19:59

标签: c# asp.net linq entity-framework

我正在尝试构建一个工具,根据员工计划工作的时间和他们的请求时间来计算名为配额的内容。

我的ShiftSet对象是一组Shift对象,由StartTime和EndTime组成(类型为time(7)。每个ShiftSet对应一天。

ScheduleExceptions是员工关闭的时间。一天中可以有任意数量的重叠或不重叠的ScheduleExceptions。它们属于日期时间数据类型。

ShiftSet的一个例子:
08:00-10:00
10:00-12:00
13:00-15:00
15:00-17:00

同一天的ScheduleExceptions示例:
07:30-10:30
14:35-16:00

我需要做的是找出员工一天工作的时间。我可以想象的方法是计算ShiftSet和ScheduleExceptions的反转的交集。

我如何随着时间的推移做到这一点?如果可能的话,我更愿意使用Linq。

4 个答案:

答案 0 :(得分:3)

article

查看这个精彩的CodeProject

对于您的具体问题,这可能过于宽泛,但它可能会为您提供一个如何解决问题的良好起点。

答案 1 :(得分:2)

正如InBetween所提到的那样,有些库已经解决了这个问题,但它们也解决了许多相关问题。如果您只想解决这个特定问题而不采取其他依赖关系,可以尝试以下方法。

// Finds ones with absolutely no overlap
var unmodified = shifts.Where(s => !exceptions.Any(e => s.Start < e.End && s.End > e.Start));

// Finds ones entirely overlapped
var overlapped = shifts.Where(s => exceptions.Any(e => e.End >= s.End && e.Start <= s.Start));

// Adjusted shifts
var adjusted = shifts.Where(s => !unmodified.Contains(s) && !overlapped.Contains(s))
                        .Select(s => new Shift
                        {
                            Start = exceptions.Where(e => e.Start <= s.Start && e.End > s.Start).Any() ? exceptions.Where(e => e.Start <= s.Start && e.End > s.Start).First().End : s.Start,
                            End = exceptions.Where(e => e.Start < s.End && e.End >= s.End).Any() ? exceptions.Where(e => e.Start < s.End && e.End >= s.End).First().Start : s.End
                        });

var newShiftSet = unmodified.Union(overlapped).Union(adjusted);

这是一个基本的例子,虽然它可以被压缩(虽然不太可读)并且有所改进。

答案 2 :(得分:0)

我没有测试下面的代码,可能是有一些bug,我在textpad中写的可能是有无效字符,Idea很简单,我尝试使用有意义的变量。

var orderedShifts = ShiftSets.OrderBy(x=>x.StartDate).ToList();

var compactShifts = new List<Shift>();
compactShifts.Add(orderedShifs[0]);

foreach (var item in orderedShift)
{
    if (item.Start <= compactShifts[compactShifts.Count-1].End 
        && item.End > compactShifts[compactShifts.Count-1].End)
    {
        compactShifts[compactShifts.Count-1].End = item.End;
    } 
    else if (item.Start > compactShifts[compactShifts.Count-1].End)
      compactShifts.Add(item);
}  

//run similar procedure for schedule exceptions to create compact schedules.

var validShifts = new List<Shift>();

foreach (var item in compactShifts)
{
   var shiftCheatingPart = compactExceptions
                          .FirstOrDefault(x=>x.Start < item.Start 
                                         && x.End > item.End)
   if (shiftCheatingPart != null)
   {
       if (item.End <= shiftCheatingPart.End)
         continue;

       validShifts.Add(new Shift{Start = shiftCheatingPart.End,End = item.End);
   }
}

var totalTimes = validShifts.Sum(x=>x.End.Sunbtract(x.Start).TotalHours);

答案 3 :(得分:0)

非常粗略的解决方案就像是

void Main()
{
    var workTime = new List<ShiftSet> {
        new ShiftSet{StartTime= new TimeSpan(8,0,0),EndTime= new TimeSpan(10,0,0)},
        new ShiftSet{StartTime= new TimeSpan(10,0,0),EndTime= new TimeSpan(12,0,0)},
        new ShiftSet{StartTime= new TimeSpan(13,0,0),EndTime= new TimeSpan(15,0,0)},
        new ShiftSet{StartTime= new TimeSpan(15,0,0),EndTime= new TimeSpan(17,0,0)}
        };


    var missingTime= new List<ShiftSet> {
        new ShiftSet{StartTime= new TimeSpan(7,30,0),EndTime= new TimeSpan(10,30,0)},
        new ShiftSet{StartTime= new TimeSpan(14,35,0),EndTime= new TimeSpan(16,0,0)}
        };


    Console.WriteLine(workTime.Sum(p=>p.Shift()) - missingTime.Sum(p=>p.Shift()));
}


public class ShiftSet 
{
    public TimeSpan StartTime {get;set;}
    public TimeSpan EndTime {get;set;}

    public double Shift() {return (EndTime-StartTime).TotalMinutes;}
}

我用分钟计算一个工作时间,所以我可以使用linq

更容易地求和

我也缺少我认为不属于ShiftSet

的特定班次信息

Because the employee is not scheduled to work from 7:30 to 8:00, we would not include that time