关于日期时间:确定两个日期范围是否重叠

Determine Whether Two Date Ranges Overlap

给定两个日期范围,确定这两个日期范围是否重叠的最简单或最有效的方法是什么?

例如,假设我们有由日期时间变量StartDate1EndDate1StartDate2EndDate2表示的范围。


(开始a<=结束b)和(结束>=开始b)

证明:让条件A表示日期范围A完全在日期范围B之后_ |---- DateRange A ------|
|---Date Range B -----| _
(如果StartA > EndB为真)

假设条件B表示日期范围A完全在日期范围B之前|---- DateRange A -----| _
_ |---Date Range B ----|
(如果EndA < StartB为真)

如果a和b都不是真的,则存在重叠。-(如果一个范围不是完全在另一个范围之后,也不是完全比另一个先,然后它们必须重叠。)

现在,德摩根的一条法律规定:

Not (A Or B)<=>Not A And Not B

即:(StartA <= EndB) and (EndA >= StartB)

注意:这包括边缘完全重叠的条件。如果你想排除这一点,将>=操作符改为><=改为<

注意事项2.多亏了@baodad,看看这个博客,实际重叠最少的是:_endA-startAendA - startBendB-startAendB - startB

(StartA <= EndB) and (EndA >= StartB)(StartA <= EndB) and (StartB <= EndA)

注意事项3.多亏了@tomosius,一个较短的版本显示:DateRangesOverlap = max(start1, start2) < min(end1, end2)这实际上是一个较长实现的语法快捷方式,其中包括额外的检查,以验证开始日期是在结束日期或之前。由此得出:

如果开始日期和结束日期可能有误,即如果可能是startA > endAstartB > endB的话,那么您还必须检查它们是否正确,这样就意味着您必须添加两个额外的有效性规则:(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB)或:(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB)或者,(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB))或:(Max(StartA, StartB) <= Min(EndA, EndB)

但是要实现Min()Max(),必须编写代码(使用C三元表示简洁):(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)


我认为,在以下情况下,这两个范围重叠就足够了:

1
(StartDate1 <= EndDate2) and (StartDate2 <= EndDate1)


本文.NET的时段库通过枚举时段关系来描述两个时段的关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ------------------------------------------------------------------------
public enum PeriodRelation
{
    After,
    StartTouching,
    StartInside,
    InsideStartTouching,
    EnclosingStartTouching,
    Enclosing,
    EnclosingEndTouching,
    ExactMatch,
    Inside,
    InsideEndTouching,
    EndInside,
    EndTouching,
    Before,
} // enum PeriodRelation

enter image description here


对于时间关系(或任何其他间隔关系)的推理,考虑Allen的间隔代数。它描述了两个区间之间可能存在的13种关系。你可以找到其他参考资料-"艾伦间隔"似乎是一个有效的搜索词。您还可以在snodgrass开发的面向时间的SQL应用程序(网址:url)中找到有关这些操作的信息,在date、darwen和lorentzos时间数据和关系模型(2002年)或时间和关系理论:关系模型和SQL中的时间数据库(2014年;实际上是T的第二版)中找到这些操作的信息。D&P;RM)。

简短的(ish)回答是:给定两个日期间隔AB,组件.start.end和约束.start <= .end,如果:

1
A.end >= B.start AND A.start <= B.end

您可以调整>=><=<的使用,以满足您对重叠程度的要求。

埃里克评论:

You can only get 13 if you count things funny... I can get"15 possible relations that two intervals can have" when I go crazy with it. By sensible counting, I get only six, and if you throw out caring whether A or B comes first, I get only three (no intersect, partially intersect, one wholly within other). 15 goes like this: [before:before, start, within, end, after], [start:start, within, end, after], [within:within, end, after], [end:end, after], [after:after].

我认为您不能计算"before:before"和"after:after"这两个条目。如果你把一些关系和它们的反义词等同起来,我可以看到7个条目(见参考的维基百科网址中的图表;它有7个条目,其中6个具有不同的反义词,equals没有明显的反义词)。三个是否合理取决于你的要求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
----------------------|-------A-------|----------------------
    |----B1----|
           |----B2----|
               |----B3----|
               |----------B4----------|
               |----------------B5----------------|
                      |----B6----|
