Création d'une plage de dates en Python

375

Je veux créer une liste de dates, en commençant par aujourd'hui, et en remontant un nombre arbitraire de jours, disons, dans mon exemple, 100 jours. Y a-t-il une meilleure façon de le faire que cela?

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
la source

Réponses:

459

Un peu mieux ...

base = datetime.datetime.today()
date_list = [base - datetime.timedelta(days=x) for x in range(numdays)]
S.Lott
la source
6
Cela ne fonctionne pas si vous utilisez des heures sensibles au fuseau horaire et qu'il y a un décalage de l'heure d'été !!! Je veux dire, quand vous utilisez une date de début et de fin et que vous la remplissez entre cette façon ...
gabn88
@ s-lott ne pourriez-vous pas simplement l'utiliser range(0, numdays)dans ce cas?
Lukas Juhrich
3
J'ai utilisé cette méthode d'une manière légèrement différente, pour générer des intervalles de dix, 5 minutes, +/- une valeur (30 secondes dans mon cas). Je pensais juste que je partagerais: datetimes = [start + timedelta(seconds=x*60+randint(-30, 30)) for x in range (0, range_end*5, 5)]où la variablerange_end = 10
MikeyE
248

Pandas est idéal pour les séries chronologiques en général et prend directement en charge les plages de dates.

Par exemple pd.date_range():

import pandas as pd
from datetime import datetime

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

Il dispose également de nombreuses options pour vous faciliter la vie. Par exemple, si vous ne voulez que des jours de semaine, vous pouvez simplement échanger bdate_range.

Voir la documentation des plages de dates

De plus, il prend entièrement en charge les fuseaux horaires pytz et peut s'étendre en douceur sur les décalages DST printemps / automne.

EDIT par OP:

Si vous avez besoin d'horaires de temps Python réels, par opposition aux horodatages 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")

Cela utilise le paramètre "end" pour correspondre à la question d'origine, mais si vous voulez des dates décroissantes:

pd.date_range(datetime.today(), periods=100).to_pydatetime().tolist()
fantabolique
la source
22
Defo la meilleure réponse si des pandas sont déjà utilisés. La seule chose que j'ajouterais, c'est que vous utiliseriez le paramètre end = si vos dates vont en arrière selon le message d'origine. pd.date_range (end = pd.datetime.today (), périodes = 100) .tolist ()
Thomas Browne
5
Un problème avec ces méthodes est lorsque vous avez besoin du type Python Datetime ... L'utilisation de Pandas ou de méthodes de plage de dates numpy créera des types Timestamp et Datetime64 ... Vous devez list(map(pd.Timestamp.to_pydatetime, datelist))récupérer le type datetime ...
Malik Koné
La pandas.datetimeclasse est obsolète et sera supprimée des pandas dans une future version. Importez datetimeplutôt à partir du module.
Trenton McKinney
82

Obtenez une plage de dates entre la date de début et la date de fin spécifiées (optimisé pour la complexité temporelle et spatiale):

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
la source
6
La suggestion initiale est d'utiliser () au lieu de [] pour obtenir un générateur_date. Efficace dans le sens où il ne sera pas nécessaire de stocker tout le tableau de dates et d'en générer une uniquement en cas de besoin.
Sandeep
2
Notez que la date_fin n'est pas générée.
Thorbjørn Ravn Andersen
8
utilisez (end-start + 1) pour obtenir la date de fin.
Sandeep
6
Ne répond pas à la question du PO, mais c'est ce que je recherchais :)
Thierry J.
2
(end-start+1)ne fonctionne pas, vous ne pouvez pas ajouter un timedelta et un int. Vous pouvez utiliser (end-start).days + 1cependant.
Stephen Paulger
44

Vous pouvez écrire une fonction de générateur qui renvoie des objets de date à partir d'aujourd'hui:

import datetime

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

Ce générateur renvoie des dates commençant à partir d'aujourd'hui et remontant un jour à la fois. Voici comment prendre les 3 premières dates:

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

L'avantage de cette approche sur la compréhension d'une boucle ou d'une liste est que vous pouvez revenir en arrière autant de fois que vous le souhaitez.

Éditer

Une version plus compacte utilisant une expression de générateur au lieu d'une fonction:

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

Usage:

>>> 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
la source
1
Les générateurs sont le moyen idéal de le faire, si le nombre de jours est arbitraire.
xssChauhan
32

ouais, réinventer la roue .... il suffit de rechercher sur le forum et vous obtiendrez quelque chose comme ceci:

from dateutil import rrule
from datetime import datetime

