Итерация по диапазону дат в Python - PullRequest
304 голосов
/ 30 июня 2009

У меня есть следующий код для этого, но как я могу сделать это лучше? Сейчас я думаю, что это лучше, чем вложенные циклы, но он начинает становиться Perl-one-linerish, когда у вас есть генератор в понимании списка.

day_count = (end_date - start_date).days + 1
for single_date in [d for d in (start_date + timedelta(n) for n in range(day_count)) if d <= end_date]:
    print strftime("%Y-%m-%d", single_date.timetuple())

Примечания * * 1004 На самом деле я не использую это для печати. Это только для демонстрационных целей. Переменные start_date и end_date являются datetime.date объектами, потому что мне не нужны временные метки. (Они будут использоваться для создания отчета). Пример вывода

Для даты начала 2009-05-30 и даты окончания 2009-06-09:

2009-05-30
2009-05-31
2009-06-01
2009-06-02
2009-06-03
2009-06-04
2009-06-05
2009-06-06
2009-06-07
2009-06-08
2009-06-09

Ответы [ 19 ]

467 голосов
/ 30 июня 2009

Почему существуют две вложенные итерации? Для меня это производит тот же список данных только с одной итерацией:

for single_date in (start_date + timedelta(n) for n in range(day_count)):
    print ...

И никакой список не сохраняется, только один генератор повторяется. Также «если» в генераторе кажется ненужным.

В конце концов, линейная последовательность должна требовать только одного итератора, а не двух.

Обновление после обсуждения с Джоном Мачином:

Возможно, самое элегантное решение - использовать функцию генератора, чтобы полностью скрыть / абстрагировать итерацию в диапазоне дат:

from datetime import timedelta, date

def daterange(start_date, end_date):
    for n in range(int ((end_date - start_date).days)):
        yield start_date + timedelta(n)

start_date = date(2013, 1, 1)
end_date = date(2015, 6, 2)
for single_date in daterange(start_date, end_date):
    print single_date.strftime("%Y-%m-%d")

Примечание: для соответствия встроенной функции range() эта итерация останавливается до того, как достигнет end_date. Поэтому для инклюзивной итерации используйте следующий день, как если бы вы использовали range().

162 голосов
/ 30 июня 2009

Это может быть более понятно:

d = start_date
delta = datetime.timedelta(days=1)
while d <= end_date:
    print d.strftime("%Y-%m-%d")
    d += delta
145 голосов
/ 30 июня 2009

Используйте библиотеку dateutil:

from datetime import date
from dateutil.rrule import rrule, DAILY

a = date(2009, 5, 30)
b = date(2009, 6, 9)

for dt in rrule(DAILY, dtstart=a, until=b):
    print dt.strftime("%Y-%m-%d")

Эта библиотека Python имеет много дополнительных функций, некоторые очень полезные, например relative delta s, и реализована в виде одного файла (модуля), который легко включается в проект.

48 голосов
/ 25 мая 2014

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

import pandas as pd
daterange = pd.date_range(start_date, end_date)

Затем вы можете зациклить диапазон дат, чтобы напечатать дату:

for single_date in daterange:
    print (single_date.strftime("%Y-%m-%d"))

У этого также есть много вариантов, чтобы облегчить жизнь. Например, если вы хотите только будние дни, вы просто должны поменяться местами в bdate_range. Смотри http://pandas.pydata.org/pandas-docs/stable/timeseries.html#generating-ranges-of-timestamps

Мощь Pandas на самом деле заключается в ее фреймах данных, которые поддерживают векторизованные операции (очень похожие на numpy), которые делают операции с большими объемами данных очень быстрыми и легкими.

EDIT: Вы также можете полностью пропустить цикл for и просто распечатать его напрямую, что проще и эффективнее:

print(daterange)
13 голосов
/ 30 июня 2009
import datetime

def daterange(start, stop, step=datetime.timedelta(days=1), inclusive=False):
  # inclusive=False to behave like range by default
  if step.days > 0:
    while start < stop:
      yield start
      start = start + step
      # not +=! don't modify object passed in if it's mutable
      # since this function is not restricted to
      # only types from datetime module
  elif step.days < 0:
    while start > stop:
      yield start
      start = start + step
  if inclusive and start == stop:
    yield start

# ...

for date in daterange(start_date, end_date, inclusive=True):
  print strftime("%Y-%m-%d", date.timetuple())

Эта функция делает больше, чем вам строго требуется, поддерживая отрицательный шаг и т. Д. Если вы учитываете логику диапазона, вам не нужно отдельное day_count, а самое главное, код становится легче читать Вы вызываете функцию из нескольких мест.

11 голосов
/ 13 октября 2016

Это наиболее удобочитаемое решение, которое я могу придумать.

import datetime

def daterange(start, end, step=datetime.timedelta(1)):
    curr = start
    while curr < end:
        yield curr
        curr += step
11 голосов
/ 12 апреля 2013

Почему бы не попробовать:

import datetime as dt

start_date = dt.datetime(2012, 12,1)
end_date = dt.datetime(2012, 12,5)

total_days = (end_date - start_date).days + 1 #inclusive 5 days

for day_number in range(total_days):
    current_date = (start_date + dt.timedelta(days = day_number)).date()
    print current_date
7 голосов
/ 25 апреля 2016

Показать последние n дней с сегодняшнего дня:

import datetime
for i in range(0, 100):
    print((datetime.date.today() + datetime.timedelta(i)).isoformat())

Выход:

2016-06-29
2016-06-30
2016-07-01
2016-07-02
2016-07-03
2016-07-04
5 голосов
/ 29 мая 2016

Функция Numpy's arange может применяться к датам:

import numpy as np
from datetime import datetime, timedelta
d0 = datetime(2009, 1,1)
d1 = datetime(2010, 1,1)
dt = timedelta(days = 1)
dates = np.arange(d0, d1, dt).astype(datetime)

Использование astype для преобразования numpy.datetime64 в массив datetime.datetime объектов.

4 голосов
/ 30 июня 2009
import datetime

def daterange(start, stop, step_days=1):
    current = start
    step = datetime.timedelta(step_days)
    if step_days > 0:
        while current < stop:
            yield current
            current += step
    elif step_days < 0:
        while current > stop:
            yield current
            current += step
    else:
        raise ValueError("daterange() step_days argument must not be zero")

if __name__ == "__main__":
    from pprint import pprint as pp
    lo = datetime.date(2008, 12, 27)
    hi = datetime.date(2009, 1, 5)
    pp(list(daterange(lo, hi)))
    pp(list(daterange(hi, lo, -1)))
    pp(list(daterange(lo, hi, 7)))
    pp(list(daterange(hi, lo, -7))) 
    assert not list(daterange(lo, hi, -1))
    assert not list(daterange(hi, lo))
    assert not list(daterange(lo, hi, -7))
    assert not list(daterange(hi, lo, 7)) 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...