Определите, перекрываются ли два диапазона дат

Учитывая два диапазона дат, каков самый простой или наиболее эффективный способ определить, перекрываются ли эти два диапазона дат?

В качестве примера предположим, что у нас есть диапазоны, обозначенные переменными DateTime от StartDate1 до EndDate1иStartDate2 до EndDate2.

Чрезвычайно похож на stackoverflow.com/questions/306316/…

Charles Bretana 28.11.2008 17:54

@CharlesBretana, спасибо за это, вы правы - это почти как двухмерная версия моего вопроса!

Ian Nelson 28.11.2008 17:57

очень похож на stackoverflow.com/questions/117962/…

Steven A. Lowe 28.11.2008 19:35

Разделите ситуацию «два диапазона дат пересекаются» на случаи (их два), а затем проверьте каждый случай.

Colonel Panic 13.10.2012 03:14

Если даты могут быть значениями NULL (или пустыми), когда они не установлены, существует этот вопрос, который является расширением этого.

lepe 24.06.2017 09:09

Алгоритм Объединить перекрывающиеся интервалы может дать некоторые подсказки.

RBT 30.05.2018 13:33

Я знаю, что это было помечено как независимый от языка, но для всех, кто реализует на Java: не изобретайте велосипед и используйте Joda Time. joda-time.sourceforge.net/api-release/org/joda/time/base/…

Stefan Haberl 13.10.2012 19:10

Привет .. A: StartDate1, B: EndDate1, C: StartDate2, D: EndDate2. если B <C или A> D, то мы предполагаем, что они не пересекаются .. Итак, мы можем легко проверить с помощью «isintersects = not (B <C или A> D)», это всегда даст нам, пересекается ли он или нет.

Nihat Erim inceoğlu 11.02.2020 15:20

Еще одна интервальная утилита для .Net github.com/AlexeyBoiko/IntervalUtility (я автор)

user1167761 10.03.2021 13:59
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1 345
9
457 852
37
Перейти к ответу Данный вопрос помечен как решенный

Ответы 37

Я считаю, что достаточно сказать, что эти два диапазона перекрываются, если:

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

Я считаю, что нотация (StartDate1 <= EndDate2) and (EndDate1 >= StartDate2) легче для понимания, Range1 в тестах всегда слева.

A.L 02.12.2013 18:46

Предполагается, что даты начала и окончания указаны включительно. Измените <= на <, если начало включающее, а конец исключающее.

Richard Schneider 09.04.2014 06:54

Это будет работать очень хорошо, даже если startDate2 предшествует startDate1. Поэтому не нужно предполагать, что startDate1 раньше startDate2.

Shehan Simen 06.02.2015 08:04

Я обнаружил, что обозначения (StartDate1 <= EndDate2) и (StartDate2 <= EndDate1) (согласно ответу) легче понять, чем в других ответах.

apc 04.08.2016 11:44

Как адаптироваться, чтобы он работал с данными, имеющими StartDate1 И / ИЛИ EndDate1? Код предполагает, что StartDate1 и EndDate1 присутствуют всегда. Что, если указана StartDate1, но не указана EndDate1 ИЛИ EndDate1, но не указана StartDate1. Как справиться с этим лишним делом?

juFo 06.08.2019 18:18

Как я могу сделать начало эксклюзивным, а конец включенным?

Jay Patel 10.01.2020 10:43

я бы сделал

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) || (right <value && value <left) для этого метода.

Patrick Huizinga 28.11.2008 18:22

Спасибо за это. Облегчает жизнь в моей голове.

sshow 10.11.2009 18:05

Зачем проверять четыре условия, если нужно проверить только два? Провал.

ErikE 09.03.2010 04:19

Ах, мои извинения, теперь я вижу, что вы разрешаете диапазонам быть в обратном порядке (StartDateX> EndDateX). Странный. В любом случае, что, если StartDate1 меньше StartDate2, а EndDate1 больше EndDate2? Код, который вы указали, не обнаружит это перекрывающееся условие.

ErikE 11.03.2010 08:17

Разве это не вернет false, если Date1 содержит весь Date2? Тогда StartDate1 находится перед StartDate2, а EndDate1 - после EndDate2.

user158037 11.08.2015 16:06

На мой взгляд, самый простой способ сделать это - сравнить, находится ли EndDate1 перед StartDate2, а EndDate2 перед StartDate1.

