Crear un rango de fechas en Python

375

Quiero crear una lista de fechas, comenzando hoy, y retrocediendo un número arbitrario de días, digamos, en mi ejemplo, 100 días. ¿Hay una mejor manera de hacerlo que esto?

import datetime

a = datetime.datetime.today()
numdays = 100
dateList = []
for x in range (0, numdays):
    dateList.append(a - datetime.timedelta(days = x))
print dateList
Thomas Browne
fuente

Respuestas:

459

Marginalmente mejor ...

base = datetime.datetime.today()
date_list = [base - datetime.timedelta(days=x) for x in range(numdays)]
S.Lott
fuente
66
¡Esto no funciona si usa las fechas y horas de la zona horaria y hay un cambio de horario de verano! Quiero decir, cuando usas una fecha de inicio y finalización y la
rellenas de
@ s-lott, ¿no podrías usarlo range(0, numdays)en este caso?
Lukas Juhrich
3
Utilicé este método de una manera ligeramente diferente, para generar diez intervalos de 5 minutos, +/- algún valor (30 segundos en mi caso). Solo pensé en compartir: datetimes = [start + timedelta(seconds=x*60+randint(-30, 30)) for x in range (0, range_end*5, 5)]donde está la variablerange_end = 10
MikeyE
248

Pandas es ideal para series temporales en general y tiene soporte directo para rangos de fechas.

Por ejemplo pd.date_range():

import pandas as pd
from datetime import datetime

datelist = pd.date_range(datetime.today(), periods=100).tolist()

También tiene muchas opciones para hacer la vida más fácil. Por ejemplo, si solo quisiera entre semana, simplemente cambiaría bdate_range.

Ver documentación del rango de fechas

Además, es totalmente compatible con las zonas horarias pytz y puede abarcar sin problemas los cambios de horario de verano de primavera / otoño.

EDITAR por OP:

Si necesita fechas y horas reales de Python, a diferencia de las marcas de tiempo de Pandas:

import pandas as pd
from datetime import datetime

pd.date_range(end = datetime.today(), periods = 100).to_pydatetime().tolist()

#OR

pd.date_range(start="2018-09-09",end="2020-02-02")

Utiliza el parámetro "fin" para que coincida con la pregunta original, pero si desea fechas descendentes:

pd.date_range(datetime.today(), periods=100).to_pydatetime().tolist()
fantabolous
fuente
22
Definitivamente la mejor respuesta si ya se están utilizando pandas. Lo único que agregaría es que usaría el parámetro end = si sus fechas van hacia atrás según la publicación original. pd.date_range (end = pd.datetime.today (), period = 100) .tolist ()
Thomas Browne
55
Un problema con estos métodos es cuando necesita el tipo de fecha y hora de Python ... El uso de Pandas o métodos de rango de fecha numpy creará los tipos de fecha y hora y fecha y hora ... Debe list(map(pd.Timestamp.to_pydatetime, datelist))recuperar el tipo de fecha y hora ...
Malik Koné
La pandas.datetimeclase está en desuso y se eliminará de los pandas en una versión futura. Importar desde el datetimemódulo en su lugar.
Trenton McKinney
82

Obtenga un rango de fechas entre las fechas de inicio y finalización especificadas (Optimizado para la complejidad de tiempo y espacio):

import datetime

start = datetime.datetime.strptime("21-06-2014", "%d-%m-%Y")
end = datetime.datetime.strptime("07-07-2014", "%d-%m-%Y")
date_generated = [start + datetime.timedelta(days=x) for x in range(0, (end-start).days)]

for date in date_generated:
    print date.strftime("%d-%m-%Y")
Sandeep
fuente
66
La sugerencia inicial es usar () en lugar de [] para obtener un generador de fechas. Eficiente en el sentido de que no habrá necesidad de almacenar toda la matriz de fechas y generar una solo cuando sea necesario.
Sandeep
2
Tenga en cuenta que end_date no se genera.
Thorbjørn Ravn Andersen
8
use (end-start + 1) para obtener la fecha de finalización.
Sandeep
66
No responde la pregunta del OP, pero eso es lo que buscaba :)
Thierry J.
2
(end-start+1)no funciona, no puede agregar un timedelta e int. Sin (end-start).days + 1embargo, podrías usar .
Stephen Paulger
44

Puede escribir una función generadora que devuelva objetos de fecha a partir de hoy:

import datetime

