Створення діапазону дат у Python


373

Я хочу створити список дат, починаючи з сьогоднішнього дня і повертаючи довільну кількість днів, скажімо, у своєму прикладі 100 днів. Чи є кращий спосіб зробити це, ніж це?

import datetime

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

Відповіді:


458

Дещо краще ...

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

6
Це не спрацьовує, якщо ви використовуєте часові часові дати, і спостерігається зміна DST !!! Я маю на увазі, коли ви використовуєте дату початку та кінця і заповнюєте їх між цим способом ...
gabn88

@ s-lott ви не могли просто використовувати range(0, numdays)в цьому випадку?
Лукаш Юріх

3
Я використовував цей метод дещо по-іншому, щоб генерувати десять, 5 хвилинних інтервалів, +/- деяке значення (у моєму випадку 30 секунд). Я тільки подумав, що datetimes = [start + timedelta(seconds=x*60+randint(-30, 30)) for x in range (0, range_end*5, 5)]range_end = 10
поділюсь

246

Pandas відмінно підходить для часових рядів і має пряму підтримку діапазонів дат.

Наприклад pd.date_range():

import pandas as pd
from datetime import datetime

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

Він також має багато варіантів, щоб полегшити життя. Наприклад, якщо ви хотіли тільки будні дні, ви просто помінялися бbdate_range .

Дивіться документацію щодо діапазону дат

Крім того, він повністю підтримує часові пояси пітцу і може плавно перетягувати зсув весни / осені DST.

РЕДАКТУВАТИ ОП:

Якщо вам потрібні фактичні дати пітона, на відміну від часових позначок 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")

При цьому використовується параметр "кінець", щоб відповідати початковому питанню, але якщо ви хочете зменшити дати:

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

22
Дефо найкраща відповідь, якщо панди вже використовуються. Єдине, що я хотів би додати, це те, що ви використовуєте параметр end = якщо ваші дати йдуть назад, як у початковій публікації. pd.date_range (end = pd.datetime.today (), періоди = 100) .tolist ()
Томас Браун

5
Одна з проблем із цими методами полягає в тому, коли вам потрібен тип дати Python Datetime ... Використання методів діапазонів дат та часу, створює типи Timetamp та Datetime64 ... Вам потрібно list(map(pd.Timestamp.to_pydatetime, datelist))повернути тип datetime ...
Malik Koné

pandas.datetimeКлас є застарілим і буде видалений з панда в версії майбутнього. datetimeЗамість цього імпортуйте з модуля.
Trenton McKinney

82

Отримати діапазон дат між вказаною початковою і кінцевою датою (Оптимізовано для складності часу та простору):

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
Початкова пропозиція полягає у використанні () замість [] для отримання генератора дат. Ефективна в тому сенсі, не потрібно буде зберігати весь масив дат і генерувати одну лише за потреби.
Сандіп,

2
Зауважте, кінцевий_даний не генерується.
Thorbjørn Ravn Andersen

8
використовувати (кінець-початок + 1), щоб отримати кінцеву дату.
Сандіп

6
Не відповідає на питання ОП, але це те, про що я пішов :)
Тьєррі Дж

2
(end-start+1)не працює, ви не можете додати timedelta та int. Ви можете використовувати, (end-start).days + 1хоча.
Стівен Полгер

44

Ви можете написати функцію генератора, яка повертає об'єкти дати починаючи з сьогоднішнього дня:

import datetime

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

Цей генератор повертає дати, починаючи з сьогоднішнього дня і повертаючись назад один день. Ось як приймати перші 3 дати:

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

Перевага такого підходу над розумінням циклу чи списку полягає в тому, що ви можете повернутися стільки разів, скільки захочете.

Редагувати

Більш компактна версія, що використовує генераторний вираз замість функції:

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

Використання:

>>> 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
Генератори - це ідеальний спосіб зробити це, якщо кількість днів є довільною.
xssChauhan

32

так, винайдіть колесо .... просто шукайте форум, і ви отримаєте щось подібне:

from dateutil import rrule
from datetime import datetime

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

22
Потрібен labix.org/python-dateutil . Виглядає приємно, але навряд чи варто зовнішньої залежності, щоб зберегти один рядок.
Бені Чернявський-Паскін

1
Не можу нікому повірити, інші відповіді дуже пітонічні. У той час як рул - це жахливе ім'я, але це принаймні легко на очах.
boatcoder

