按DayOfWeek工作时间日历迭代时间间隔

时间:2016-03-24 20:40:50

标签: c# algorithm datetime iterator intervals

我有一个字典,按周提供工作时间间隔,如MsProject提供的: enter image description here

字典具有以下类型:

new Dictionary<DayOfWeek, IEnumerable<Tuple<TimeSpan, TimeSpan>>>
{
    [DayOfWeek.Monday] = new[]
    {
       Tuple.Create(TimeSpan.FromHours(8), TimeSpan.FromHours(12)),    //8AM-12PM
       Tuple.Create(TimeSpan.FromHours(13), TimeSpan.FromHours(17))    //1PM-5PM
    },
    [DayOfWeek.Tuesday] = new[]
    {
       Tuple.Create(TimeSpan.FromHours(8), TimeSpan.FromHours(12)),    //8AM-12PM
       Tuple.Create(TimeSpan.FromHours(13), TimeSpan.FromHours(17))    //1PM-5PM
    },
...

我的目标是实现一个迭代器,产生日期时间间隔,从作为参数传递的datetime开始

IEnumerable<Tuple<DateTime, TimeSpan>> IterateTimeslots(DateTime startingFrom);

测试用例#1

字典中包含工作小时(晚上8点至12点),(下午1点至下午5点)的星期几和星期六,执行

  

IterateTimeslots(DateTime.Parse(&#34; 03/20/2016 12:00 AM&#34;))// Sunday

应该返回:

(03/21/2016 8AM, 4hrs)
(03/21/2016 1PM, 4hrs)
(03/22/2016 8AM, 4Hrs)
(03/22/2016 8AM, 4Hrs)
... (endless iteration)

测试用例#2(合并相邻区间): 字典可以表示第二天的间隔,如下所示:

[Monday] = (12AM-9AM),(9PM-12AM)     //Ending 12AM = TimeSpan.FromHours(24)
[Tuesday] = (12AM-9AM),(9PM-12AM)
...

迭代器应将相邻间隔合并为一个。所以对于startDate&#34; 03/20/2016 12 AM&#34;它将返回以下内容:

(03/21/2016 12AM, 9Hrs)
(03/21/2016 9PM, 12Hrs) //Intervals crosses to next day
(03/22/2016 9PM, 12Hrs) //Intervals crosses to next day
(03/23/2016 9PM, 12Hrs) //Intervals crosses to next day
... (endless enumeration)

测试用例#3(24/7): 24/7工作时间应使用TimeSpan = TimeSpan.MaxValue迭代单个时间间隔。所以字典将包含每周的每一天smth,如下所示:

[Sunday] = (12AM-12AM)
[Monday] = (12AM-12AM)
[Tuesday] = (12AM-12AM)
...

所以对于startDate&#34; 03/20/2016 8AM &#34;它应该只用一个项目返回枚举:

(03/20/2016 8AM, TimeSpan.MaxValue)

如何实施 IterateTimeslots 功能?任何帮助都是适用的

2 个答案:

答案 0 :(得分:0)

我对此进行了实验并提出了以下建议。抱歉,超过一半的代码是测试数据,但我添加了它,以便整个程序可以快速运行。另外,我还没有构建问题中准确提到的代码。在给定IEnumerable<Tuple<DateTime, TimeSpan>>作为输入的情况下,代码主要侧重于生成Dictionary<DayOfWeek, IEnumerable<Tuple<TimeSpan, TimeSpan>>>

public class Program
{
    public static void Main(string[] args)
    {

        Dictionary<DayOfWeek, IEnumerable<Tuple<TimeSpan, TimeSpan>>> workHours = new Dictionary<DayOfWeek, IEnumerable<Tuple<TimeSpan, TimeSpan>>>
        {
            {
                DayOfWeek.Sunday,  
                new[] {
                   Tuple.Create(TimeSpan.FromHours(8), TimeSpan.FromHours(12)),    //8AM-12PM
                   Tuple.Create(TimeSpan.FromHours(13), TimeSpan.FromHours(17))    //1PM-5PM
                }
            },
            {
                DayOfWeek.Monday,  
                new[] {
                   Tuple.Create(TimeSpan.FromHours(8), TimeSpan.FromHours(12)),    //8AM-12PM
                   Tuple.Create(TimeSpan.FromHours(13), TimeSpan.FromHours(17))    //1PM-5PM
                }
            },
            {
                DayOfWeek.Tuesday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(8), TimeSpan.FromHours(12)),    //8AM-12PM
                   Tuple.Create(TimeSpan.FromHours(13), TimeSpan.FromHours(17))    //1PM-5PM
                }
            }
            ,
            {
                DayOfWeek.Wednesday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(8), TimeSpan.FromHours(12)),    //8AM-12PM
                   Tuple.Create(TimeSpan.FromHours(13), TimeSpan.FromHours(17))    //1PM-5PM
                }
            }
            ,
            {
                DayOfWeek.Thursday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(8), TimeSpan.FromHours(12)),    //8AM-12PM
                   Tuple.Create(TimeSpan.FromHours(13), TimeSpan.FromHours(17))    //1PM-5PM
                }
            }
            ,
            {
                DayOfWeek.Friday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(8), TimeSpan.FromHours(12)),    //8AM-12PM
                   Tuple.Create(TimeSpan.FromHours(13), TimeSpan.FromHours(17))    //1PM-5PM
                }
            }
            ,
            {
                DayOfWeek.Saturday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(8), TimeSpan.FromHours(12)),    //8AM-12PM
                   Tuple.Create(TimeSpan.FromHours(13), TimeSpan.FromHours(17))    //1PM-5PM
                }
            }
        };


        Dictionary<DayOfWeek, IEnumerable<Tuple<TimeSpan, TimeSpan>>> workHours1 = new Dictionary<DayOfWeek, IEnumerable<Tuple<TimeSpan, TimeSpan>>>
        {
            {
                DayOfWeek.Sunday,  
                new[] {
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(9)),    //12AM-9AM
                   Tuple.Create(TimeSpan.FromHours(21), TimeSpan.FromHours(24))    //9PM-12AM
                }
            },
            {
                DayOfWeek.Monday,  
                new[] {
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(9)),    //12AM-9AM
                   Tuple.Create(TimeSpan.FromHours(21), TimeSpan.FromHours(24))    //9PM-12AM
                }
            },
            {
                DayOfWeek.Tuesday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(9)),    //12AM-9AM
                   Tuple.Create(TimeSpan.FromHours(21), TimeSpan.FromHours(24))    //9PM-12AM
                }
            }
            ,
            {
                DayOfWeek.Wednesday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(9)),    //12AM-9AM
                   Tuple.Create(TimeSpan.FromHours(21), TimeSpan.FromHours(24))    //9PM-12AM
                }
            }
            ,
            {
                DayOfWeek.Thursday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(9)),    //12AM-9AM
                   Tuple.Create(TimeSpan.FromHours(21), TimeSpan.FromHours(24))    //9PM-12AM
                }
            }
            ,
            {
                DayOfWeek.Friday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(9)),    //12AM-9AM
                   Tuple.Create(TimeSpan.FromHours(21), TimeSpan.FromHours(24))    //9PM-12AM
                }
            }
            ,
            {
                DayOfWeek.Saturday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(9)),    //12AM-9AM
                   Tuple.Create(TimeSpan.FromHours(21), TimeSpan.FromHours(24))    //9PM-12AM
                }
            }
        };


        Dictionary<DayOfWeek, IEnumerable<Tuple<TimeSpan, TimeSpan>>> workHours2 = new Dictionary<DayOfWeek, IEnumerable<Tuple<TimeSpan, TimeSpan>>>
        {
            {
                DayOfWeek.Sunday,  
                new[] {
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(24))    //12AM-12AM
                }
            },
            {
                DayOfWeek.Monday,  
                new[] {
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(24))    //12AM-12AM
                }
            },
            {
                DayOfWeek.Tuesday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(24))    //12AM-12AM
                }
            }
            ,
            {
                DayOfWeek.Wednesday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(24))    //12AM-12AM
                }
            }
            ,
            {
                DayOfWeek.Thursday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(24))    //12AM-12AM
                }
            }
            ,
            {
                DayOfWeek.Friday, 
                new[]{
                   Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(24))    //12AM-12AM
                }
            }
            ,
            {
                DayOfWeek.Saturday, 
                new[]{
                  Tuple.Create(TimeSpan.FromHours(0), TimeSpan.FromHours(24))    //12AM-12AM
                }
            }
        };


        DateTime currDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
        DateTime firstDateOfWeek = currDate.AddDays(-1 * (int)currDate.DayOfWeek);

        Console.WriteLine("First Date Of Week " + firstDateOfWeek);

        //this list will produce values of test case #1
        List<Tuple<DateTime, TimeSpan>> dailyWorkHours = workHours.SelectMany(kvp => kvp.Value, 
            (k, v) => Tuple.Create<DateTime, TimeSpan> ( 
                firstDateOfWeek.AddDays((int)k.Key).AddHours(v.Item1.Hours), 
                v.Item2 - v.Item1 
            )).ToList();

        foreach (var item in dailyWorkHours)
        {
            Console.WriteLine(item.Item1 + " " + item.Item2);
        }

        //handle Test case #2
        //Test case #3 is extension of test case #2
        List<Tuple<DateTime, TimeSpan>> finalWorkHours = new List<Tuple<DateTime, TimeSpan>>();

        finalWorkHours.Add(dailyWorkHours.First());

        //idea is to compare current item in dailyWorkHours 
        //with the previous item (that is currently in finalWorkHours)
        foreach (var item in dailyWorkHours)
        {
            if (dailyWorkHours.IndexOf(item) == 0)
            {
                continue;
            }

            Tuple<DateTime, TimeSpan> finalLast = finalWorkHours.Last();

            //handle Test case #2 merge last item in finalWorkHours with current item
            //if last item's time span added to last item's date and it equals to current item date
            if (DateTime.Compare(finalLast.Item1.AddHours(finalLast.Item2.TotalHours), item.Item1) == 0)
            {
                finalWorkHours.RemoveAt(finalWorkHours.Count - 1);
                finalWorkHours.Add(Tuple.Create(finalLast.Item1, finalLast.Item2.Add(item.Item2)));
            }
            else {
                finalWorkHours.Add(item);
            }
        }

        Console.WriteLine("Final work hours");

        foreach (var item in finalWorkHours)
        {
            Console.WriteLine(item.Item1 + " " + item.Item2.TotalHours + " hrs");
        }

        Console.ReadKey();
    }
}

