Efektywne obliczanie nakładania się zakresów dat w Pythonie?

Mam dwa zakresy dat, gdzie każdy zakres jest określony przez datę początkową i końcową (oczywiście datetime.date ()). Te dwa zakresy mogą się nakładać lub nie. Potrzebuję liczby dni nakładania się. Oczywiście mogę wstępnie wypełnić dwa zestawy ze wszystkimi datami w obu zakresach i wykonać przecięcie zestawu, ale to jest możliwe inefficient...is czy istnieje lepszy sposób, poza innym rozwiązaniem, używając długiej sekcji if-elif obejmującej wszystkie przypadki ?

Author: Oz123, 2012-01-28

5 answers

  • Określ ostatnią z dwóch dat rozpoczęcia i najwcześniejszą z dwóch dat zakończenia.
  • Oblicz timedelta, odejmując je.
  • jeśli delta jest dodatnia, to jest liczba dni nakładania się.

Oto przykładowe obliczenie:

>>> from datetime import datetime
>>> from collections import namedtuple
>>> Range = namedtuple('Range', ['start', 'end'])

>>> r1 = Range(start=datetime(2012, 1, 15), end=datetime(2012, 5, 10))
>>> r2 = Range(start=datetime(2012, 3, 20), end=datetime(2012, 9, 15))
>>> latest_start = max(r1.start, r2.start)
>>> earliest_end = min(r1.end, r2.end)
>>> delta = (earliest_end - latest_start).days + 1
>>> overlap = max(0, delta)
>>> overlap
52
 117
Author: Raymond Hettinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-29 03:54:03

Wywołania funkcji są droższe niż operacje arytmetyczne.

Najszybszy sposób wykonania tego wymaga 2 odejmowania i 1 min ():

min(r1.end - r2.start, r2.end - r1.start).days + 1

W porównaniu z następnym najlepszym, który wymaga 1 odejmowania, 1 min () i max ():

(min(r1.end, r2.end) - max(r1.start, r2.start)).days + 1

Oczywiście w przypadku obu wyrażeń nadal trzeba sprawdzić dodatnie nakładanie się.

 6
Author: John Machin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-28 10:10:28

Pseudocode:

 1 + max( -1, min( a.dateEnd, b.dateEnd) - max( a.dateStart, b.dateStart) )
 3
Author: ypercubeᵀᴹ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-28 09:07:52

Zaimplementowałem klasę TimeRange, jak widać poniżej.

Get_overlapped_range najpierw neguje wszystkie Nie pokrywające się opcje przez prosty warunek, a następnie oblicza pokrywający się zakres, biorąc pod uwagę wszystkie możliwe opcje.

Aby uzyskać liczbę dni, musisz wziąć wartość TimeRange, która została zwrócona z get_overlapped_range i podzielić czas trwania przez 60*60*24 (:

Class TimeRange (object):

def __init__(self, start, end):
    self.start = start
    self.end = end
    self.duration = self.end - self.start

def is_overlapped(self, time_range):
    if max(self.start, time_range.start) < min(self.end, time_range.end):
        return True
    else:
        return False

def get_overlapped_range(self, time_range):
    if not self.is_overlapped(time_range):
        return

    if time_range.start >= self.start:
        if self.end >= time_range.end:
            return TimeRange(time_range.start, time_range.end)
        else:
            return TimeRange(time_range.start, self.end)
    elif time_range.start < self.start:
        if time_range.end >= self.end:
            return TimeRange(self.start, self.end)
        else:
            return TimeRange(self.start, time_range.end)

def __repr__(self):
    return '{0} ------> {1}'.format(*[time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(d))
                                      for d in [self.start, self.end]])
 1
Author: Elad Sofer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-15 14:37:49
def get_overlap(r1,r2):
    latest_start=max(r1[0],r2[0])
    earliest_end=min(r1[1],r2[1])
    delta=(earliest_end-latest_start).days
    if delta>0:
        return delta+1
    else:
        return 0
 0
Author: andros1337,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-02 10:49:14