确定两个日期范围是否重叠


1248

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

例如,假设我们有DateTime变量StartDate1to EndDate1 StartDate2 to 表示的范围EndDate2



@CharlesBretana对此表示感谢,您是对的-这几乎就像我的问题的二维版本!
伊恩·尼尔森


2
将情况“两个日期范围相交”划分为多个案例(有两个),然后针对每个案例进行测试。
Panic Panic 2012年

1
此代码可以正常工作。您可以在这里看到我的答案: stackoverflow.com/a/16961719/1534785
Jeyhun Rahimov 2013年

Answers:


2286

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

证明:
让ConditionA表示DateRange A完全在DateRange B之后
_ |---- DateRange A ------| |---Date Range B -----| _
(如果为,则为True StartA > EndB

让ConditionB表示DateRange A完全在DateRange B之前
|---- DateRange A -----| _ _ |---Date Range B ----|
(如果为,则为True EndA < StartB

如果A或B都不为真,则存在重叠-
(如果一个范围既不完全在另一个范围之后,
也不完全在另一个范围之前,则它们必须重叠。)

现在,De Morgan的一项法律规定

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)
这实际上是较长实现的语法快捷方式,其中包括额外的检查,以验证开始日期在endDates或之前。从上面导出:

如果开始日期和结束日期可能不正确,即如果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)


29
这是基于以下两个假设的简化逻辑:1)StartA <EndA;2)StartB <EndB。这似乎很明显,但实际上数据可能来自未知来源,例如用户输入或未经消毒的数据库。请记住,在使用此简化逻辑之前,您将需要验证输入数据以确保这两个假设正确,否则一切都会崩溃。经验教训;)
Devy

12
@戴维,你是对的。除非startA = endA,它也将起作用。确实,这正是这些字眼StartEnd意思。如果您有两个名为Top和Bottom或East和West或HighValue和LoValue的变量,则可以假定或暗示某物或某物,某处应确保值对中的一个不存储在相反的变量中。-只能使用两对值中的一对,因为如果同时切换两对值也可以使用。
Charles Bretana

15
您可以轻松地添加nullable startend(其语义为“ null start” =“从时间开始”和“ null end” =“至时间结束”),例如:(startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)
Kevin Robatel

9
关于Stackexchange的最佳答案!很高兴看到有关此智能公式为何有效的解释!
阿比尔·苏尔

4
这是我能想到的最紧凑的表格,如果输入无效(开始日期> =结束日期),它也会返回falseDateRangesOverlap = max(start1, start2) < min(end1, end2)
tomosius

406

我相信,只要满足以下两个条件,就可以说两个范围重叠:

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

76
我发现这种(StartDate1 <= EndDate2) and (EndDate1 >= StartDate2)表示法更容易理解,在测试中,Range1始终位于左侧。
AL

8
假设开始日期和结束日期包含在内。更改<=<start是否包含在内,end是否包含在内。
理查德·施耐德2014年

即使startDate2在startDate1之前,这也将很好地工作。因此,无需假设startDate1早于startDate2。
Shehan Simen

3
我发现(StartDate1 <= EndDate2)和(StartDate2 <= EndDate1)表示法(根据答案)比其他答案更易于理解。
apc

如何进行调整以使其与具有StartDate1和/或EndDate1的数据一起使用?该代码假定StartDate1和EndDate1始终存在。如果指定了StartDate1但未指定EndDate1或EndDate1但未指定StartDate1怎么办。如何处理这种额外情况?
juF19年

117

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

// ------------------------------------------------------------------------
public enum PeriodRelation
{
    After,
    StartTouching,
    StartInside,
    InsideStartTouching,
    EnclosingStartTouching,
    Enclosing,
    EnclosingEndTouching,
    ExactMatch,
    Inside,
    InsideEndTouching,
    EndInside,
    EndTouching,
    Before,
} // enum PeriodRelation

在此处输入图片说明


很好,我也用Java实现了Allens区间代数,请参见IntervalRelation和IsoInterval
Meno Hochschild

80

