我要检测两个时间段是否重叠。 每个周期都有开始日期和结束日期。 我需要检测我的第一个时间段(A)是否与另一个时间段(B/C)重叠。 在我的例子中,如果B的起点等于A的终点,它们就不重叠(倒数也是) 我发现了以下几个案例:

实际上我是这样做的:

tStartA < tStartB && tStartB < tEndA //For case 1
OR
tStartA < tEndB && tEndB <= tEndA //For case 2
OR
tStartB < tStartA  && tEndB > tEndA //For case 3

(情况4在情况1或情况2的账户中被计入)

这是可行的,但似乎不是很有效。

首先,c#中有一个现有的类可以对这个(时间段)进行建模,类似于一个时间跨度,但是有一个固定的开始日期。

其次:是否已经有c#代码(如在DateTime类)可以处理这个问题?

第三:如果没有,你会用什么方法最快地进行比较?


当前回答

试试这个。此方法将确定(两个)日期跨度是否重叠,而不考虑方法输入参数的顺序。这也可以用于两个以上的日期跨度,通过单独检查每个日期跨度组合(例如,有3个日期跨度,对span2运行span1,对span3运行span2,对span3运行span1):

public static class HelperFunctions
{
    public static bool AreSpansOverlapping(Tuple<DateTime,DateTime> span1, Tuple<DateTime,DateTime> span2, bool includeEndPoints)
    {
        if (span1 == null || span2 == null)
        {
            return false;
        }
        else if ((new DateTime[] { span1.Item1, span1.Item2, span2.Item1, span2.Item2 }).Any(v => v == DateTime.MinValue))
        {
            return false;
        }
        else
        {
            if (span1.Item1 > span1.Item2)
            {
                span1 = new Tuple<DateTime, DateTime>(span1.Item2, span1.Item1);
            }
            if (span2.Item1 > span2.Item2)
            {
                span2 = new Tuple<DateTime, DateTime>(span2.Item2, span2.Item1);
            }

            if (includeEndPoints)
            {
                return 
                ((
                    (span1.Item1 <= span2.Item1 && span1.Item2 >= span2.Item1) 
                    || (span1.Item1 <= span2.Item2 && span1.Item2 >= span2.Item2)
                ) || (
                    (span2.Item1 <= span1.Item1 && span2.Item2 >= span1.Item1) 
                    || (span2.Item1 <= span1.Item2 && span2.Item2 >= span1.Item2)
                ));
            }
            else
            {
                return 
                ((
                    (span1.Item1 < span2.Item1 && span1.Item2 > span2.Item1) 
                    || (span1.Item1 < span2.Item2 && span1.Item2 > span2.Item2)
                ) || (
                    (span2.Item1 < span1.Item1 && span2.Item2 > span1.Item1) 
                    || (span2.Item1 < span1.Item2 && span2.Item2 > span1.Item2)
                ) || (
                    span1.Item1 == span2.Item1 && span1.Item2 == span2.Item2
                ));
            }
        }
    }
}

测试:

static void Main(string[] args)
{  
    Random r = new Random();

    DateTime d1;
    DateTime d2;
    DateTime d3;
    DateTime d4;

    for (int i = 0; i < 100; i++)
    {
        d1 = new DateTime(2012,1, r.Next(1,31));
        d2 = new DateTime(2012,1, r.Next(1,31));
        d3 = new DateTime(2012,1, r.Next(1,31));
        d4 = new DateTime(2012,1, r.Next(1,31));

        Console.WriteLine("span1 = " + d1.ToShortDateString() + " to " + d2.ToShortDateString());
        Console.WriteLine("span2 = " + d3.ToShortDateString() + " to " + d4.ToShortDateString());
        Console.Write("\t");
        Console.WriteLine(HelperFunctions.AreSpansOverlapping(
            new Tuple<DateTime, DateTime>(d1, d2),
            new Tuple<DateTime, DateTime>(d3, d4),
            true    //or use False, to ignore span's endpoints
            ).ToString());
        Console.WriteLine();
    }

    Console.WriteLine("COMPLETE");
    System.Console.ReadKey();
}

其他回答

检查这个简单的方法(建议把这个方法放在你的dateUtility中

public static bool isOverlapDates(DateTime dtStartA, DateTime dtEndA, DateTime dtStartB, DateTime dtEndB)
        {
            return dtStartA < dtEndB && dtStartB < dtEndA;
        }

CodeProject上有一个很棒的库,评论也很好:http://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET

这个库做了很多关于重叠、交叉等方面的工作。它太大了,不能全部复制/粘贴,但我会看看哪些具体的部分对你有用。

简单检查两个时间段是否重叠:

bool overlap = a.start < b.end && b.start < a.end;

或者在你的代码中:

bool overlap = tStartA < tEndB && tStartB < tEndA;

(如果你改变了主意,想说两个刚刚接触的周期重叠,可以用<=代替<。)

自定义间隔树结构如何?您必须稍微调整一下,以定义在定义域中两个间隔“重叠”的含义。

这个问题可能会帮助你在c#中找到一个现成的间隔树实现。

我不相信框架本身有这个类。也许是第三方库……

但是为什么不创建一个Period值对象类来处理这种复杂性呢?这样可以确保其他约束,比如验证开始日期时间和结束日期时间。喜欢的东西:

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

namespace Whatever.Domain.Timing {
    public class Period {
        public DateTime StartDateTime {get; private set;}
        public DateTime EndDateTime {get; private set;}

        public Period(DateTime StartDateTime, DateTime EndDateTime) {
            if (StartDateTime > EndDateTime)
                throw new InvalidPeriodException("End DateTime Must Be Greater Than Start DateTime!");
            this.StartDateTime = StartDateTime;
            this.EndDateTime = EndDateTime;
        }


        public bool Overlaps(Period anotherPeriod){
            return (this.StartDateTime < anotherPeriod.EndDateTime && anotherPeriod.StartDateTime < this.EndDateTime)
        }

        public TimeSpan GetDuration(){
            return EndDateTime - StartDateTime;
        }

    }

    public class InvalidPeriodException : Exception {
        public InvalidPeriodException(string Message) : base(Message) { }    
    }
}

这样你就可以单独比较每个时期……