----------------------|-------A-------|----------------------
                      |------B7-------|
                      |----------B8-----------|
                         |----B9----|
                         |----B10-----|
                         |--------B11--------|
                                      |----B12----|
                                         |----B13----|
----------------------|-------A-------|----------------------


如果还应计算重叠本身,则可以使用以下公式:

1
2
3
4
overlap = max(0, min(EndDate1, EndDate2) - max(StartDate1, StartDate2))
if (overlap > 0) {
    ...
}


只要确保特定范围更早开始,就可以大大简化检查基于范围之间相互关系的多个条件的所有解决方案!如果有必要,您可以通过在前面交换范围来确保第一个范围更早(或同时)开始。

然后,如果另一个范围开始小于或等于第一个范围结束(如果范围包括开始时间和结束时间),或者小于(如果范围包括开始时间和结束时间),则可以检测重叠。

假设两端都包含,只有四种可能性,其中一种是不重叠的:

1
2
3
4
5
|----------------------|        range 1
|--->                           range 2 overlap
 |--->                          range 2 overlap
                       |--->    range 2 overlap
                        |--->   range 2 no overlap

范围2的端点未进入该范围。所以,在伪代码中:

1
2
3
4
5
6
def doesOverlap (r1, r2):
    if r1.s > r2.s:
        swap r1, r2
    if r2.s > r1.e:
        return false
    return true

这可以进一步简化为:

1
2
3
4
def doesOverlap (r1, r2):
    if r1.s > r2.s:
        swap r1, r2
    return r2.s <= r1.e

如果范围在开始时是包含的,在结束时是独占的,则只需在第二个if语句中用>=替换>(对于第一个代码段:在第二个代码段中,使用<而不是<=):

1
2
3
4
5
|----------------------|        range 1
|--->                           range 2 overlap
 |--->                          range 2 overlap
                       |--->    range 2 no overlap
                        |--->   range 2 no overlap

您极大地限制了必须进行的检查的数量,因为通过确保范围1从不在范围2之后开始,您可以提前删除一半的问题空间。


这里还有另一个使用JavaScript的解决方案。我的解决方案的特点:

  • 将空值处理为无穷大
  • 假设下界是包含的,上界是独占的。
  • 附带一系列测试

测试是基于整数的,但是由于Javascript中的日期对象是可比较的,所以您也可以只放入两个日期对象。或者你可以输入毫秒时间戳。

代码:

1
2
3
4
5
6
7
8
/**
 * Compares to comparable objects to find out whether they overlap.
 * It is assumed that the interval is in the format [from,to) (read: from is inclusive, to is exclusive).
 * A null value is interpreted as infinity
 */
function intervalsOverlap(from1, to1, from2, to2) {
    return (to2 === null || from1 < to2) && (to1 === null || to1 > from2);
}

测验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
describe('', function() {
    function generateTest(firstRange, secondRange, expected) {
        it(JSON.stringify(firstRange) + ' and ' + JSON.stringify(secondRange), function() {
            expect(intervalsOverlap(firstRange[0], firstRange[1], secondRange[0], secondRange[1])).toBe(expected);
        });
    }

    describe('no overlap (touching ends)', function() {
        generateTest([10,20], [20,30], false);
        generateTest([20,30], [10,20], false);

        generateTest([10,20], [20,null], false);
        generateTest([20,null], [10,20], false);

        generateTest([null,20], [20,30], false);
        generateTest([20,30], [null,20], false);
    });

    describe('do overlap (one end overlaps)', function() {
        generateTest([10,20], [19,30], true);
        generateTest([19,30], [10,20], true);

        generateTest([10,20], [null,30], true);
        generateTest([10,20], [19,null], true);
        generateTest([null,30], [10,20], true);
        generateTest([19,null], [10,20], true);
    });

    describe('do overlap (one range included in other range)', function() {
        generateTest([10,40], [20,30], true);
        generateTest([20,30], [10,40], true);

        generateTest([10,40], [null,null], true);
        generateTest([null,null], [10,40], true);
    });

    describe('do overlap (both ranges equal)', function() {
        generateTest([10,20], [10,20], true);

        generateTest([null,20], [null,20], true);
        generateTest([10,null], [10,null], true);
        generateTest([null,null], [null,null], true);
    });
});

与Karma&Jasmine&Phantomjs一起运行时的结果:

PhantomJS 1.9.8 (Linux): Executed 20 of 20 SUCCESS (0.003 secs / 0.004 secs)


