두 날짜 범위가 겹치는 지 확인


1248

두 개의 기간이 주어지면 두 기간이 중복되는지 여부를 결정하는 가장 간단하거나 효율적인 방법은 무엇입니까?

예를 들어, DateTime 변수로 표시되는 범위 StartDate1EndDate1 로 있다고 가정 StartDate2합니다 EndDate2.


3
하는 것이 매우 유사한 stackoverflow.com/questions/306316/...
찰스 Bretana

@CharlesBretana 감사합니다. 맞습니다. 제 질문의 2 차원 버전과 거의 같습니다!
이안 넬슨


2
'두 날짜 범위가 교차하는'상황을 사례로 나누고 (두 개가 있음) 각 사례를 테스트합니다.
대령 패닉

1
이 코드는 잘 작동합니다. 내 대답은 여기에서 볼 수 있습니다 : stackoverflow.com/a/16961719/1534785
Jeyhun Rahimov

답변:


2289

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

증명 :
조건 A가 DateRange A가 DateRange B 이후에 완전히 있음을 의미하도록 함
_ |---- DateRange A ------| |---Date Range B -----| _
(참인 경우 StartA > EndB)

조건 B가 DateRange A가 DateRange B 이전에 완전 함을 의미 함
|---- DateRange A -----| _ _ |---Date Range B ----|
(참인 경우 EndA < StartB)

그러면 A 또는 B가 모두 참
이 아닌 경우 오버랩이 존재합니다.- 한 범위가 다른 범위를 완전히
따르지 않거나 다른 범위를 완전히 따르지 않으면 범위가 겹치게됩니다.

이제 De Morgan의 법률 중 하나는 다음과 같이 말합니다.

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)


29
이것은 다음 두 가지 가정을 기반으로하는 단순화 된 논리입니다. 1) StartA <EndA; 2) StartB <EndB. 명백한 것처럼 보이지만 실제로 데이터는 사용자 입력 또는 데이터베이스를 삭제하지 않고 알 수없는 출처에서 가져올 수 있습니다. 이 단순화 된 논리를 사용하기 전에 두 가지 가정이 모두 충족되는지 확인하려면 입력 데이터의 유효성을 검사해야합니다. 그렇지 않으면 모든 것이 무너집니다. 내 경험에서 배운 교훈;)
Devy

12
@Devy, 당신은 맞습니다. startA = endA 인 경우에도 작동합니다. 실제로, 그것은 정확히 단어 StartEnd의미입니다. Top과 Bottom, East와 West 또는 HighValue와 LoValue라는 두 개의 변수가있는 경우, 값 쌍 중 하나가 반대 변수에 저장되지 않았는지 확인해야합니다. -두 쌍 중 하나만 전환됩니다. 두 쌍의 값을 모두 전환하면 작동합니다.
Charles Bretana

15
당신은 쉽게 널 (NULL) 추가 할 수 있습니다 startend: 그런 ( "시간의 끝으로"=와 "널 끝" "시간의 처음부터"= "시작 NULL"이라는 의미로)(startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)
케빈 Robatel

9
Stackexchange에 대한 최고의 답변! 이 똑똑한 공식이 왜 효과가 있는지에 대한 설명을 보는 것이 좋습니다!
Abeer Sul

4
내가 생각할 수있는 가장 간단한 형식은 다음과 같습니다. 잘못된 입력 (시작 날짜> = 종료 날짜)의 경우에도 false를 반환합니다.DateRangesOverlap = max(start1, start2) < min(end1, end2)
tomosius

406

다음과 같은 경우 두 범위가 겹치는 것으로 충분하다고 생각합니다.

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

76
나는 (StartDate1 <= EndDate2) and (EndDate1 >= StartDate2)표기법을 이해하기가 쉽다는 것을 알았습니다 . Range1은 항상 테스트에서 왼쪽에 있습니다.
AL

