wake-up-neo.com

Generieren Sie eine Liste von Datumszeiten zwischen einem Intervall

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?

47
Joucks

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
73
Martijn Pieters

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.

15
Óscar López

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
7
Todor

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 continues verstehen und nicht mehrere Bibliotheken oder Datumsumwandlungscode enthalten.

0
Dan Coates

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
0
Victor