Это, конечно, если вы рассматриваете интервалы, в которых StartDate всегда предшествует EndDate.

Ответ принят как подходящий

(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)
На самом деле это синтаксический ярлык для более длинной реализации, которая включает дополнительные проверки, чтобы убедиться, что даты начала совпадают с endDates или раньше. Получая это сверху:

Если даты начала и окончания могут быть не по порядку, то есть, если возможно, что 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)

в любом случае мы можем получить фактическое количество секунд перекрытия

rashid 01.04.2015 08:42

Да, но я не думаю, что есть простой способ ... Без написания реального SQL можно было бы использовать один алгоритм: использование точек A0, A1, B0 и B1 в качестве точек: если нет перекрытия, то 0; Когда Bo и B1 находятся между A0 и A1, тогда это B1-B0; Когда только B0 находится между A0 и A1, тогда это A1-B0; когда только B1 находится между A0 и A1, тогда это B1-A0; иначе это A1-A0

Charles Bretana 01.04.2015 16:55

Это упрощенная логика, основанная на этих двух предположениях: 1) StartA <EndA; 2) StartB <EndB. Это кажется очевидным, но на самом деле данные могут поступать из неизвестного источника, такого как ввод пользователя или база данных, без очистки. Имейте в виду, что вам нужно будет проверить входные данные, чтобы убедиться, что эти два предположения верны, прежде чем вы сможете использовать эту упрощенную логику, иначе все развалится. Урок извлечен из собственного опыта;)

Devy 27.07.2015 22:08

@Devy, ты прав. За исключением того, что он также будет работать, если startA = endA. Действительно, это именно то, что означают слова Start и End. Если у вас есть две переменные с именами Top и Bottom, или East and West, или HighValue и LoValue, можно предположить или подразумевать, что что-то или кто-то где-то должен гарантировать, что одна из пар значений не хранится в противоположных переменных. -Только одна из двух пар, потому что она также будет работать, если поменять обе пары значений.

Charles Bretana 05.08.2015 15:08

@Devy Это похоже на несвязанную проблему. Нарисуйте линию, чтобы показать 5-9 августа в календаре, затем проведите линию, чтобы показать 7-3 августа, и скажите мне, пересекаются ли они. Вы не можете, потому что вы не можете провести линию, начиная с 7 августа и заканчивая 3 августа (только наоборот).

thelem 20.11.2015 14:48

@rashid, вот пост может дать вам несколько подсказок о том, как получить фактическую величину перекрытия.

Baodad 15.12.2015 20:39

Вы можете легко добавить обнуляемые start и end (с семантикой "нулевое начало" = "с начала времени" и "нулевой конец" = "до конца времени") вот так: (startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)

Kevin Robatel 26.02.2016 16:38

Лучший ответ на Stackexchange! Приятно видеть объяснение того, почему эта умная формула работает!

Abeer Sul 13.03.2016 01:36

Я был ошеломлен, когда посмотрел на него в первый раз, я сказал себе: этого не может быть. Но после тестирования всех возможных случаев у меня получился только тот случай, когда один из bools имеет значение true: (true, false), когда A полностью перед B, и (false, true), когда A полностью после B ... Это это один из самых интересных способов изучить логическую математику! Начиная со всех возможных случаев и заканчивая этим, довольно интересно

Antoine Pelletier 24.08.2016 19:04

Вот самая компактная форма, которую я мог придумать, которая также возвращает false в случае недопустимого ввода (дата начала> = дата окончания) DateRangesOverlap = max(start1, start2) < min(end1, end2)

tomosius 21.09.2016 00:25

@tomosius, я добавил примечание к своему ответу, чтобы объяснить ваш комментарий

Charles Bretana 08.02.2017 21:49

Это лучший ответ, который я когда-либо видел в StackOverflow.

Sampath 17.10.2017 10:27

Это красота!

Ravi Sanwal 28.09.2018 21:19
silentmatt.com/rectangle-intersection Используйте этот инструмент, чтобы легко проверить различные случаи с помощью перетаскивания и проверить свое решение.
habib 11.11.2019 17:42

@KevinRobatel, это работает для всех сценариев, но когда оба endA и endB равны нулю, это сценарий, в котором независимо от того, какие даты установлены, они будут конфликтовать, но в вашем сценарии они пройдут.

xcyteh 16.11.2020 18:36

