2016-08-24 30 views
2

同様の問題に対処する記事はたくさんありますが、問題のある制約と同じ制約はありません。Python2:日曜日 - 土曜日の週の開始日/終了日指定された日付範囲の場合

データセンターから任意の数週間分のデータを取得するスクリプトを作成しています。取得する週は、外部ユーザーがスクリプトに提供する日付範囲によって異なります。データセンターの週は日曜日から土曜日までです。 Pythonの週は月曜日から日曜日までです。

日付範囲の各日付の前の日曜日と土曜日の日付を取得する必要があります。問題を複雑にするために、週開始日も週終了日も要求された範囲外になることはありません。これにより、範囲内の各日付から1日を単に減算することができなくなります。

いくつかのシナリオ例:

例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))を "現在の週"に続けての第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に置き換えます。 2番目を怠け者にしたい場合は、return文のzip_longestのまわりのlist()ラッパーを削除してください。

+0

これは素晴らしいです!両方のソリューションが本当にうまく機能しました。素晴らしい説明も!私は複数のアップヴォートを与えることができたらいいと思う。ありがとうございました! –

+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)]] 
関連する問題