8
시작 날짜와 종료 날짜가 모두 포함 된 것으로 가정합니다. 변경 <=하는 <경우 시작은 포괄적이고 끝이 배타적입니다.
Richard Schneider

startDate2가 startDate1보다 앞서도 잘 작동합니다. 따라서 startDate1이 startDate2보다 이전이라고 가정 할 필요가 없습니다.
Shehan Simen

3
(답변에 따라) (StartDate1 <= EndDate2) 및 (StartDate2 <= EndDate1) 표기법이 다른 답변보다 이해하기 쉽다는 것을 알았습니다.
apc

StartDate1 및 / 또는 EndDate1이있는 데이터와 작동하도록 적응하는 방법은 무엇입니까? 이 코드는 StartDate1 및 EndDate1이 항상 존재한다고 가정합니다. StartDate1이 제공되었지만 EndDate1이 없거나 EndDate1이 제공되었지만 StartDate1이 제공되지 않은 경우 어떻게됩니까? 이 여분의 경우를 처리하는 방법?
juFo

117

이 문서의 .NET 용 기간 라이브러리 에서는 열거 형 PeriodRelation에 의한 두 기간의 관계에 대해 설명합니다 .

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

여기에 이미지 설명을 입력하십시오


자바 니스, 내가 구현 한 Allens 간격 대수는 역시 볼 IntervalRelation 및 IsoInterval의 API
특산 Hochschild

80

시간적 관계 (또는 다른 간격 관계에 대한 추론)에 대해서는 Allen의 간격 대수를 고려하십시오 . 두 구간이 서로에 대해 가질 수있는 13 가지 가능한 관계를 설명합니다. 다른 참조를 찾을 수 있습니다. "Allen Interval"은 실용적인 검색어 인 것 같습니다. 또한 이러한 작업에 대한 정보는 Snodgrass의 SQL에서 시간 지향적 응용 프로그램 개발 (PDF에서 온라인으로 제공), 날짜, Darwen 및 Lorentzos 시간 데이터 및 관계형 모델 (2002) 또는 시간 및 관계 이론 : 시간 데이터베이스 관계형 모델 및 SQL (2014; 사실상 TD & RM의 두 번째 버전).


짧은 (ish) 답변은 두 개의 날짜 간격 AB구성 요소 .start.end제약 조건이있는 .start <= .end경우 다음 과 같은 경우 두 간격이 겹칩니다.

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

중첩 정도에 대한 요구 사항을 충족하도록 >=vs ><=vs 의 사용을 조정할 수 있습니다 <.


ErikE 의견 :

당신이 재미있는 것을 세면 13을 얻을 수있다. 현명한 계산으로, 나는 단지 6을 얻습니다. 그리고 당신이 A 또는 B가 먼저 오는 것을 돌보는다면, 나는 단지 3을 얻습니다 (교차하지 않고, 부분적으로 교차하고, 하나는 완전히 다른 것 안에 있습니다). 15는 다음과 같이 진행됩니다 : [이전 : 전, 시작, 내부, 종료, 후], [시작 : 시작, 내부, 종료, 후], [내 :: 내, 끝, 후], [종료 : 종료, 후], [ 이후 : 후].

나는 '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을 얻을 수 있습니다 ... 내가 그것에 갈 때 "두 간격이 가질 수있는 15 가능한 관계"를 얻을 수 있습니다. 현명한 계산으로, 나는 단지 6을 얻습니다. 그리고 당신이 A 또는 B가 먼저 오는 것을 돌보는다면, 나는 단지 3을 얻습니다 (교차하지 않고, 부분적으로 교차하고, 하나는 완전히 다른 것 안에 있습니다). 15는 다음과 같이 진행됩니다 : [이전 : 시작, 시작, 내부, 종료, 이후], [시작 : 시작, 내부, 종료, 이후], [내 :: 내, 종료, 이후], [종료 : 종료, 이후], [ 후 : 후].
ErikE

