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

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

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

42 ответа

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

X START 1 Y END 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

Я нашел еще один довольно простой подход. Если начальная и конечная дата 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())));
}

Чтобы охватить все перекрывающиеся случаи в пакете PHP/Laravel Carbon, вы можете расширить логику в ответе Чарльза следующим образом.

      if ( ($startTime1->between($startTime2, $endTime2, true) || $endTime1->between($startTime2, $endTime2, true)) || (($startTime1 <= $endTime2) && ($endTime2 <= $endTime1)) ){
//Complete Overlap, Partial Left Overlap, Partial Right Overlap. 
}

Это проверяет, находится ли StartTime1 в диапазоне (StartTime2-EndTime2) или EndTime1 находится в диапазоне (StartTime2-EndTime2).

Остальная часть для полного перекрытия, как объяснено в других ответах.

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

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

Вот реализация TypeScript, которую мне легко читать и рассуждать. Я написал для него несколько модульных тестов, и он ведет себя так, как я и ожидал.

      type DateRange = {
  start: Date;
  end: Date;
};

const doDateRangesOverlap = (a: DateRange, b: DateRange) => {
  // First, let's figure out which date range starts earlier.
  const [earlierStart, laterStart] = a.start < b.start ? [a, b] : [b, a];

  // Now we can just do a simple check - does the earlier date range
  // end after the later date range has begun?
  return earlierStart.end > laterStart.start;
};
      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'])

Пока для этого есть простые решения, может быть неэффективно использовать дополнительные пакеты только для выполнения простых задач. Однако, если вы уже используетеdate-fnsуже в вашем проекте есть метод, называемыйareIntervalsOverlappingкоторый делает то же самое.

Синтаксис:

      areIntervalsOverlapping(intervalLeft, intervalRight, [options])

Пример:

      // For overlapping time intervals:
areIntervalsOverlapping(
  { start: new Date(2014, 0, 10), end: new Date(2014, 0, 20) },
  { start: new Date(2014, 0, 17), end: new Date(2014, 0, 21) }
)
//=> true

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

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

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

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

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

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;
    }
if (StartDate1 > StartDate2) swap(StartDate, EndDate);

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

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

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
Другие вопросы по тегам