Iteracja poprzez zakres dat w Pythonie

Mam następujący kod, aby to zrobić, ale jak Mogę zrobić to lepiej? W tej chwili myślę, że jest to lepsze niż zagnieżdżone pętle, ale zaczyna być Perl-one-linerish, gdy masz generator w zrozumieniu listy.

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

Uwagi

    Nie używam tego do drukowania. To tylko w celach demonstracyjnych.
  • zmienne start_date i end_date są obiektami datetime.date, ponieważ nie potrzebuję znaczników czasu. (Zostaną wykorzystane do wygenerowania raport).

Przykładowe Wyjście

Dla daty rozpoczęcia 2009-05-30 i daty zakończenia 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
Author: Peter O., 2009-06-30

19 answers

Dlaczego są dwie zagnieżdżone iteracje? Dla mnie tworzy tę samą listę danych z tylko jedną iteracją:

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

I Żadna lista nie jest przechowywana, tylko jeden generator jest iterowany. Również "jeśli" w generatorze wydaje się być niepotrzebne.

W końcu Sekwencja liniowa powinna wymagać tylko jednego iteratora, a nie dwóch.

Aktualizacja po dyskusji z Johnem Machinem:

Być może najbardziej eleganckim rozwiązaniem jest użycie funkcji generatora do całkowitego ukrycia / abstrakcji iteracji poza zakresem dat:

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

Uwaga: dla spójności z wbudowaną funkcją range() iteracja ta zatrzymuje przed osiągnięciem end_date. Więc dla iteracji inkluzywnej użyj następnego dnia, tak jak z range().

 392
Author: Ber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-03 10:35:18

To może być bardziej jasne:

d = start_date
delta = datetime.timedelta(days=1)
while d <= end_date:
    print d.strftime("%Y-%m-%d")
    d += delta
 151
Author: Sean Cavanagh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-23 21:12:50

Użyj dateutil biblioteka:

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

Ta biblioteka Pythona ma wiele bardziej zaawansowanych funkcji, niektóre bardzo przydatne, jak relative delta s - i jest zaimplementowana jako pojedynczy plik (moduł), który można łatwo włączyć do projektu.

 134
Author: nosklo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-10 18:15:36

Pandas jest świetny dla szeregów czasowych w ogóle i ma bezpośrednie wsparcie dla zakresów dat.

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

Możesz następnie zapętlić daterange, aby wydrukować datę:

for single_date in daterange:
    print (single_date.strftime("%Y-%m-%d"))
Ma również wiele opcji ułatwiających życie. Na przykład, jeśli chcesz tylko dni tygodnia, po prostu zamienisz w bdate_range. Zobacz też http://pandas.pydata.org/pandas-docs/stable/timeseries.html#generating-ranges-of-timestamps [[3]}moc pand jest tak naprawdę jej ramami danych, które wspierają wektoryzowane operacje (podobnie jak numpy), które sprawiają, że operacje na dużych ilościach danych są bardzo szybkie i łatwe.

Edytuj: Można również całkowicie pominąć pętlę for i po prostu wydrukować ją bezpośrednio, co jest łatwiejsze i bardziej wydajne: {]}

print(daterange)
 42
Author: fantabolous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-23 07:17:20
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())

Ta funkcja robi więcej niż wymaga, wspierając ujemny krok, itp. Dopóki uwzględniasz logikę zakresu, nie potrzebujesz oddzielnego day_count, a co najważniejsze, kod staje się łatwiejszy do odczytania, gdy wywołujesz funkcję z wielu miejsc.

 13
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-29 21:32:12

Dlaczego nie spróbować:

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
 11
Author: john,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-23 13:35:24

To najbardziej czytelne dla człowieka rozwiązanie, jakie przychodzi mi do głowy.

import datetime

def daterange(start, end, step=datetime.timedelta(1)):
    curr = start
    while curr < end:
        yield curr
        curr += step
 7
Author: Patrick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-13 14:28:08

Funkcja Numpy ' ego arange może być zastosowana do dat:

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)

Użycie {[2] } polega na przekonwertowaniu z numpy.datetime64 do tablicy datetime.datetime obiektów.

 5
Author: Tor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-29 10:53:27

Pokaż ostatnie n dni od dzisiaj:

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

Wyjście:

2016-06-29
2016-06-30
2016-07-01
2016-07-02
2016-07-03
2016-07-04
 5
Author: user1767754,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-21 23:27:07
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)) 
 4
Author: John Machin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-30 11:49:11

Mam podobny problem, ale muszę powtarzać co miesiąc zamiast codziennie.

This is my solution

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

Przykład #1

date_start = datetime(2016, 6, 1)
date_end = datetime(2017, 1, 1)

for p in monthly_range(date_start, date_end):
    print(p)

Wyjście

2016-06-01
2016-07-01
2016-08-01
2016-09-01
2016-10-01
2016-11-01
2016-12-01
2017-01-01

Przykład #2

date_start = datetime(2017, 1, 1)
date_end = datetime(2016, 6, 1)

for p in monthly_range(date_start, date_end):
    print(p)

Wyjście

2017-01-01
2016-12-01
2016-11-01
2016-10-01
2016-09-01
2016-08-01
2016-07-01
2016-06-01
 3
Author: juanmhidalgo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-24 07:34:17
for i in range(16):
    print datetime.date.today() + datetime.timedelta(days=i)
 2
Author: user368996,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-10 18:04:10

Nie mogę uwierzyć* to pytanie istnieje od 9 lat bez nikogo sugerującego prostą funkcję rekurencyjną:

from datetime import datetime, timedelta

def walk_days(start_date, end_date):
    if start_date <= end_date:
        print(start_date.strftime("%Y-%m-%d"))
        next_date = start_date + timedelta(days=1)
        walk_days(next_date, end_date)

#demo
start_date = datetime(2009, 5, 30)
end_date   = datetime(2009, 6, 9)

walk_days(start_date, end_date)

Wyjście:

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: *Teraz mogę w to uwierzyć -- zobacz czy Python optymalizuje rekurencję ogonową? . Dziękuję Tim .

 2
Author: Pocketsand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-31 12:29:27

Możesz wygenerować serię dat pomiędzy dwoma datami używając biblioteki pandas w prosty i zaufany sposób

import pandas as pd

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

Możesz zmienić częstotliwość generowania dat, ustawiając freq jako D, M, Q, Y (dzienne, miesięczne, kwartalne, roczne )

 1
Author: Shinto Joseph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-09 05:41:50

A co z następującym zakresem powiększonym o dni:

for d in map( lambda x: startDate+datetime.timedelta(days=x), xrange( (stopDate-startDate).days ) ):
  # Do stuff here
  • startDate i stopDate to datetime.obiekty daty

Dla wersji ogólnej:

for d in map( lambda x: startTime+x*stepTime, xrange( (stopTime-startTime).total_seconds() / stepTime.total_seconds() ) ):
  # Do stuff here
  • startTime i stopTime to datetime.Data lub datetime.obiekt datetime (oba powinny być tego samego typu)
  • stepTime jest obiektem timedelta

Zauważ to .total_seconds() jest obsługiwane tylko po Pythonie 2.7, jeśli utkniesz z wcześniejszą wersją, Możesz napisać własną Funkcja:

def total_seconds( td ):
  return float(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
 0
Author: teambob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-13 03:42:20

Ta funkcja ma kilka dodatkowych funkcji:

  • może przekazać łańcuch pasujący do DATE_FORMAT dla początku lub końca i jest konwertowany do obiektu date
  • może przekazać obiekt date dla początku lub końca
  • Sprawdzanie błędów w przypadku, gdy koniec jest starszy niż początek

    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 <= 0:
                raise ValueError('The start date must be before the end date.')
          for n in range(0, days):
                yield get_date(n)
    
    
    start = '2014/12/1'
    end = '2014/12/31'
    print list(daterange(start, end))
    
    start_ = datetime.date.today()
    end = '2015/12/1'
    print list(daterange(start, end))
    
 0
Author: DMfll,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-27 04:01:52

Oto kod dla funkcji ogólnego zakresu dat, podobny do odpowiedzi Bera, ale bardziej elastyczny:

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'.")
 0
Author: Turtles Are Cute,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-15 17:59:27
> 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']
 0
Author: LetzerWille,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-11 16:04:44

Nieco inne podejście do odwracalnych kroków poprzez przechowywanie range args w krotce.

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)
 0
Author: GollyJer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-03 16:13:41