Creazione di un intervallo di date in Python


375

Voglio creare un elenco di date, a partire da oggi, e tornare indietro di un numero arbitrario di giorni, diciamo, nel mio esempio 100 giorni. C'è un modo migliore per farlo di questo?

import datetime

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

Risposte:


459

Marginalmente meglio ...

base = datetime.datetime.today()
date_list = [base - datetime.timedelta(days=x) for x in range(numdays)]

6
Questo non funziona se si utilizzano i periodi di tempo sensibili al fuso orario e c'è un turno di ora legale !!! Voglio dire, quando usi una data di inizio e di fine e la riempi in questo modo ...
gabn88

@ s-lott non potresti semplicemente usarlo range(0, numdays)in questo caso?
Lukas Juhrich,

3
Ho usato questo metodo in modo leggermente diverso, per generare intervalli di dieci, 5 minuti, +/- un valore (30 secondi nel mio caso). Ho appena pensato di condividere: datetimes = [start + timedelta(seconds=x*60+randint(-30, 30)) for x in range (0, range_end*5, 5)]dove la variabilerange_end = 10
MikeyE

248

Pandas è ottimo per le serie storiche in generale e ha un supporto diretto per gli intervalli di date.

Per esempio pd.date_range():

import pandas as pd
from datetime import datetime

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

Ha anche molte opzioni per semplificare la vita. Ad esempio, se volevi solo i giorni feriali, devi semplicemente scambiarti bdate_range.

Vedi la documentazione dell'intervallo di date

Inoltre supporta pienamente i fusi orari di Pytz e può attraversare senza difficoltà turni di ora legale primavera / autunno.

MODIFICA per OP:

Se hai bisogno di dati effettivi su Python, al contrario dei timestamp di Panda:

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

Questo utilizza il parametro "end" per abbinare la domanda originale, ma se vuoi le date decrescenti:

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

22
Defo la risposta migliore se i panda sono già in uso. L'unica cosa che aggiungerei è che useresti il ​​parametro end = se le tue date tornano indietro come nel post originale. pd.date_range (end = pd.datetime.today (), period = 100) .tolist ()
Thomas Browne,

5
Un problema con questo metodo è quando hai bisogno del tipo Datetime Python ... L'uso dei panda o dei metodi di intervallo di date numpy creerà i tipi Timestamp e Datetime64 ... Devi list(map(pd.Timestamp.to_pydatetime, datelist))recuperare il tipo datetime ...
Malik Koné

La pandas.datetimeclasse è obsoleta e verrà rimossa dai panda in una versione futura. Importa datetimeinvece dal modulo.
Trenton McKinney,

82

Ottieni un intervallo di date tra la data di inizio e di fine specificata (ottimizzata per complessità temporale e di spazio):

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

6
Il suggerimento iniziale è di usare () invece di [] per ottenere un date_generator. Efficiente nel senso che non sarà necessario memorizzare l'intera gamma di date e generarne una solo quando necessario.
Sandeep,

2
Nota end_date non viene generato.
Thorbjørn Ravn Andersen,

8
utilizzare (end-start + 1) per ottenere la data di fine.
Sandeep,

6
Non risponde alla domanda del PO, ma è quello che stavo cercando :)
Thierry J.,

2
(end-start+1)non funziona, non è possibile aggiungere un timedelta e int. Potresti usare (end-start).days + 1però.
Stephen Paulger,

44

È possibile scrivere una funzione generatore che restituisce oggetti data a partire da oggi:

import datetime

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

Questo generatore restituisce le date a partire da oggi e andando indietro di un giorno alla volta. Ecco come prendere le prime 3 date:

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

Il vantaggio di questo approccio rispetto alla comprensione di un loop o di una lista è che puoi tornare indietro tutte le volte che vuoi.

modificare

Una versione più compatta che utilizza un'espressione del generatore anziché una funzione:

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

1
Generatori è il modo ideale per farlo, se il numero di giorni è arbitrario.
xssChauhan,

32

sì, reinventa la ruota .... basta cercare nel forum e otterrai qualcosa del genere:

from dateutil import rrule
from datetime import datetime

list(rrule.rrule(rrule.DAILY,count=100,dtstart=datetime.now()))

22
Richiede labix.org/python-dateutil . Sembra carino ma difficilmente vale una dipendenza esterna solo per salvare una riga.
Beni Cherniavsky-Paskin,

1
Non riesco a credere a nessuno, le altre risposte sono molto pitoniche. Mentre la regola è un nome terribile, questo è almeno facile per gli occhi.
Boatcoder