Прошли годы, но я все еще возвращаюсь к этому ответу, потому что это лучшее объяснение.

patrickmdnet 15.01.2021 22:01

Чтобы рассуждать о временных отношениях (или любых других интервальных отношениях, если уж на то пошло), рассмотрите Алгебра интервалов Аллена. Он описывает 13 возможных отношений, которые могут иметь два интервала по отношению друг к другу. Вы можете найти и другие ссылки - "Интервал Аллена" кажется оперативным поисковым запросом. Вы также можете найти информацию об этих операциях в Разработка приложений, ориентированных на время, на SQL Snodgrass (PDF-файл доступен онлайн по адресу URL), а также в Date, Darwen and Lorentzos Временные данные и реляционная модель (2002) или Время и реляционная теория: временные базы данных в реляционной модели и SQL (2014; фактически второе издание TD&RM).


Краткий ответ: если даны два интервала дат A и B с компонентами .start и .end и ограничением .start <= .end, то два интервала перекрываются, если:

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

Вы можете настроить использование >= против > и <= против < в соответствии с вашими требованиями к степени перекрытия.


ErikE комментирует:

You can only get 13 if you count things funny... I can get "15 possible relations that two intervals can have" when I go crazy with it. By sensible counting, I get only six, and if you throw out caring whether A or B comes first, I get only three (no intersect, partially intersect, one wholly within other). 15 goes like this: [before:before, start, within, end, after], [start:start, within, end, after], [within:within, end, after], [end:end, after], [after:after].

Я думаю, что вы не можете сосчитать две записи «до: до» и «после: после». Я мог бы увидеть 7 записей, если вы приравняете некоторые отношения к их обратным (см. Диаграмму в указанном URL-адресе Википедии; он имеет 7 записей, 6 из которых имеют другую обратную сторону, при этом равные не имеют отдельного обратного). А разумность трех зависит от ваших требований.

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

Вы можете получить только 13, если посчитаете забавные вещи ... Я могу получить «15 возможных отношений, которые могут иметь два интервала», когда я схожу с ума от этого. Путем разумного подсчета я получаю только шесть, и если вы не заботитесь о том, идет ли сначала A или B, я получаю только три (без пересечения, частичное пересечение, одно полностью внутри другого). 15 выглядит так: [до: до, начало, внутри, конец, после], [начало: начало, внутри, конец, после], [внутри: внутри, конец, после], [конец: конец, после], [ после: после].

ErikE 09.03.2010 04:18

@Emtucifor: Я думаю, вы не можете сосчитать две записи «до: до» и «после: после».

Jonathan Leffler 09.03.2010 05:37

Повторите ваше обновление: от B1 до A - до: до, а от B13 до A - после: после. На вашей красивой диаграмме отсутствует начало: начало между B5 B6 и конец: конец между B11 и B12. Если нахождение на конечной точке имеет значение, вы должны его подсчитать, поэтому окончательный результат будет 15, а не 13. Я не считаю, что конечная точка имеет значение, поэтому я лично считаю ее [до: до, внутри, после], [ inside: inside, after], [after: after], что доходит до 6. Я думаю, что вся проблема конечных точек - это просто заблуждение относительно того, являются ли границы включающими или исключающими. Исключительность конечных точек не меняет основных отношений!

ErikE 10.03.2010 23:38

То есть в моей схеме они эквивалентны: (B2, B3, B4), (B6, B7, B9, B10), (B8, B11, B12). Я понимаю, что B7 подразумевает информацию о том, что два диапазона в точности совпадают. Но я не уверен, что эта информация дополнительный должна быть частью базовых отношений пересечения. Например, если два интервала имеют одинаковую длину, даже если они не совпадают или даже не перекрываются, следует ли это рассматривать как еще одно «отношение»? Я говорю «нет», и, поскольку этот дополнительный аспект - единственное, что отличает B7 от B6, я думаю, что наличие оконечных точек как отдельных случаев делает вещи непоследовательными.

ErikE 10.03.2010 23:51

@Emtucifor: Хорошо, я понимаю, почему я неправильно определил «до: до» и «после: после» в качестве записей; однако я не могу представить, как должны выглядеть записи start: start и end: end. Поскольку вы не можете редактировать мою диаграмму, можете ли вы отправить мне электронное письмо (см. Мой профиль) с измененной копией диаграммы, показывающей отношения «начало: начало» и «конец: конец»? У меня нет серьезных проблем с вашими группировками.