def date_generator():
  from_date = datetime.datetime.today()
  while True:
    yield from_date
    from_date = from_date - datetime.timedelta(days=1)

Este generador devuelve fechas a partir de hoy y retrocede un día a la vez. Aquí es cómo tomar las primeras 3 fechas:

>>> import itertools
>>> dates = itertools.islice(date_generator(), 3)
>>> list(dates)
[datetime.datetime(2009, 6, 14, 19, 12, 21, 703890), datetime.datetime(2009, 6, 13, 19, 12, 21, 703890), datetime.datetime(2009, 6, 12, 19, 12, 21, 703890)]

La ventaja de este enfoque sobre la comprensión de un bucle o lista es que puede regresar tantas veces como desee.

Editar

Una versión más compacta que usa una expresión generadora en lugar de una función:

date_generator = (datetime.datetime.today() - datetime.timedelta(days=i) for i in itertools.count())

Uso:

>>> dates = itertools.islice(date_generator, 3)
>>> list(dates)
[datetime.datetime(2009, 6, 15, 1, 32, 37, 286765), datetime.datetime(2009, 6, 14, 1, 32, 37, 286836), datetime.datetime(2009, 6, 13, 1, 32, 37, 286859)]
Ayman Hourieh
fuente
1
Los generadores son la forma ideal de hacerlo, si el número de días es arbitrario.
xssChauhan
32

sí, reinventa la rueda ... solo busca en el foro y obtendrás algo como esto:

from dateutil import rrule
from datetime import datetime

list(rrule.rrule(rrule.DAILY,count=100,dtstart=datetime.now()))
BOFH
fuente
22
Requiere labix.org/python-dateutil . Se ve bien, pero apenas vale la pena una dependencia externa solo para guardar una línea.
Beni Cherniavsky-Paskin
1
No puedo creerle a nadie las otras respuestas son muy pitónicas. Si bien rrule es un nombre terrible, este es al menos fácil de ver.
Boatcoder
77
@ BeniCherniavsky-Paskin: es muy fácil introducir errores sutiles mientras se implementa la aritmética de período (calendario). dateutil.rruleimplementa iCalendar RFC : es más fácil usar funciones con un comportamiento bien definido en lugar de múltiples implementaciones de casi la misma funcionalidad que son muy diferentes. dateutil.rrulepermite limitar la corrección de errores a un solo lugar.
jfs
3
@ Mark0978: el rrulenombre no es arbitrario; es del rfc correspondiente
jfs
1
Sí, no estaba culpando a dateutil por la desafortunada elección de nombres :-)
boatcoder
32

También puede usar el ordinal de día para simplificarlo:

def date_range(start_date, end_date):
    for ordinal in range(start_date.toordinal(), end_date.toordinal()):
        yield datetime.date.fromordinal(ordinal)

O como se sugiere en los comentarios, puede crear una lista como esta:

date_range = [
    datetime.date.fromordinal(ordinal) 
    for ordinal in range(
        start_date.toordinal(),
        end_date.toordinal(),
    )
]
Hosane
fuente
18

Por el título de esta pregunta esperaba encontrar algo así range(), que me permitiera especificar dos fechas y crear una lista con todas las fechas intermedias. De esa manera, no es necesario calcular el número de días entre esas dos fechas, si no se sabe de antemano.

Entonces, con el riesgo de estar un poco fuera de tema, este one-liner hace el trabajo:

import datetime
start_date = datetime.date(2011, 01, 01)
end_date   = datetime.date(2014, 01, 01)

dates_2011_2013 = [ start_date + datetime.timedelta(n) for n in range(int ((end_date - start_date).days))]

Todos los créditos a esta respuesta !

encantador de serpientes
fuente
1
Esa no es una línea más que muchas de las otras respuestas a la pregunta.
Thomas Browne
44
Nunca he pretendido que esto fuera más sencillo que las respuestas de los demás, ni que esta fuera una mejor solución. Mostré un fragmento de código que hace algo ligeramente diferente de lo que pediste, pero que me hubiera encantado encontrar aquí dado el título de la pregunta.
snake_charmer
11

Aquí hay una respuesta ligeramente diferente a partir de la respuesta de S.Lott que da una lista de fechas entre dos fechas starty end. En el siguiente ejemplo, desde principios de 2017 hasta hoy.

start = datetime.datetime(2017,1,1)
end = datetime.datetime.today()
daterange = [start + datetime.timedelta(days=x) for x in range(0, (end-start).days)]
Derek Powell
fuente
7

