Bestimmen Sie, ob sich zwei Datumsbereiche überschneiden

1249

Was ist bei zwei Datumsbereichen der einfachste oder effizienteste Weg, um festzustellen, ob sich die beiden Datumsbereiche überschneiden?

Angenommen, wir haben Bereiche, die mit DateTime-Variablen StartDate1to EndDate1 und StartDate2 to gekennzeichnet sind EndDate2.

Ian Nelson
quelle
3
Sehr
Charles Bretana
@ CharlesBretana danke dafür, du hast recht - das ist fast wie eine zweidimensionale Version meiner Frage!
Ian Nelson
2
sehr ähnlich zu stackoverflow.com/questions/117962/…
Steven A. Lowe
2
Teilen Sie die Situation "Die beiden Datumsbereiche überschneiden sich" in Fälle (es gibt zwei) und testen Sie sie für jeden Fall.
Colonel Panic
1
Dieser Code funktioniert gut. Sie können meine Antwort hier sehen: stackoverflow.com/a/16961719/1534785
Jeyhun Rahimov

Antworten:

2289

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

Beweis:
Lassen Sie Bedingung A bedeuten, dass DateRange A vollständig nach DateRange B liegt
_ |---- DateRange A ------| |---Date Range B -----| _
(True if StartA > EndB)

ConditionB bedeutet, dass DateRange A vollständig vor DateRange B liegt
|---- DateRange A -----| _ _ |---Date Range B ----|
(True if EndA < StartB)

Dann besteht eine Überlappung, wenn weder A noch B wahr sind -
(Wenn ein Bereich weder vollständig nach dem anderen
noch vollständig vor dem anderen liegt, müssen sie sich überlappen.)

Nun besagt eines von De Morgans Gesetzen :

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

Was bedeutet: (StartA <= EndB) and (EndA >= StartB)


HINWEIS: Dies schließt Bedingungen ein, bei denen sich die Kanten genau überlappen. Wenn Sie dies ausschließen möchten,
ändern Sie die >=Operatoren in >und <= in<


ANMERKUNG 2. Dank @Baodad finden Sie in diesem Blog , die tatsächliche Überlappung ist am wenigsten:
{ endA-startA, endA - startB, endB-startA, endB - startB}

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


NOTIZ 3. Dank @tomosius lautet eine kürzere Version:
DateRangesOverlap = max(start1, start2) < min(end1, end2)
Dies ist eigentlich eine syntaktische Verknüpfung für eine längere Implementierung, die zusätzliche Überprüfungen enthält, um zu überprüfen, ob die Startdaten am oder vor den Enddaten liegen. Ableiten von oben:

Wenn Start- und Enddaten nicht in Ordnung sein können, dh wenn es möglich ist, dass startA > endAoder startB > endB, dann müssen Sie auch überprüfen, ob sie in Ordnung sind, was bedeutet, dass Sie zwei zusätzliche Gültigkeitsregeln hinzufügen müssen:
(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB) oder:
(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB) oder,
(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB)) oder:
(Max(StartA, StartB) <= Min(EndA, EndB)

Aber um zu implementieren Min()und Max(), müssen Sie codieren (mit C ternary für die Knappheit):
(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)

Charles Bretana
quelle
29
Dies ist eine vereinfachte Logik, die auf diesen beiden Annahmen basiert: 1) StartA <EndA; 2) StartB <EndB. Es scheint offensichtlich zu sein, aber in Wirklichkeit können Daten aus unbekannten Quellen wie Benutzereingaben oder einer Datenbank ohne Bereinigung stammen. Denken Sie daran, dass Sie die Eingabedaten validieren müssen, um sicherzustellen, dass diese beiden Annahmen zutreffen, bevor Sie diese vereinfachte Logik verwenden können. Andernfalls fällt alles auseinander. Lektion aus meiner eigenen Erfahrung gelernt;)
Devy
12
@ Devy, du bist richtig. Nur dass es auch funktioniert, wenn startA = endA. Genau das bedeuten die Worte Startund Bedeutungen End. Wenn Sie zwei Variablen mit den Namen Top und Bottom oder East and West oder HighValue und LoValue haben, kann davon ausgegangen oder impliziert werden, dass irgendwo etwas oder jemand sicherstellen sollte, dass eines der Wertepaare nicht in den entgegengesetzten Variablen gespeichert wird. -Nur eines der beiden Paare, da es auch funktioniert, wenn beide Wertepaare vertauscht werden.
Charles Bretana
15
Sie können einfach nullable startund end(mit der Semantik "null start" = "Vom Anfang der Zeit" und "null end" = "Bis zum Ende der Zeit") wie (startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)
folgt
9
Beste Antwort auf Stackexchange! Es fühlt sich gut an, eine Erklärung dafür zu sehen, warum diese intelligente Formel funktioniert!
Abeer Sul
4
Hier ist die kompakteste Form, die ich mir DateRangesOverlap = max(start1, start2) < min(end1, end2)
vorstellen kann
406