Jonathan Leffler 11.03.2010 00:07

start: start будет началом и концом одновременно, началом A. Поскольку время в компьютерах всегда имеет степень детализации или разрешение, это по-прежнему обозначает (возможно, очень короткий) временной диапазон. end: end будет начинаться и заканчиваться в конце A. Я напишу электронное письмо для полноты.

ErikE 11.03.2010 08:14

После обмена электронной почтой с Джонатаном я понял, что в моей модели, где время начала включено, а время окончания является исключительным, диапазон, начинающийся и заканчивающийся одновременно, имеет нулевую длину (потому что он заканчивается раньше, чем начинается). Так что [start: start] и [end: end] не имеют смысла в моей схеме. Однако предложенная Джонатаном схема требует, чтобы конечные точки были инклюзивными, иначе диапазоны B2 и A (например) не пересекались бы. Для меня это проблематично, потому что время в компьютере не отображается с бесконечной точностью. См. Следующий комментарий ->

ErikE 12.03.2010 20:18

Если время окончания включено, то для обозначения неперекрывающихся, но смежных диапазонов в компьютере необходимо вычесть наименьшее допустимое приращение времени из времени окончания более раннего сегмента. Например, в SQL Server с типом данных datetime диапазоны A: '20100101 09:00' - '20100101 10:00' и B: '20100101 10:00' - '20100101 11:00' будут перекрываться на 1 / 300-я миллисекунда. Чтобы сделать их смежными и неперекрывающимися, первый диапазон необходимо изменить на A: «20100101 09:00» - «20100101 09: 59: 59.997». Но это совершенно неприемлемо по нескольким причинам (Далее ->)

ErikE 12.03.2010 20:21

Первая и менее важная причина в том, что люди не думают о времени таким образом. Они хотят сказать, что диапазон начинается с 9 и заканчивается на 10. Если вы собираете данные от пользователя, вам нужно либо представить мусор 09: 59: 59.997 (это никогда не сработает), либо все время корректировать, когда вы хранить их (не намного лучше). Вторая и более важная причина заключается в том, что базовый тип данных не должен изменять бизнес-значение данных. Что, если столбец datetime был преобразован в datetime2 (technet.microsoft.com/en-us/library/bb677335.aspx), который имеет значения с 00:00:00 до 23:59: 59.9999999?

ErikE 12.03.2010 20:23

Теперь есть разрыв между концом A и началом B. Единственное разумное решение всего этого безумия - это просто рассматривать конечные времена как исключительные. Как только это будет сделано, 13 диапазонов, упомянутых Джонатаном, полностью разрушатся. Нет особой разницы между двумя соседними или разделенными неперекрывающимися диапазонами. После удаления свернутых или эквивалентных случаев у нас остается только 6 основных соотношений (3, если A и B считаются взаимозаменяемыми).

ErikE 12.03.2010 20:26

Плюс разрешения интервалов нулевой длины состоит в том, что это тривиально обобщается на проверки «включенности» временных точек.

moooeeeep 06.01.2016 22:48

Вот общий метод, который может быть полезен локально.

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

Все решения, которые проверяют множество условий, основанных на том, где диапазоны находятся по отношению друг к другу, могут быть значительно упрощены с помощью просто убедитесь, что определенный диапазон начинается раньше!. Вы гарантируете, что первый диапазон начинается раньше (или в то же время), меняя диапазоны, если необходимо, заранее.

Затем вы можете обнаружить перекрытие, если начало другого диапазона меньше или равно первому концу диапазона (если диапазоны включены, содержат время начала и окончания) или меньше (если диапазоны включают начало и не включают конец) .

Предполагая инклюзивность на обоих концах, есть только четыре возможности, одна из которых не перекрывается:

|----------------------|        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.

+1 за упоминание инклюзивной / исключительной проблемы. Я собирался сам придумать ответ, когда у меня будет время, но сейчас в этом нет необходимости. Дело в том, что вы почти никогда не позволяете одновременно включать начало и конец. В моей отрасли принято рассматривать начало как исключительное, а конец как инклюзивное, но в любом случае все в порядке, если вы остаетесь последовательными. На данный момент это первый полностью правильный ответ на этот вопрос ... ИМО.

Brian Gideon 06.08.2010 07:50

В этой статье Библиотека периодов времени для .NET описывается отношение двух периодов времени с помощью перечисления PeriodRelation:

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

