2016-06-16 2 views
5

Я ищу, чтобы найти пересекающиеся даты между двумя датами в диапазоне, как:перекрывающиеся даты между двумя диапазонами дат в питоне

range1 = start(2016-06-01) end (2016-06-20) 
range2 = start(2016-06-10) end (2016-06-13) 

результата здесь 4 даты (2016-06-10,2016-06-11,2016-06-12,2016-06-13). другой пример:

range1 = start(2016-06-01) end (2016-06-20) range2 = start(2016-06-18) 
end (2016-06-25) 

результат здесь 3 даты (2016-06-18,2016-06-19,2016-06-20). , и если даты не совпадают, то результат равен 0 датам.

Обнаружено, что this полезен при определении количества совпадающих дат, но мне было интересно, могу ли я получить фактические даты, не используя длинные инструкции if/else?

Заранее благодарен!

ответ

2

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

from datetime import date, timedelta 


def f(d1, d2): 
    delta = d2 - d1 
    return set([d1 + timedelta(days=i) for i in range(delta.days + 1)]) 

range1 = [date(2016, 6, 1), date(2016, 6, 20)] 
range2 = [date(2016, 6, 10), date(2016, 6, 13)] 

print f(*range1) & f(*range2) 

Для выполнения целей можно также отливать d1 + timedelta(days=i) к str при генерации даты в заданном диапазоне.

+0

Thanks Kardaj! просто интересно о части производительности, почему кастинг в строку улучшит производительность? – tkyass

+0

Поскольку в случае, когда вы собираетесь генерировать много дат, вы предпочитаете хранить менее сложную структуру данных, такую ​​как строка, которая для этого случая не приведет к потере информации. – kardaj

1

Когда вы не работаете с Python3 еще и, следовательно, не может использовать память эффективных range объектов, вы могли бы сделать namedtuple, как показано на the answer you link to (иначе вы могли бы точно также использовать новые range объекты). Оттуда, все, что вам нужно сделать, это использовать datetime.date.fromordinal на перекрывающей DATERANGE:

>>> from datetime import date 
>>> from collections import namedtuple 
>>> Range = namedtuple('Range', ['start', 'end']) 
>>> r1 = Range(start=date(2016, 1, 1), end=date(2016, 2, 5)) 
>>> r2 = Range(start=date(2016, 1, 28), end=date(2016, 2, 28)) 
>>> latest_start = max(r1.start, r2.start) 
>>> earliest_end = min(r1.end, r2.end) 
>>> overlap = (earliest_end - latest_start).days + 1 
>>> overlapping_dates = [] # default 
>>> if overlap > 0: 
...  overlapping_dates = range(latest_start.toordinal(), earliest_end.toordinal() + 1) # as numbers 
...  overlapping_dates = [ date.fromordinal(x) for x in overlapping_dates ] # back to datetime.date objects 
... 
>>> overlapping_dates 
[datetime.date(2016, 1, 28), 
datetime.date(2016, 1, 29), 
datetime.date(2016, 1, 30), 
datetime.date(2016, 1, 31), 
datetime.date(2016, 2, 1), 
datetime.date(2016, 2, 2), 
datetime.date(2016, 2, 3), 
datetime.date(2016, 2, 4), 
datetime.date(2016, 2, 5)] 

Подход с использованием set тоже будет работать (один из этих подходов в истории редактирования этого ответа), но, как правило, менее эффективен, потому что он должен иметь все даты в памяти, даже те, которые не находятся на перекрестке.

0

я написал небольшую функцию для этого в последнее время, который возвращает есть ли перекрытие, а затем из-ранжировать

def date_overlap(start1, end1, start2, end2): 
    overlaps = start1 <= end2 and end1 >= start2 
    if not overlaps: 
     return False, None, None 
    return True, max(start1, start2), min(end1, end2) 

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

Смежные вопросы