Linq加入和/或过滤DateTime评估?

时间:2014-01-14 12:58:44

标签: c# linq

我有一个时钟事件列表,我遇到的问题是我不知道该事件是打卡还是打卡。我的班级看起来像这样。

private class TimeClock
{
    public string EmployeeID { get; set; }
    public DateTime ShiftDate { get; set; }
    public DateTime TimeStamp { get; set; }
}

鉴于具体的EmployeeID和ShiftDate,我需要确定他们何时进出。这可以通过简单的<评价。

var allShifts = (from p1 in punches
                 join p2 in punches 
                    on new { p1.EmployeeID, p1.ShiftDate } equals 
                       new { p2.EmployeeID, p2.ShiftDate }
                 where p1.TimeStamp < p2.TimeStamp
                 select new Shift
                 {
                      EmployeeID = p1.EmployeeID,
                      Hours = p2.TimeStamp.Subtract(p1.TimeStamp).TotalHours
                 }).ToList();

这很有效,但如果一名员工每天不止一次打入和打出,那么这种情况很明显。在那种情况下,我希望看到两个班次,一个用于第一次打入/打出,另一个用于第二个。

我很感激帮助。 井架

2 个答案:

答案 0 :(得分:2)

您可以按员工和日期对拳头进行分组。然后,从每个组中,您可以选择打卡和换出对,并计算总小时数的总和:

 from p in punches
 group p by new { p.EmployeeID, p.ShiftDate } into g
 let punch = g.First()
 select new Shift
 {
      EmployeeID = punch.EmployeeID,
      FirstName = punch.FirstName,
      LastName = punch.LastName,
      TimeClockID = punch.TimeClockID,
      Hours = g.OrderBy(p => p.TimeStamp)
               .Select((p,i) => new {Punch = p, Index = i})
               .GroupBy(x => x.Index / 2)
               .Select(inOut => new {
                   PunchInTime = inOut.First().Punch.TimeStamp, 
                   PunchOutTime = inOut.Last().Punch.TimeStamp 
               })
               .Sum(x => x.PunchOutTime.Subtract(x.PunchInTime).TotalHours)
  }).ToList();

以下是如何选择进出的对象

  • 按时间戳订购员工当前日期的记录
  • { Punch, Index }对象上使用打孔对象及其索引在有序的打孔列表中对每组打孔进行投影
  • 通过索引/ 2对这些对象进行分组(因此索引是一个整数,那么这个分区将为两个连续的打孔产生相同的值)。对于索引0,1,2,3,4,5,您将具有分组键值0,0,1,1,2,2
  • 从每组两个拳击计算总小时数的总和(注意,inOut是两个匿名对象的分组{ Punch, Index }

答案 1 :(得分:2)

如果稍微调整Shift模型:

public class TimeClock
{
    public int EmployeeId { get; set; } 
    public int TimeClockId { get; set; }
    public DateTime TimeStamp { get; set; }
}

public class Shift
{
    public TimeClock PunchIn { get; set; }
    public TimeClock PunchOut { get; set; }
    public int EmployeeId { get; set; }

    public bool HasShiftEnded 
    { 
        get { return PunchIn != null && PunchOut != null; } 
    }

    public double? DurationInHours
    {
        get
        {
            if (HasShiftEnded)
                return (PunchOut.TimeStamp - PunchIn.TimeStamp).TotalHours;

            return null;
        }
    }
}

您可以使用以下LINQ查询:

var shifts = punches
    .Where (x => x.EmployeeId == 1 )
    .OrderBy (x => x.TimeStamp)
    .Select ((x, i) => new { Index = i, Punch = x })
    .GroupBy (x => x.Index / 2)
    .Select (x => x.Select (v => v.Punch))
    .Select (x => new Shift
    {
        EmployeeId = x.ElementAt(0).EmployeeId,
        PunchIn = x.ElementAt(0),
        PunchOut = x.ElementAtOrDefault(1)
    });

以下是:

  1. 按员工ID或任何其他标准(例如轮班日期)过滤拳。
  2. 按时间顺序排列过滤的项目
  3. 将此列表拆分为两个相邻项目的列表。
  4. 对于两个项目的每个列表 - 创建一个新的Shift。没有PunchOut标记的Shift不完整(或正在进行中)。
  5. 以下是您可以试用的complete LINQPad script