Цикл по дням python

Итерация по диапазону дат в Python

у меня есть следующий код для этого, но как я могу сделать это лучше? Сейчас я думаю, что это лучше, чем вложенные циклы, но он начинает становиться 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  

Примечания

  • я на самом деле не использую это для печати. Это только для демонстрационных целей.
  • на 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 ответов

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

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

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

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

d = start_date delta = datetime.timedelta(days=1) while d  
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 - и реализуется как один файл (модуль), который легко включается в проект.

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

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

вы можете затем цикл над daterange для печати даты:

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

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

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

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 и самое главное, код становится проще читать, как вы вызываете функцию из нескольких мест.

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 

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

import datetime def daterange(start, end, step=datetime.timedelta(1)): curr = start while curr < end: yield curr curr += step 

библиотеки numpy это 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 объекты.

показать последние 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 
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)) 

у меня аналогичная проблема, но мне нужно повторять ежемесячно, а не ежедневно.

import calendar from datetime import datetime, timedelta def days_in_month(dt): return calendar.monthrange(dt.year, dt.month)[1] def monthly_range(dt_start, dt_end): forward = dt_end >= dt_start finish = False dt = dt_start while not finish: yield dt.date() if forward: days = days_in_month(dt) dt = dt + timedelta(days=days) finish = dt > dt_end else: _tmp_dt = dt.replace(day=1) - timedelta(days=1) dt = (_tmp_dt.replace(day=dt.day)) finish = dt < dt_end 
date_start = datetime(2016, 6, 1) date_end = datetime(2017, 1, 1) for p in monthly_range(date_start, date_end): print(p) 
2016-06-01 2016-07-01 2016-08-01 2016-09-01 2016-10-01 2016-11-01 2016-12-01 2017-01-01 
date_start = datetime(2017, 1, 1) date_end = datetime(2016, 6, 1) for p in monthly_range(date_start, date_end): print(p) 
2017-01-01 2016-12-01 2016-11-01 2016-10-01 2016-09-01 2016-08-01 2016-07-01 2016-06-01 
for i in range(16): print datetime.date.today() + datetime.timedelta(days=i) 

не могу поверить* этот вопрос существует в течение 9 лет, и никто не предлагает простую рекурсивную функцию:

from datetime import datetime, timedelta def walk_days(start_date, end_date): if start_date  
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 

Edit: *теперь я могу в это поверить-смотри оптимизирует ли Python хвостовую рекурсию? . Спасибо Тим.

вы можете создать серию дат между двумя датами, используя библиотеку pandas просто и доверчиво

import pandas as pd print pd.date_range(start='1/1/2010', end='1/08/2018', freq='M') 

вы можете изменить частоту генерации дат, установив freq как D, M, Q, Y (ежедневно, ежемесячно, ежеквартально, ежегодно )

как насчет следующего для выполнения диапазона, увеличенного на дни:

for d in map( lambda x: startDate+datetime.timedelta(days=x), xrange( (stopDate-startDate).days ) ): # Do stuff here 
for d in map( lambda x: startTime+x*stepTime, xrange( (stopTime-startTime).total_seconds() / stepTime.total_seconds() ) ): # Do stuff here 
  • startTime и stopTime являются datetime.дата или время.объект datetime (оба должны быть одного типа)
  • stepTime является объектом timedelta

обратите внимание, что .total_seconds () поддерживается только после python 2.7, если вы застряли с в более ранней версии вы можете написать свою собственную функцию:

def total_seconds( td ): return float(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 

эта функция имеет некоторые дополнительные функции:

  • может передать строку, соответствующую DATE_FORMAT для начала или конца, и она преобразуется в объект date
  • может передать объект даты для начала или конца
  • проверка ошибок в случае, если конец старше начала
import datetime from datetime import timedelta DATE_FORMAT = '%Y/%m/%d' def daterange(start, end): def convert(date): try: date = datetime.datetime.strptime(date, DATE_FORMAT) return date.date() except TypeError: return date def get_date(n): return datetime.datetime.strftime(convert(start) + timedelta(days=n), DATE_FORMAT) days = (convert(end) - convert(start)).days if days  

вот код для общей функции диапазона дат, похожий на ответ Бера, но более гибкий:

def count_timedelta(delta, step, seconds_in_interval): """Helper function for iterate. Finds the number of intervals in the timedelta.""" return int(delta.total_seconds() / (seconds_in_interval * step)) def range_dt(start, end, step=1, interval='day'): """Iterate over datetimes or dates, similar to builtin range.""" intervals = functools.partial(count_timedelta, (end - start), step) if interval == 'week': for i in range(intervals(3600 * 24 * 7)): yield start + datetime.timedelta(weeks=i) * step elif interval == 'day': for i in range(intervals(3600 * 24)): yield start + datetime.timedelta(days=i) * step elif interval == 'hour': for i in range(intervals(3600)): yield start + datetime.timedelta(hours=i) * step elif interval == 'minute': for i in range(intervals(60)): yield start + datetime.timedelta(minutes=i) * step elif interval == 'second': for i in range(intervals(1)): yield start + datetime.timedelta(seconds=i) * step elif interval == 'millisecond': for i in range(intervals(1 / 1000)): yield start + datetime.timedelta(milliseconds=i) * step elif interval == 'microsecond': for i in range(intervals(1e-6)): yield start + datetime.timedelta(microseconds=i) * step else: raise AttributeError("Interval must be 'week', 'day', 'hour' 'second', \ 'microsecond' or 'millisecond'.") 
> pip install DateTimeRange from datetimerange import DateTimeRange def dateRange(start, end, step): rangeList = [] time_range = DateTimeRange(start, end) for value in time_range.range(datetime.timedelta(days=step)): rangeList.append(value.strftime('%m/%d/%Y')) return rangeList dateRange("2018-09-07", "2018-12-25", 7) Out[92]: ['09/07/2018', '09/14/2018', '09/21/2018', '09/28/2018', '10/05/2018', '10/12/2018', '10/19/2018', '10/26/2018', '11/02/2018', '11/09/2018', '11/16/2018', '11/23/2018', '11/30/2018', '12/07/2018', '12/14/2018', '12/21/2018'] 

немного другой подход к обратимым шагам, сохраняя range args в кортеж.

def date_range(start, stop, step=1, inclusive=False): day_count = (stop - start).days if inclusive: day_count += 1 if step > 0: range_args = (0, day_count, step) elif step < 0: range_args = (day_count - 1, -1, step) else: raise ValueError("date_range(): step arg must be non-zero") for i in range(*range_args): yield start + timedelta(days=i) 

Источник

Читайте также:  Какие фреймворки javascript изучать
Оцените статью