我愿意

1
StartDate1.IsBetween(StartDate2, EndDate2) || EndDate1.IsBetween(StartDate2, EndDate2)

其中IsBetween类似于

1
2
3
    public static bool IsBetween(this DateTime value, DateTime left, DateTime right) {
        return (value > left && value < right) || (value < left && value > right);
    }


我知道这被标记为语言不可知论,但对于所有在Java中实现的人来说:不要重新发明轮子,使用JoDA时间。

http://joda time.sourceforge.net/api release/org/joda/time/base/abstractinterval.html重叠(org.joda.time.readableinterval)


enter image description here

以下是实现魔力的代码:

1
2
3
4
 var isOverlapping =  ((A == null || D == null || A <= D)
            && (C == null || B == null || C <= B)
            && (A == null || B == null || A <= B)
            && (C == null || D == null || C <= D));

在哪里?

  • A-1启动
  • B->一端
  • C>2启动
  • D>2

证明?查看此测试控制台代码注册表。


这是我在Java中的解决方案,它也适用于无界的时间间隔。

1
2
3
4
5
6
private Boolean overlap (Timestamp startA, Timestamp endA,
                         Timestamp startB, Timestamp endB)
{
    return (endB == null || startA == null || !startA.after(endB))
        && (endA == null || startB == null || !endA.before(startB));
}


这里发布的解决方案不适用于所有重叠的范围…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
----------------------|-------A-------|----------------------
    |----B1----|
           |----B2----|
               |----B3----|
               |----------B4----------|
               |----------------B5----------------|
                      |----B6----|
----------------------|-------A-------|----------------------
                      |------B7-------|
                      |----------B8-----------|
                         |----B9----|
                         |----B10-----|
                         |--------B11--------|
                                      |----B12----|
                                         |----B13----|
----------------------|-------A-------|----------------------

我的工作解决方案是:

1
2
3
4
5
6
7
AND (
  ('start_date' BETWEEN STARTDATE AND ENDDATE) -- caters for inner and end date outer
  OR
  ('end_date' BETWEEN STARTDATE AND ENDDATE) -- caters for inner and start date outer
  OR
  (STARTDATE BETWEEN 'start_date' AND 'end_date') -- only one needed for outer range where dates are inside.
)

这是我使用moment.js的javascript解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Current row dates
var dateStart = moment("2014-08-01","YYYY-MM-DD");
var dateEnd = moment("2014-08-30","YYYY-MM-DD");

// Check with dates above
var rangeUsedStart = moment("2014-08-02","YYYY-MM-DD");
var rangeUsedEnd = moment("2014-08-015","YYYY-MM-DD");

// Range covers other ?
if((dateStart <= rangeUsedStart) && (rangeUsedEnd <= dateEnd)) {
    return false;
}
// Range intersects with other start ?
if((dateStart <= rangeUsedStart) && (rangeUsedStart <= dateEnd)) {
    return false;
}
// Range intersects with other end ?
if((dateStart <= rangeUsedEnd) && (rangeUsedEnd <= dateEnd)) {
    return false;
}

// All good
return true;

the simplest

最简单的方法是使用精心设计的专用库进行日期时间工作。

1
someInterval.overlaps( anotherInterval )

java.time&threeten额外

业务中最好的是在Java 8和之后构建的EDCOX1 0框架。再加上用附加类来补充java.time的threeten额外项目,特别是我们在这里需要的Interval类。

对于这个问题上的language-agnostic标记,两个项目的源代码都可以在其他语言中使用(注意它们的许可证)。

Interval

org.threeten.extra.Interval类很方便,但需要日期-时间时刻(java.time.Instant对象)而不是仅日期值。因此,我们继续使用一天中的第一个时间(UTC)来表示日期。

1
2
Instant start = Instant.parse("2016-01-01T00:00:00Z" );
Instant stop = Instant.parse("2016-02-01T00:00:00Z" );

创建一个Interval来表示这段时间。

1
Interval interval_A = Interval.of( start , stop );

我们还可以定义一个具有起始时刻加上一个DurationInterval

1
2
Instant start_B = Instant.parse("2016-01-03T00:00:00Z" );
Interval interval_B = Interval.of( start_B , Duration.of( 3 , ChronoUnit.DAYS ) );

比较重叠的测试是很容易的。