Un poco de respuesta tardía, lo sé, pero tuve el mismo problema y decidí que la función de rango interno de Python era un poco escasa a este respecto, así que la he anulado en un módulo de utilidad mío.

from __builtin__ import range as _range
from datetime import datetime, timedelta

def range(*args):
    if len(args) != 3:
        return _range(*args)
    start, stop, step = args
    if start < stop:
        cmp = lambda a, b: a < b
        inc = lambda a: a + step
    else:
        cmp = lambda a, b: a > b
        inc = lambda a: a - step
    output = [start]
    while cmp(start, stop):
        start = inc(start)
        output.append(start)

    return output

print range(datetime(2011, 5, 1), datetime(2011, 10, 1), timedelta(days=30))
Rob Young
fuente
1
Creo que quieres output = []y cambiar las líneas en el while cmp(...)bucle. Compara range(0,10,1)y _range(0,10,1).
sigfpe
3
Creo que esta respuesta sería mejor si nombraras la función date_range.
Brandon Bradley
7

En base a las respuestas, escribí para mí esto:

import datetime;
print [(datetime.date.today() - datetime.timedelta(days=x)).strftime('%Y-%m-%d') for x in range(-5, 0)]

Salida:

['2017-12-11', '2017-12-10', '2017-12-09', '2017-12-08', '2017-12-07']

La diferencia es que obtengo el date"objeto", no el " datetime.datetime.

wmlynarski
fuente
7

Si hay dos fechas y necesita el rango intente

from dateutil import rrule, parser
date1 = '1995-01-01'
date2 = '1995-02-28'
datesx = list(rrule.rrule(rrule.DAILY, dtstart=parser.parse(date1), until=parser.parse(date2)))
Nelu
fuente
5

Aquí está la esencia que creé, desde mi propio código, esto podría ayudar. (Sé que la pregunta es demasiado antigua, pero otros pueden usarla)

https://gist.github.com/2287345

(lo mismo a continuación)

import datetime
from time import mktime

def convert_date_to_datetime(date_object):
    date_tuple = date_object.timetuple()
    date_timestamp = mktime(date_tuple)
    return datetime.datetime.fromtimestamp(date_timestamp)

def date_range(how_many=7):
    for x in range(0, how_many):
        some_date = datetime.datetime.today() - datetime.timedelta(days=x)
        some_datetime = convert_date_to_datetime(some_date.date())
        yield some_datetime

def pick_two_dates(how_many=7):
    a = b = convert_date_to_datetime(datetime.datetime.now().date())
    for each_date in date_range(how_many):
        b = a
        a = each_date
        if a == b:
            continue
        yield b, a
roopesh
fuente
4

Aquí hay una línea para que los scripts de bash obtengan una lista de días de la semana, esto es python 3. Modificado fácilmente para lo que sea, el int al final es el número de días en el pasado que desea.

python -c "import sys,datetime; print('\n'.join([(datetime.datetime.today() - datetime.timedelta(days=x)).strftime(\"%Y/%m/%d\") for x in range(0,int(sys.argv[1])) if (datetime.datetime.today() - datetime.timedelta(days=x)).isoweekday()<6]))" 10

Aquí hay una variante para proporcionar una fecha de inicio (o más bien, finalización)

python -c "import sys,datetime; print('\n'.join([(datetime.datetime.strptime(sys.argv[1],\"%Y/%m/%d\") - datetime.timedelta(days=x)).strftime(\"%Y/%m/%d \") for x in range(0,int(sys.argv[2])) if (datetime.datetime.today() - datetime.timedelta(days=x)).isoweekday()<6]))" 2015/12/30 10

Aquí hay una variante para las fechas arbitrarias de inicio y finalización. No es que esto no sea terriblemente eficiente, pero es bueno para poner un bucle for en un script bash:

python -c "import sys,datetime; print('\n'.join([(datetime.datetime.strptime(sys.argv[1],\"%Y/%m/%d\") + datetime.timedelta(days=x)).strftime(\"%Y/%m/%d\") for x in range(0,int((datetime.datetime.strptime(sys.argv[2], \"%Y/%m/%d\") - datetime.datetime.strptime(sys.argv[1], \"%Y/%m/%d\")).days)) if (datetime.datetime.strptime(sys.argv[1], \"%Y/%m/%d\") + datetime.timedelta(days=x)).isoweekday()<6]))" 2015/12/15 2015/12/30
Tim P
fuente
Es posible que desee actualizar su publicación de respuesta con el código en su comentario. Python se destroza en los comentarios y necesita un poco de formato.
Jake Bathman
3

