我有2个包含UTC日期的列表。要确定列表是否包含重叠日期,我将执行以下操作:
list1.Where(
x =>
list2.Any(
y =>
x.StartDate < y.EndDate &&
y.StartDate < x.EndDate));
有没有办法实际返回重叠期?这些列表本身是唯一的,因为list1不包含重叠日期,list2将不包含iteself中的重叠日期。
例如,如果我有2个列表包含以下开始和结束日期时间
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次结束的MIN会起作用,但不确定这在语法上会是什么样的
答案 0 :(得分:3)
给出以下DateRange
类:
public class DateRange
{
public DateRange(DateTime startDate, DateTime endDate)
{
StartDate = startDate;
EndDate = endDate;
}
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
以及以下的非常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
Lists
查询将是 O(n 2 ) 这可以使用类似于sorted array merging的算法在 O(n) 中进行排序。
答案 1 :(得分:0)
这给了我你的例子的正确答案。
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();
}
答案 2 :(得分:0)
以下是基于您的逻辑构建的代码。我没有使用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))
};
}
答案 3 :(得分:0)
我使用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
方法来自this answer。