Bei zwei Datumsangaben (start_date
und end_date
) möchte ich zwischen diesen beiden Datumsangaben eine Liste mit anderen Datumsangaben erstellen, wobei die neuen Datumsangaben durch ein variables Intervall voneinander getrennt sind. z.B. alle 4 Tage zwischen 2011-10-10 und 2011-12-12 oder alle 8 Stunden zwischen jetzt und morgen 19:00 Uhr.
Vielleicht entspricht dies ungefähr der Klasse Dateperiod PHP.
Was wäre der effizienteste Weg, um dies in Python zu erreichen?
Verwenden Sie datetime.timedelta
:
from datetime import date, datetime, timedelta
def perdelta(start, end, delta):
curr = start
while curr < end:
yield curr
curr += delta
>>> for result in perdelta(date(2011, 10, 10), date(2011, 12, 12), timedelta(days=4)):
... print result
...
2011-10-10
2011-10-14
2011-10-18
2011-10-22
2011-10-26
2011-10-30
2011-11-03
2011-11-07
2011-11-11
2011-11-15
2011-11-19
2011-11-23
2011-11-27
2011-12-01
2011-12-05
2011-12-09
Funktioniert sowohl für Datums- als auch für Datumszeitobjekte. Dein zweites Beispiel:
>>> for result in perdelta(datetime.now(),
... datetime.now().replace(hour=19) + timedelta(days=1),
... timedelta(hours=8)):
... print result
...
2012-05-21 17:25:47.668022
2012-05-22 01:25:47.668022
2012-05-22 09:25:47.668022
2012-05-22 17:25:47.668022
Versuche dies:
from datetime import datetime
from dateutil.relativedelta import relativedelta
def date_range(start_date, end_date, increment, period):
result = []
nxt = start_date
delta = relativedelta(**{period:increment})
while nxt <= end_date:
result.append(nxt)
nxt += delta
return result
Das Beispiel in der Frage "Alle 8 Stunden zwischen jetzt und morgen 19:00" würde folgendermaßen aussehen:
start_date = datetime.now()
end_date = start_date + relativedelta(days=1)
end_date = end_date.replace(hour=19, minute=0, second=0, microsecond=0)
date_range(start_date, end_date, 8, 'hours')
Beachten Sie, dass die gültigen Werte für period
die für die relativedelta
relative Informationen definierten Werte sind, nämlich: 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'microseconds'
.
Meine Lösung gibt ein list zurück, wie in der Frage erforderlich. Wenn Sie nicht alle Elemente gleichzeitig benötigen, können Sie Generatoren verwenden, wie in @MartijnPieters antworten.
Ich mochte die beiden Antworten von @Martijn Pieters und @ Óscar López wirklich sehr.
from datetime import date, datetime, timedelta
def datetime_range(start, end, delta):
current = start
if not isinstance(delta, timedelta):
delta = timedelta(**delta)
while current < end:
yield current
current += delta
start = datetime(2015,1,1)
end = datetime(2015,1,31)
#this unlocks the following interface:
for dt in datetime_range(start, end, {'days': 2, 'hours':12}):
print dt
print dt
2015-01-01 00:00:00
2015-01-03 12:00:00
2015-01-06 00:00:00
2015-01-08 12:00:00
2015-01-11 00:00:00
2015-01-13 12:00:00
2015-01-16 00:00:00
2015-01-18 12:00:00
2015-01-21 00:00:00
2015-01-23 12:00:00
2015-01-26 00:00:00
2015-01-28 12:00:00
Die hier vorgeschlagenen Lösungen eignen sich gut für Intervalle von Tagen, Stunden usw., die timedelta
verwenden, oder alles, was dateutil.relativedelta
unterstützt, wenn Sie auf Bibliotheken von Drittanbietern zurückgreifen möchten. Aber ich wollte meine Lösung für den konkreten Fall von monatlichen Intervallen im Format yyyymm teilen, fragte here (aber als Duplikat dieser Frage markiert).
def iterate_months(start_ym, end_ym):
for ym in range(int(start_ym), int(end_ym) + 1):
if ym % 100 > 12 or ym % 100 == 0:
continue
yield str(ym)
list(iterate_months('201710', '201803'))
Ausgabe:
['201710', '201711', '201712', '201801', '201802', '201803']
Diese Lösung ist ziemlich spezifisch für diese spezielle Notwendigkeit für die Formatierung von JJJJMM (obwohl sie in meiner Welt zumindest häufig vorkommt) und ist möglicherweise nicht die effizienteste Antwort mit der großen Anzahl von continue
s verstehen und nicht mehrere Bibliotheken oder Datumsumwandlungscode enthalten.
Alle hier angegebenen Lösungen sind spezifisch für Fälle, in denen start <stop ist, aber Sie können sie leicht anpassen, um Fälle zu behandeln, in denen stop <das Bedienermodul verwendet, wie der folgende Code, angepasst an @ MartijnPieters Antwort, veranschaulicht.
import datetime
import operator
def time_range(start: datetime.datetime, stop, step: datetime.timedelta):
"Imitate range function for datetimes instead of ints."
sec = step.total_seconds()
if sec == 0:
raise ValueError("step must not be 0 seconds")
if sec < 0:
compare = operator.gt
else:
compare = operator.lt
x = start
while compare(x, stop):
yield x
x += step # immutable