Ich glaube, es reicht zu sagen, dass sich die beiden Bereiche überschneiden, wenn:

(StartDate1 <= EndDate2) and (StartDate2 <= EndDate1)
Ian Nelson
quelle
76
Ich finde die (StartDate1 <= EndDate2) and (EndDate1 >= StartDate2)Notation leichter zu verstehen, Range1 ist in den Tests immer links.
AL
8
Dies setzt voraus, dass Start- und Enddatum inklusive sind. Wechseln Sie <=zu, <wenn Start inklusive und Ende exklusiv ist.
Richard Schneider
Dies funktioniert sehr gut, auch wenn startDate2 vor startDate1 liegt. Sie müssen also nicht davon ausgehen, dass startDate1 früher als startDate2 ist.
Shehan Simen
3
Ich fand die Notation (StartDate1 <= EndDate2) und (StartDate2 <= EndDate1) (gemäß Antwort) leichter zu verstehen als die in anderen Antworten.
apc
Wie kann ich mich anpassen, damit es mit Daten funktioniert, die StartDate1 UND / ODER EndDate1 haben? Der Code setzt voraus, dass StartDate1 und EndDate1 immer vorhanden sind. Was ist, wenn StartDate1 angegeben ist, aber kein EndDate1 ODER EndDate1 angegeben ist, aber nicht StartDate1. Wie gehe ich mit diesem zusätzlichen Fall um?
JuFo
117

Dieser Artikel Time Period Library für .NET beschreibt die Beziehung zweier Zeiträume durch die Aufzählung PeriodRelation :

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

Geben Sie hier die Bildbeschreibung ein


quelle
Schön, ich habe Allens Intervallalgebra auch in Java implementiert, siehe die API von IntervalRelation und IsoInterval
Meno Hochschild
80

Betrachten Sie Allens Intervallalgebra , um über zeitliche Beziehungen (oder andere Intervallbeziehungen) nachzudenken . Es beschreibt die 13 möglichen Beziehungen, die zwei Intervalle zueinander haben können. Sie können andere Referenzen finden - "Allen Interval" scheint ein operativer Suchbegriff zu sein. Informationen zu diesen Operationen finden Sie auch in Snodgrass ' Entwicklung zeitorientierter Anwendungen in SQL (PDF online verfügbar unter URL) sowie in Datums-, Darwen- und Lorentzos- Zeitdaten und dem relationalen Modell (2002) oder Zeit- und relationale Theorie: Zeitliche Datenbanken in das relationale Modell und SQL (2014; effektiv die zweite Ausgabe von TD & RM).


Die kurze (ish) Antwort lautet: Bei zwei Datumsintervallen Aund Bmit Komponenten .startund .endund der Einschränkung .start <= .endüberlappen sich zwei Intervalle, wenn:

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

Sie können die Verwendung von >=vs >und <=vs <anpassen, um Ihre Anforderungen an den Grad der Überlappung zu erfüllen.


ErikE kommentiert:

Sie können nur 13 bekommen, wenn Sie die Dinge lustig zählen ... Ich kann "15 mögliche Beziehungen bekommen, die zwei Intervalle haben können", wenn ich verrückt danach werde. Durch vernünftiges Zählen bekomme ich nur sechs, und wenn Sie sich darum kümmern, ob A oder B an erster Stelle steht, bekomme ich nur drei (keine Kreuzung, teilweise Kreuzung, eine ganz in der anderen). 15 geht so: [vor: vor, Start, innerhalb, Ende, nach], [Start: Start, innerhalb, Ende, nach], [innerhalb: innerhalb, Ende, nach], [Ende: Ende, nach], [ nachher: ​​nachher].

Ich denke, dass Sie die beiden Einträge 'vor: vor' und 'nach: nach' nicht zählen können. Ich könnte 7 Einträge sehen, wenn Sie einige Beziehungen mit ihren Umkehrungen gleichsetzen (siehe das Diagramm in der referenzierten Wikipedia-URL; es enthält 7 Einträge, von denen 6 eine andere Umkehrung haben, wobei Gleichungen keine eindeutige Umkehrung haben). Und ob drei sinnvoll sind, hängt von Ihren Anforderungen ab.

----------------------|-------A-------|----------------------
    |----B1----|
           |----B2----|
               |----B3----|
               |----------B4----------|
               |----------------B5----------------|
                      |----B6----|
----------------------|-------A-------|----------------------
                      |------B7-------|
                      |----------B8-----------|
                         |----B9----|
                         |----B10-----|
                         |--------B11--------|
                                      |----B12----|
                                         |----B13----|
----------------------|-------A-------|----------------------
Jonathan Leffler
quelle
1
Sie können nur 13 bekommen, wenn Sie die Dinge lustig zählen ... Ich kann "15 mögliche Beziehungen bekommen, die zwei Intervalle haben können", wenn ich verrückt danach werde. Durch vernünftiges Zählen bekomme ich nur sechs, und wenn Sie sich darum kümmern, ob A oder B an erster Stelle steht, bekomme ich nur drei (keine Kreuzung, teilweise Kreuzung, eine ganz in der anderen). 15 geht so: [vor: vor, Start, innerhalb, Ende, nach], [Start: Start, innerhalb, Ende, nach], [innerhalb: innerhalb, Ende, nach], [Ende: Ende, nach], [ nachher: ​​nachher].
ErikE
@Emtucifor: Ich denke, dass Sie die beiden Einträge 'vor: vor' und 'nach: nach' nicht zählen können.
Jonathan Leffler
Bezüglich Ihres Updates: B1 bis A ist vor: vor und B13 bis A ist nach: nach. In Ihrem schönen Diagramm fehlt Start: Start zwischen B5 B6 und Ende: Ende zwischen B11 und B12. Wenn es wichtig ist, sich auf einem Endpunkt zu befinden, müssen Sie ihn zählen, sodass die endgültige Bilanz 15 und nicht 13 ist. Ich denke nicht, dass die Endpunktsache von Bedeutung ist, also zähle ich sie persönlich [vor: vor, innerhalb, nach] , [innerhalb: innerhalb, nach], [nach: nach], was zu 6 kommt. Ich denke, die ganze Endpunktsache ist nur Verwirrung darüber, ob die Grenzen inklusiv oder exklusiv sind. Die Exklusivität der Endpunkte ändert nichts an den Kernbeziehungen!
ErikE
Das heißt, in meinem Schema sind diese äquivalent: (B2, B3, B4), (B6, B7, B9, B10), (B8, B11, B12). Mir ist klar, dass B7 die Information impliziert, dass die beiden Bereiche genau übereinstimmen. Ich bin jedoch nicht davon überzeugt, dass diese zusätzlichen Informationen Teil der Basiskreuzungsbeziehungen sein sollten. Wenn zum Beispiel zwei Intervalle exakt gleich lang sind, auch wenn sie nicht zusammenfallen oder sich sogar überlappen, sollte dies als eine andere "Beziehung" betrachtet werden? Ich sage nein, und da dieser zusätzliche Aspekt das einzige ist, was B7 von B6 unterscheidet, denke ich, dass Endpunkte als separate Fälle die Dinge inkonsistent machen.
ErikE
@Emtucifor: OK - Ich verstehe, warum ich 'vor: vor' und 'nach: nach' als Einträge falsch identifiziert habe; Ich kann mir jedoch nicht vorstellen, wie die Einträge 'start: start' und 'end: end' aussehen sollten. Da Sie mein Diagramm nicht bearbeiten können, können Sie mir eine E-Mail (siehe mein Profil) mit einer modifizierten Kopie des Diagramms senden, in der die Beziehungen "Start: Start" und "Ende: Ende" aufgeführt sind. Ich habe keine größeren Probleme mit Ihren Gruppierungen.
Jonathan Leffler
30