1
Boolean overlaps = interval_A.overlaps( interval_B );

您可以将一个Interval与另一个IntervalInstant进行比较:

  • abuts
  • contains
  • encloses
  • equals
  • isAfter
  • isBefore
  • overlaps

所有这些都使用Half-Open方法来定义一个时间跨度,其中开始是包含的,结束是排他的。


如果您使用的日期范围尚未结束(仍在进行中),例如未设置enddate='0000-00-00'您不能在两者之间使用,因为0000-00-00不是有效的日期!

我使用了这个解决方案:

1
2
3
4
(Startdate BETWEEN '".$startdate2."' AND '".$enddate2."')  //overlap: starts between start2/end2
OR (Startdate < '".$startdate2."'
  AND (enddate = '0000-00-00' OR enddate >= '".$startdate2."')
) //overlap: starts before start2 and enddate not set 0000-00-00 (still on going) or if enddate is set but higher then startdate2

如果startdate2高于enddate,则没有重叠!


这是@charles bretana完美答案的延伸。

然而,答案并不能区分开、闭和半开(或半闭)间隔。

例1:A、B为闭合间隔

1
2
3
4
5
6
7
8
9
A = [StartA, EndA]
B = [StartB, EndB]

                         [---- DateRange A ------]   (True if StartA > EndB)
[--- Date Range B -----]                          


[---- DateRange A -----]                             (True if EndA < StartB)
                         [--- Date Range B ----]

重叠iff:(StartA <= EndB) and (EndA >= StartB)

案例2:A、B为开放区间

1
2
3
4
5
6
7
8
A = (StartA, EndA)
B = (StartB, EndB)

                         (---- DateRange A ------)   (True if StartA >= EndB)
(--- Date Range B -----)                          

(---- DateRange A -----)                             (True if EndA <= StartB)
                         (--- Date Range B ----)

重叠iff:(StartA < EndB) and (EndA > StartB)

案例3:A,B右开

1
2
3
4
5
6
7
8
A = [StartA, EndA)
B = [StartB, EndB)

                         [---- DateRange A ------)   (True if StartA >= EndB)
[--- Date Range B -----)                          

[---- DateRange A -----)                             (True if EndA <= StartB)
                         [--- Date Range B ----)

重叠条件:(StartA < EndB) and (EndA > StartB)

案例4:A,B左开

1
2
3
4
5
6
7
8
A = (StartA, EndA]
B = (StartB, EndB]

                         (---- DateRange A ------]   (True if StartA >= EndB)
(--- Date Range B -----]                          

(---- DateRange A -----]                             (True if EndA <= StartB)
                         (--- Date Range B ----]

重叠条件:(StartA < EndB) and (EndA > StartB)

案例5:A右开,B关

1
2
3
4
5
6
7
8
9
A = [StartA, EndA)
B = [StartB, EndB]

                         [---- DateRange A ------)    (True if StartA > EndB)
[--- Date Range B -----]                          


[---- DateRange A -----)                              (True if EndA <= StartB)  
                         [--- Date Range B ----]

重叠条件:(StartA <= EndB) and (EndA > StartB)

等。。。

最后,两个区间重叠的一般条件是

