파이썬에서 다양한 날짜를 반복


367

이 작업을 수행하려면 다음 코드가 있지만 어떻게 더 잘 수행 할 수 있습니까? 지금은 중첩 루프보다 낫다고 생각하지만 목록 이해에 발전기가있을 때 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())

노트

  • 실제로 이것을 사용하여 인쇄하지 않습니다. 그것은 단지 데모 목적입니다.
  • start_dateend_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

3
지적하자면 : 'time.strftime ( "% Y- % m- % d", single_date.timetuple ())'과 더 짧은 'single_date.strftime ( "% Y- %)에는 차이가 없다고 생각합니다. m- % d ") '. 대부분의 답변은 더 긴 스타일을 복사하는 것 같습니다.
Mu Mind

8
와우,이 답변은 너무 복잡합니다. 이것을보십시오 : stackoverflow.com/questions/7274267/…
Gringo Suave

@GringoSuave : Sean Cavanagh의 답변은 무엇입니까?
jfs


1
중복 여부에 관계없이 다른 페이지에서 더 간단한 답변을 얻을 수 있습니다.
Gringo Suave

답변:


552

왜 중첩 반복이 두 개입니까? 나를 위해 하나의 반복으로 동일한 데이터 목록을 생성합니다.

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

그리고 목록이 저장되지 않고 하나의 생성기 만 반복됩니다. 또한 생성기의 "if"는 필요하지 않은 것 같습니다.

결국, 선형 시퀀스는 반복자가 아닌 반복자가 하나만 필요합니다.

John Machin과의 토론 후 업데이트 :

아마도 가장 우아한 솔루션은 생성기 함수를 사용하여 날짜 범위에서 반복을 완전히 숨기거나 추상화하는 것입니다.

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


4
-1 ... day_count의 예비 계산이 있고 간단한 while 루프로 충분할 때 range를 사용하는 것은 좋지 않습니다.
John Machin 2016 년

7
@ John Machin : 알겠습니다. 그러나 일부 카운터 또는 값의 명시 적 증가와 함께 while 루프를 반복 반복합니다. interation 패턴은 더 pythonic (적어도 개인적 관점에서는)이고 더 일반적입니다. 반복이 어떻게 수행되는지에 대한 세부 사항을 숨기면서 반복을 표현할 수 있기 때문입니다.
Ber

10
@Ber : 전혀 마음에 들지 않습니다. 이중 나쁘다. 이미 반복했습니다! 문제가 발생한 구조를 생성기에 래핑하면 실행 오버 헤드가 훨씬 더 커지고 사용자의 관심을 다른 곳으로 넘겨 3- 라이너의 코드 및 / 또는 문서를 읽을 수 있습니다. -2
John Machin 2016 년

8
@ John Machin : 동의하지 않습니다. 요점은 줄 수를 절대 최소값으로 줄이는 것이 아닙니다. 결국, 우리는 여기서 Perl을 말하지 않습니다. 또한 내 코드는 반복을 하나만 수행합니다 (제너레이터가 작동하는 방식이지만 알고 있습니다). *** 내 요점은 재사용 및 자체 설명 코드에 대한 개념을 추상화하는 것입니다. 나는 이것이 가장 짧은 코드를 갖는 것보다 훨씬 가치가 있다고 주장한다.
Ber

9
간결함을 원한다면 발전기 표현식을 사용할 수 있습니다.(start_date + datetime.timedelta(n) for n in range((end_date - start_date).days))
Mark Ransom

219

이것은 더 분명 할 수 있습니다.

from datetime import date, timedelta

start_date = date(2019, 1, 1)
end_date = date(2020, 1, 1)
delta = timedelta(days=1)
while start_date <= end_date:
    print (start_date.strftime("%Y-%m-%d"))
    start_date += delta

3
매우 명확하고 짧지 만 계속 사용하려면 제대로 작동하지 않습니다.
rslite

사용의 내 경우에 대한 사랑 일
doomdaam

169

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 deltas와 같이 매우 유용 하며 프로젝트에 쉽게 포함되는 단일 파일 (모듈)로 구현됩니다.


3
여기에 대한 루프의 마지막 날짜가 유의 포함until의 마지막 날짜 반면 daterange에 방법 레지 던츠 베를린의 답변 입니다 독점end_date.
Ninjakannon


77

팬더는 일반적으로 시계열에 적합하며 날짜 범위를 직접 지원합니다.

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와 매우 유사)을 지원합니다.

편집 : for 루프를 완전히 건너 뛰고 직접 인쇄하면 쉽고 효율적입니다.

print(daterange)

"많은 NumPy와 같은"- 팬더가 NumPy와에 내장되어 있습니다 : P
자크 Saucier

15
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가장 중요하게는 여러 함수에서 함수를 호출 할 때 코드를 쉽게 읽을 수 있습니다. 장소.


덕분에 범위의 매개 변수와 더 가깝게 이름이 바뀌어 본문에서 변경하는 것을 잊었습니다.

+1 ...하지만 단계가 타임 델타가되도록 허용 할 때 (a) dateTIMErange ()라고하고 timedelta (hours = 12) 및 timedelta (hours = 36)의 단계를 올바르게 수행하거나 ( b) 정수가 아닌 트랩 단계 또는 (c) 호출자를 번거 로움없이 저장하고 단계를 타임 델타 대신 며칠로 표현합니다.
John Machin 2016 년

모든 timedelta는 이미 작동하지만 (a) 때문에 이것을 작성한 후 datetime_range 및 date_range를 개인 스크랩 모음에 추가했습니다. 다른 함수가 (c)에 가치가 있는지 확실하지 않으면 days = 1의 가장 일반적인 경우는 이미 처리되어 있으며 명시적인 타임 델타를 통과해야 혼란을 피할 수 있습니다. 어딘가에 업로드하는 것이 가장 좋습니다 : bitbucket.org/kniht/scraps/src/tip/python/gen_range.py

