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

30byixjq  于 2021-06-21  发布在  Mysql
关注(0)|答案(20)|浏览(468)

给定两个日期范围,确定两个日期范围是否重叠的最简单或最有效的方法是什么?
例如,假设我们有由datetime变量表示的范围 StartDate1EndDate1StartDate2EndDate2 .

gpnt7bae

gpnt7bae1#

记住解决方法的简单方法是 min(ends)>max(starts)

ut6juiuv

ut6juiuv2#

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

someInterval.overlaps( anotherInterval )

java.time和额外的310

行业中最好的就是 java.time 内置于Java8及更高版本的框架。再加上一个额外的项目,它用额外的类来补充java.time,特别是 Interval 我们需要上课。
至于 language-agnostic 在这个问题上,两个项目的源代码都可以在其他语言中使用(注意他们的许可证)。

间隔

这个 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 : abuts contains encloses equals isAfter isBefore overlaps 所有这些都使用 Half-Open 一种定义时间跨度的方法,其中开始是包含的,结束是独占的。

ovfsdjhp

ovfsdjhp3#

@bretana给出的数学解很好,但忽略了两个具体细节:
闭间期或半开间期
空间隔
关于区间边界的开闭状态,@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

如上图所示,空中提琴

ccgok5k5

ccgok5k54#

我会的

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

pu3pd22g5#

这是对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 ----]

重叠敌我识别码: (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 ----)

重叠敌我识别码: (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) 等。。。
最后给出了两个区间重叠的一般条件
(开始<?endb)和(enda>?星(TB)
哪里?当比较两个包含的端点时,将严格不等式转化为非严格不等式。

j8yoct9x

j8yoct9x6#

我认为,如果出现以下情况,就可以说这两个范围是重叠的:

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

bwitn5fc7#

对于时间关系(或任何其他区间关系)的推理,请考虑allen的区间代数。它描述了两个间隔之间可能存在的13种关系。你可以找到其他的参考资料-“艾伦间隔”似乎是一个有效的搜索词。您还可以在snodgrass开发的面向时间的sql应用程序(pdf可在线访问url)以及date、darwen和lorentzos temporal data and the relational model(2002年)或time and relational theory:temporal databases in the relational model and sql(2014年;实际上是td&rm的第二版)。
简短的回答是:给定两个日期间隔 A 以及 B 带组件 .start 以及 .end 以及约束条件 .start <= .end ,则两个间隔重叠,如果:

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

您可以调整 >=> 以及 <=< 以满足您对重叠程度的要求。
埃里克评论:
如果你把有趣的事情算进去,你只能得到13分。。。我可以得到“15个可能的关系,两个间隔可以有”当我疯了。通过合理的计算,我只得到六个,如果你不在乎a或b是第一个,我只得到三个(不相交,部分相交,一个完全在另一个内)。15是这样的:[before:before,开始,在内,结束,在后][start:start,内,尾,后][within:within,结束,之后][end:end,之后][after:after].
我想你不能数这两个条目before:before'和'after:after'. 我可以看到7个条目,如果你把它们的一些关系与它们的倒数相等(见维基百科网址中的图表;它有7个条目,其中6个具有不同的逆,而equals没有不同的逆)。三个是否合理取决于你的要求。

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

ntjbwcob8#

下面是另一个使用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次(0.003秒/0.004秒)

9avjhtql

9avjhtql9#

所有的解决方案,检查了大量的条件的基础上,范围是相互关系,可以大大简化,只要确保一个特定的范围开始更早!如果需要的话,可以通过预先交换范围来确保第一个范围更早开始(或者同时开始)。
然后,如果另一个范围开始小于或等于第一个范围结束(如果范围包含开始时间和结束时间)或小于(如果范围包含开始时间和结束时间),则可以检测重叠。
假设两端都包含,只有四种可能性,其中一种是不重叠的:

|----------------------|        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之后开始,你提前消除了一半的问题空间。

x33g5p2x

x33g5p2x10#

答案对我来说太简单了,所以我创建了一个更通用的动态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)
4zcjmb1e

4zcjmb1e11#


下面是实现这一魔力的代码:

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结束
证据?查看这个测试控制台代码。

57hvy0tb

57hvy0tb12#

这是我使用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;
vql8enpb

vql8enpb13#

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

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

5f0d552i14#

在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
xyhw6mcr

xyhw6mcr15#

(starta<=endb)和(enda>=startb)
证明:
让conditiona表示daterange a完全在daterange b之后

_                        |---- DateRange A ------|
|---Date Range B -----|                          _

(如果 StartA > EndB )
让conditionb表示daterange a完全在daterange 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-startA , endA - startB , endB-startA , endB - startB }
(StartA <= EndB) and (EndA >= StartB) (StartA <= EndB) and (StartB <= EndA) 注3。感谢@tomosius,一个简短的版本写道: DateRangesOverlap = max(start1, start2) < min(end1, end2) 这实际上是一个较长实现的语法快捷方式,包括额外的检查,以验证开始日期是否在结束日期或之前。从上面得出:
如果开始和结束日期可能不正常,即,如果有可能 startA > endA 或者 startB > 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)

相关问题