Biorąc pod uwagę dwa zakresy dat, jaki jest najprostszy lub najskuteczniejszy sposób ustalenia, czy oba zakresy dat się pokrywają?
Jako przykład załóżmy, że mamy zakresy oznaczone zmiennymi DateTime StartDate1
do EndDate1
i StartDate2
do EndDate2
.
datetime
math
language-agnostic
Ian Nelson
źródło
źródło
Odpowiedzi:
(StartA <= EndB) i (EndA> = StartB)
Dowód:
Niech warunek A oznacza, że zakres dat A całkowicie po zakresie dat B
_ |---- DateRange A ------| |---Date Range B -----| _
(prawda, jeśli
StartA > EndB
)Niech warunek B oznacza, że zakres dat A jest całkowicie przed zakresem dat B
|---- DateRange A -----| _ _ |---Date Range B ----|
(prawda, jeśli
EndA < StartB
)Zatem nakładanie się istnieje, jeśli ani A, ani B nie jest prawdziwe -
(Jeśli jeden zakres nie jest całkowicie za drugim,
ani całkowicie przed drugim, wówczas muszą się nakładać).
Teraz jedno z praw De Morgana mówi:
Not (A Or B)
<=>Not A And Not B
Co przekłada się na:
(StartA <= EndB) and (EndA >= StartB)
UWAGA: Obejmuje to warunki, w których krawędzie dokładnie się pokrywają. Jeśli chcesz, aby wykluczyć, że
zmiany
>=
operatorom>
, a<=
do<
UWAGA 2. Dzięki @Baodad, zobaczyć ten blog , rzeczywista zakładka jest najmniej:
{
endA-startA
,endA - startB
,endB-startA
,endB - startB
}(StartA <= EndB) and (EndA >= StartB)
(StartA <= EndB) and (StartB <= EndA)
UWAGA 3. Dzięki @tomosius krótsza wersja brzmi:
DateRangesOverlap = max(start1, start2) < min(end1, end2)
Jest to w rzeczywistości skrót składniowy dla dłuższej implementacji, która obejmuje dodatkowe kontrole w celu sprawdzenia, czy daty rozpoczęcia są w lub przed datami końcowymi. Wyprowadzając to z góry:
Jeśli daty rozpoczęcia i zakończenia mogą być nieprawidłowe, tj. Jeśli jest to możliwe,
startA > endA
lubstartB > endB
, musisz również sprawdzić, czy są one w porządku, co oznacza, że musisz dodać dwie dodatkowe reguły ważności:(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB)
lub:(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB)
lub(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB))
lub:(Max(StartA, StartB) <= Min(EndA, EndB)
Ale do wdrożenia
Min()
iMax()
musisz napisać kod (używając ternary C do zwięzłości):(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)
źródło
Start
iEnd
znaczą. Jeśli masz dwie zmienne o nazwie Góra i Dół lub Wschód i Zachód lub HighValue i LoValue, można założyć lub sugerować, że coś lub ktoś, gdzieś, powinien upewnić się, że jedna z par wartości nie jest przechowywana w przeciwnych zmiennych. -Tylko jedna z dwóch par, ponieważ, no cóż, zadziała również, jeśli obie pary wartości zostaną przełączone.start
iend
(z semantycznym, że „null start” = „Od początku czasu” i „null end” = „Do końca czasu”) w ten sposób:(startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)
DateRangesOverlap = max(start1, start2) < min(end1, end2)
Uważam, że wystarczy powiedzieć, że dwa zakresy pokrywają się, jeśli:
źródło
(StartDate1 <= EndDate2) and (EndDate1 >= StartDate2)
notacja jest łatwiejsza do zrozumienia, Range1 jest zawsze po lewej stronie w testach.<=
na,<
czy początek jest włączający, a koniec wyłączny.W tym artykule Biblioteka okresów dla .NET opisuje relację dwóch okresów przez wyliczenie PeriodRelation :
źródło
Aby uzyskać uzasadnienie dotyczące relacji czasowych (lub jakichkolwiek innych relacji przedziałowych, przejdź do tego), rozważ Algebrę przedziałową Allena . Opisuje 13 możliwych relacji, jakie mogą mieć dwa przedziały względem siebie. Możesz znaleźć inne odniesienia - „Allen Interval” wydaje się być operacyjnym terminem wyszukiwania. Informacje na temat tych operacji można także znaleźć w opracowywaniu aplikacji zorientowanych na czas w języku SQL przez Snodgrass (plik PDF dostępny online pod adresem URL) oraz w danych czasowych, danych Darwen i Lorentzos oraz modelu relacyjnym (2002) lub teorii czasu i relacji: bazy danych czasowych w Relational Model i SQL (2014; skutecznie druga edycja TD&RM).
Krótka (ish) odpowiedź brzmi: biorąc pod uwagę dwa przedziały dat
A
orazB
z komponentami.start
i.end
ograniczeniem.start <= .end
, wówczas dwa przedziały nakładają się, jeśli:Możesz dostroić użycie
>=
vs>
i<=
vs,<
aby spełnić wymagania dotyczące stopnia nakładania się.Komentarze ErikE:
Myślę, że nie można policzyć dwóch wpisów „przed: przed” i „po: po”. Mogę zobaczyć 7 pozycji, jeśli zrównujesz niektóre relacje z ich odwrotnościami (patrz diagram w odnośniku do adresu URL Wikipedii; ma 7 pozycji, z których 6 ma inną odwrotność, a równa się nie ma wyraźnej odwrotności). A czy trzy są rozsądne, zależy od twoich wymagań.
źródło
Jeśli samo nakładanie również powinno zostać obliczone, możesz użyć następującej formuły:
źródło
Wszystkie rozwiązania, które sprawdzają wiele warunków w zależności od tego, gdzie zakresy są względem siebie, można znacznie uprościć, upewniając się, że określony zakres zaczyna się wcześniej! Zapewniasz, że pierwszy zakres zaczyna się wcześniej (lub w tym samym czasie), zamieniając zakresy, jeśli to konieczne z góry.
Następnie można wykryć nakładanie się, jeśli początek drugiego zakresu jest mniejszy lub równy pierwszemu końcowi zakresu (jeśli zakresy obejmują zarówno czas rozpoczęcia, jak i koniec) lub mniej niż (jeśli zakresy obejmują początek i koniec) .
Zakładając włącznie na obu końcach, istnieją tylko cztery możliwości, z których jedna nie nakłada się:
Punkt końcowy zakresu 2 nie wchodzi w to. Tak więc w pseudokodzie:
Można to jeszcze bardziej uprościć:
Jeśli zakresy są włączające na początku, a wyłączne na końcu, wystarczy zastąpić
>
je>=
w drugiejif
instrukcji (dla pierwszego segmentu kodu: w drugim segmencie kodu należy użyć<
zamiast<=
):Znacznie ograniczasz liczbę kontroli, które musisz wykonać, ponieważ usuwasz połowę przestrzeni problemów, zapewniając, że zasięg 1 nigdy nie rozpocznie się po zasięgu 2.
źródło
Oto kolejne rozwiązanie wykorzystujące JavaScript. Specjalizacje mojego rozwiązania:
Testy są oparte na liczbach całkowitych, ale ponieważ obiekty daty w JavaScript są porównywalne, możesz po prostu dodać także dwa obiekty daty. Lub możesz dodać milisekundowy znacznik czasu.
Kod:
Testy:
Wynik podczas działania z karmą, jaśminem i PhantomJS:
źródło
chciałbym zrobić
Gdzie
IsBetween
jest coś takiegoźródło
Oto kod, który robi magię:
Gdzie..
Dowód? Sprawdź ten kod kodu konsoli testowej .
źródło
Oto moje rozwiązanie w Javie , które działa również w nieograniczonych odstępach czasu
źródło
!startA.after(endB)
oznacza startA <= endB i!endA.before(startB)
oznacza startB <= endA. Są to kryteria dla przedziału zamkniętego, a nie otwartego.endB == null
istartA == null
sprawdź, czy przerwa jest otwarta.endB == null
,startA == null
,endA == null
IstartB == null
są wszystkie kryteria, aby sprawdzić nieograniczonego przedziale i nie otwarty przedział. Przykładem różnic między przedziałami nieograniczonymi i otwartymi: (10, 20) i (20, null) są dwa otwarte przedziały, które się nie pokrywają. Ten ostatni ma nieograniczony koniec. Twoja funkcja zwróci true, ale odstępy nie pokrywają się, ponieważ odstępy nie obejmują 20. (używanych numerów zamiast znaczników czasu dla uproszczenia)Opublikowane tutaj rozwiązanie nie działało dla wszystkich nakładających się zakresów ...
moim działającym rozwiązaniem było:
źródło
To było moje rozwiązanie javascript z moment.js:
źródło
Łatwym sposobem na zapamiętanie rozwiązania byłoby
min(ends)>max(starts)
źródło
W Microsoft SQL SERVER - funkcja SQL
źródło
Najprostszym sposobem jest użycie dobrze zaprojektowanej biblioteki dedykowanej do pracy z datą i godziną.
java.time i ThreeTen-Extra
Najlepsze w branży to
java.time
platforma wbudowana w Javę 8 i nowsze wersje. Dodaj do tego projekt ThreeTen-Extra , który uzupełnia java.time o dodatkowe klasy, szczególnieInterval
klasę, której potrzebujemy tutaj.Jeśli chodzi o
language-agnostic
tag w tym pytaniu, kod źródłowy dla obu projektów jest dostępny do użycia w innych językach (pamiętaj o ich licencjach).Interval
org.threeten.extra.Interval
Klasa jest przydatny, ale wymaga Date-Time momentach (java.time.Instant
obiekty) zamiast wartości daty-only. Kontynuujemy, wykorzystując pierwszą chwilę dnia w UTC do przedstawienia daty.Utwórz symbol
Interval
reprezentujący ten przedział czasu.Możemy również zdefiniować
Interval
moment początkowy plusDuration
.Porównywanie z testem na nakładanie się jest łatwe.
Możesz porównać
Interval
z innymInterval
lubInstant
:abuts
contains
encloses
equals
isAfter
isBefore
overlaps
Wszystkie wykorzystują
Half-Open
podejście do definiowania przedziału czasu, w którym początek jest włączający, a zakończenie wyłączny .źródło
To rozszerzenie doskonałej odpowiedzi @ charles-bretana.
Odpowiedź nie rozróżnia jednak przedziałów otwartych, zamkniętych i półotwartych (lub półotwartych).
Przypadek 1 : A, B to przedziały zamknięte
Pokrywaj się iff:
(StartA <= EndB) and (EndA >= StartB)
Przypadek 2 : A, B to przerwy otwarte
Pokrywaj się iff:
(StartA < EndB) and (EndA > StartB)
Przypadek 3 : A, B prawy otwarty
Warunek nakładania się:
(StartA < EndB) and (EndA > StartB)
Przypadek 4 : A, B pozostawiono otwarte
Warunek nakładania się:
(StartA < EndB) and (EndA > StartB)
Przypadek 5 : A otwarte, B zamknięte
Warunek nakładania się:
(StartA <= EndB) and (EndA > StartB)
itp...
Wreszcie ogólny warunek nakładania się dwóch przedziałów to
(StartA <🞐 EndB) i (EndA> 🞐 StartB)
gdzie 🞐 zamienia surową nierówność w niespecyficzną, ilekroć dokonuje się porównania między dwoma uwzględnionymi punktami końcowymi.
źródło
Krótka odpowiedź z wykorzystaniem momentjs :
odpowiedź opiera się na powyższych odpowiedziach, ale jest skrócona.
źródło
Jeśli używasz zakresu dat, który jeszcze się nie zakończył (wciąż trwa), np. Nie ustawiłeś endDate = „0000-00-00”, nie możesz użyć MIĘDZY, ponieważ 0000-00-00 nie jest prawidłową datą!
Użyłem tego rozwiązania:
Jeśli startdate2 jest wyższy niż enddate, nie ma nakładania się!
źródło
Odpowiedź jest dla mnie zbyt prosta, dlatego utworzyłem bardziej ogólną dynamiczną instrukcję SQL, która sprawdza, czy dana osoba ma nakładające się daty.
źródło
Rozwiązanie matematyczne podane przez @Bretana jest dobre, ale pomija dwa konkretne szczegóły:
O zamkniętym lub otwartym stanie granic przedziałów, rozwiązanie @Bretana obowiązuje dla zamkniętych przedziałów
można przepisać na półotwarte interwały aby:
Ta korekta jest konieczna, ponieważ z definicji granica przedziału otwartego nie należy do zakresu wartości przedziału.
A jeśli chodzi o puste odstępy czasu , cóż, tutaj pokazany powyżej związek NIE obowiązuje. Puste interwały, które z definicji nie zawierają żadnej prawidłowej wartości, należy traktować jako przypadek szczególny. Wykazuję to za pomocą mojej biblioteki czasu Java Time4J na tym przykładzie:
Wiodący nawias kwadratowy „[” oznacza zamknięty początek, a ostatni nawias „)” oznacza otwarty koniec.
Jak pokazano powyżej, puste przedziały naruszają powyższe warunki nakładania się (szczególnie startA <endB), więc Time4J (i inne biblioteki również) musi traktować to jako specjalny przypadek zbocza, aby zagwarantować, że nakładanie się dowolnego dowolnego przedziału z pustym przedziałem nie istnieje. Oczywiście przedziały dat (które są domyślnie zamknięte w Time4J, ale mogą być również częściowo otwarte, jak puste przedziały dat) są obsługiwane w podobny sposób.
źródło
Oto ogólna metoda, która może być przydatna lokalnie.
źródło
źródło
Za pomocą Java util.Date, oto co zrobiłem.
źródło
Moim zdaniem najłatwiej to zrobić, porównując, jeśli albo EndDate1 jest przed StartDate2, a EndDate2 przed StartDate1.
To oczywiście, jeśli bierzesz pod uwagę przedziały, w których StartDate jest zawsze przed EndDate.
źródło
Miałem sytuację, w której mieliśmy daty zamiast dat, a daty mogły się nakładać tylko na początku / na końcu. Przykład poniżej:
(Zielony to aktualny interwał, niebieskie bloki to prawidłowe interwały, czerwone to nakładające się interwały).
Dostosowałem odpowiedź Iana Nelsona do następującego rozwiązania:
To pasuje do wszystkich przypadków nakładania się, ale ignoruje dozwolone przypadki nakładania się.
źródło
Podziel problem na przypadki, a następnie obsłuż każde z nich .
Sytuacja „przecinają się dwa zakresy dat” obejmuje dwa przypadki - pierwszy zakres dat rozpoczyna się w drugim lub drugi zakres dat rozpoczyna się w pierwszym.
źródło
Możesz spróbować:
źródło
To było moje rozwiązanie, zwraca wartość true, gdy wartości się nie pokrywają:
X START 1 Y KONIEC 1
A START 2 B KONIEC 2
źródło
Dla ruby znalazłem też:
Znalazłem go tutaj z dobrym wyjaśnieniem -> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails
źródło
Poniższe zapytanie daje mi identyfikatory, dla których podany zakres dat (daty rozpoczęcia i zakończenia pokrywają się z dowolną datą (daty rozpoczęcia i zakończenia) w mojej nazwie tabeli
źródło