0

我有一个处理日期时间段的问题,当一天被划分为从不同时间开始的班次时。我希望在 C# 中解决这个问题

示例 1:我有一个从 20150901 06:00am 到 20150902 03:00am 的事件。我的工作班次是 D(06:00 到 18:00)和 N(18:00 到 06:00)。我想将事件分配给班次日期时间值。

START DATETIME      END DATETIME
20150901 0600       20150902 0300

SHIFT  START  END
D     0600   1800
N     1800   0600

我应该得到的时间段是:

DATE     SHIFT    STARTTIME    ENDTIME
20150901 D        0600         1800
20150901 N        1800         0300

示例 2:一天分为四班,但一天从早上 06:00 开始(01:0600-1200、02:1200-1800、03:1800-2400、04:0000-0600)。活动从 20150901 08:00am 到 20150903 10:00am

START DATETIME      END DATETIME
20150901 0800       20150903 1000

SHIFT  START  END
01     0600   1159
02     1200   1759
03     1800   2359
04     0000   0559

我应该得到的时间段是:

DATE     SHIFT    STARTTIME   ENDTIME
20150901 01       0800        1200
20150901 02       1200        1800
20150901 03       1800        0000
20150901 04       0000        0600
20150902 01       0600        1200
20150902 02       1200        1800
20150902 03       1800        0000
20150902 04       0000        0600
20150903 01       0600        1000

示例 3:一天分为四班,但一天从早上 06:00 开始(01:0600-1200、02:1200-1800、03:1800-2400、04:0000-0600)。活动从 20150901 02:00am 到 20150902 10:00am

START DATETIME      END DATETIME
20150901 0200       20150902 1000

SHIFT  START  END
01     0600   1159
02     1200   1759
03     1800   2359
04     0000   0559

我应该得到的时间段是:

DATE     SHIFT    STARTTIME   ENDTIME
20150831 04       0200        0600
20150901 01       0800        1200
20150901 02       1200        1800
20150901 03       1800        0000
20150901 04       0000        0600
20150902 01       0600        1000

编辑1:添加我拥有的源代码。

编辑 2:添加示例 3 并更正 Matt Johnson 提出的一些观点

该代码适用于某些情况,但是当事件以某种方式超出轮班边界时我仍然遇到错误(例如,事件在第一次轮班之前开始)。

    public class slot
    {
        public DateTime date;
        public TimeSpan startHour;
        public TimeSpan endHour;
        public string shiftCode;
    }


    public List<slot> getSlots()
    {
        slot[] shifts = new slot[4];
        shifts[0] = new slot();
        shifts[0].startHour = new TimeSpan(06, 00, 00);
        shifts[0].endHour = new TimeSpan(12, 00, 00);   
        shifts[0].shiftCode = "01";
        shifts[1] = new slot();
        shifts[1].startHour = new TimeSpan(12, 00, 00);
        shifts[1].endHour = new TimeSpan(18, 00, 00);
        shifts[1].shiftCode = "02";
        shifts[2] = new slot();
        shifts[2].startHour = new TimeSpan(18, 00, 00);
        shifts[2].endHour = new TimeSpan(00, 00, 00);
        shifts[2].shiftCode = "03";
        shifts[3] = new slot();
        shifts[3].startHour = new TimeSpan(00, 00, 00);
        shifts[3].endHour = new TimeSpan(06, 00, 00);
        shifts[3].shiftCode = "04";

        DateTime startEvent = new DateTime(2015, 09, 01, 08, 00, 00);
        DateTime endEvent = new DateTime(2015, 09, 03, 10, 00, 00);

        int i = 0;
        //To find the starting shift for the event
        while (!(
                shifts[i].startHour <= shifts[i].endHour
                    ? (startEvent.TimeOfDay >= shifts[i].startHour && startEvent.TimeOfDay < shifts[i].endHour)
                    : (startEvent.TimeOfDay >= shifts[i].startHour || startEvent.TimeOfDay < shifts[i].endHour)
                ))
            i++;

        DateTime slotStart = startEvent;
        DateTime slotEnd = slotStart.Date + shifts[i].endHour;

        List<slot> slotList = new List<slot>();

        while(endEvent >= slotEnd)
        {
            slot newSlot = new slot();
            newSlot.date = slotStart;
            newSlot.startHour = slotStart.TimeOfDay;
            newSlot.endHour = shifts[i].endHour;
            newSlot.shiftCode = shifts[i].shiftCode;
            slotList.Add(newSlot);
            i++;

            if (i >= shifts.Length)
                i = 0;

            if (shifts[i].startHour < newSlot.endHour)
                slotStart = slotStart.Date.AddDays(1);

            slotEnd = slotStart.Date + shifts[i].endHour;
            slotStart = slotStart.Date + shifts[i].startHour;
        }

        slot lastSlot = new slot();
        lastSlot.date = slotStart;
        lastSlot.startHour = slotStart.TimeOfDay;
        lastSlot.endHour = endEvent.TimeOfDay;
        lastSlot.shiftCode = shifts[i].shiftCode;
        slotList.Add(lastSlot);

        return slotList;
    }