Wenn die Überlappung selbst ebenfalls berechnet werden soll, können Sie die folgende Formel verwenden:

overlap = max(0, min(EndDate1, EndDate2) - max(StartDate1, StartDate2))
if (overlap > 0) { 
    ...
}
Vitalii Fedorenko
quelle
Überlappung ist also die Zeit, die sich die beiden Ereignisse teilen? Funktioniert dies für all die verschiedenen Arten, wie sich Ereignisse überschneiden können?
NSjonas
18

Alle Lösungen, die eine Vielzahl von Bedingungen basierend auf der Beziehung der Bereiche zueinander prüfen, können erheblich vereinfacht werden, indem nur sichergestellt wird, dass ein bestimmter Bereich früher beginnt! Sie stellen sicher, dass der erste Bereich früher (oder gleichzeitig) beginnt, indem Sie die Bereiche bei Bedarf im Voraus austauschen.

Dann können Sie eine Überlappung erkennen, wenn der Start des anderen Bereichs kleiner oder gleich dem ersten Bereichsende ist (wenn Bereiche inklusive sind und sowohl die Start- als auch die Endzeit enthalten) oder kleiner als (wenn Bereiche Start und Exklusiv enthalten). .

Unter der Annahme, dass an beiden Enden inklusive ist, gibt es nur vier Möglichkeiten, von denen eine nicht überlappt:

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

Der Endpunkt des Bereichs 2 wird nicht eingegeben. Also im Pseudocode:

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

Dies könnte noch weiter vereinfacht werden in:

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

Wenn die Bereiche am Anfang und exklusiv am Ende inklusive sind, müssen Sie nur ersetzen >mit >=in der zweiten ifAnweisung (für das erste Codesegment: im zweiten Codesegment, dann würden Sie verwenden , <anstatt <=):

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

Sie begrenzen die Anzahl der durchzuführenden Überprüfungen erheblich, da Sie die Hälfte des Problemraums frühzeitig entfernen, indem Sie sicherstellen, dass Bereich 1 niemals nach Bereich 2 beginnt.

paxdiablo
quelle
2
+1 für die Erwähnung des Inklusiv- / Exklusivproblems. Ich wollte selbst eine Antwort finden, wenn ich Zeit hatte, aber jetzt keine Notwendigkeit. Die Sache ist, dass Sie fast nie zulassen, dass Start und Ende gleichzeitig inklusiv sind. In meiner Branche ist es üblich, den Anfang als exklusiv und das Ende als inklusiv zu behandeln, aber so oder so ist es in Ordnung, solange Sie konsequent bleiben. Dies ist die erste völlig richtige Antwort auf diese Frage ... IMO.
Brian Gideon
14

Hier ist noch eine andere Lösung mit JavaScript. Besonderheiten meiner Lösung:

  • Behandelt Nullwerte als unendlich
  • Angenommen, die Untergrenze ist inklusive und die Obergrenze exklusiv.
  • Kommt mit einer Reihe von Tests

Die Tests basieren auf ganzen Zahlen, aber da Datumsobjekte in JavaScript vergleichbar sind, können Sie auch nur zwei Datumsobjekte einwerfen. Oder Sie werfen den Millisekunden-Zeitstempel ein.

Code:

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

Tests:

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

Ergebnis beim Ausführen mit Karma & Jasmin & PhantomJS:

PhantomJS 1.9.8 (Linux): 20 von 20 ERFOLGEN ausgeführt (0,003 Sekunden / 0,004 Sekunden)

Yankee
quelle
9

Ich würde es tun

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

Wo IsBetweenist so etwas

    public static bool IsBetween(this DateTime value, DateTime left, DateTime right) {
        return (value > left && value < right) || (value < left && value > right);
    }
