C# 'invert' 日期时间列表

C# 'invert' list of DateTime

我有一个 DateTimes 列表,我需要 'invert'(找不到更好的词)这个列表。

public class Available
{
    public Available(DateTime startDate, DateTime endDate)
    {
        if (!startDate.Day.Equals(endDate.Day))
            throw new Exception("The start and end days are not equal.");

        this.StartDate = startDate;
        this.EndDate = endDate;
    }

    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

List<Available> availableTimes = new List<Available>()
{
    new Available(new DateTime(2015, 3, 16, 08, 00, 00), new DateTime(2015, 3, 16, 10, 00, 00)),
    new Available(new DateTime(2015, 3, 16, 12, 00, 00), new DateTime(2015, 3, 16, 14, 00, 00)),
    new Available(new DateTime(2015, 3, 16, 15, 00, 00), new DateTime(2015, 3, 16, 16, 00, 00)),
};

我需要将此可用时间列表转换为特定日期的阻塞时间列表,但我有点卡住了。在这种情况下,我需要:

2015-3-16 00:00 - 2015-3-16 08:00, 
2015-3-16 10:00 - 2015-3-16 12:00, 
2015-3-16 14:00 - 2015-3-16 15:00, 
2015-3-16 16:00 - 2015-3-16 23:59

有什么好主意吗?

如果您的列表总是 "sorted" 并且没有重叠时间,则算法应该不会太难。 "Global start/end time" 指的是您的示例的值 2015-3-16 00:002015-3-16 23:59

start = global start time
for each item in list:
    yield new (start, item.start)
    start = item.end
yield new (start, global end time)

将其翻译成 C# 方法留作练习。

如果您只想按日期对列表项进行排序,使用 link,这非常简单:

availableTimes = availableTimes.OrderByDescending(x => x.startDate).ToList();

Heinzi 的算法变成了 C#。假定可用间隔不重叠并已排序。

        DateTime firstDay = availableTimes[0].StartDate;
        DateTime previousTime = new DateTime(firstDay.Year, firstDay.Month, firstDay.Day, 0, 0, 0);
        List<Available> unavailableTimes = new List<Available>();

        foreach (Available available in availableTimes)
        {
            unavailableTimes.Add(new Available(previousTime, available.StartDate));
            previousTime = available.EndDate;
        }

        var dateTime = previousTime.Date;
        DateTime endDay = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 23, 59, 59);
        unavailableTimes.Add(new Available(previousTime, endDay));

您必须将 DateTime 连接到一维数组:

        var minBorder = new DateTime(2015, 3, 16, 00, 00, 00);
        var maxBorder = new DateTime(2015, 3, 16, 23, 59, 00);

        var times = new List<DateTime>();
        times.Add(minBorder);
        foreach (var at in availableTimes) {
            times.Add(at.StartDate);
            times.Add(at.EndDate);
        }
        times.Add(maxBorder);

结果:

然后按照您需要的时间间隔,如下所示:

    public static IList<Available> Invert(IList<DateTime> input) {
        var result = new List<Available>();
        for (var i = 0; i < input.Count; i += 2) {
            result.Add(
                new Available(input[i], input[i + 1])
            );
        }
        return result;
    }

试试这个,将您的 class 重命名为 DateTimeRange,因为它现在具有双重含义,包括被阻止,并且还必须假设 23:59 是午夜 -1 刻度。

using System;
using System.Collections.Generic;
using System.Linq;

public class DateTimeRange
{
    public DateTimeRange(DateTime startDate, DateTime endDate)
    {
        if (!startDate.Day.Equals(endDate.Day))
            throw new Exception("The start and end days are not equal.");

        this.StartDate = startDate;
        this.EndDate = endDate;
    }

    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}    



public class Program
{
    static List<DateTimeRange> availableTimes = new List<DateTimeRange>()
    {
        new DateTimeRange(new DateTime(2015, 3, 16, 08, 00, 00), new DateTime(2015, 3, 16, 10, 00, 00)),
        new DateTimeRange(new DateTime(2015, 3, 16, 12, 00, 00), new DateTime(2015, 3, 16, 14, 00, 00)),
        new DateTimeRange(new DateTime(2015, 3, 16, 15, 00, 00), new DateTime(2015, 3, 16, 16, 00, 00)),
    };

    private static IEnumerable<DateTimeRange> GetBlockedTimes(IEnumerable<DateTimeRange> ranges)
    {
        var min = ranges.Select(r => r.StartDate).Min().Date;
        var max = ranges.Select(r => r.EndDate).Max().AddDays(1).Date.AddTicks(-1);

        foreach(var range in ranges.OrderBy(r => r.StartDate))
        {
            yield return new DateTimeRange(min, range.StartDate);
            min = range.EndDate;
        }

        yield return new DateTimeRange(min, max);
    }

    public static void Main()
    {
        foreach(var item in GetBlockedTimes(availableTimes))
        {
            Console.WriteLine(item.StartDate + " - " + item.EndDate);
        }
    }
}

您应该注意边际案例的开始和结束时间范围。

List<DateTimeRange> availableTimes = new List<DateTimeRange>()
{
    new DateTimeRange(new DateTime(2015, 3, 16, 00, 00, 00), new DateTime(2015, 3, 16, 1, 00, 00)),
    new DateTimeRange(new DateTime(2015, 3, 16, 08, 00, 00), new DateTime(2015, 3, 16, 10, 00, 00)),
    new DateTimeRange(new DateTime(2015, 3, 16, 12, 00, 00), new DateTime(2015, 3, 16, 14, 00, 00)),
    new DateTimeRange(new DateTime(2015, 3, 16, 15, 00, 00), new DateTime(2015, 3, 16, 16, 00, 00)),
    new DateTimeRange(new DateTime(2015, 3, 16, 19, 00, 00), new DateTime(2015, 3, 16, 23, 59, 59)),
};
var gap = GetGapsForDay(availableTimes);




public IEnumerable<DateTimeRange> GetGapsForDay(List<DateTimeRange> ranges)
{
    var start = ranges.First().StartDate.Date;
    var end = ranges.First().StartDate.Date.AddDays(1).AddMinutes(-1);

    foreach(var item in ranges.OrderBy(i => i.StartDate))
    {
        if(start < item.StartDate)
            yield return new DateTimeRange(start, item.StartDate);

        start = item.EndDate;
    }
    if (ranges.Max(i => i.EndDate) < end)
        yield return new DateTimeRange(start, end);
}