Хорошо, я также реализовал алгебру интервалов Алленса на Java, см. API IntervalRelation и IsoInterval

Meno Hochschild 10.04.2017 09:05

Отличное резюме для написания спецификаций для перекрывающихся дат

ToTenMilan 01.08.2020 18:23
if (StartDate1 > StartDate2) swap(StartDate, EndDate);

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

Достаточно второй строки. Какова цель первой строки? Что такое StartDate и EndDate?

0xF 22.10.2013 15:20

Если необходимо рассчитать и само перекрытие, вы можете использовать следующую формулу:

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

Итак, перекрытие - это количество времени, которое разделяют два события? Работает ли это для всех возможных способов перекрытия событий?

NSjonas 09.09.2016 23:15

Разделите проблему на случаи, затем обработайте каждый случай.

Ситуация «пересечение двух диапазонов дат» покрывается двумя случаями: первый диапазон дат начинается в пределах второго или второй диапазон дат начинается в пределах первого.

Это было мое решение для javascript с 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;

Представленное здесь решение не работало для всех перекрывающихся диапазонов ...

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

мое рабочее решение было:

AND (
  ('start_date' BETWEEN STARTDATE AND ENDDATE) -- caters for inner and end date outer
  OR
  ('end_date' BETWEEN STARTDATE AND ENDDATE) -- caters for inner and start date outer
  OR
  (STARTDATE BETWEEN 'start_date' AND 'end_date') -- only one needed for outer range where dates are inside.
) 

Вы можете попробовать это:

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

Если вы используете диапазон дат, который еще не закончился (все еще продолжается), например не задано endDate = '0000-00-00' вы не можете использовать BETWEEN, потому что 0000-00-00 не является действительной датой!

Я использовал это решение:

(Startdate BETWEEN '".$startdate2."' AND '".$enddate2."')  //overlap: starts between start2/end2
OR (Startdate < '".$startdate2."' 
  AND (enddate = '0000-00-00' OR enddate >= '".$startdate2."')
) //overlap: starts before start2 and enddate not set 0000-00-00 (still on going) or if enddate is set but higher then startdate2

Если startdate2 больше, чем enddate, перекрытия нет!

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

Не могли бы добавить несколько слов пояснения?

Phantômaxx 18.07.2014 21:31

Это было мое решение, оно возвращает истину, когда значения не перекрываются:

X СТАРТ 1 Y КОНЕЦ 1

СТАРТ 2 B КОНЕЦ 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

Вот что я сделал с помощью 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;
    }

Вот еще одно решение с использованием 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): Executed 20 of 20 SUCCESS (0.003 secs / 0.004 secs)

Для рубина я также нашел это:

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

Ниже запрос дает мне идентификаторы, для которых предоставленный диапазон дат (даты начала и окончания перекрываются с любой из дат (даты начала и окончания) в моем table_name

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

the simplest

Самый простой способ - использовать хорошо спроектированную специальную библиотеку для работы с датой и временем.

someInterval.overlaps( anotherInterval )

java.time и ThreeTen-Extra

Лучшее в бизнесе - это фреймворк java.time, встроенный в Java 8 и новее. Добавьте к этому проект ThreeTen-Extra, который дополняет java.time дополнительными классами, в частности классом 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 с начальным моментом плюс 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 для определения промежутка времени, где начало - инклюзивный, а окончание - эксклюзивный.

Ответ для меня слишком прост, поэтому я создал более общий динамический оператор 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)

У меня была ситуация, когда у нас были даты вместо времени, и даты могли перекрываться только в начале / конце. Пример ниже:

(Зеленый - текущий интервал, синие блоки - допустимые интервалы, красные - перекрывающиеся интервалы).

Я адаптировал ответ Яна Нельсона к следующему решению:

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

Это соответствует всем случаям перекрытия, но игнорирует разрешенные случаи перекрытия.

Простое решение:

compare the two dates: 
    A = the one with smaller start date, B = the one with bigger start date
if (A.end < B.start)
    return false
return true

Вот мое решение в Ява, которое тоже работает с неограниченными интервалами

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 04.10.2017 12:33

@Henrik оба термина работают en.wikipedia.org/wiki/Interval_(mat Mathematics)#Terminology

Khaled.K 04.10.2017 14:03
!startA.after(endB) означает startA <= endB, а !endA.before(startB) означает startB <= endA. Это критерии закрытого интервала, а не открытого интервала.
Henrik 04.10.2017 15:03

