Создание диапазона дат в Python

375

Я хочу создать список дат, начиная с сегодняшнего дня и заканчивая произвольным числом дней, скажем, в моем примере 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
Томас Браун
источник

Ответы:

459

Маргинально лучше ...

base = datetime.datetime.today()
date_list = [base - datetime.timedelta(days=x) for x in range(numdays)]
С. Лотт
источник
6
Это не работает, если вы используете datetime с учетом часового пояса и происходит смена летнего времени !!! Я имею в виду, когда вы используете начальную и конечную дату и заполняете ее между этим способом ...
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
MikeyE
251

Pandas отлично подходит для временных рядов в целом и имеет прямую поддержку диапазонов дат.

Например pd.date_range():

import pandas as pd
from datetime import datetime

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

У этого также есть много вариантов, чтобы сделать жизнь легче. Например, если вы хотите только будние дни, вы просто должны поменяться bdate_range.

Смотрите документацию по диапазону дат

Кроме того, он полностью поддерживает часовые пояса Pytz и может плавно охватывать смену летнего и весеннего времени.

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

Если вам нужны реальные даты Python, а не 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")

Это использует параметр "end", чтобы соответствовать исходному вопросу, но если вы хотите, чтобы даты по убыванию:

pd.date_range(datetime.today(), periods=100).to_pydatetime().tolist()
fantabolous
источник
22
Дефо лучший ответ, если панды уже используются. Единственное, что я хотел бы добавить, это то, что вы используете параметр end =, если ваши даты идут в обратном направлении, как в оригинальном сообщении. pd.date_range (end = pd.datetime.today (), period = 100) .tolist ()
Томас Браун
5
Одна из проблем, связанных с этими методами, заключается в том, что вам нужен тип Python Datetime ... Использование методов Pandas или NumPy диапазона дат создаст типы Timestamp и Datetime64 ... Вам необходимо list(map(pd.Timestamp.to_pydatetime, datelist))вернуть тип datetime ...
Малик Коне,
pandas.datetimeКласс является устаревшим и будет удален из панда в версии будущего. Импортируйте из datetimeмодуля вместо.
Трентон МакКинни
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")
Sandeep
источник
6
Первоначальное предложение - использовать () вместо [] для получения date_generator. Эффективно в том смысле, что нет необходимости хранить весь массив дат и генерировать их только при необходимости.
Сандип
2
Обратите внимание, что end_date не генерируется.
Турбьёрн Равн Андерсен
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()))
BOFH
источник
22
Требуется labix.org/python-dateutil . Выглядит красиво, но вряд ли стоит внешних зависимостей, чтобы сохранить только одну строку.
Бени Чернявский-Паскин
1
Никто не может поверить, что другие ответы очень питонны. В то время как рурл - ужасное имя, это, по крайней мере, легко для глаз.
кодек
7
@ BeniCherniavsky-Paskin: очень легко вводить тонкие ошибки при реализации арифметики периода (календаря). dateutil.rruleреализует iCalendar RFC - проще использовать функции с четко определенным поведением вместо нескольких реализаций с почти одинаковыми функциями, которые когда-либо немного различаются. dateutil.rruleпозволяет ограничить исправление ошибок одним местом.
Jfs
3
@ Mark0978: rruleимя не является произвольным; это от соответствующего RFC
JFS
1
Да, я не обвинял dateutil в неудачном выборе имен :-)
кодировщик
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(),
    )
]
Hosane
источник
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.

wmlynarski
источник
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
roopesh
источник
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

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
источник
3

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

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

Конечно, это не выиграет ничего, как Code-Golf, но я думаю, что это элегантно.

BarocliniCplusplus
источник
Процедура arangewith довольно удобна, но listOfDatesсостоит из numy datetime64 вместо собственных python datetime.
F.Raab
2
Однако вы можете использовать np.arange(…).astype(dt.datetime)для arangeвозврата нативного python datetime вместо numpy datetime64.
Ф.Рааб,
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
xmduhan
источник
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()
Sasmita
источник
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)
Димитрис Кугиумцис
источник