Bob
quelle
Ich würde es vorziehen (links <Wert && Wert <rechts) || (rechts <Wert && Wert <links) für diese Methode.
Patrick Huizinga
Danke dafür. Erleichtert die Arbeit in meinem Kopf.
Show
1
Warum sollten Sie vier Bedingungen prüfen, wenn Sie nur zwei prüfen müssen? Scheitern.
ErikE
3
Ah, ich entschuldige mich, ich sehe jetzt, dass Sie zulassen, dass die Bereiche in umgekehrter Reihenfolge sind (StartDateX> EndDateX). Seltsam. Was ist, wenn StartDate1 kleiner als StartDate2 und EndDate1 größer als EndDate2 ist? Der von Ihnen angegebene Code erkennt diesen überlappenden Zustand nicht.
ErikE
3
Wird diese Rückgabe nicht falsch sein, wenn Date1 das gesamte Date2 enthält? Dann ist StartDate1 vor StartDate2 und EndDate1 ist nach EndDate2
user158037
9

Geben Sie hier die Bildbeschreibung ein

Hier ist der Code, der die Magie macht:

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

Wo..

  • A -> 1Start
  • B -> 1Ende
  • C -> 2Start
  • D -> 2Ende

Beweis? Schauen Sie sich diesen Testkonsolencode an .

Sandeep Talabathula
quelle
Das funktioniert, aber ich würde es vorziehen, nur zwei Szenarien auf nicht überlappende Szenarien zu testen
John Albert,
Vielen Dank, dass Sie dies anhand von Bildern erklärt haben. Ihre Antwort ist die perfekte Lösung für diese Frage.
Rakesh Verma
8

Hier ist meine Lösung in Java , die auch in unbegrenzten Intervallen funktioniert

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));
}
Khaled.K
quelle
Ich denke, Sie meinten unbegrenzte Ziele statt offener Intervalle.
Henrik
@ Henrik beide Begriffe arbeiten en.wikipedia.org/wiki/Interval_(mathematics)#Terminology
Khaled.K
!startA.after(endB)bedeutet startA <= endB und !endA.before(startB)bedeutet startB <= endA. Dies sind die Kriterien für ein geschlossenes Intervall und kein offenes Intervall.
Henrik
@Henrik true, und die anderen Bedingungen wie endB == nullund startA == nullauf ein offenes Intervall prüfen.
Khaled.K
1
endB == null, startA == null, endA == nullUnd startB == nullsind alle Kriterien für ein unbeschränktes Intervall zu überprüfen und nicht ein offenes Intervall. Beispiel für die Unterschiede zwischen unbegrenzten und offenen Intervallen: (10, 20) und (20, null) sind zwei offene Intervalle, die sich nicht überlappen. Der letzte hat ein unbegrenztes Ende. Ihre Funktion wird true zurückgeben, aber die Intervalle überschneiden sich nicht, da die Intervalle keine 20 enthalten. (Der Einfachheit halber werden anstelle von Zeitstempeln Zahlen verwendet)
Henrik
7

Die hier veröffentlichte Lösung funktionierte nicht für alle überlappenden Bereiche ...

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

Meine Arbeitslösung war:

UND (
  ('start_date' ZWISCHEN STARTDATE UND ENDDATE) - Für das äußere und das äußere Enddatum
  ODER
  ('end_date' ZWISCHEN STARTDATE UND ENDDATE) - sorgt für das innere und das äußere Startdatum
  ODER
  (STARTDATE ZWISCHEN 'start_date' UND 'end_date') - nur eines wird für den äußeren Bereich benötigt, in dem sich die Daten befinden.
) 
auf_
quelle
5

Dies war meine Javascript-Lösung mit 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;
Ignacio Pascual
quelle
4

Eine einfache Möglichkeit, sich an die Lösung zu erinnern, wäre
min(ends)>max(starts)

Radacina
quelle
3

In Microsoft SQL Server - SQL-Funktion

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
Prasenjit Banerjee
quelle
3

das einfachste

Am einfachsten ist es, eine ausgereifte Bibliothek für Datums- und Uhrzeitarbeiten zu verwenden.

someInterval.overlaps( anotherInterval )

