Python2:在指定日期范围内检索星期日 - 星期六星期开始/结束日期
有很多帖子可以解决类似的问题,但没有一个与我的问题有相同的限制。Python2:在指定日期范围内检索星期日 - 星期六星期开始/结束日期
我在写一个脚本,可以从数据中心获取任意数量的数据。它提取哪个星期取决于外部用户提供给我的脚本的日期范围。数据中心的周从周日到周六。 Python的周从星期一到星期日运行。
我需要能够获取日期范围内每个日期之后的星期日和星期六的日期。使问题复杂化的是,周开始日期和周结束日期都不能超出要求的范围。这可以防止我简单地从范围内的每个日期减去一天。
一些示例场景:
例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
]
你应该能够做到这一点很容易地使用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)
添加到它来生成即将到来的星期六,并且如果我在日期范围之外,我会将最后日期“剪辑”为日期范围。
使用你的例子:
>>> 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
完成类似的东西:
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))
需要注意的是,如果你想第一个偷懒,只是将dts.append(x)
的所有实例替换为yield x
。如果你想第二个懒惰,只需在return语句中删除围绕zip_longest
的list()
包装。
这太棒了!这两种解决方案都非常好。很好的解释!我希望我可以给一个以上的赞成票。谢谢! –
@NickMiller你可以分类,因为你问过这个问题 - 你可以通过选择投票按钮下方的绿色复选标记来接受答案。 – Paul
完成!再次感谢,保罗。 –
这里有一个稍微不详细,尽管简洁的答案。
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)]]
我想你可以设置一周的开始日期。让我深入研究它。 – Vasif
您可以使用calendar.setfirstweekday(calendar.SUNDAY)。不幸的是,日历类没有函数返回星期开始/结束日期。至少不是我一直能找到的。 –