2016-08-24 3 views
2

비슷한 문제를 해결하는 게시물이 많이 있지만 문제가있는 제약 조건은 모두 동일하지 않습니다.Python2 : 일요일 - 토요일 주 시작/종료 날짜 주어진 날짜 범위

데이터 센터에서 주 단위로 데이터를 가져 오는 스크립트를 작성하고 있습니다. 가져 오는 주간은 외부 사용자가 내 스크립트에 제공 한 날짜 범위에 따라 다릅니다. 데이터 센터의 주간은 일요일부터 토요일까지입니다. 파이썬의 주간은 월요일부터 일요일까지입니다.

날짜 범위의 각 날짜 이후의 일요일과 토요일 날짜를 가져올 수 있어야합니다. 문제를 복잡하게 만들려면 주 시작일이나 주말 종료일이 요청 된 범위를 벗어날 수 없습니다. 이렇게하면 해당 범위의 각 날짜에서 하루를 간단히 빼는 것을 방지 할 수 있습니다.

일부 예제 시나리오 :

예 1)

requested_date_range = [datetime(2016,7,1,0,0),datetime(2016,8,5,0,0)] 
what I get from the various Python utilities (dateutil, datetime_periods, etc): 

[ 
[datetime(2016,6,27,0,0),datetime(2016,7,3,0,0)], 
[datetime(2016,7,4,0,0),datetime(2016,7,10,0,0)], 
[datetime(2016,7,11,0,0),datetime(2016,7,17,0,0)], 
[datetime(2016,7,18,0,0),datetime(2016,7,24,0,0)], 
[datetime(2016,7,25,0,0),datetime(2016,7,31,0,0)], 
[datetime(2016,8,1,0,0),datetime(2016,8,7,0,0)] 
] 

what I actually need: 
[ 
[datetime(2016,7,1,0,0),datetime(2016,7,2,0,0)], #"week" starts on first day of requested range and ends on the following Saturday 
[datetime(2016,7,3,0,0),datetime(2016,7,9,0,0)], #Sunday through Saturday 
[datetime(2016,7,10,0,0),datetime(2016,7,16,0,0)], #Sunday through Saturday 
[datetime(2016,7,17,0,0),datetime(2016,7,23,0,0)], #Sunday through Saturday 
[datetime(2016,7,24,0,0),datetime(2016,7,30,0,0)], #Sunday through Saturday 
[datetime(2016,7,31,0,0),datetime(2016,8,5,0,0)] #"week" starts on Sunday and ends on last day of requested range 
] 

예 2)

requested_date_range = [datetime(2016,7,3,0,0),datetime(2016,8,7,0,0)] 
what I get from the various Python utilities (dateutil, datetime_periods, etc): 
[ 
[datetime(2016,6,27,0,0),datetime(2016,7,3,0,0)], 
[datetime(2016,7,4,0,0),datetime(2016,7,10,0,0)], 
[datetime(2016,7,11,0,0),datetime(2016,7,17,0,0)], 
[datetime(2016,7,18,0,0),datetime(2016,7,24,0,0)], 
[datetime(2016,7,25,0,0),datetime(2016,7,31,0,0)], 
[datetime(2016,8,1,0,0),datetime(2016,8,7,0,0)] 
] 
what I actually need: 
[ 
[datetime(2016,7,3,0,0),datetime(2016,7,9,0,0)], #"week" starts on first day of requested range 
[datetime(2016,7,10,0,0),datetime(2016,7,16,0,0)], #Sunday through Saturday 
[datetime(2016,7,17,0,0),datetime(2016,7,23,0,0)], #Sunday through Saturday 
[datetime(2016,7,24,0,0),datetime(2016,7,30,0,0)], #Sunday through Saturday 
[datetime(2016,7,31,0,0),datetime(2016,8,6,0,0)], #Sunday through Saturday 
[datetime(2016,8,7,0,0),datetime(2016,8,7,0,0)] #"week" ends up being only one day long because the max requested date falls on a Sunday 
] 
+0

시작일을 설정할 수 있다고 생각합니다. 내가 그것을 파헤 치자. – Vasif

+0

calendar.setfirstweekday (calendar.SUNDAY)를 사용할 수 있습니다. 불행히도, 캘린더 클래스에는 주 시작/종료 날짜를 반환하는 함수가 없습니다. 적어도 나는 그것을 발견 할 수 없었다. –

답변

1

당신은 dateutil.relativedelta를 사용하여 아주 쉽게이 작업을 수행 할 수 있어야합니다. 아래의 예 기능 : 위의 함수에서

from dateutil.relativedelta import relativedelta 
from dateutil.relativedelta import MO, TU, WE, TH, FR, SA, SU 

def week_range(range_start, range_end): 
    dts = [] 
    WEEK_START = relativedelta(weekday=SU(+2)) 
    WEEK_END = relativedelta(weekday=SA) 

    c_wstart = range_start + relativedelta(weekday=SU(+1)) 
    c_wend = c_wstart + WEEK_END 

    if range_start < c_wstart: 
     dts.append((range_start, range_start + WEEK_END)) 

    while True: 
     if c_wend > range_end: 
      c_wend = range_end 

     dts.append((c_wstart, c_wend)) 

     if c_wend >= range_end: 
      break 

     c_wstart = c_wstart + WEEK_START 
     c_wend = c_wstart + WEEK_END 

     if c_wstart > range_end: 
      break 

    return dts 

, 내가 먼저 범위 시작을하고 또는 원래 날짜 이후에 나에게 첫 번째 일요일을 제공하는, 그것에 relativedelta(weekday=SU)를 추가 할 수 있습니다. 그런 다음 relativedelta(weekday=SU(+2))을 "현재 주"에 연속적으로 추가하여 초를 일 (또는 "일주일 시작"이 항상 일요일이므로 항상 다음 주일) 이후에 일요일 또는 그 이후에 얻습니다.

