Criando um intervalo de datas no Python

374

Quero criar uma lista de datas, começando com hoje e retornando um número arbitrário de dias, digamos, no meu exemplo, 100 dias. Existe uma maneira melhor de fazer isso do que isso?

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
fonte

Respostas:

459

Marginalmente melhor ...

base = datetime.datetime.today()
date_list = [base - datetime.timedelta(days=x) for x in range(numdays)]
S.Lott
fonte
6
Isso não funcionará se você usar as datas com reconhecimento de fuso horário e houver uma mudança no horário de verão !!! Quero dizer, quando você usa uma data de início e fim e encha-o no meio desta forma ...
gabn88
@ s-lott você não poderia simplesmente usar range(0, numdays)neste caso?
Lukas Juhrich 27/10/2015
3
Eu usei esse método de uma maneira um pouco diferente, para gerar intervalos de dez, 5 minutos, +/- algum valor (30 segundos no meu caso). Eu só percebi que eu iria partilhar: datetimes = [start + timedelta(seconds=x*60+randint(-30, 30)) for x in range (0, range_end*5, 5)]onde a variávelrange_end = 10
MikeyE
247

Pandas é ótimo para séries temporais em geral e tem suporte direto para períodos.

Por exemplo pd.date_range():

import pandas as pd
from datetime import datetime

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

Ele também tem muitas opções para facilitar a vida. Por exemplo, se você quisesse apenas dias da semana, você apenas trocaria bdate_range.

Consulte a documentação do período

Além disso, ele suporta totalmente fusos horários de pytz e pode abranger suavemente as mudanças de horário de verão da primavera / outono.

EDITAR por OP:

Se você precisar de datetime reais em python, em oposição aos carimbos de data e hora do 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")

Isso usa o parâmetro "end" para corresponder à pergunta original, mas se você quiser datas decrescentes:

pd.date_range(datetime.today(), periods=100).to_pydatetime().tolist()
fantabolous
fonte
22
Defo a melhor resposta se pandas já estiver sendo usado. A única coisa que gostaria de acrescentar é que você usaria o parâmetro end = se suas datas retrocederem conforme a postagem original. pd.date_range (final = pd.datetime.today (), = 100 períodos) .ToList ()
Thomas Browne
5
Um problema com esses métodos é quando você precisa do tipo Data / Hora do Python ... O uso de métodos do Pandas ou de intervalo de datas numpy criará tipos de Data e Hora e Data / Hora64 ... Você precisa list(map(pd.Timestamp.to_pydatetime, datelist))recuperar o tipo de data e hora ...
Malik Koné
A pandas.datetimeclasse foi descontinuada e será removida dos pandas em uma versão futura. Importe do datetimemódulo.
Trenton McKinney
82

Obtenha intervalo de datas entre a data inicial e final especificada (otimizado para complexidade de tempo e espaço):

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
fonte
6
A sugestão inicial é usar () em vez de [] para obter um date_generator. Eficiente no sentido de que não haverá necessidade de armazenar todo o conjunto de datas e gerar um somente quando necessário.
Sandeep
2
Observe que o end_date não é gerado.
Thorbjørn Ravn Andersen
8
use (início inicial + 1) para obter a data final.
Sandeep
6
Não responde a pergunta do OP, mas é o que eu estava depois :)
Thierry J.
2
(end-start+1)não funciona, você não pode adicionar um timedelta e int. Você poderia usar (end-start).days + 1embora.
Stephen Paulger 17/01
44

Você pode escrever uma função geradora que retorna objetos de data a partir de hoje:

import datetime

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

Este gerador retorna datas a partir de hoje e voltando um dia de cada vez. Veja como tirar as 3 primeiras datas:

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

A vantagem dessa abordagem sobre um loop ou compreensão da lista é que você pode voltar quantas vezes quiser.

Editar

Uma versão mais compacta usando uma expressão de gerador em vez de uma função:

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
fonte
11
Geradores é a maneira ideal de fazer isso, se o número de dias for arbitrário.
xssChauhan
32

sim, reinvente a roda .... basta pesquisar no fórum e você terá algo como isto:

from dateutil import rrule
from datetime import datetime

