C# 在两个列表中查找重叠日期并返回重叠期间

C# 在两个列表中查找重叠日期并返回重叠期间,c#,.net,linq,C#,.net,Linq,我有两个包含UTC日期的列表。要确定列表是否包含重叠日期,我将执行以下操作: list1.Where( x => list2.Any( y => x.StartDate < y.EndDate && y.StartDate < x.EndDate)); 我想返回: 1/1 10AM - 1/1 11AM 1/1 4PM - 1/1 5Pm 1/2 5AM - 1/2 8AM 日期永远不会为空 我认为取两个起点中的

我有两个包含UTC日期的列表。要确定列表是否包含重叠日期,我将执行以下操作:

list1.Where(
    x =>
    list2.Any(
    y =>
    x.StartDate < y.EndDate &&
    y.StartDate < x.EndDate));
我想返回:

1/1 10AM - 1/1 11AM
1/1 4PM - 1/1 5Pm
1/2 5AM - 1/2 8AM
日期永远不会为空


我认为取两个起点中的最大值和两个终点中的最小值是可行的,但考虑到以下
日期范围
类,我不确定这在语法上会是什么样子:

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

    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}
以及以下简单的比较功能:

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
查询将是O(n2),这可以在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();
    }
class日期跨度
{
公共日期时间开始日期;
公共日期时间结束日期;
公共日期跨度(日期时间开始,日期时间结束)
{
开始日期=开始;
结束日期=结束;
}
公共日期跨度(日期时间开始,整数持续时间)
{
开始日期=开始;
EndDate=开始时长(持续时间);
}
}
公共void TestStuff()
{
var dates1=new System.Collections.Generic.List();
日期1.添加(新的日期跨度(新的日期时间(2016,1,1,5,0,0,17));
日期1.添加(新的日期跨度(新的日期时间(2016,1,2,4,0,0,4));
var dates2=new System.Collections.Generic.List();
dates2.Add(新的日期跨度(新的日期时间(2016,1,1,10,0,0,1));
dates2.Add(新的日期跨度(新的日期时间(2016,1,1,16,0,0,1));
dates2.Add(新的日期跨度(新的日期时间(2016,1,2,5,0,0,17));
变量e=日期1。选择多个((日期跨度x)=>
{
var result=新列表();
foreach(dates2.Where(y=>x.StartDatedates2.Any(y=>x.StartDate
下面是基于您的逻辑构建的代码。我没有使用
where
任何组合,而是使用笛卡尔积,它更清楚地表示了您的逻辑。只需将
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))
                        };


}
struct-DateRange{
公共日期时间开始日期,结束日期;
公共重写字符串ToString(){
返回$“{StartDate}-{EndDate}”;
}
};
静态void Main(字符串[]参数){
var list1=新列表(){
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 DateRange(){StartDate=DateTime.Parse(“1/1/2016上午10点”),EndDate=DateTime.Parse(“1/1/2016上午11点”),
new DateRange(){StartDate=DateTime.Parse(“2016年1月1日下午4点”),EndDate=DateTime.Parse(“2016年1月1日下午5点”),
new DateRange(){StartDate=DateTime.Parse(“1/2/2016 5AM”),EndDate=DateTime.Parse(“1/2/2016 10PM”)}
};
var overlapList=来自列表1中的外部
从列表2中的内部
其中outer.StartDate
为了使代码更具可读性,我使用了
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}");
}
var q=
从列表1中的x开始
来自列表2中的y
让开始=最大值(x.开始日期,y.开始日期)
设end=Min(x.EndDate,y.EndDate)
从哪里开始
选择新{开始,结束};
foreach(q中的var项目)
{
WriteLine($“{item.start}-{item.end}”);
}

Max
Min
方法来自。

因此,结果中的最后一项是1/2 5AM-1/2 8AM,而不是1/2 5AM-1/2 10pm?请更正到8AM,因为它们在8AM后不再重叠。这是一个非常优雅的解决方案
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))
                        };


}
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}");
}