É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?
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
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.
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
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 continue
s, mais présente l'avantage d'être comprendre et n'implique pas un nombre de bibliothèques ou de code de conversion de date.
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