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

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

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

42 ответа

Решение

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

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

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

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

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

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


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

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

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


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

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

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

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

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

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

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

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

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

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

Вот еще одно решение с использованием 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);
    });
});

Результат при беге с кармой, жасмином и фантомом:

PhantomJS 1.9.8 (Linux): Выполнено 20 из 20 УСПЕХ (0,003 с / 0,004 с)

Вот код, который делает волшебство:

 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 -> 1End
  • C -> 2старт
  • D -> 2End

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

Легко запомнить решение будет
min(ends)>max(starts)

Я бы сделал

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

Вот мое решение на Java, которое работает и на неограниченных интервалах

private Boolean overlap (Timestamp startA, Timestamp endA,
                         Timestamp startB, Timestamp endB)
{
    return (endB == null || startA == null || !startA.after(endB))
        && (endA == null || startB == null || !endA.before(startB));
}

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

---------------------- | ------- -------|----------------------
    |----B1----|
           |----B2----|
               |----B3----|
               |---------- ---------- B4 |
               |----------------B5----------------|
                      |---- В6----|
----------------------|------- -------|----------------------
                      |------B7-------|
                      |----------B8-----------|
                         |----B9----|
                         |----B10-----|
                         |--------B11--------|
                                      |----B12----|
                                         |----B13----|
----------------------|------- ------- | ----------- -----------

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

А ТАКЖЕ (
  ('start_date' BETWEEN STARTDATE AND ENDDATE) - обслуживает внутреннюю и внешнюю дату окончания
  ИЛИ ЖЕ
  ('end_date' BETWEEN STARTDATE AND ENDDATE) - обслуживает внутреннюю и внешнюю дату начала
  ИЛИ ЖЕ
  (STARTDATE BETWEEN 'start_date' И 'end_date') - только один необходим для внешнего диапазона, где даты находятся внутри.) 

Я знаю, что это было помечено как независимое от языка, но для всех вас, реализующих в Java: не изобретайте колесо и не используйте Joda Time.

http://joda-time.sourceforge.net/api-release/org/joda/time/base/AbstractInterval.html)

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

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

      (StartDate1, EndDate1) overlaps (StartDate2, EndDate2)

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

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


В PostgreSQL также легко проверить на включение , используя диапазоны дат.

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

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

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

Это продолжение отличного ответа @charles-bretana.

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

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

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

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


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

Перекрываются, если: (StartA <= EndB) and (EndA >= StartB)

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

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

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

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

Перекрываются, если: (StartA < EndB) and (EndA > StartB)

Дело 3: A, B прямо открыто

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

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

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

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

Дело 4: A, B оставлено открытым

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

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

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

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

Случай 5: Право открыто, Б закрыто

A = [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 StartB)

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

Вот еще один сокращенный ответ с использованием моментов:

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

ответ основан на ответах выше, но его сокращают.

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

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

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

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

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

В 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

простейший

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

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 подход к определению промежутка времени, когда начало включительно, а окончание - исключительно.

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

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

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

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

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

(StartA 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

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

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

По моему мнению, самый простой способ сделать это - сравнить EndDate1 с StartDate2, а EndDate2 с StartDate1.

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

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

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

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

Предположим, вы предоставляете @StartDate а также @EndDate из вашей формы ввода.

условия:

Если @StartDate впереди existingStartDate и позади existingEndDatе тогда мы можем сказать @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))

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

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

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

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

Используя 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;
    }

Вот мое упрощение. Предположим интервалы

             | |
     | |

не перекрываются, xa < xb и ya < yb для упрощения обработки. Вышеуказанные условия могут быть тривиально проверены. Тогда вы закончите с

            ya       yb
      |--------|
xa |--|xb
   |----|
         |----|
ya > xa => return ya - xa < xb - xa
else    => return xa < yb

Как вы могли заметить, второй интервал актуален только в случае, если xa >= ya.

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