我有这些日期,2020年2月2日和2020年6月30日,我想检查一下它们之间有多少天跳过了12月25日或5月1日这样的指定日期(起初我虽然使用字典, ,但我不知道该怎么做;我还想跳过周末。
我这样做是为了跳过周末,但我仍然对如何跳过假期一无所知;有没有一种方法可以创建一种“黑名单”,因此,如果该差异通过该列表中的任何一天,它将减去一天。
这是周末的“解决方法”:
date_input = '4/2/2020'
date_end = '30/6/2020'
start = datetime.strptime(date_input, "%d-%m-%Y").date()
end = datetime.strptime(date_end, "%d-%m-%Y").date()
Gap = (end - start).days
N_weeks = Gap / 7
weekends = (math.trunc(N_weeks)) * 2
final_result = str((Gap) - weekends)
如果您有list个应该跳过的日期,那么您可以测试其中是否有一个属于您的开始日期和结束日期的范围。 date
对象是可排序的,因此可以使用:
skip_count = 0
for to_skip in dates_to_skip:
if start <= to_skip < end:
skip_count += 1
如果您要跳过的日期列表为大,则以上处理可能需要很长时间。在那种情况下,您要使用bisection快速确定start
和end
之间的匹配日期数,方法是确保要跳过的日期列表保持为sorted order,然后使用在bisect
module中找到要插入bisect
和start
的索引;这两个索引之间的差异是您要从范围计数中减去的匹配日期数:
end
注意,from bisect import bisect_left
def count_skipped(start, end, dates_to_skip):
"""Count how many dates in dates_to_skip fall between start and end
start is inclusive, end is exclusive
"""
if start >= end:
return 0
start_idx = bisect_left(dates_to_skip, start)
end_idx = bisect_left(dates_to_skip, end)
return end_idx - start_idx
为您提供所有值的索引bisect.bisect_left()
中的等于或大于开始日期。对于结束日期,dates_to_skip[idx:]
中的所有值都将较低(dates_to_skip[:idx]
本身可以等于dates_to_skip[idx]
,但不包括end
)。
使用end
的优势在于,需要O(logN)步骤来计算N个日期列表中有多少个日期落在bisect
和start
之间,而上面的简单化end
循环需要O(N)个步骤。有5个或10个要测试的日期无关紧要,但是如果有1000个日期,则for to_skip in dates_to_skip:
方法仅需要10个步骤,而不是1000个就变得很重要。
请注意,您的周末计算是不正确,这太简单了。这是一个示例,显示周末日期的数量在11天的两个不同时段中有所不同:
假设您的开始日期是一个星期一,结束日期是又一个星期的星期五,您之间只有1个周末,所以11-2 = 9个工作日(不计算结束日期):
bisect
在上表中,| M | T | W | T | F | S | S |
|-----|---|---|---|-----|---- |-----|
| [1] | 2 | 3 | 4 | 5 | _1_ | _2_ |
| 6 | 7 | 8 | 9 | (E) | | |
是开始日期,[1]
是结束日期,数字表示工作日;跳过的周末数以(E)
,_1_
数字计算。
但是如果开始日期是星期五,而结束日期是下一个第二周的星期二,那么您在开始和结束之间的整天数相同,但是现在您必须减去two个周末;这两天之间只有7个工作日:
_2_
您需要从开始日期开始查找下一个星期六,从结束日期开始查找上一个饱和度,然后计算这两个日期之间的星期数。如果开始日期或结束日期本身就是周末,则需要相应地调整计数:
| M | T | W | T | F | S | S |
|---|-----|---|---|-----|-----|-----|
| | | | | [1] | _1_ | _2_ |
| 2 | 3 | 4 | 5 | 6 | _3_ | _4_ |
| 7 | (E) | | | | | |
将它们放在一起:
from datetime import timedelta
def count_weekend_days(start, end):
"""Count the number of weekend days (Saturday, Sunday)
Start and end dates are inclusive.
"""
if start >= end:
return 0
result = 0
if start.weekday() == 6: # start is a Sunday
result += 1
if end.weekday() == 5: # end is a Sunday
result += 1
# find the next Saturday from the start
if start.weekday() != 5:
start += timedelta(days=(5 - start.weekday()) % 7)
# find the previous Saturday from the end
if end.weekday() != 5:
end -= timedelta(days=(end.weekday() + 2) % 7)
if start >= end:
return result
# start and end are Saturdays, The difference between these days
# is going to be a whole multiple of 7
# Floor division by 7 gives the number of whole weekends
weekends = (end - start).days // 7
return result + (weekends * 2)