list(rrule.rrule(rrule.DAILY,count=100,dtstart=datetime.now()))
BOFH
la source
22
Nécessite labix.org/python-dateutil . Semble bien, mais ne vaut guère une dépendance externe juste pour enregistrer une ligne.
Beni Cherniavsky-Paskin
1
Je ne peux croire personne, les autres réponses sont très pythoniques. Bien que règle soit un nom terrible, celui-ci est au moins doux pour les yeux.
Boatcoder
7
@ BeniCherniavsky-Paskin: il est très facile d'introduire des bogues subtils lors de l'implémentation de l'arithmétique de période (calendrier). dateutil.rruleimplémente iCalendar RFC - il est plus facile d'utiliser des fonctions avec un comportement bien défini au lieu de plusieurs implémentations de presque les mêmes fonctionnalités qui sont si légèrement différentes. dateutil.rrulepermet de limiter la correction de bugs à un seul endroit.
jfs
3
@ Mark0978: le rrulenom n'est pas arbitraire; c'est du rfc correspondant
jfs
1
Oui, je ne blâmais pas dateutil pour le malheureux choix de noms :-)
boatcoder
32

Vous pouvez également utiliser l'ordinal de jour pour le simplifier:

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

Ou, comme suggéré dans les commentaires, vous pouvez créer une liste comme celle-ci:

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

D'après le titre de cette question, je m'attendais à trouver quelque chose comme range()ça, qui me permettrait de spécifier deux dates et de créer une liste avec toutes les dates entre les deux. De cette façon, on n'a pas besoin de calculer le nombre de jours entre ces deux dates, si on ne le sait pas au préalable.

Donc, avec le risque d'être légèrement hors sujet, ce one-liner fait le travail:

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

Tous les crédits à cette réponse !

charmeur de serpent
la source
1
Ce n'est pas une ligne plus que la plupart des autres réponses à la question.
Thomas Browne
4
Je n'ai jamais prétendu qu'il s'agissait davantage d'un vol simple que les autres réponses, ni que c'était une meilleure solution. J'ai montré un morceau de code qui fait quelque chose de légèrement différent de ce que vous avez demandé mais que j'aurais été heureux de trouver ici étant donné le titre de la question.
snake_charmer
11

Voici une réponse légèrement différente à partir de la réponse de S.Lott qui donne une liste de dates entre deux dates startet end. Dans l'exemple ci-dessous, de début 2017 à aujourd'hui.

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
la source
7

Une réponse tardive, je sais, mais j'ai juste eu le même problème et j'ai décidé que la fonction de plage interne de Python manquait un peu à cet égard, je l'ai donc remplacée dans un de mes modules 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))
Rob Young
la source
1
Je pense que vous voulez output = []et permuter les lignes dans la while cmp(...)boucle. Comparez range(0,10,1)et _range(0,10,1).
sigfpe
3
Je pense que cette réponse serait meilleure si vous nommiez la fonction date_range.
Brandon Bradley
7

Sur la base des réponses, j'ai écrit moi-même ceci:

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

Production:

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

La différence est que j'obtiens l' dateobjet ' ', pas celui ' datetime.datetime'.

wmlynarski
la source
7

S'il y a deux dates et que vous avez besoin de la plage, essayez

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
la source
5

Voici l'essentiel que j'ai créé, à partir de mon propre code, cela pourrait aider. (Je sais que la question est trop ancienne, mais d'autres peuvent l'utiliser)

https://gist.github.com/2287345

(même chose ci-dessous)

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
la source
4

Voici un liner pour les scripts bash pour obtenir une liste des jours de la semaine, c'est python 3. Facilement modifié pour tout, l'int à la fin est le nombre de jours dans le passé que vous voulez.

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

Voici une variante pour fournir une date de début (ou plutôt de fin)

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

Voici une variante pour les dates de début et de fin arbitraires. pas que ce ne soit pas terriblement efficace, mais c'est bon pour mettre une boucle for dans 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
la source
Pourrait vouloir mettre à jour votre message de réponse avec le code dans votre commentaire. Python est mutilé dans les commentaires et a un peu besoin de la mise en forme.
Jake Bathman
3

Matplotlib lié

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
la source
3

Je sais que cela a été répondu, mais je vais mettre ma réponse à des fins historiques, et puisque je pense que c'est simple.

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

Bien sûr, il ne gagnera rien comme le golf de code, mais je pense qu'il est élégant.

BarocliniCplusplus
la source
Le arangeavec les étapes est assez sympa, mais se listOfDatescompose de numpy datetime64 au lieu de datetimes natifs python.
F.Raab
2
Cependant, vous pouvez utiliser np.arange(…).astype(dt.datetime)pour arangerenvoyer le datetime natif python au lieu de numpy datetime64.
F.Raab
2

Un autre exemple qui compte en avant ou en arrière, à partir de la réponse 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)

donne

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

et

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)

donne

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

Notez que la date de début est incluse dans la déclaration, donc si vous voulez quatre dates au total, utilisez timedelta(days=3)

Chad Lowe
la source
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
la source
1

Un générateur de plages de dates mensuelles avec datetimeet dateutil. Simple et facile à comprendre:

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
la source
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
la source
1
Il voulait revenir, pas avant .. Ainsi , le enddevrait base - datetime.timedelta. De plus ... Pourquoi cette solution est-elle meilleure que l'original?
frarugi87
0

À partir des réponses ci-dessus, j'ai créé cet exemple pour le générateur de 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)
Dimitris Kougioumtzis
la source