答案 1 :(得分:0)

在这里(参见代码中的注释):

public static class Algorithms
{
    public static IEnumerable<Tuple<DateTime, TimeSpan>> IterateTimeslots(
        this Dictionary<DayOfWeek, IEnumerable<Tuple<TimeSpan, TimeSpan>>> input,
        DateTime startingFrom)
    {
        // Convert the input to ordered array of ranges containing
        // absolute offsets from the beginning of the week
        var ranges = input.SelectMany(e => e.Value.Select(v => new
        {
            Start = TimeSpan.FromDays((int)e.Key) + v.Item1,
            End = TimeSpan.FromDays((int)e.Key) + v.Item2
        }))
        .OrderBy(e => e.Start)
        .ToArray();
        // Corner case: empty input
        if (ranges.Length == 0) yield break;
        // Test case #2: merge adjacent ranges
        int first = 0, last = first;
        for (int i = 1; i < ranges.Length; i++)
        {
            if (ranges[i].Start > ranges[last].End)
                ranges[++last] = ranges[i];
            else
                ranges[last] = new { Start = ranges[last].Start, End = ranges[i].End };
        }
        var weekLength = TimeSpan.FromDays(7);
        if (ranges[last].End == weekLength && ranges[first].Start == TimeSpan.Zero)
        {
            // Test case #3: (24/7)
            if (last == first)
            {
                yield return Tuple.Create(startingFrom, TimeSpan.MaxValue);
                yield break;
            }
            ranges[last] = new { Start = ranges[last].Start, End = ranges[last].End + ranges[first].End };
            first++;
        }
        // Test case #1: Generate infinite intervals 
        var weekStart = new DateTime(startingFrom.Year, startingFrom.Month, startingFrom.Day,
            0, 0, 0, startingFrom.Kind) - TimeSpan.FromDays((int)startingFrom.DayOfWeek);
        for (int i = 0; ; i++)
        {
            if (i > last)
            {
                weekStart += weekLength;
                i = first;
            }
            var start = weekStart + ranges[i].Start;
            var end = weekStart + ranges[i].End;
            if (end > startingFrom)
            {
                if (start < startingFrom) start = startingFrom;
                yield return Tuple.Create(start, end - start);
            }
        }
    }
}