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

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

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

30 ответов


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


Примечание: это включает в себя условия, где края перекрываются ровно. Если вы хотите исключить это,
изменить >= операторы > и <= to <


примечание 2. Спасибо в @Baodad, см. этот блог, фактическое перекрытие меньше:
{ endA-startA, endA - startB, endB-startA, endB - startB }

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


ПРИМЕЧАНИЕ3. Благодаря @tomosius, более короткая версия гласит:
DateRangesOverlap = max(start1, start2) < min(end1, end2)
На самом деле это синтаксический ярлык для более длинной реализации, который включает дополнительные проверки, чтобы убедиться, что даты начала включены или до даты окончания. Выводя это свыше:

если даты начала и окончания могут быть не в порядке, то есть, если это возможно, что startA > endA или startB > endB, то вы также должны проверить, что они в порядке, так что это означает, что вы должны добавить два дополнительных правила действительности:
(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB) или:
(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB) или,
(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB)) или:
(Max(StartA, StartB) <= Min(EndA, EndB)

но реализовать Min() и Max(), вы должны закодировать (используя тройку C для краткости),:
(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)


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

(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

enter image description here


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


короткий (иш) ответ: учитывая два интервала дат A и B С ЭЛЕМЕНТАМИ .start и .end ограничение .start <= .end, то два интервала перекрываются, если:

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

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


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. Специальности моего решения:

  • обрабатывает нулевые значения как бесконечность
  • предполагает, что нижняя граница является инклюзивной,а верхняя-эксклюзивной.
  • поставляется с кучей тестов

тесты основаны на целых числах, но поскольку объекты date в JavaScript сопоставимы, вы можете просто бросить два объекта date. Или ты можешь добавить миллисекунду. отметка времени.

код:

/**
 * Compares to comparable objects to find out whether they overlap.
 * It is assumed that the interval is in the format [from,to) (read: from is inclusive, to is exclusive).
 * A null value is interpreted as infinity
 */
function intervalsOverlap(from1, to1, from2, to2) {
    return (to2 === null || from1 < to2) && (to1 === null || to1 > from2);
}

тесты:

describe('', function() {
    function generateTest(firstRange, secondRange, expected) {
        it(JSON.stringify(firstRange) + ' and ' + JSON.stringify(secondRange), function() {
            expect(intervalsOverlap(firstRange[0], firstRange[1], secondRange[0], secondRange[1])).toBe(expected);
        });
    }

    describe('no overlap (touching ends)', function() {
        generateTest([10,20], [20,30], false);
        generateTest([20,30], [10,20], false);

        generateTest([10,20], [20,null], false);
        generateTest([20,null], [10,20], false);

        generateTest([null,20], [20,30], false);
        generateTest([20,30], [null,20], false);
    });

    describe('do overlap (one end overlaps)', function() {
        generateTest([10,20], [19,30], true);
        generateTest([19,30], [10,20], true);

        generateTest([10,20], [null,30], true);
        generateTest([10,20], [19,null], true);
        generateTest([null,30], [10,20], true);
        generateTest([19,null], [10,20], true);
    });

    describe('do overlap (one range included in other range)', function() {
        generateTest([10,40], [20,30], true);
        generateTest([20,30], [10,40], true);

        generateTest([10,40], [null,null], true);
        generateTest([null,null], [10,40], true);
    });

    describe('do overlap (both ranges equal)', function() {
        generateTest([10,20], [10,20], true);

        generateTest([null,20], [null,20], true);
        generateTest([10,null], [10,null], true);
        generateTest([null,null], [null,null], true);
    });
});

результат при запуске с karma & jasmine & PhantomJS:

PhantomJS 1.9.8 (Linux): выполнено 20 из 20 успешных (0.003 сек / 0.004 сек)


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

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


Я бы сделал

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

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

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

здесь 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));
}

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

enter image description here

вот код, который делает магию:

 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 - > 1Start
  • B - > 1End
  • C - > 2Start
  • D - > 2End

доказательства? Проверьте этот тест консольный код gist.


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


простой

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

someInterval.overlaps( anotherInterval )

java.time & ThreeTen-Extra

лучшее в бизнесе-это java.time фреймворк, встроенный в Java 8 и более поздние версии. Добавьте к этому ThreeTen-Extra проект, который дополняет java.время с дополнительными классами, в частности Interval класс нам нужно здесь.

что касается language-agnostic тег по этому вопросу исходный код для обоих проектов доступен для использования на других языках (обратите внимание на их лицензии).

Interval

на org.threeten.extra.Interval класс удобен, но требует моментов времени даты (java.time.Instant objects), а не только значения даты. Поэтому мы продолжаем использовать первый момент дня в 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:

все они используют Half-Open подход к определению промежутка времени, где начало включительно и эксклюзивные.


это расширение на отличный ответ!--12--> by @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)

etc...

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

(StartA StartB)

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


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

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

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

использование Java util.Свидание, вот что я сделал.

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

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

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


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

enter image description here

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

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

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

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


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

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

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

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


if (StartDate1 > StartDate2) swap(StartDate, EndDate);

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

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

ситуация "два диапазона дат пересекаются" покрывается двумя случаями - первый диапазон дат начинается во втором, или второй диапазон дат начинается в первом.


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

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

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

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

для ruby я также нашел это:

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

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