在两个列表中找到重叠的日期并返回重叠的时间段

本文关键字:重叠 日期 时间段 返回 列表 两个 | 更新日期: 2023-09-27 18:03:33

我有两个包含UTC日期的列表。为了确定列表是否包含重叠的日期,我正在做以下操作:

list1.Where(
    x =>
    list2.Any(
    y =>
    x.StartDate < y.EndDate &&
    y.StartDate < x.EndDate));

是否有一种方法可以实际返回重叠的周期?这些列表本身是唯一的,因为list1不会包含重叠的日期,list2也不会包含重叠的日期。

例如,如果我有两个包含以下开始和结束日期时间的列表

list 1:
   1/1 5AM - 1/1 10PM
   1/2 4AM - 1/2 8AM
list 2:
   1/1 10AM - 1/1 11AM
   1/1 4PM - 1/1 5PM
   1/2 5AM - 10PM

我想返回:

1/1 10AM - 1/1 11AM
1/1 4PM - 1/1 5Pm
1/2 5AM - 1/2 8AM

日期永远不会为NULL。

我在想采取最大的2个开始和最小的2个结束将工作,但不确定如何看语法

在两个列表中找到重叠的日期并返回重叠的时间段

给定以下DateRange类:

public class DateRange
{
    public DateRange(DateTime startDate, DateTime endDate)
    {
        StartDate = startDate;
        EndDate = endDate;
    }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

和followingNaïve DateTime比较函数:

public static DateTime MinDate(DateTime first, DateTime second)
{
    return first < second ? first : second;
}
public static DateTime MaxDate(DateTime first, DateTime second)
{
    return first > second ? first : second;
}

可以使用以下Linq:

list1.SelectMany(x =>
    list2.Where(y => x.StartDate < y.EndDate && y.StartDate < x.EndDate)
         .Select(y => new { first = x, second = y })))
             // Here you will have:
             // {
             //     x = (1/1 5AM - 1/1 10PM), y = (1/1 10AM - 1/1 11AM),
             //     x = (1/1 5AM - 1/1 10PM), y = (1/1 4PM - 1/1 5PM),
             //     x = (1/2 4AM - 1/2 8AM), y = (1/2 5AM - 10PM)
             // }
    .Select(x => new DateRange(MaxDate(x.first.StartDate, x.second.StartDate), MinDate(x.first.EndDate, x.second.EndDate))
             // Here you will have:
             // {
             //     (1/1 10AM - 1/1 11AM),
             //     (1/1 4PM - 1/1 5PM),
             //     (1/2 5AM - 1/2 8AM)
             // }

注意,这种Linq查询将是O(n2)Lists排序时,这可以在O(n)中实现,使用类似于排序数组合并的算法。

用你的例子给我正确的答案。

    class DateSpan
    {
        public DateTime StartDate;
        public DateTime EndDate;
        public DateSpan(DateTime start, DateTime end)
        {
            StartDate = start;
            EndDate = end;
        }
        public DateSpan(DateTime start, int duration)
        {
            StartDate = start;
            EndDate = start.AddHours(duration);
        }
    }
    public void TestStuff()
    {
        var dates1 = new System.Collections.Generic.List<DateSpan>();
        dates1.Add(new DateSpan(new DateTime(2016, 1, 1, 5, 0, 0), 17));
        dates1.Add(new DateSpan(new DateTime(2016, 1, 2, 4, 0, 0), 4));
        var dates2 = new System.Collections.Generic.List<DateSpan>();
        dates2.Add(new DateSpan(new DateTime(2016, 1, 1, 10, 0, 0), 1));
        dates2.Add(new DateSpan(new DateTime(2016, 1, 1, 16, 0, 0), 1 ));
        dates2.Add(new DateSpan(new DateTime(2016, 1, 2, 5,0,0), 17 ));
        var e = dates1.SelectMany((DateSpan x) =>
        {
            var result = new List<DateSpan>();
            foreach (var o in dates2.Where(y => x.StartDate < y.StartDate && y.StartDate < x.EndDate).ToList())
            {
                result.Add(new DateSpan(new DateTime(Math.Max(x.StartDate.Ticks, o.StartDate.Ticks)), new DateTime(Math.Min(x.EndDate.Ticks, o.EndDate.Ticks))));
            }
            return result;
        });
        //var d = dates1.Where(x => dates2.Any(y => x.StartDate < y.StartDate && y.StartDate < x.EndDate)).ToList();
    }

下面是基于您的逻辑构建的代码。我没有使用whereany的组合,而是使用笛卡尔积,这样可以更清楚地表达你的逻辑。然后结合MinMax函数得到你想要的

struct DateRange {
    public DateTime StartDate, EndDate;
    public override string ToString() {
        return $"{StartDate} - {EndDate}";
    }
};
static void Main(string[] args) {
    var list1 = new List<DateRange>() {
        new DateRange() { StartDate = DateTime.Parse("1/1/2016 5AM"), EndDate = DateTime.Parse("1/1/2016 10PM") },
        new DateRange() { StartDate = DateTime.Parse("1/2/2016 4AM"), EndDate = DateTime.Parse("1/2/2016 8AM") }
    };
    var list2 = new List<DateRange>() {
        new DateRange() { StartDate = DateTime.Parse("1/1/2016 10AM"), EndDate = DateTime.Parse("1/1/2016 11AM")},
        new DateRange() { StartDate = DateTime.Parse("1/1/2016 4PM"), EndDate = DateTime.Parse("1/1/2016 5PM")},
        new DateRange() { StartDate = DateTime.Parse("1/2/2016 5AM"), EndDate = DateTime.Parse("1/2/2016 10PM")}
    };
    var overlapList = from outer in list1
                        from inner in list2
                        where outer.StartDate < inner.EndDate && inner.StartDate < outer.EndDate
                        select new DateRange() {
                            StartDate = new DateTime(Math.Max(outer.StartDate.Ticks, inner.StartDate.Ticks)),
                            EndDate = new DateTime(Math.Min(outer.EndDate.Ticks, inner.EndDate.Ticks))
                        };

}

我使用let子句是为了使代码更易于阅读:

var q = 
    from x in list1
    from y in list2
    let start = Max(x.StartDate, y.StartDate)
    let end = Min(x.EndDate, y.EndDate)
    where start < end
    select new { start, end };
foreach (var item in q)
{
    Console.WriteLine($"{item.start}-{item.end}");
}

MaxMin方法来自此答案