4

3 回答 3

1

一些东西:

  1. 您需要将日期增量与班次重置分开:

    你有:

    if (i >= shifts.Length)
    {
        i = 0;
        slotStart = slotStart.AddDays(1);
    }
    

    应该替换为:

    if (i >= shifts.Length)
    {
        i = 0;
    }
    
    if (shifts[i].startHour < newSlot.startHour)
    {
        slotStart = slotStart.Date.AddDays(1);
    }
    

    这是罪魁祸首。

  2. 在您开始时间的测试中,逻辑稍有偏差。它应该是:

    while (!(
        shifts[i].startHour <= shifts[i].endHour
            ? (startEvent.TimeOfDay >= shifts[i].startHour && startEvent.TimeOfDay < shifts[i].endHour)
            : (startEvent.TimeOfDay >= shifts[i].startHour || startEvent.TimeOfDay < shifts[i].endHour)
        ))
        i++;
    

    你写的只是略有不同。您曾经&&将第一个测试与真实部分合并,但是当与等式的其余部分混合时,这在逻辑上并不成立。

  3. 通常,您应该考虑调整代码以使用半开范围。而不是从 06:00 到 17:59 的转变,它应该从 06:00 到 18:00。然后在测试端点时使用独占运算符。换句话说start <= testValue < end。这有几个优点,包括易于减去以获得持续时间,并防止精度错误(因为 17:59:59.999 仍在范围内)。 注意:这一点基于编辑前的原始问题。.

  4. 通过使用 LINQ 而不是那些循环,您可能会省去很多麻烦。此外,您的命名约定与正常的 C# 样式不匹配。

  5. 在使用Noda Time类型(例如LocalDateLocalTime和)时,这些类型的问题往往更容易推理LocalDateTime。如果稍后您需要考虑时区、夏令时等 - 您将能够更好地映射到ZonedDateTime值、计算Duration值或投影到Instant值。

  6. 您是否考虑过当您没有一整天的轮班时间时该怎么办?您可能希望计算缺失的期间并将其显示为错误输出,或使用“未签名”班次代码。

于 2015-09-16T05:06:11.037 回答
0

为了帮助您,顶部循环可能类似于:

var currentTime = startTime;
while(currentTime < endTime) {
    var shift = FindShift(currentTime);
    if (shift.EndTime > endTime) {
        shift.EndTime = endTime;
    }
    Print(shift);
    currentTime = shift.EndTime;
}
于 2015-09-15T21:45:46.307 回答
0

这是我现在正在使用的结果代码。到目前为止,我用几个数据对其进行了测试,它的工作正常,即使在时间范围内也是如此。

我遵循了马特约翰逊给我的一些指示来让它发挥作用,所以谢谢马特。

需要考虑的是,轮班日从第一个轮班 StartHour 开始。这意味着如果班组从早上 6:00 开始,则当天的前几个小时属于前一天。

示例:一天分为四班。活动从 20150901 02:00am 到 20150902 04:00am。因此,即使根据班次日,它似乎开始于 20150901 并在 20150902 结束,它将是 20150931 和 20150901

START DATETIME      END DATETIME
20150901 0200       20150902 0400