list(rrule.rrule(rrule.DAILY,count=100,dtstart=datetime.now()))
BOFH
fonte
22
Requer labix.org/python-dateutil . Parece bom, mas dificilmente vale uma dependência externa apenas para salvar uma linha.
Beni Cherniavsky-Paskin
11
Não consigo acreditar em ninguém, as outras respostas são muito pitonicas. Embora rrule seja um nome terrível, este é pelo menos fácil para os olhos.
boatcoder 23/09/15
7
@ BeniCherniavsky-Paskin: é muito fácil introduzir erros sutis durante a implementação da aritmética do período (calendário). dateutil.rruleimplementa o iCalendar RFC - é mais fácil usar funções com um comportamento bem definido em vez de várias implementações com quase a mesma funcionalidade que são um pouco diferentes. dateutil.rrulepermite limitar a correção de erros em um único local.
jfs
3
@ Mark0978: o rrulenome não é arbitrário; é do rfc correspondente
jfs
11
Sim, eu não estava culpando dateutil para a escolha infeliz de nomes :-)
boatcoder
32

Você também pode usar o ordinal do dia para simplificar:

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

Ou, conforme sugerido nos comentários, você pode criar uma lista como esta:

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

A partir do título desta pergunta, eu esperava encontrar algo como range(), que me permitisse especificar duas datas e criar uma lista com todas as datas intermediárias. Dessa forma, não é necessário calcular o número de dias entre essas duas datas, se não o soubermos de antemão.

Portanto, com o risco de ser um pouco fora de tópico, esse recurso faz o trabalho:

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 os créditos para esta resposta !

encantador de serpente
fonte
11
Essa não é uma linha única, assim como muitas das outras respostas à pergunta.
22416 Thomas Thomase
4
Eu nunca fingi que isso era mais uma linha do que as outras respostas, nem que essa era uma solução melhor. Eu mostrei um pedaço de código que faz algo ligeiramente diferente do que você solicitou, mas que eu ficaria feliz em encontrar aqui, com o título da pergunta.
precisa saber é o seguinte
11

Aqui está uma resposta um pouco diferente, baseada na resposta de S.Lott, que fornece uma lista de datas entre duas datas starte end. No exemplo abaixo, do início de 2017 até hoje.

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

Uma resposta tardia, eu sei, mas acabei de ter o mesmo problema e decidi que a função de alcance interno do Python estava um pouco ausente nesse aspecto, então substituí-a em um módulo utilitário meu.

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
fonte
11
Eu acho que você quer output = []e trocar as linhas no while cmp(...)loop. Compare range(0,10,1)e _range(0,10,1).
SIGFPE
3
Eu acho que essa resposta seria melhor se você nomeasse a função date_range.
Brandon Bradley
7

Com base nas respostas que escrevi para mim mesmo:

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

Resultado:

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

A diferença é que eu recebo o dateobjeto ' ', não o ' datetime.datetime'.

wmlynarski
fonte
7

Se houver duas datas e você precisar do intervalo, tente

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

Aqui está a essência que eu criei, a partir do meu próprio código, isso pode ajudar. (Eu sei que a pergunta é muito antiga, mas outros podem usá-la)

https://gist.github.com/2287345

(mesma coisa abaixo)

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

Aqui está uma lista de scripts bash para obter uma lista de dias da semana, este é o python 3. Modificado facilmente para qualquer que seja o int, no final, é o número de dias no passado que você deseja.

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

Aqui está uma variante para fornecer uma data de início (ou melhor, final)

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

Aqui está uma variante para datas de início e término arbitrárias. não que isso não seja muito eficiente, mas seja bom para inserir um loop for em um 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
fonte
Talvez você queira atualizar sua resposta com o código no seu comentário. Python fica confuso nos comentários e meio que precisa da formatação.
Jake Bathman
3

Relacionado com Matplotlib

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

Sei que isso foi respondido, mas colocarei minha resposta para propósitos históricos e, como acho que é direta.

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

Claro que não vai ganhar nada como código-golfe, mas acho que é elegante.

BarocliniCplusplus
fonte
O arangecom as etapas é bastante bom, mas listOfDatesconsiste em datetime64 numpy em vez de datetime nativo do python.
F.Raab
2
No entanto, você pode usar np.arange(…).astype(dt.datetime)para arangeretornar o datetime do python nativo em vez do numpy datetime64.
F.Raab
2

Outro exemplo que conta para frente ou para trás, a partir da resposta 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)

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

Observe que a data de início está incluída no retorno; portanto, se você quiser quatro datas no total, use timedelta(days=3)

Chad Lowe
fonte
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
fonte
1

Um gerador de período mensal com datetimee dateutil. Simples e fácil de se 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
fonte
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
fonte
11
Ele queria voltar, não avançar .. Então enddeveria ser base - datetime.timedelta. Além disso ... Por que esta solução é melhor que a original?
Frrugi87
0

Das respostas acima, criei este exemplo para o gerador de datas

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
fonte