web-dev-qa-db-fra.com

Générer une liste de dates/heures entre un intervalle

Étant donné deux dates (start_date et end_date), j'aimerais générer une liste d'autres dates-heures entre ces deux dates, les nouvelles dates étant séparées par un intervalle variable. par exemple. tous les 4 jours entre 2011-10-10 et 2011-12-12 ou toutes les 8 heures d'ici à 19h.

Peut-être que quelque chose à peu près équivalent à la classe Dateperiod PHP.

Quel serait le moyen le plus efficace d'accomplir cela en Python?

47
Joucks

Utilisez 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

Fonctionne pour les objets dates et datetime. Votre deuxième exemple:

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

Essaye ça:

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

L'exemple de la question "Toutes les 8 heures entre aujourd'hui et demain 19 heures" serait écrit ainsi:

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')    

Notez que les valeurs valides pour period sont celles définies pour les informations relatives relativedelta , à savoir: 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'microseconds'.

Ma solution renvoie un list, comme requis dans la question. Si vous n'avez pas besoin de tous les éléments à la fois, vous pouvez utiliser des générateurs, comme dans @MartijnPieters.

15
Óscar López

J'ai vraiment aimé les deux réponses de Martijn Pieters et de Óscar López ..__ Permettez-moi de suggérer ma solution combinée entre ces deux réponses.

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

Les solutions suggérées ici fonctionnent bien pour des intervalles de jours, d'heures, etc., en utilisant timedelta ou tout ce que dateutil.relativedelta prend en charge si vous souhaitez vous fier à des bibliothèques tierces. Mais je voulais partager ma solution pour le cas spécifique des intervalles mensuels dans le format yyyymm, demandé ici (mais marqué comme une copie de cette question).

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'))

Sortie:

['201710', '201711', '201712', '201801', '201802', '201803']

Cette solution est assez spécifique à ce besoin particulier de formatage yyyymm (même si elle est fréquente dans mon monde, du moins) et peut ne pas être la réponse la plus efficace avec le grand nombre de continues, mais présente l'avantage d'être comprendre et n'implique pas un nombre de bibliothèques ou de code de conversion de date.

0
Dan Coates

Toutes les solutions données jusqu'ici sont spécifiques aux cas où start <stop, mais vous pouvez facilement les adapter pour traiter les cas où stop <commence à utiliser le module opérateur comme le code suivant, adapté de la réponse de @ MartijnPieters, illustre.

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