java.time & ThreeTen-Extra

Das Beste im Geschäft ist das java.timein Java 8 und höher integrierte Framework. Fügen Sie dazu das ThreeTen-Extra- Projekt hinzu, das java.time durch zusätzliche Klassen ergänzt, insbesondere die IntervalKlasse, die wir hier benötigen.

Für das language-agnosticTag in dieser Frage steht der Quellcode für beide Projekte zur Verwendung in anderen Sprachen zur Verfügung (beachten Sie deren Lizenzen).

Interval

Die org.threeten.extra.IntervalKlasse ist praktisch, erfordert jedoch Datums- und Uhrzeitmomente ( java.time.InstantObjekte) anstelle von Nur-Datum-Werten. Wir verwenden also den ersten Moment des Tages in UTC, um das Datum darzustellen.

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

Erstellen Sie eine Interval, um diese Zeitspanne darzustellen.

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

Wir können auch ein Intervalmit einem Startmoment plus a definieren Duration.

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

Der Vergleich mit dem Testen auf Überlappungen ist einfach.

Boolean overlaps = interval_A.overlaps( interval_B );

Sie können eine mit einer Intervalanderen vergleichen Intervaloder Instant:

Alle diese verwenden den Half-OpenAnsatz, um eine Zeitspanne zu definieren, in der der Anfang inklusive und das Ende exklusiv ist .

Basil Bourque
quelle
3

Dies ist eine Erweiterung der hervorragenden Antwort von @ charles-bretana.

Die Antwort unterscheidet jedoch nicht zwischen offenen, geschlossenen und halboffenen (oder halbgeschlossenen) Intervallen.

Fall 1 : A, B sind geschlossene Intervalle

A = [StartA, EndA]
B = [StartB, EndB]

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


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

Überlappung iff: (StartA <= EndB) and (EndA >= StartB)

Fall 2 : A, B sind offene Intervalle

A = (StartA, EndA)
B = (StartB, EndB)

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

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

Überlappung iff: (StartA < EndB) and (EndA > StartB)

Fall 3 : A, B rechts offen

A = [StartA, EndA)
B = [StartB, EndB)

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

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

Überlappungsbedingung: (StartA < EndB) and (EndA > StartB)

Fall 4 : A, B offen gelassen

A = (StartA, EndA]
B = (StartB, EndB]

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

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

Überlappungsbedingung: (StartA < EndB) and (EndA > StartB)

Fall 5 : A rechts offen, B geschlossen

A = [StartA, EndA)
B = [StartB, EndB]

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


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

Überlappungsbedingung: (StartA <= EndB) and (EndA > StartB)

usw...

Schließlich ist die allgemeine Bedingung für die Überlappung zweier Intervalle

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

wobei 🞐 eine strikte Ungleichung in eine nicht strenge Ungleichung umwandelt, wenn der Vergleich zwischen zwei eingeschlossenen Endpunkten durchgeführt wird.

user2314737
quelle
Die Fälle zwei, drei und vier haben die gleiche Überlappungsbedingung. Ist dies beabsichtigt?
Marie
@ Marie, ich habe nur ein paar Fälle aufgelistet (nicht alle)
user2314737
Dies, aber so ausführlich wie Jonathan Lefflers Antwort, würde ich als akzeptierte Antwort auf die Frage der OP im Sinn haben.
mbx
3

Kurze Antwort mit momentjs :

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

Die Antwort basiert auf den obigen Antworten, ist jedoch verkürzt.

Nitin Jadhav
quelle
2

Wenn Sie einen Datumsbereich verwenden, der noch nicht beendet wurde (noch läuft), z. B. nicht endDate = '0000-00-00' setzen, können Sie ZWISCHEN nicht verwenden, da 0000-00-00 kein gültiges Datum ist!

Ich habe diese Lösung verwendet:

(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

Wenn startdate2 höher als enddate ist, gibt es keine Überlappung!

Jack
quelle
2

Die Antwort ist zu einfach für mich, daher habe ich eine allgemeinere dynamische SQL-Anweisung erstellt, die prüft, ob eine Person überlappende Daten hat.

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)
Tom McDonough
quelle
2