내가 생성하는 각 날짜에 다음 토요일을 생성하기 위해 relativedelta(weekday=SA)을 추가하고, 날짜 범위를 벗어난 경우 마지막 날짜를 날짜 범위로 "클립"합니다.

사용하여 예 : 그냥 당신이 원하는 경우 첫 번째는 게으른 것을

from dateutil.rrule import rrule, rruleset 
from dateutil.rrule import WEEKLY, SU, SA 
from datetime import timedelta 

from itertools import zip_longest, chain 
def week_range_rrule(range_start, range_end, weekday_start=SU, weekday_end=SA): 
    # Beginning of the week rule 
    rr1 = rrule(WEEKLY, byweekday=weekday_start, 
       dtstart=range_start, until=range_end) 

    # End of the week rule - adding 1 second to the range end because 
    # "until" isn't inclusive 
    rr2 = rrule(WEEKLY, byweekday=weekday_end, 
       dtstart=range_start+relativedelta(SA), 
       until=range_end+timedelta(seconds=1)) 

    # Combine these into a rule set 
    rrs = rruleset() 
    rrs.rrule(rr1) 
    rrs.rrule(rr2) 

    # Explicitly add range start and end to the rules, in case they don't 
    # fall on neat week boundaries 
    rrs.rdate(range_start) 
    rrs.rdate(range_end) 

    if next(iter(rr2)) == range_start: 
     rrs = chain((range_start,), rrs) 

    # Modified version of the "grouper" recipe from itertools 
    args = [iter(rrs)] * 2 

    return list(zip_longest(*args, fillvalue=range_end)) 

참고 :

>>> week_range(datetime(2016, 7, 1), datetime(2016, 8, 5)) 
[(datetime.datetime(2016, 7, 1, 0, 0), datetime.datetime(2016, 7, 2, 0, 0)), 
(datetime.datetime(2016, 7, 3, 0, 0), datetime.datetime(2016, 7, 9, 0, 0)), 
(datetime.datetime(2016, 7, 10, 0, 0), datetime.datetime(2016, 7, 16, 0, 0)), 
(datetime.datetime(2016, 7, 17, 0, 0), datetime.datetime(2016, 7, 23, 0, 0)), 
(datetime.datetime(2016, 7, 24, 0, 0), datetime.datetime(2016, 7, 30, 0, 0)), 
(datetime.datetime(2016, 7, 31, 0, 0), datetime.datetime(2016, 8, 5, 0, 0))] 
>>> week_range(datetime(2016, 7, 3), datetime(2016, 8, 7)) 
[(datetime.datetime(2016, 7, 3, 0, 0), datetime.datetime(2016, 7, 9, 0, 0)), 
(datetime.datetime(2016, 7, 10, 0, 0), datetime.datetime(2016, 7, 16, 0, 0)), 
(datetime.datetime(2016, 7, 17, 0, 0), datetime.datetime(2016, 7, 23, 0, 0)), 
(datetime.datetime(2016, 7, 24, 0, 0), datetime.datetime(2016, 7, 30, 0, 0)), 
(datetime.datetime(2016, 7, 31, 0, 0), datetime.datetime(2016, 8, 6, 0, 0)), 
(datetime.datetime(2016, 8, 7, 0, 0), datetime.datetime(2016, 8, 7, 0, 0))] 

당신의 취향에 따라, 당신은 또한 rruleset를 사용하여 비슷한 작업을 수행 할 수 dts.append(x)의 모든 인스턴스를 yield x으로 바꿉니다. 두 번째 것을 게으르려면, return 문에 zip_longest 주위에 list() 래퍼를 제거하십시오.

+0

환상적입니다! 두 가지 솔루션 모두 잘 작동했습니다. 큰 설명도! 나는 하나 이상의 upvote를 줄 수 있었으면 좋겠다. 고맙습니다! –

+0

@NickMiller 질문을 한 이후에 할 수있는 일은 - 투표 버튼 아래에있는 녹색 체크 표시를 선택하여 대답을 수락 할 수 있습니다. – Paul

+0

완료! 다시 한 번 고마워, 폴. –

1

간결하면서도 대답은 다소 적습니다.

import datetime as dt 

if __name__ == "__main__": 
    weekend_index = (6, 5) # Sunday, Saturday 
    requested_range = (dt.datetime(2016, 7, 9, 0, 0), dt.datetime(2016, 8, 11, 0, 0)) 

    start, end = requested_range 
    sun, sat = weekend_index 
    cur = start 
    my_range = [] 

    while cur < end: 
      cr = [] 
      cr.append(cur) 
      cur = end if end < cur+dt.timedelta(days=6) else (cur+dt.timedelta(days=(sun if cur.weekday() == sun else (sat-cur.weekday())))) 
      cr.append(cur) 
      cur += dt.timedelta(days=1) 
      my_range.append(cr) 

print(my_range) # Returns: 
# [[datetime.datetime(2016, 7, 9, 0, 0), datetime.datetime(2016, 7, 9, 0, 0)], 
# [datetime.datetime(2016, 7, 10, 0, 0), datetime.datetime(2016, 7, 16, 0, 0)], 
# [datetime.datetime(2016, 7, 17, 0, 0), datetime.datetime(2016, 7, 23, 0, 0)], 
# [datetime.datetime(2016, 7, 24, 0, 0), datetime.datetime(2016, 7, 30, 0, 0)], 
# [datetime.datetime(2016, 7, 31, 0, 0), datetime.datetime(2016, 8, 6, 0, 0)], 
# [datetime.datetime(2016, 8, 7, 0, 0), datetime.datetime(2016, 8, 11, 0, 0)]] 
관련 문제