通过在主日期范围内使用多个日期范围来获得时间跨度

时间:2014-02-16 21:47:17

标签: c# datetime

我要求通过将主要的开始和结束日期时间与开始和结束日期时间的多个“忽略”对进行比较来找出“有效”时间跨度。

可能存在任何数量的这些“忽略”对,它们的范围可以介于初始开始和结束日期时间对之间,甚至完全覆盖它们。

示例输入和预期输出如下(现在使用简单的时间表示):

示例1

Main Start: 04:00 
Main End  : 14:00 

Ignore Pair 1: 03:00 - 06:00 
Ignore Pair 2: 05:00 - 09:00 
Ignore Pair 3: 12:00 - 13:00 

Expected Result: Timespan(4 'Valid' Hours)

示例2

Main Start: 04:00 
Main End  : 14:00 

Ignore Pair 1: 03:00 - 12:00

Expected Result: Timespan(2 'Valid' Hours)

示例3

Main Start: 04:00 
Main End  : 14:00 

Ignore Pair 1: 03:00 - 20:00

Expected Result: Timespan(0 'Valid' Hours)

示例4

Main Start: 04:00 
Main End  : 14:00 

Ignore Pair 1: 08:00 - 12:00

Expected Result: Timespan(6 'Valid' Hours)

道歉,如果有什么不合理的话,如果有任何需要进一步阐述,请告诉我。

1 个答案:

答案 0 :(得分:1)

这应该会给你一个有效范围的列表。这是第一个没有优化的简单划痕。代码应该用注释来解释。如果您想获得“有效”时间,只需在Console.WriteLine("{0} valid hours", valid.Sum(r => (r.End - r.Start).TotalHours));中添加结果中的范围 它是在假设下编码的,范围是有效的(开始< end)!

用于存储时间范围的类:

class TimeRange
{
    public DateTime Start { get; set; }
    public DateTime End { get; set; }

    public TimeRange ()
    {

    }
    public TimeRange(TimeSpan todayStart, TimeSpan todayEnd)
    {
        Start = DateTime.Today + todayStart;
        End = DateTime.Today + todayEnd;
    }
}

<强> Testcode:

static void Main(string[] args)
{
    var main = new TimeRange(new TimeSpan(4, 0, 0), new TimeSpan(14, 0, 0));
    var except = new List<TimeRange>{
        new TimeRange(new TimeSpan(3, 0, 0), new TimeSpan(6, 0, 0)),
        new TimeRange(new TimeSpan(5, 0, 0), new TimeSpan(9, 0, 0)),
        new TimeRange(new TimeSpan(12, 0, 0), new TimeSpan(13, 0, 0))
    };

    var valid = GetFreeSlots(main, except);
}

<强>算法:

private static List<TimeRange> GetFreeSlots(TimeRange main, List<TimeRange> except)
{
    // 1. ignore Ranges outside
    except = except.Where(e => main.Start < e.End && main.End > e.Start).ToList();

    // 2. shrink the main timerange from overlapping ranges
    while (true)
    {
        var x = except.FirstOrDefault(e => e.Start <= main.Start);
        if (x != null)
        {
            if (x.End >= main.End)
            {
                return new List<TimeRange>();
            }
            main.Start = x.End;
            except.Remove(x);
        }
        else
            break;
    }

    while (true)
    {
        var x = except.FirstOrDefault(e => e.End >= main.End);
        if (x != null)
        {
            main.End = x.Start;
            except.Remove(x);
        }
        else
            break;
    }

    if (!except.Any())
    {
        return new List<TimeRange> { main };
    }

    // 3. add range[start main to start of the 1. exception] to the list of valid ranges and shrink the main time range to start = end of the 1. exception and go through the procedure again                      
    except.OrderBy(e => e.Start);
    var valid = new List<TimeRange>{new TimeRange{Start = main.Start, End = except[0].Start}};
    main.Start = except[0].End;
    except.RemoveAt(0);
    return valid.Union(GetFreeSlots(main, except)).ToList();
}