对于时间关系(或其他任何区间关系)的推理,请考虑艾伦的区间代数。它描述了两个间隔相对于彼此可能具有的13种可能的关系。您可以找到其他参考文献-“艾伦间隔”似乎是一个有效的搜索词。您还可以在Snodgrass的SQL中开发面向时间的应用程序中找到有关这些操作的信息(可在URL上在线获取PDF),以及在Date,Darwen和Lorentzos的Temporal Data and Relational Model(2002)或 Time and Relational Theory:Temporal Databases中。关系模型和SQL(2014年;有效的TD&RM第二版)。


简短的答案是:给定两个日期间隔,A并且B具有分量.start.end和约束.start <= .end,则在以下情况下两个间隔重叠:

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

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


ErikE评论:

如果把事情搞怪的话,你只能得到13。。。。。。。。。。。。。。通过合理的计数,我只有6个,如果您不关心A或B是第一个,则我只有3个(没有相交,部分相交,一个完全相交)。15像这样:[before:before,start,inside,end,after],[start:start,inside,end,after],[within:within,end,after],[end:end,after],[ after:after]。

我认为您不能计算“ before:before”和“ after:after”这两个条目。如果将它们的逆关系等同起来,我可以看到7个条目(请参阅引用的Wikipedia URL中的图;它有7个条目,其中6个具有不同的逆,而等号没有明显的逆)。而三个是否明智取决于您的要求。

----------------------|-------A-------|----------------------
    |----B1----|
           |----B2----|
               |----B3----|
               |----------B4----------|
               |----------------B5----------------|
                      |----B6----|
----------------------|-------A-------|----------------------
                      |------B7-------|
                      |----------B8-----------|
                         |----B9----|
                         |----B10-----|
                         |--------B11--------|
                                      |----B12----|
                                         |----B13----|
----------------------|-------A-------|----------------------

1
如果把事情搞怪的话,你只能得到13。。。。。。。。。。。。。。。。。通过合理的计数,我只有6个,如果您不关心A或B是第一个,则我只有3个(没有相交,部分相交,一个完全相交)。15像这样:[before:before,start,inside,end,after],[start:start,inside,end,after],[within:within,end,after],[end:end,after],[ after:after]。
ErikE 2010年

@Emtucifor:我认为您不能计算“ before:before”和“ after:after”两个条目。
乔纳森·勒夫勒

重新更新:B1到A之前:before,B13到A之后:after。您的漂亮图表缺少开始:在B5和B6之间开始,以及结束:在B11和B12之间结束。如果是在端点上是显著,那么你必须算上它,所以最后的帐簿是15,不是13,我认为端点的事情是显著,所以我个人算什么[前:前,中,后] ,[在:内部,之后],[之后:之后],得出6。我认为整个端点问题只是对边界是包容性还是排他性感到困惑。端点的排他性不会改变核心关系!
ErikE 2010年

也就是说,在我的方案中,这些是等效的:(B2,B3,B4),(B6,B7,B9,B10),(B8,B11,B12)。我意识到B7暗示了两个范围完全重合的信息。但是我不认为这些附加信息应该是基本相交关系的一部分。例如,当两个间隔的长度完全相同时,即使不是重合的甚至是重叠的,也应该将其视为另一个“关系”吗?我说不,并且认为这是使B7与B6有所区别的唯一方面,然后我认为端点为单独的情况会使情况变得不一致。
ErikE 2010年

@Emtucifor:好的-我明白了为什么我错误地将'before:before'和'after:after'标识为条目;但是,我无法想象'start:start'和'end:end'条目的外观。由于您无法编辑我的图表,因此您可以通过电子邮件发送给我(请参阅我的个人资料),其中包含显示“开始:开始”和“结束:结束”关系的图表的修改副本吗?我对您的分组没有重大问题。
乔纳森·莱夫勒

30

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

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

那么这两个事件共享的时间是重叠的吗?这对事件可以重叠的所有不同方式起作用吗?
NSjonas

18

通过确保特定范围开始得较早,就可以大大简化所有基于范围相对位置检查多个条件的解决方案您可以通过在必要时预先交换范围来确保第一个范围更早(或同时)开始。

然后,如果另一个范围起点小于或等于第一个范围终点(如果包含范围,则包含开始时间和结束时间)或小于(如果范围包括起点并且不包括终点),则可以检测到重叠。

假设两端都具有包容性,则只有四种可能性,其中一种是非重叠的:

|----------------------|        range 1
|--->                           range 2 overlap
 |--->                          range 2 overlap
                       |--->    range 2 overlap
                        |--->   range 2 no overlap

范围2的端点没有输入。因此,用伪代码:

def doesOverlap (r1, r2):
    if r1.s > r2.s:
        swap r1, r2
    if r2.s > r1.e:
        return false
    return true

这可以进一步简化为:

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

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

|----------------------|        range 1
|--->                           range 2 overlap
 |--->                          range 2 overlap
                       |--->    range 2 no overlap
                        |--->   range 2 no overlap

您极大地限制了必须执行的检查次数,因为通过确保范围1在范围2之后永远不会开始,可以尽早消除问题空间的一半。


2
+1提及包含/排除问题。有空的时候,我本来打算自己回答一个问题,但现在不需要了。问题是,您几乎永远不允许同时包含开始和结束。在我的行业中,通常习惯将开始视为排他性,将结束视为包容性,但是只要保持一致就可以。到目前为止,这是对这个问题的第一个完全正确的答案。
Brian Gideon

14

这是使用JavaScript的另一种解决方案。我的解决方案的特色:

  • 将空值处理为无穷大
  • 假设下限是包含的,上限是排他的。
  • 附带一堆测试

这些测试基于整数,但是由于JavaScript中的日期对象具有可比性,因此您也可以只放入两个日期对象。或者,您可以输入毫秒级的时间戳。

码:

/**
 * 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);
}

测试:

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):执行20之20成功(0.003秒/0.004秒)


9

我会做

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

哪里IsBetween

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

我希望(左<值&&值<右)|| (右<值&&值<左)用于此方法。
帕特里克·惠辛加

谢谢你 使事情变得更容易。
sshow

1
当只需要检查两个条件时,为什么还要检查四个条件?失败。
ErikE 2010年

3
抱歉,我现在看到您允许范围以相反的顺序(StartDateX> EndDateX)。奇怪。无论如何,如果StartDate1小于StartDate2且EndDate1大于EndDate2怎么办?您提供的代码不会检测到这种重叠情况。
ErikE 2010年

3
如果Date1包含整个Date2,这是否返回false?然后StartDate1在StartDate2之前,EndDate1在EndDate2之后
user158037 2015年

9

在此处输入图片说明

这是执行魔术的代码:

 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-> 1尾
  • C-> 2开始
  • D-> 2尾

证明?查看此测试控制台代码要点


那行得通,但是我更愿意测试不重叠的情况,只有两种情况
John Albert

感谢您使用图片对此进行解释。您的答案是此问题的完美解决方案。
Rakesh Verma,

8

这是我在Java中的解决方案,它也可以无限制地工作

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

我认为您的意思是无止境的而不是开放的时间间隔。
亨里克


!startA.after(endB)表示startA <= endB,!endA.before(startB)表示startB <= endA。这些是关闭间隔而非打开间隔的标准。
Henrik

@Henrik为true,其他条件如endB == nullstartA == null检查打开间隔。
Khaled.K

1
endB == nullstartA == nullendA == nullstartB == null都是标准以检查无界间隔,而不是一个开放的间隔。无界和开放时间间隔之间差异的示例:(10,20)和(20,null)是两个不重叠的开放时间间隔。最后一个确实有无限的结局。您的函数将返回true,但间隔不会重叠,因为间隔不包括20。(为简化起见,使用了数字而不是时间戳)
Henrik

7

此处发布的解决方案不适用于所有重叠范围...

---------------------- | ------- A ------- || ----------- -----------
    | ---- B1 ---- |
           | ---- B2 ---- |
               | ---- B3 ---- |
               | ---------- B4 ---------- |
               | ---------------- B5 ---------------- |
                      | ---- B6 ---- |
---------------------- | ------- A ------- || ----------- -----------
                      | ------ B7 ------- |
                      | ---------- B8 ------------- |
                         | ---- B9 ---- |
                         | ---- B10 ----- |
                         | -------- B11 -------- |
                                      | ---- B12 ---- |
                                         | ---- B13 ---- |
---------------------- | ------- A ------- || ----------- -----------

我的工作解决方案是:

AND(
  (“ start_date”介于STARTDATE和ENDDATE之间)-满足内部和外部结束日期
  要么
  (“ START_ATE”和“ ENDDATE”之间的“ end_date”)—满足内部和外部开始日期
  要么
  (“开始日期”和“结束日期”之间的STARTDATE)—仅在日期在内部的外部范围中需要。
) 

5

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

// 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;


3

在Microsoft SQL SERVER中-SQL函数

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

3

最简单的

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

someInterval.overlaps( anotherInterval )

java.time和ThreeTen-Extra

业务上最好的是java.time内置在Java 8及更高版本中的框架。添加到ThreeTen-Extra项目中,该项目用其他类(特别是Interval我们在此需要的类)来补充java.time 。

至于language-agnostic此课题上的标签,两个项目的源代码都可以用其他语言使用(请注意其许可)。

Interval

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

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

创建一个Interval代表该时间跨度的。

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

我们还可以定义一个Interval带有起始时间加上一个的Duration

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

比较测试是否容易。

Boolean overlaps = interval_A.overlaps( interval_B );

您可以将一个Interval与另一个Interval或进行比较Instant

所有这些Half-Open方法都使用一种方法来定义一个时间范围,其中开始是包含在内的,结束是包含排除的


3

这是@ charles-bretana 的出色答案的扩展。

但是,答案并没有在打开,关闭和半打开(或半关闭)间隔之间进行区分。

情况1:A,B为封闭间隔

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为开放间隔

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右开

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保持打开状态

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闭

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)

等等...

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

(StartA <🞐EndB)和(EndA>🞐StartB)

其中,每当在两个包含的端点之间进行比较时,🞐就会将严格的不等式变成非严格的不等式。


情况二,三和四具有相同的重叠条件,这是故意的吗?
Marie

@Marie,我只列出了一些情况(不是全部)
user2314737

这,但正如乔纳森·莱夫勒Jonathan Leffler)的答案所详尽阐述的那样,是我对OP问题的公认答案。
mbx

3

使用momentjs的简短答案

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

答案是基于以上答案,但它有所缩短。


2

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

我使用了以下解决方案:

(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没有重叠!


2

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

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)

2

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

  1. 封闭或半开放区间的方面
  2. 空间隔

关于区间边界的闭合或打开状态,@ Bretana的解对于闭合区间有效

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

可以半开时间间隔重写

(StartA <EndB)和(EndA> StartB)

此校正是必需的,因为根据定义,开放的区间边界不属于区间的值范围。


关于空间,那么,上面显示的关系不成立。根据定义,不包含任何有效值的空间隔必须作为特殊情况处理。我通过以下示例通过Java时间库Time4J进行了演示:

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)

前方括号“ [”表示开始是封闭的,最后方括号“)”表示结束是开放的。

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 <endB),因此,Time4J(以及其他库)也必须将其作为特殊的边缘情况进行处理,以确保任意间隔与空间隔的重叠不存在。当然,以类似的方式处理日期间隔(在Time4J中默认关闭,但也可以半开,如空的日期间隔)。


1

这是可以在本地使用的通用方法。

    // 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("\nRecords 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
         */
    }

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

3
介意加一些解释吗?
Phantômaxx

1

使用Java util.Date,这就是我所做的。

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

1

我认为最简单的方法是比较EndDate1是否在StartDate2之前,而EndDate2在StartDate1之前。

当然,如果您考虑的间隔是StartDate总是在EndDate之前。


1

我遇到的情况是我们有日期而不是日期时间,并且日期只能在开始/结束时重叠。下面的例子:

在此处输入图片说明

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

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

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

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


0

将问题分成案例然后处理每个案例

“两个日期范围相交”的情况有两种情况-第一个日期范围在第二个日期范围内开始,或者第二个日期范围在第一个日期范围内开始。


0

您可以尝试以下方法:

//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);

0

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

X START 1 Y END 1

A START 2 B END 2

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

0

对于红宝石,我也发现了这一点:

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


0

以下查询为我提供了ID,该ID提供的日期范围(开始和结束日期)与table_name中的任何日期(开始和结束日期)重叠

select id from table_name where (START_DT_TM >= 'END_DATE_TIME'  OR   
(END_DT_TM BETWEEN 'START_DATE_TIME' AND 'END_DATE_TIME'))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.