2012-05-21 13 views
42

Dadas dos fechas (start_date y end_date), me gustaría generar una lista de otras fechas y horas entre estas dos fechas, las nuevas fechas están separadas por un intervalo variable. p.ej. cada 4 días entre 2011-10-10 y 2011-12-12 o cada 8 horas entre hoy y mañana a las 19p.m.Generar una lista de fechas entre un intervalo

Tal vez algo más o menos equivalente a la clase PHP Dateperiod.

¿Cuál sería la forma más eficiente de lograr esto en Python?

Respuesta

65

Uso 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 

funciona tanto para las fechas y los objetos de fecha y hora. Su segundo ejemplo:

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

Python debería permitir las fechas en 'rango'. Solo tendría sentido. Hay una razón por la que es débil tipeado ... –

+3

@TylerCrompton: "Explicit es mejor que implícito". ¿Qué se incrementaría en el rango: días, segundos, microsegundos, milisegundos, minutos, horas o semanas? –

+3

@NoctisSkytower El valor del paso sería un objeto 'timedelta'. –

13

Prueba esto:

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 

El ejemplo de la pregunta, "cada 8 horas entre hoy y mañana 19:00" se escribiría así:

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

Observe que los valores válidos para period son los definidos para la información relativa al relativedelta, a saber: 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'microseconds'.

Mi solución devuelve lista, como se requiere en la pregunta. Si no necesita todos los elementos a la vez, puede usar generadores, como en la respuesta de @MartijnPieters.

4

Me han gustado las dos respuestas de @Martijn Pieters y @ Óscar López. Permítanme sugerir mi solución combinada entre esas dos respuestas.

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