SHIFT  START  END
01     0600   1159
02     1200   1759
03     1800   2359
04     0000   0559

我应该得到的时间段是:

DATE     SHIFT    STARTTIME   ENDTIME
20150831 04       0200        0600
20150901 01       0800        1200
20150901 02       1200        1800
20150901 03       1800        0000
20150901 04       0000        0400

源代码:

    public class Slot
    {
        public DateTime Date;
        public TimeSpan StartHour;
        public TimeSpan EndHour;
        public string ShiftCode;
    }

    public List<Slot> GetSlots()
    {
        //Shifts Array
        Slot[] shifts = new Slot[4];
        shifts[0] = new Slot();
        shifts[0].StartHour = new TimeSpan(06, 00, 00);
        shifts[0].EndHour = new TimeSpan(12, 00, 00);   
        shifts[0].ShiftCode = "01";
        shifts[1] = new Slot();
        shifts[1].StartHour = new TimeSpan(12, 00, 00);
        shifts[1].EndHour = new TimeSpan(18, 00, 00);
        shifts[1].ShiftCode = "02";
        shifts[2] = new Slot();
        shifts[2].StartHour = new TimeSpan(18, 00, 00);
        shifts[2].EndHour = new TimeSpan(24, 00, 00);
        shifts[2].ShiftCode = "03";
        shifts[3] = new Slot();
        shifts[3].StartHour = new TimeSpan(00, 00, 00);
        shifts[3].EndHour = new TimeSpan(06, 00, 00);
        shifts[3].ShiftCode = "04";

        //Event TimeStamps
        DateTime startEvent = new DateTime(2015, 09, 01, 02, 00, 00);
        DateTime endEvent = new DateTime(2015, 09, 03, 08, 00, 00);

        if (endEvent < startEvent)
            return null;

        int i = 0;
        //To find the starting shift for the event
        while (!(
                shifts[i].StartHour <= shifts[i].EndHour
                    ? (startEvent.TimeOfDay >= shifts[i].StartHour && startEvent.TimeOfDay < shifts[i].EndHour)
                    : (startEvent.TimeOfDay >= shifts[i].StartHour || startEvent.TimeOfDay < shifts[i].EndHour)
                ))
            i++;

        //to establish the date part of the datetime according to the starttime of the shift day
        startEvent = startEvent.AddTicks(-shifts[0].StartHour.Ticks).Date + startEvent.TimeOfDay;
        endEvent = endEvent.AddTicks(-shifts[0].StartHour.Ticks).Date + endEvent.TimeOfDay;

        DateTime slotStart = startEvent;
        DateTime slotEnd = slotStart.Date + shifts[i].EndHour;

        List<Slot> slotList = new List<Slot>();

        while (endEvent.Date > slotEnd.Date || (endEvent.Date == slotEnd.Date && !(
                shifts[i].StartHour <= shifts[i].EndHour
                    ? (endEvent.TimeOfDay >= shifts[i].StartHour && endEvent.TimeOfDay < shifts[i].EndHour)
                    : (endEvent.TimeOfDay >= shifts[i].StartHour || endEvent.TimeOfDay < shifts[i].EndHour)
               )))
        {
            Slot newSlot = new Slot();
            newSlot.Date = slotStart.Date;
            newSlot.StartHour = slotStart.TimeOfDay;
            newSlot.EndHour = shifts[i].EndHour;
            newSlot.ShiftCode = shifts[i].ShiftCode;
            slotList.Add(newSlot);
            i++;

            if (i >= shifts.Length)
            {
                i = 0;
                slotStart = slotStart.Date.AddDays(1);
            }

            slotEnd = slotStart.Date + shifts[i].EndHour;
            slotStart = slotStart.Date + shifts[i].StartHour;
        }

        Slot lastSlot = new Slot();
        lastSlot.Date = slotStart;
        lastSlot.StartHour = slotStart.TimeOfDay;
        lastSlot.EndHour = endEvent.TimeOfDay;
        lastSlot.ShiftCode = shifts[i].ShiftCode;
        slotList.Add(lastSlot);

        return slotList;
    }
于 2015-09-16T22:27:20.053 回答