Matplotlib relacionados

from matplotlib.dates import drange
import datetime

base = datetime.date.today()
end  = base + datetime.timedelta(days=100)
delta = datetime.timedelta(days=1)
l = drange(base, end, delta)
Milton
fuente
3

Sé que esto ha sido respondido, pero escribiré mi respuesta con fines históricos, y dado que creo que es sencillo.

import numpy as np
import datetime as dt
listOfDates=[date for date in np.arange(firstDate,lastDate,dt.timedelta(days=x))]

Claro que no ganará nada como code-golf, pero creo que es elegante.

BarocliniCplusplus
fuente
El arangecon los pasos es bastante bueno, pero listOfDatesconsiste en numpy datetime64 en lugar de python datetime nativo.
F.Raab
2
Sin embargo, puede utilizar np.arange(…).astype(dt.datetime)para arangedevolver la fecha y hora de python nativa en lugar de numpy datetime64.
F.Raab
2

Otro ejemplo que cuenta hacia adelante o hacia atrás, a partir de la respuesta de Sandeep.

from datetime import date, datetime, timedelta
from typing import Sequence
def range_of_dates(start_of_range: date, end_of_range: date) -> Sequence[date]:

    if start_of_range <= end_of_range:
        return [
            start_of_range + timedelta(days=x)
            for x in range(0, (end_of_range - start_of_range).days + 1)
        ]
    return [
        start_of_range - timedelta(days=x)
        for x in range(0, (start_of_range - end_of_range).days + 1)
    ]

start_of_range = datetime.today().date()
end_of_range = start_of_range + timedelta(days=3)
date_range = range_of_dates(start_of_range, end_of_range)
print(date_range)

da

[datetime.date(2019, 12, 20), datetime.date(2019, 12, 21), datetime.date(2019, 12, 22), datetime.date(2019, 12, 23)]

y

start_of_range = datetime.today().date()
end_of_range = start_of_range - timedelta(days=3)
date_range = range_of_dates(start_of_range, end_of_range)
print(date_range)

da

[datetime.date(2019, 12, 20), datetime.date(2019, 12, 19), datetime.date(2019, 12, 18), datetime.date(2019, 12, 17)]

Tenga en cuenta que la fecha de inicio está incluida en la declaración, por lo que si desea cuatro fechas totales, use timedelta(days=3)

Chad Lowe
fuente
1
from datetime import datetime, timedelta
from dateutil import parser
def getDateRange(begin, end):
    """  """
    beginDate = parser.parse(begin)
    endDate =  parser.parse(end)
    delta = endDate-beginDate
    numdays = delta.days + 1
    dayList = [datetime.strftime(beginDate + timedelta(days=x), '%Y%m%d') for x in range(0, numdays)]
    return dayList
xmduhan
fuente
1

Un generador de rango de fechas mensual con datetimey dateutil. Simple y fácil de entender:

import datetime as dt
from dateutil.relativedelta import relativedelta

def month_range(start_date, n_months):
        for m in range(n_months):
            yield start_date + relativedelta(months=+m)
aysa
fuente
0
import datetime    
def date_generator():
    cur = base = datetime.date.today()
    end  = base + datetime.timedelta(days=100)
    delta = datetime.timedelta(days=1)
    while(end>base):
        base = base+delta
        print base

date_generator()
Sasmita
fuente
1
Quería retroceder, no avanzar ... Así enddebería ser base - datetime.timedelta. Además ... ¿Por qué esta solución es mejor que la original?
frarugi87
0

De las respuestas anteriores, creé este ejemplo para el generador de fechas

import datetime
date = datetime.datetime.now()
time = date.time()
def date_generator(date, delta):
  counter =0
  date = date - datetime.timedelta(days=delta)
  while counter <= delta:
    yield date
    date = date + datetime.timedelta(days=1)
    counter +=1

for date in date_generator(date, 30):
   if date.date() != datetime.datetime.now().date():
     start_date = datetime.datetime.combine(date, datetime.time())
     end_date = datetime.datetime.combine(date, datetime.time.max)
   else:
     start_date = datetime.datetime.combine(date, datetime.time())
     end_date = datetime.datetime.combine(date, time)
   print('start_date---->',start_date,'end_date---->',end_date)
Dimitris Kougioumtzis
fuente