@Henrik true, а другие условия, такие как endB == null и startA == null, проверяют наличие открытого интервала.

Khaled.K 04.10.2017 17:32
endB == null, startA == null, endA == null и startB == null - все критерии для проверки неограниченного интервала, а не открытого интервала. Пример различий между неограниченными и открытыми интервалами: (10, 20) и (20, null) - это два открытых интервала, которые не перекрываются. У последнего есть неограниченный конец. Ваша функция вернет истину, но интервалы не перекрываются, потому что интервалы не включают 20. (для простоты используются числа вместо временных меток)
Henrik 04.10.2017 18:39

@Henrik Я понял, я исправил это сейчас.

Khaled.K 05.10.2017 12:11

Математическое решение, данное @Bretana, хорошее, но не учитывает две конкретные детали:

  1. аспект закрытых или полуоткрытых интервалов
  2. пустые интервалы

О закрытом или открытом состоянии границ интервала, решение @Bretana действительно для закрытых интервалов

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

можно переписать для полуоткрытых интервалов в:

(StartA < EndB) and (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, но могут быть и полуоткрытыми, как и пустые интервалы дат) обрабатываются аналогичным образом.

Это расширение отличный ответ, созданное @ charles-bretana.

Однако ответ не делает различия между открытыми, закрытыми и полуоткрытыми (или полузакрытыми) интервалами.

Случай 1: A, B - закрытые интервалы

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

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


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

Перекрытие iff: (StartA <= EndB) and (EndA >= StartB)

Случай 2: A, B - открытые интервалы

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

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

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

Перекрытие iff: (StartA < EndB) and (EndA > StartB)

Случай 3: A, B правое открытое

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

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

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

Условие перекрытия: (StartA < EndB) and (EndA > StartB)

Случай 4: A, B оставлено открытым

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

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

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

Условие перекрытия: (StartA < EndB) and (EndA > StartB)

Дело 5: A правое открытое, B закрытое

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

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


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

Условие перекрытия: (StartA <= EndB) and (EndA > StartB)

так далее...

Наконец, общее условие перекрытия двух интервалов:

(StartA <? EndB) и (EndA> ? StartB)

где ? превращает строгое неравенство в нестрогое всякий раз, когда выполняется сравнение между двумя включенными конечными точками.

Случаи два, три и четыре имеют одинаковое условие перекрытия, это намеренно?

Marie 22.11.2017 17:09

@ Мари, я только что перечислил несколько случаев (не все)

user2314737 22.11.2017 20:34

Это, но так подробно, как Ответ Джонатана Леффлера, было бы тем, что я имел в виду как принятый ответ на вопрос OP.

mbx 20.04.2020 16:23

Если вы предоставляете диапазон дат в качестве входных данных и хотите узнать, перекрывается ли он с существующим диапазоном дат в базе данных, следующие условия могут успешно удовлетворить ваши требования.

Assume you provide a @StartDate and @EndDate from your form input.

условия:

Если @StartDate опережает existingStartDate и отстает от existingEndDate, то мы можем сказать, что @StartDate находится в середине существующего диапазона дат, поэтому мы можем сделать вывод, что он будет перекрываться.

@StartDate >=existing.StartDate And @StartDate <= existing.EndDate) 

Если @StartDate отстает от existingStartDate, но @EndDate опережает existingStartDate, мы можем сказать, что он будет перекрываться

 (@StartDate <= existing.StartDate And @EndDate >= existing.StartDate)

Если @StartDate отстает от existingStartDate, а @EndDate опережает existingEndDate, мы можем сделать вывод, что указанный диапазон дат поглощает существующий диапазон дат, таким образом перекрываясь

 (@StartDate <= existing.StartDate And @EndDate >= existing.EndDate))

Если какое-либо из условий выполняется, указанный вами диапазон дат перекрывается с существующими в базе данных.

Вот код, который творит чудеса:

 var isOverlapping =  ((A == null || D == null || A <= D) 
            && (C == null || B == null || C <= B)
            && (A == null || B == null || A <= B)
            && (C == null || D == null || C <= D));

Где..

  • A -> 1 Пуск
  • B -> 1 конец
  • C -> 2 Старт
  • D -> 2 конец

Доказательство? Посмотрите этот тест суть кода консоли.