(SARTA <?)?endb)和(enda>??

在哪里??在两个包含的端点之间进行比较时,将严格不等式转化为非严格不等式。


答案对我来说太简单了,所以我创建了一个更通用的动态SQL语句,它检查一个人是否有重叠的日期。

1
2
3
4
5
6
7
8
9
10
SELECT DISTINCT T1.EmpID
FROM Table1 T1
INNER JOIN Table2 T2 ON T1.EmpID = T2.EmpID
    AND T1.JobID <> T2.JobID
    AND (
        (T1.DateFrom >= T2.DateFrom AND T1.dateFrom <= T2.DateTo)
        OR (T1.DateTo >= T2.DateFrom AND T1.DateTo <= T2.DateTo)
        OR (T1.DateFrom < T2.DateFrom AND T1.DateTo IS NULL)
    )
    AND NOT (T1.DateFrom = T2.DateFrom)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public static class NumberExtensionMethods
    {
        public static Boolean IsBetween(this Int64 value, Int64 Min, Int64 Max)
        {
            if (value >= Min && value <= Max) return true;
            else return false;
        }

        public static Boolean IsBetween(this DateTime value, DateTime Min, DateTime Max)
        {
            Int64 numricValue = value.Ticks;
            Int64 numericStartDate = Min.Ticks;
            Int64 numericEndDate = Max.Ticks;

            if (numricValue.IsBetween(numericStartDate, numericEndDate) )
            {
                return true;
            }

            return false;
        }
    }

public static Boolean IsOverlap(DateTime startDate1, DateTime endDate1, DateTime startDate2, DateTime endDate2)
        {
            Int64 numericStartDate1 = startDate1.Ticks;
            Int64 numericEndDate1 = endDate1.Ticks;
            Int64 numericStartDate2 = startDate2.Ticks;
            Int64 numericEndDate2 = endDate2.Ticks;

            if (numericStartDate2.IsBetween(numericStartDate1, numericEndDate1) ||
                numericEndDate2.IsBetween(numericStartDate1, numericEndDate1) ||
                numericStartDate1.IsBetween(numericStartDate2, numericEndDate2) ||
                numericEndDate1.IsBetween(numericStartDate2, numericEndDate2))
            {
                return true;
            }

            return false;
        }


if (IsOverlap(startdate1, enddate1, startdate2, enddate2))
            {
                Console.WriteLine("IsOverlap");
            }


这里有一个在本地有用的通用方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    // Takes a list and returns all records that have overlapping time ranges.
    public static IEnumerable<T> GetOverlappedTimes<T>(IEnumerable<T> list, Func<T, bool> filter, Func<T,DateTime> start, Func<T, DateTime> end)
    {
        // Selects all records that match filter() on left side and returns all records on right side that overlap.
        var overlap = from t1 in list
                      where filter(t1)
                      from t2 in list
                      where !object.Equals(t1, t2) // Don't match the same record on right side.
                      let in1 = start(t1)
                      let out1 = end(t1)
                      let in2 = start(t2)
                      let out2 = end(t2)
                      where in1 <= out2 && out1 >= in2
                      let totover = GetMins(in1, out1, in2, out2)
                      select t2;

        return overlap;
    }

    public static void TestOverlap()
    {
        var tl1 = new TempTimeEntry() { ID = 1, Name ="Bill", In ="1/1/08 1:00pm".ToDate(), Out ="1/1/08 4:00pm".ToDate() };
        var tl2 = new TempTimeEntry() { ID = 2, Name ="John", In ="1/1/08 5:00pm".ToDate(), Out ="1/1/08 6:00pm".ToDate() };
        var tl3 = new TempTimeEntry() { ID = 3, Name ="Lisa", In ="1/1/08 7:00pm".ToDate(), Out ="1/1/08 9:00pm".ToDate() };
        var tl4 = new TempTimeEntry() { ID = 4, Name ="Joe", In ="1/1/08 3:00pm".ToDate(), Out ="1/1/08 8:00pm".ToDate() };
        var tl5 = new TempTimeEntry() { ID = 1, Name ="Bill", In ="1/1/08 8:01pm".ToDate(), Out ="1/1/08 8:00pm".ToDate() };
        var list = new List<TempTimeEntry>() { tl1, tl2, tl3, tl4, tl5 };
        var overlap = GetOverlappedTimes(list, (TempTimeEntry t1)=>t1.ID==1, (TempTimeEntry tIn) => tIn.In, (TempTimeEntry tOut) => tOut.Out);

        Console.WriteLine("
Records overlap:");
        foreach (var tl in overlap)
            Console.WriteLine("Name:{0} T1In:{1} T1Out:{2}", tl.Name, tl.In, tl.Out);
        Console.WriteLine("Done");

        /*  Output:
            Records overlap:
            Name:Joe T1In:1/1/2008 3:00:00 PM T1Out:1/1/2008 8:00:00 PM
            Name:Lisa T1In:1/1/2008 7:00:00 PM T1Out:1/1/2008 9:00:00 PM
            Done
         */
    }


在我看来,最简单的方法是比较enddate1是否早于startdate2,enddate2是否早于startdate1。

当然,如果您考虑的是开始日期总是在结束日期之前的间隔。


在Microsoft SQL Server中-SQL函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
CREATE FUNCTION IsOverlapDates
(
    @startDate1 as datetime,
    @endDate1 as datetime,
    @startDate2 as datetime,
    @endDate2 as datetime
)
RETURNS int
AS
BEGIN
DECLARE @Overlap as int
SET @Overlap = (SELECT CASE WHEN  (
        (@startDate1 BETWEEN @startDate2 AND @endDate2) -- caters for inner and end date outer
        OR
        (@endDate1 BETWEEN @startDate2 AND @endDate2) -- caters for inner and start date outer
        OR
        (@startDate2 BETWEEN @startDate1 AND @endDate1) -- only one needed for outer range where dates are inside.
        ) THEN 1 ELSE 0 END
    )
    RETURN @Overlap

END
GO

--Execution of the above code
DECLARE @startDate1 as datetime
DECLARE @endDate1 as datetime
DECLARE @startDate2 as datetime
DECLARE @endDate2 as datetime
DECLARE @Overlap as int
SET @startDate1 = '2014-06-01 01:00:00'
SET @endDate1 =   '2014-06-01 02:00:00'
SET @startDate2 = '2014-06-01 01:00:00'
SET @endDate2 =   '2014-06-01 01:30:00'

SET @Overlap = [dbo].[IsOverlapDates]  (@startDate1, @endDate1, @startDate2, @endDate2)

SELECT Overlap = @Overlap

使用Java UTIL.DATE,在这里我做了什么。

1
2
3
4
5
6
7
8
9
10
    public static boolean checkTimeOverlaps(Date startDate1, Date endDate1, Date startDate2, Date endDate2)
    {
        if (startDate1 == null || endDate1 == null || startDate2 == null || endDate2 == null)
           return false;

        if ((startDate1.getTime() <= endDate2.getTime()) && (startDate2.getTime() <= endDate1.getTime()))
           return true;

        return false;
    }

我有这样一种情况,我们有日期而不是日期时间,日期只能在开始/结束时重叠。下面的例子:

enter image description here

(绿色是当前间隔,蓝色块是有效间隔,红色块是重叠间隔)。

我将Ian Nelson的答案改编为以下解决方案:

1
2
   (startB <= startA && endB > startA)
|| (startB >= startA && startB < endA)

这匹配所有重叠情况,但忽略允许的重叠情况。


@bretana给出的数学解很好,但忽略了两个具体细节:

  • 闭合或半开放间隔的方面
  • 空间隔
  • 关于区间边界的封闭或开放状态,@bretana的解对封闭区间有效。

    (StartA <= EndB) and (EndA >= StartB)

    可将半开间隔重写为:

    (StartA < EndB) and (EndA > StartB)

    由于开放间隔边界不属于定义的间隔值范围,因此需要进行此更正。

    关于空间隔,这里上面显示的关系不成立。定义中不包含任何有效值的空间隔必须作为特殊情况处理。我通过这个例子演示了我的Java时间库Time4J:

    1
    2
    3
    4
    5
    MomentInterval a = MomentInterval.between(Instant.now(), Instant.now().plusSeconds(2));
    MomentInterval b = a.collapse(); // make b an empty interval out of a

    System.out.println(a); // [2017-04-10T05:28:11,909000000Z/2017-04-10T05:28:13,909000000Z)
    System.out.println(b); // [2017-04-10T05:28:11,909000000Z/2017-04-10T05:28:11,909000000Z)

    前导方括号"["表示一个闭合的开始,而最后一个括号""表示一个开放的结束。

    1
    2
    3
    4
    5
    6
    System.out.println(
         "startA < endB:" + a.getStartAsInstant().isBefore(b.getEndAsInstant())); // false
    System.out.println(
         "endA > startB:" + a.getEndAsInstant().isAfter(b.getStartAsInstant())); // true

    System.out.println("a overlaps b:" + a.intersects(b)); // a overlaps b: false

    如上图所示,空间隔违反了上面的重叠条件(尤其是starta


    您可以尝试以下操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //custom date for example
    $d1 = new DateTime("2012-07-08");
    $d2 = new DateTime("2012-07-11");
    $d3 = new DateTime("2012-07-08");
    $d4 = new DateTime("2012-07-15");

    //create a date period object
    $interval = new DateInterval('P1D');
    $daterange = iterator_to_array(new DatePeriod($d1, $interval, $d2));
    $daterange1 = iterator_to_array(new DatePeriod($d3, $interval, $d4));
    array_map(function($v) use ($daterange1) { if(in_array($v, $daterange1)) print"Bingo!";}, $daterange);

    把问题分成几个案例,然后处理每个案例。

    "两个日期范围相交"的情况包含两种情况-第一个日期范围从第二个日期范围开始,或第二个日期范围从第一个日期范围开始。


    对于Ruby,我还发现了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Interval < ActiveRecord::Base

      validates_presence_of :start_date, :end_date

      # Check if a given interval overlaps this interval    
      def overlaps?(other)
        (start_date - other.end_date) * (other.start_date - end_date) >= 0
      end

      # Return a scope for all interval overlapping the given interval, including the given interval itself
      named_scope :overlapping, lambda { |interval| {
        :conditions => ["id <> ? AND (DATEDIFF(start_date, ?) * DATEDIFF(?, end_date)) >= 0", interval.id, interval.end_date, interval.start_date]
      }}

    end

    在这里找到的解释很好->http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails


    1
    2
    3
    if (StartDate1 > StartDate2) swap(StartDate, EndDate);

    (StartDate1 <= EndDate2) and (StartDate2 <= EndDate1);


    下面的查询给出了所提供日期范围(开始和结束日期与表名称中的任何日期(开始和结束日期)重叠的ID

    1
    2
    select id from table_name where (START_DT_TM >= 'END_DATE_TIME'  OR  
    (END_DT_TM BETWEEN 'START_DATE_TIME' AND 'END_DATE_TIME'))

    简易解决方案:

    1
    2
    3
    4
    5
    compare the two dates:
        A = the one with smaller start date, B = the one with bigger start date
    if(A.end < B.start)
        return false
    return true

    如果您提供一个日期范围作为输入,并想知道它是否与数据库中现有的日期范围重叠,那么以下条件可以成功满足您的需求

    Assume you provide a @StartDate and @EndDate from your form input.

    条件是:

    如果@StartDateexistingStartDate之前,在existingEndDate之后,那么我们可以说@StartDate在现有日期范围的中间,因此我们可以得出它将重叠的结论。

    1
    @StartDate >=existing.StartDate And @StartDate <= existing.EndDate)

    如果@StartDateexistingStartDate后面,而@EndDateexistingStartDate前面,我们可以说它会重叠

    1
     (@StartDate <= existing.StartDate And @EndDate >= existing.StartDate)

    如果@StartDateexistingStartDate之后,@EndDateexistingEndDate之前,我们可以得出结论,提供的日期范围会吞噬现有的日期范围,因此重叠

    1
     (@StartDate <= existing.StartDate And @EndDate >= existing.EndDate))

    如果任何条件为真,则提供的日期范围与数据库中现有的日期范围重叠。


    下面是另一个使用momentjs的简短答案:

    1
    2
    3
    4
    function isOverlapping(startDate1, endDate1, startDate2, endDate2){
    return moment(startDate1).isSameOrBefore(endDate2) &&
    moment(startDate2).isSameOrBefore(endDate1);
    }

    答案是基于以上答案,但它被缩短了。


    一个容易记住的解决方案是min(ends)>max(starts)


    这是我的解决方案,当值不重叠时返回true:

    X开始1Y端1

    开始2B端2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    TEST1: (X <= A || X >= B)
            &&
    TEST2: (Y >= B || Y <= A)
            &&
    TEST3: (X >= B || Y <= A)


    X-------------Y
        A-----B

    TEST1:  TRUE
    TEST2:  TRUE
    TEST3:  FALSE
    RESULT: FALSE

    ---------------------------------------

    X---Y
          A---B

    TEST1:  TRUE
    TEST2:  TRUE
    TEST3:  TRUE
    RESULT: TRUE

    ---------------------------------------

          X---Y
    A---B

    TEST1:  TRUE
    TEST2:  TRUE
    TEST3:  TRUE
    RESULT: TRUE

    ---------------------------------------

         X----Y
    A---------------B

    TEST1:  FALSE
    TEST2:  FALSE
    TEST3:  FALSE
    RESULT: FALSE

    一个适用于我的紧凑公式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class ValidityRuleRange {
            private final Date from;
            private final Date to;
        ...
        private boolean isOverlap(ValidityRuleRange vrr) {
            int c1 = from.compareTo(vrr.getTo());
            int c2 = to.compareTo(vrr.getFrom());
            return c1 == 0 || c2 == 0 || c1 + c2 == 0;
        }