@ Emtucifor : 나는 'before : before'와 'after : after'의 두 항목을 셀 수 없다고 생각합니다.
Jonathan Leffler

다시 업데이트 : B1 ~ A는 이전 : 이전 : B13 ~ A는 이후 : 이후 좋은 다이어그램에는 B5 B6 사이의 start : start 및 B11과 B12 사이의 end : end가 없습니다. 끝점에있는 것이 중요하면 계산해야합니다. 따라서 최종 집계는 13 이 아니라 15입니다 . 끝 점이 중요하다고 생각 하지 않으므로 개인적으로 계산하기 전에 [전, 후, 후] , [within : within, after], [after : after] 6에 나옵니다. 전체 엔드 포인트가 경계를 포함하는지 독점인지에 대해 혼란스러워합니다. 엔드 포인트의 배타성은 핵심 관계를 변경하지 않습니다!
ErikE

즉, 내 계획에서 이들은 (B2, B3, B4), (B6, B7, B9, B10), (B8, B11, B12)와 동일합니다. 나는 B7이 두 범위가 정확히 일치한다는 정보를 의미한다는 것을 알고 있습니다. 그러나 나는이 추가 정보가 기본 교차 관계의 일부 라고 확신하지 않습니다 . 예를 들어, 일치하지 않거나 겹치지 않아도 두 구간의 길이가 정확히 같은 경우 다른 "관계"로 간주해야합니까? 나는 아니오라고 말하고이 추가 측면이 B7과 B6을 구별하는 유일한 것이므로 종점을 별도의 사례로 사용하면 일관되지 않은 것으로 생각합니다.
ErikE

@Emtucifor : OK-왜 내가 'before : before'와 'after : after'를 항목으로 잘못 식별했는지 알 수 있습니다. 그러나 'start : start'및 'end : end'항목의 모양은 알 수 없습니다. 다이어그램을 편집 할 수 없으므로 'start : start'및 'end : end'관계를 보여주는 다이어그램의 수정 된 사본을 이메일로 보내 주시겠습니까 (내 프로파일 참조)? 나는 당신의 그룹화에 큰 문제가 없습니다.
Jonathan Leffler 2016 년

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 나는 시간이 있었을 때 답을 스스로 만들려고했지만 지금은 필요 없습니다. 문제는 시작과 끝이 동시에 포함되도록 거의 허용하지 않는다는 것입니다. 제 업종에서는 시작을 배타적으로, 끝을 포괄적으로 취급하는 것이 일반적이지만 관례를 유지하는 한 어느 쪽이든 좋습니다. 이것은 지금 까지이 질문에 대한 첫 번째 완전 정답입니다 ... IMO.
Brian Gideon

14

JavaScript를 사용하는 또 다른 솔루션이 있습니다. 내 솔루션의 전문 분야 :

  • null 값을 무한대로 처리
  • 하한을 포함하고 상한을 독점으로 가정합니다.
  • 많은 테스트와 함께 제공

테스트는 정수를 기반으로하지만 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);
    }

선호합니다 (left <value && value <right) || 이 방법에 대한 (오른쪽 <value && value <왼쪽).
Patrick Huizinga

고마워 내 머릿속에서 일을 더 쉽게 만듭니다.
sshow 2016

1
두 개만 확인하면 왜 네 가지 조건을 확인 하시겠습니까? 불합격.
ErikE

3
아 죄송합니다. 이제 범위를 역순으로 허용하고 있음을 알았습니다 (StartDateX> EndDateX). 이상한. 어쨌든 StartDate1이 StartDate2보다 작고 EndDate1이 EndDate2보다 큰 경우 어떻게해야합니까? 제공 한 코드는이 겹치는 조건을 감지하지 못합니다.
ErikE

3
Date1에 전체 Date2가 포함되어 있으면 false를 반환하지 않습니까? 그런 다음 StartDate1은 StartDate2 이전이고 EndDate1은 EndDate2 이후
user158037

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 끝