7
@ BeniCherniavsky-Paskin: è molto facile introdurre bug sottili durante l'implementazione dell'aritmetica del periodo (calendario). dateutil.rruleimplementa iCalendar RFC - è più facile usare funzioni con un comportamento ben definito invece di molteplici implementazioni con quasi le stesse funzionalità che sono così leggermente diverse. dateutil.rruleconsente di limitare la correzione dei bug in un unico posto.
jfs,

3
@ Mark0978: il rrulenome non è arbitrario; proviene dal corrispondente rfc
jfs il

1
Sì, non stavo incolpando dateutil per la sfortunata scelta di nomi :-)
boatcoder

32

Puoi anche usare l'ordinale del giorno per semplificare:

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

O come suggerito nei commenti è possibile creare un elenco come questo:

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

18

Dal titolo di questa domanda mi aspettavo di trovare qualcosa del genere range(), che mi permettesse di specificare due date e creare un elenco con tutte le date in mezzo. In questo modo non è necessario calcolare il numero di giorni tra queste due date, se non lo si conosce in anticipo.

Quindi, con il rischio di essere leggermente fuori tema, questo one-liner fa il lavoro:

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

Tutti i crediti per questa risposta !


1
Questa non è una fodera più di molte delle altre risposte alla domanda.
Thomas Browne,

4
Non ho mai fatto finta che questa fosse più una linea di risposta delle altre risposte, né che questa fosse una soluzione migliore. Ho mostrato un pezzo di codice che fa qualcosa di leggermente diverso da quello che hai chiesto, ma che sarei stato felice di trovare qui dato il titolo della domanda.
snake_charmer,

11

Ecco una risposta leggermente diversa a partire dalla risposta di S.Lott che fornisce un elenco di date tra due date starte end. Nell'esempio seguente, dall'inizio del 2017 ad oggi.

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

7

Lo so un po 'di una risposta tardiva, ma ho avuto lo stesso problema e ho deciso che la funzione di intervallo interno di Python era un po' carente da questo punto di vista, quindi l'ho ignorato in un mio modulo util.

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

1
Penso che tu voglia output = []e scambiare le linee nel while cmp(...)ciclo. Confronta range(0,10,1)e _range(0,10,1).
sigfpe,

3
Penso che questa risposta sarebbe migliore se si nominasse la funzione date_range.
Brandon Bradley,

7

Sulla base delle risposte che ho scritto per me stesso:

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

Produzione:

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

La differenza è che ottengo l' dateoggetto " ", non quello " datetime.datetime".


7

Se ci sono due date e hai bisogno dell'intervallo, prova

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

5

Ecco cosa ho creato, dal mio codice, questo potrebbe aiutare. (So ​​che la domanda è troppo vecchia, ma altri possono usarla)

https://gist.github.com/2287345

(stessa cosa sotto)

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

4

Ecco una riga per gli script bash per ottenere un elenco di giorni feriali, questo è Python 3. Facilmente modificabile per qualsiasi cosa, l'int alla fine è il numero di giorni nel passato che desideri.

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

Ecco una variante per fornire una data di inizio (o meglio, di fine)

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

Ecco una variante per date di inizio e fine arbitrarie. non che questo non sia terribilmente efficiente, ma sia utile per inserire un ciclo for in uno 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

Potrebbe voler aggiornare il tuo post di risposta con il codice nel tuo commento. Python viene alterato nei commenti e un po 'ha bisogno della formattazione.
Jake Bathman,

3

Matplotlib correlati

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)

3

So che è stata data una risposta, ma scriverò la mia risposta per scopi storici e dal momento che penso sia diretta.

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

Sicuramente non vincerà nulla come il code-golf, ma penso che sia elegante.


Il arangecon i passaggi è abbastanza carino, ma è listOfDatescostituito da datetime64 intorpidito invece che da tempi nativi di Python.
F.Raab,

2
Tuttavia, è possibile utilizzare np.arange(…).astype(dt.datetime)per arangerestituire datetime python nativo anziché numetime datetime64.
F.Raab,

2

Un altro esempio che conta in avanti o indietro, a partire dalla risposta di 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)

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

e

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)

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

Si noti che la data di inizio è inclusa nel reso, quindi se si desidera quattro date totali, utilizzare timedelta(days=3)


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

1

Un generatore di intervalli di date mensili con datetimee dateutil. Semplice e facile da capire:

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)

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

1
Voleva tornare indietro, non in avanti .. Quindi enddovrebbe essere base - datetime.timedelta. Inoltre ... Perché questa soluzione è migliore di quella originale?
frarugi87,

0

Dalle risposte sopra ho creato questo esempio per il generatore di date

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.