Это работает, но я бы предпочел проверить отсутствие перекрытия, только два сценария

John Albert 25.10.2018 14:55

Спасибо, что объяснили это с помощью изображений. Ваш ответ - идеальное решение для этого вопроса.

Rakesh Verma 31.07.2019 13:20

Краткий ответ с использованием momentjs:

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

ответ основан на приведенных выше ответах, но в сокращенном виде.

Самый простой способ запомнить решение -
. min(ends)>max(starts)

компактная формула, которая работает для меня

class ValidityRuleRange {
        private final Date from;
        private final Date to;
    ...
    private boolean isOverlap(ValidityRuleRange vrr) {
        int c1 = from.compareTo(vrr.getTo());
        int c2 = to.compareTo(vrr.getFrom());
        return c1 == 0 || c2 == 0 || c1 + c2 == 0;
    }

Я нашел еще один довольно простой подход. Если начальная и конечная дата daterange1 приходится на дату начала daterange2 или начальная и конечная дата daterange1 приходится на конечную дату daterange2, это означает, что они не пересекаются друг с другом.

public boolean doesIntersect(DateRangeModel daterange1, DateRangeModel  daterange2) {
    return !(
            (daterange1.getStartDate().isBefore(daterange2.getStartDate())
                    && daterange1.getEndDate().isBefore(daterange2.getStartDate())) ||
                    (daterange1.getStartDate().isAfter(daterange2.getStartDate())
                            && daterange1.getEndDate().isAfter(daterange2.getEndDate())));
}
def if_lives_overlap(s1, s2):
    """
    https://stackoverflow.com/a/325964/827391
        5   10    15  20
        ----------------
    s1: -----
    s2:           -----
    >>> if_lives_overlap(s1 = {'start': datetime(2020, 1, 5),
    ...                       'end': datetime(2020, 1, 10)},
    ...                  s2 = {'start': datetime(2020, 1, 15),
    ...                       'end': datetime(2020, 1, 20)})
    False


    s1:           -----
    s2: -----
    >>> if_lives_overlap(s1 = {'start': datetime(2020, 1, 15),
    ...                       'end': datetime(2020, 1, 20)},
    ...                  s2 = {'start': datetime(2020, 1, 5),
    ...                       'end': datetime(2020, 1, 10)})
    False

    s1: ----------
    s2:      ----------
    >>> if_lives_overlap(s1 = {'start': datetime(2020, 1, 5),
    ...                       'end': datetime(2020, 1, 15)},
    ...                  s2 = {'start': datetime(2020, 1, 10),
    ...                       'end': datetime(2020, 1, 20)})
    True

    s1:      ----------
    s2: ----------
    >>> if_lives_overlap(s1 = {'start': datetime(2020, 1, 10),
    ...                       'end': datetime(2020, 1, 20)},
    ...                  s2 = {'start': datetime(2020, 1, 5),
    ...                       'end': datetime(2020, 1, 15)})
    True


    s1: ---------------
    s2:      -----
    >>> if_lives_overlap(s1 = {'start': datetime(2020, 1, 5),
    ...                       'end': datetime(2020, 1, 20)},
    ...                  s2 = {'start': datetime(2020, 1, 10),
    ...                       'end': datetime(2020, 1, 15)})
    True

    s1:      -----
    s2: ---------------
    >>> if_lives_overlap(s1 = {'start': datetime(2020, 1, 10),
    ...                       'end': datetime(2020, 1, 15)},
    ...                  s2 = {'start': datetime(2020, 1, 5),
    ...                       'end': datetime(2020, 1, 20)})
    True
    """

    return (s1['start'] <= s2['end']) and (s1['end'] >= s2['start'])

Поскольку было несколько ответов для разных языков и сред, вот один для стандартного ANSI SQL.

В стандартном SQL это так же просто, как

(StartDate1, EndDate1) overlaps (StartDate2, EndDate2)

предполагая, что все четыре столбца являются столбцами DATE или TIMESTAMP. Он возвращает истину, если оба диапазона имеют хотя бы один общий день (при значениях DATE).

(Однако не все СУБД поддерживают это)


В PostgreSQL также легко проверить включение с помощью диапазоны дат

daterange(StartDate1, EndDate1) @> daterange(StartDate2, EndDate2)

приведенное выше возвращает истину, если второй диапазон полностью включен в первый (что отличается от «перекрытий»).

Другие вопросы по теме