Die mathematische Lösung von @Bretana ist gut, vernachlässigt jedoch zwei spezifische Details:

  1. Aspekt geschlossener oder halboffener Intervalle
  2. leere Intervalle

Über den geschlossenen oder offenen Zustand von Intervallgrenzen gilt die Lösung von @Bretana für geschlossene Intervalle

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

kann für halboffene Intervalle umgeschrieben werden in:

(StartA <EndB) und (EndA> StartB)

Diese Korrektur ist notwendig, da eine offene Intervallgrenze per Definition nicht zum Wertebereich eines Intervalls gehört.


Und über leere Intervalle , nun, hier gilt die oben gezeigte Beziehung NICHT. Leere Intervalle, die per Definition keinen gültigen Wert enthalten, müssen als Sonderfall behandelt werden. Ich demonstriere es anhand meiner Java- Zeitbibliothek Time4J anhand dieses Beispiels:

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)

Die führende eckige Klammer "[" zeigt einen geschlossenen Start an, während die letzte Klammer ")" ein offenes Ende anzeigt.

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

Wie oben gezeigt, verletzen leere Intervalle die obige Überlappungsbedingung (insbesondere startA <endB), daher muss Time4J (und auch andere Bibliotheken) dies als speziellen Randfall behandeln, um sicherzustellen, dass sich ein beliebiges Intervall mit einem leeren Intervall überlappt ist nicht vorhanden. Natürlich werden Datumsintervalle (die in Time4J standardmäßig geschlossen sind, aber auch halb geöffnet sein können, wie leere Datumsintervalle) auf ähnliche Weise behandelt.

Meno Hochschild
quelle
1

Hier ist eine generische Methode, die lokal nützlich sein kann.

    // 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
         */
    }
staceyw
quelle
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");
            }
mmarjeh
quelle
3
Möchten Sie einige erklärende Worte hinzufügen?
Phantômaxx
1

Mit Java util.Date, hier was ich getan habe.

    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;
    }
Shehan Simen
quelle
1

Der einfachste Weg, dies zu tun, wäre meiner Meinung nach zu vergleichen, ob EndDate1 vor StartDate2 und EndDate2 vor StartDate1 liegt.

Dies gilt natürlich, wenn Sie Intervalle in Betracht ziehen, in denen StartDate immer vor EndDate liegt.

AlexDrenea
quelle
1

Ich hatte eine Situation, in der wir Daten anstelle von Daten hatten und die Daten sich nur am Anfang / Ende überschneiden konnten. Beispiel unten:

Geben Sie hier die Bildbeschreibung ein

(Grün ist das aktuelle Intervall, blaue Blöcke sind gültige Intervalle, rote sind überlappende Intervalle).

Ich habe die Antwort von Ian Nelson an die folgende Lösung angepasst:

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

Dies entspricht allen Überlappungsfällen, ignoriert jedoch die zulässigen Überlappungsfälle.

Gus
quelle
0

Teilen Sie das Problem in Fälle auf und behandeln Sie dann jeden Fall .

Die Situation, dass sich zwei Datumsbereiche überschneiden, wird in zwei Fällen behandelt: Der erste Datumsbereich beginnt innerhalb des zweiten oder der zweite Datumsbereich beginnt innerhalb des ersten.

Oberst Panik
quelle
0

Sie können dies versuchen:

//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);
Ilya
quelle
0

Dies war meine Lösung. Sie gibt true zurück, wenn sich die Werte nicht überschneiden:

X START 1 Y END 1

A START 2 B ENDE 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
Fez Vrasta
quelle
0

Für Ruby habe ich auch folgendes gefunden:

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

Fand es hier mit netter Erklärung -> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails

Mahatmanich
quelle
0

Die folgende Abfrage gibt mir die IDs an, für die sich der angegebene Datumsbereich (Start- und Enddatum) mit einem der Daten (Start- und Enddatum) in meinem Tabellennamen überschneidet

select id from table_name where (START_DT_TM >= 'END_DATE_TIME'  OR   
(END_DT_TM BETWEEN 'START_DATE_TIME' AND 'END_DATE_TIME'))
Shravan Ramamurthy
quelle