7
@ БеніЧернявський-Паскін: вводити непомітні помилки під час впровадження арифметики періоду (календаря) дуже просто. dateutil.rruleреалізує iCalendar RFC - легше використовувати функції з чітко визначеною поведінкою, а не декілька реалізацій майже одного функціоналу, які коли-небудь настільки відрізняються. dateutil.rruleдозволяє обмежити виправлення помилок на одному місці.
jfs

3
@ Mark0978: rruleім'я не довільне; це з відповідного rfc
jfs

1
Так, я не звинувачував dateutil у нещасному виборі імен :-)
boatcoder

32

Ви також можете скористатись порядковим днем, щоб зробити його простішим:

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

Або як запропоновано в коментарях, ви можете створити такий список:

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

18

З назви цього питання я очікував знайти щось подібне range(), що дозволить мені вказати дві дати та створити список з усіма датами між ними. Таким чином, не потрібно обчислювати кількість днів між цими двома датами, якщо заздалегідь це не знати.

Тож, ризикуючи бути трохи поза темою, цей однокласник виконує цю роботу:

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

Усі кредити на цю відповідь !


1
Це вже не один лайнер, ніж багато інших відповідей на питання.
Томас Браун

4
Я ніколи не робив виду, що це скоріше однолінійний, ніж відповіді інших, а також те, що це не було кращим рішенням. Я показав фрагмент коду, який робить щось трохи інше, ніж те, про що ви просили, але що я був би радий знайти тут, надавши назву питання.
snake_charmer

11

Ось дещо інша відповідь, яка формує відповідь С.Лотта, що дає перелік дат між двома датами startта end. У наведеному нижче прикладі - від початку 2017 року до сьогодні.

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

Я знаю трохи пізньої відповіді, але у мене була та сама проблема, і я вирішив, що функція внутрішнього діапазону Python у цьому відношенні трохи не вистачає, тому я перекрив її в моєму корисному модулі.

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
Я думаю, ти хочеш output = []і поміняти рядки в while cmp(...)циклі. Порівняйте range(0,10,1)і _range(0,10,1).
sigfpe

3
Я думаю, що ця відповідь буде краще, якби ви назвали функцію date_range.
Брендон Бредлі

7

На основі відповідей я написав для себе це:

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

Вихід:

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

Різниця полягає в тому, що я отримую dateоб'єкт ' ', а не ' datetime.datetime'.


7

Якщо є дві дати, і вам потрібен діапазон, спробуйте

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

Ось істота, яку я створив із власного коду, це може допомогти. (Я знаю, що питання занадто старе, але інші можуть його використовувати)

https://gist.github.com/2287345

(те саме нижче)

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

Ось один вкладиш для скриптів bash, щоб отримати список буднів, це python 3. Легко модифікується для будь-якого, int в кінці - кількість днів у минулому, яку ви хочете.

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

Ось варіант надання дати початку (а точніше, кінцевої) дати

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

Ось варіант для довільних дати початку та закінчення. не те, що це не надзвичайно ефективно, але добре для введення циклу for в 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

Можливо, ви хочете оновити своє повідомлення відповіді кодом у вашому коментарі. Python збивається в коментарях, і своєрідне потребує форматування.
Джейк Батман

3

Матплотліб, пов'язаний

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

Я знаю, що на це відповіли, але я викладу свою відповідь в історичних цілях, і оскільки я думаю, що це прямо вперед.

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

Звичайно, він не виграє нічого, як код-гольф, але я думаю, що це елегантно.


arangeЗ кроком досить добре, але listOfDatesскладається з Numpy datetime64 замість пітона рідної DateTimes.
F.Raab

2
Однак ви можете використовувати np.arange(…).astype(dt.datetime)для arangeповернення нативної дати python замість nummy datetime64.
F.Raab

2

Ще один приклад, який рахується вперед або назад, починаючи з відповіді Сандіпа.

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

і

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

Зверніть увагу, що дата початку включена у зворотній зв'язок, тому, якщо вам потрібно чотири загальні дати, використовуйте 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

Генератор місячних діапазонів дат із datetimeта dateutil. Простий і зрозумілий:

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
Він хотів повернутися назад, а не вперед .. Так endмає бути base - datetime.timedelta. Більше того ... Чому це рішення краще, ніж оригінальне?
frarugi87

0

З наведених вище відповідей я створив цей приклад для генератора дат

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.