당신이 step.days)을 (step.total_seconds에 대해 확인하고, 안 일 이외의 단위에이 작품을 만들기 위해
amohr

12

이것은 내가 생각할 수있는 가장 사람이 읽을 수있는 솔루션입니다.

import datetime

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

11

시도해 보지 않겠습니까?

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

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.


슈퍼 린 건설! 마지막 줄은 나를 위해 작동dates = np.arange(d0, d1, dt).astype(datetime.datetime)
pyano

시간당 / 분당 /…과 같은 고정 된 둥근 단계 대신 모든 타임 델타를 허용하는 일반 단일 라이너 솔루션을 게시 한 경우 +1
F.Raab

7

오늘부터 마지막 ​​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

print((datetime.date.today() + datetime.timedelta(i)).isoformat())
TitanFighter

@TitanFighter 편집을 자유롭게 수행하십시오, 나는 그것을 받아 들일 것입니다.
user1767754

2
나는 노력했다. 편집에는 최소 6 자 이상이 필요하지만이 경우 "("및 ")"문자 만 추가하면됩니다.
TitanFighter

print((datetime.date.today() + datetime.timedelta(i))).isoformat ()이 없으면 정확히 동일한 출력을 제공합니다. YYMMDD를 인쇄하려면 스크립트가 필요합니다. 아무도 그 방법을 알고 있습니까?
mr.zog 2016 년

print 문 대신 for 루프에서이 작업을 수행하십시오d = datetime.date.today() + datetime.timedelta(i); d.strftime("%Y%m%d")
user1767754

5
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

완벽을 기하기 위해 Pandas에는 period_range범위를 벗어난 타임 스탬프 기능 도 있습니다 .

import pandas as pd

pd.period_range(start='1/1/1626', end='1/08/1627', freq='D')

3

비슷한 문제가 있지만 매일 대신 매월 반복해야합니다.

이것은 나의 해결책이다

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

실시 예 # 1

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

실시 예 # 2

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

3

't * 간단한 재귀 함수를 제안 사람없이이 질문에 9 년 동안 존재했다고 생각 :

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)

산출:

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

편집 : * 이제 믿을 수 있습니다- 파이썬이 꼬리 재귀를 최적화합니까?를 참조하십시오 . . 고마워 .


3
간단한 루프를 재귀로 바꾸는 이유는 무엇입니까? 약 2 년 반이 넘는 범위에서는이 기능이 중단됩니다.
Tim-Erwin

@ Tim-Erwin 솔직히 CPython이 꼬리 재귀를 최적화하지 않는다는 것을 몰랐으므로 귀하의 의견은 소중합니다.
Pocketsand

2

팬더 라이브러리를 사용하여 간단하고 확실하게 두 날짜 사이에 일련의 날짜를 생성 할 수 있습니다

import pandas as pd

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

주파수를 D, M, Q, Y (일별, 월별, 분기 별, 연간)로 설정하여 날짜 생성 빈도를 변경할 수 있습니다.


2014 년에 이미이 글에서 답했습니다
Alexey Vazhnov

2
> 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']

1

이 기능에는 몇 가지 추가 기능이 있습니다.

  • 시작 또는 종료에 대해 DATE_FORMAT와 일치하는 문자열을 전달할 수 있으며 날짜 오브젝트로 변환됩니다.
  • 시작 또는 끝 날짜 개체를 전달할 수 있습니다
  • 끝이 시작보다 오래된 경우 오류 검사

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

1

다음은 Ber의 답변과 비슷하지만 더 유연한 일반 날짜 범위 함수에 대한 코드입니다.

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

일 단위로 범위를 늘리는 다음은 어떻습니까?

for d in map( lambda x: startDate+datetime.timedelta(days=x), xrange( (stopDate-startDate).days ) ):
  # Do stuff here
  • startDate 및 stopDate는 datetime.date 객체입니다.

일반 버전의 경우 :

for d in map( lambda x: startTime+x*stepTime, xrange( (stopTime-startTime).total_seconds() / stepTime.total_seconds() ) ):
  # Do stuff here
  • startTime과 stopTime은 datetime.date 또는 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

0

range인수를 튜플 에 저장하여 가역 단계에 대한 약간 다른 접근 방식 .

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
import datetime
from dateutil.rrule import DAILY,rrule

date=datetime.datetime(2019,1,10)

date1=datetime.datetime(2019,2,2)

for i in rrule(DAILY , dtstart=date,until=date1):
     print(i.strftime('%Y%b%d'),sep='\n')

산출:

2019Jan10
2019Jan11
2019Jan12
2019Jan13
2019Jan14
2019Jan15
2019Jan16
2019Jan17
2019Jan18
2019Jan19
2019Jan20
2019Jan21
2019Jan22
2019Jan23
2019Jan24
2019Jan25
2019Jan26
2019Jan27
2019Jan28
2019Jan29
2019Jan30
2019Jan31
2019Feb01
2019Feb02

스택 오버플로에 오신 것을 환영합니다! 이 코드는 문제를 해결하는 방법과 이유에 대한 설명 , 특히 너무 많은 답변이 있는 질문에 대한 설명포함 하여 질문을 해결할 수 있지만 실제로 게시물의 품질을 개선하는 데 도움이되며 더 많은 투표 결과를 초래할 수 있습니다. 지금 질문하는 사람 만이 아니라 앞으로 독자들에게 질문에 대답하고 있음을 기억하십시오. 제발 편집 설명을 추가하고 제한 및 가정이 적용 무엇의 표시를 제공하는 답변을. 검토에서
이중 신호음
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.