증명? 이 테스트 콘솔 코드 gist를 확인하십시오 .


작동,하지만 난, 중복되지 테스트하기 위해 두 개의 시나리오를 선호
존 알버트

이미지를 사용하여 설명해 주셔서 감사합니다. 귀하의 답변은이 질문에 대한 완벽한 솔루션입니다.
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));
}

열린 간격 대신 무제한 끝을 의미한다고 생각합니다.
Henrik

@Henrik 두 용어 모두 작동 en.wikipedia.org/wiki/Interval_ (
수학) #Terminology

!startA.after(endB)startA <= endB를 !endA.before(startB)의미하고 startB <= endA를 의미합니다. 개방 간격이 아니라 폐쇄 간격에 대한 기준입니다.
Henrik

@Henrik true 및 열린 간격 endB == null과 같은 다른 조건 을 startA == null확인하십시오.
Khaled.K

1
endB == null, startA == null, endA == nullstartB == null전혀 바운드 간격을 확인하는 조건이 아닌 개방 구간이다. 제한되지 않은 간격과 열린 간격의 차이에 대한 예 : (10, 20) 및 (20, null)은 겹치지 않는 두 개의 열린 간격입니다. 마지막 끝은 끝이 없습니다. 간격이 20을 포함하지 않기 때문에 함수는 true를 반환하지만 간격이 겹치지 않습니다. (단순을 위해 타임 스탬프 대신 사용 된 숫자)
Henrik

7

여기에 게시 된 솔루션은 모든 겹치는 범위에서 작동하지 않았습니다 ...

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

내 작업 솔루션은 다음과 같습니다.

AND (
  ( 'start_date'시작 날짜와 종료 날짜 사이)-내부 및 종료 날짜 외부를 제공합니다.
  또는
  ( 'end_date'시작 날짜와 종료 날짜 사이)-내부 및 시작 날짜 외부를 제공합니다.
  또는
  ( 'start_date'와 'end_date'사이의 STARTDATE)-날짜가 들어있는 외부 범위에는 하나만 필요합니다.
) 

5

이것은 moment.js가있는 내 자바 스크립트 솔루션이었습니다.

// 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.timeJava 8 이상에 내장 된 프레임 워크입니다. 여기에 java.time을 추가 클래스, 특히 여기에 필요한 클래스로 보완 하는 ThreeTen-Extra 프로젝트가 추가 Interval됩니다.

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시작 모멘트에 a를 더하여를 정의 할 수도 있습니다 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)

여기서 🞐는 포함 된 두 끝점을 비교할 때마다 엄격한 불평등을 엄격하지 않은 불평등으로 바꿉니다.


사례 2, 3 및 4는 동일한 중복 조건을 갖습니다. 이것은 의도적 인 것입니까?
Marie

@Marie, 방금 몇 가지 사례 만 나열했습니다
user2314737

이것은 Jonathan Leffler의 대답 처럼 정교하게 OPs 질문에 대한 대답으로 내가 생각했던 것입니다.
mbx

3

momentjs를 사용한 짧은 답변 :

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

대답은 위의 답변을 기반으로하지만 단축되었습니다.


2

endDate = '0000-00-00'으로 설정하지 않고 아직 끝나지 않은 날짜 범위를 사용하는 경우 (예 : endDate = '0000-00-00') 0000-00-00은 유효한 날짜가 아니므로 BETWEEN을 사용할 수 없습니다!

이 솔루션을 사용했습니다.

(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 시작 1 Y 끝 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

아래 쿼리는 제공된 날짜 범위 (시작 및 종료 날짜가 내 table_name의 날짜 (시작 및 종료 날짜)와 겹치는 id를 제공합니다.

select id from table_name where (START_DT_TM >= 'END_DATE_TIME'  OR   
(END_DT_TM BETWEEN 'START_DATE_TIME' AND 'END_DATE_TIME'))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.