Пересечение интервала даты Python

Из общего интереса мне интересно, есть ли более элегантный/эффективный способ сделать это. У меня есть функция, которая сравнивает два начальных/конечных кортежа дат, возвращающих true, если они пересекаются.

from datetime import date
def date_intersection(t1, t2):
    t1start, t1end = t1[0], t1[1]
    t2start, t2end = t2[0], t2[1]

    if t1end < t2start: return False
    if t1end == t2start: return True
    if t1start == t2start: return True
    if t1start < t2start and t2start < t1end: return True
    if t1start > t2start and t1end < t2end: return True
    if t1start < t2start and t1end > t2end: return True
    if t1start < t2end and t1end > t2end: return True
    if t1start > t2start and t1start < t2end: return True
    if t1start == t2end: return True
    if t1end == t2end: return True 
    if t1start > t2end: return False

so if:

d1 = date(2000, 1, 10)
d2 = date(2000, 1, 11)
d3 = date(2000, 1, 12)
d4 = date(2000, 1, 13)

тогда:

>>> date_intersection((d1,d2),(d3,d4))
False
>>> date_intersection((d1,d2),(d2,d3))
True
>>> date_intersection((d1,d3),(d2,d4))
True

и т.п.

Мне любопытно узнать, есть ли более питонический/элегантный/более эффективный/менее подробный/в целом лучший способ сделать это, возможно, с помощью mxDateTime или какого-нибудь умного хака с timedelta или set()?

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

Спасибо


person jjon    schedule 15.09.2010    source источник
comment
Вы должны использовать timedeltas для представления длительности t1 ⇔ t2, и я думаю о следующем бите. docs.python.org/library/datetime.html#datetime.timedelta   -  person msw    schedule 15.09.2010


Ответы (6)


На самом деле это не более Pythonic, но вы можете просто по логике определить пересечение. Эти конкретные проблемы возникают часто:

return (t1start <= t2start <= t1end) or (t2start <= t1start <= t2end)

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

person Andrew    schedule 15.09.2010
comment
да! очень хорошо. Это все еще заставляет меня немного косить, но вы свернули мой исчерпывающий анализ случая в очень четкое выражение. Большое спасибо, это помогает прояснить мои мысли. - person jjon; 16.09.2010
comment
Согласно моему модульному тесту, это не работает, если конец t1 является началом t2. - person David D.; 22.11.2017
comment
Когда t2start ‹= t1end? Я думаю, вам следует еще раз проверить модульный тест. - person Andrew; 23.11.2017

Альтернативное и, надеюсь, более понятное решение:

def has_overlap(A_start, A_end, B_start, B_end):
    latest_start = max(A_start, B_start)
    earliest_end = min(A_end, B_end)
    return latest_start <= earliest_end:

Мы можем легко получить интервал перекрытия, это (latest_start, earliest_end). Обратите внимание, что самое последнее_начало может быть равно самому раннему_концу.

Следует отметить, что это предполагает, что A_start <= A_end и B_start <= B_end.

person OliverUv    schedule 15.11.2012
comment
Этот самый читаемый. Кроме того, вы можете легко вернуть (earliest_end-latest_start), чтобы получить измерение общего количества перекрытий. - person cxrodgers; 26.08.2014
comment
Это лучший ответ! - person SeF; 30.04.2021

Вот версия, которая дает вам диапазон пересечения. ИМХО, это может быть не самое оптимальное количество условий, но оно ясно показывает, когда t2 перекрывается с t1. Вы можете изменить на основе других ответов, если вам просто нужен истинный/ложный.

if (t1start <= t2start <= t2end <= t1end):
    return t2start,t2end
elif (t1start <= t2start <= t1end):
    return t2start,t1end
elif (t1start <= t2end <= t1end):
    return t1start,t2end
elif (t2start <= t1start <= t1end <= t2end):
    return t1start,t1end
else:
    return None
person fseto    schedule 15.09.2010
comment
FYI... операторы сравнения цепочки: stackoverflow.com/ вопросы/101268/скрытые-функции-питона/ - person fseto; 16.09.2010
comment
Большое спасибо за ссылку на цепочки операторов, это очень полезно. - person jjon; 16.09.2010
comment
Это можно уточнить, используя min в датах окончания. например если начало1 ‹= начало2 ‹= конец1: возврат (начало2, мин(конец1, конец2)) - person JeremyKun; 22.01.2014

Final Comparison: start <= other_finish and other_start <= finish

# All of the conditions below result in overlap I have left out the non overlaps

start <= other_start | start <= other_finish | other_start <= finish | finish <= other_finish 

      0                        1                        1                        0                   
      0                        1                        1                        1
      1                        1                        1                        0          
      1                        1                        1                        1

Только start ‹= other_finish и other_start ‹=finish должны быть истинными, чтобы возвращалось перекрытие.

person kameron tanseli    schedule 11.02.2016
comment
Хороший ответ будет включать некоторое объяснение того, почему это что-то решит. - person Qirel; 11.02.2016

intersection_length = min(t1end, t2end) - max(t1start, t2start)
return intersection_length >= 0 

Это примет пересечение, состоящее из одной точки; в противном случае используйте > 0.

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

  1. Это правильный интервал, возможно, нулевой длины.

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

person Monkimo    schedule 06.10.2020

if t1end < t2start or t1start > t2end: return False
if t1start <= t2end or t2start <= t1start: return True
return False

Разве это не покроет все пересекающиеся множества?

person nmichaels    schedule 15.09.2010
comment
Что ж, ваш код упрощает возврат t1end ›= t2start и t1start ‹= t2end - person Andrew; 16.09.2010