Определите, перекрываются ли два диапазона дат
Учитывая два диапазона дат, каков самый простой или эффективный способ определить, перекрываются ли два диапазона дат?
В качестве примера предположим, что у нас есть диапазоны, обозначенные переменными 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