在两个列表中找到重叠的日期并返回重叠的时间段
本文关键字:重叠 日期 时间段 返回 列表 两个 | 更新日期: 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();
}
下面是基于您的逻辑构建的代码。我没有使用where
和any
的组合,而是使用笛卡尔积,这样可以更清楚地表达你的逻辑。然后结合Min
和Max
函数得到你想要的
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}");
}
Max
和Min
方法来自此答案