在python中,将n个工作日添加到给定日期,忽略假日和周末
我试图将n(整数)个工作日添加到给定日期,添加的日期必须避免节假日和周末(不包括在工作日中)这需要一些工作,因为任何库中都没有任何定义的节假日构造(至少据我所知)。您需要创建自己的枚举在python中,将n个工作日添加到给定日期,忽略假日和周末,python,datetime,Python,Datetime,我试图将n(整数)个工作日添加到给定日期,添加的日期必须避免节假日和周末(不包括在工作日中)这需要一些工作,因为任何库中都没有任何定义的节假日构造(至少据我所知)。您需要创建自己的枚举 通过在datetime对象上调用.weekday()=5 要添加N天,请使用以下方法: def advance(d, days): delta = datetime.timedelta(1) for x in range(days): d = d + delta
通过在datetime对象上调用
.weekday()<6
可以轻松地检查周末。没有真正的快捷方式。尝试以下方法:
skip(self,d)
,该方法返回应该跳过的日期的True
datetime
或类似的方法,因为一天中的几分钟会让你丧命True
,或d.weekday()>=5
def advance(d, days):
delta = datetime.timedelta(1)
for x in range(days):
d = d + delta
while holidayHelper.skip(d):
d = d + delta
return d
跳过周末很容易做到以下几点:
import datetime
def date_by_adding_business_days(from_date, add_days):
business_days_to_add = add_days
current_date = from_date
while business_days_to_add > 0:
current_date += datetime.timedelta(days=1)
weekday = current_date.weekday()
if weekday >= 5: # sunday = 6
continue
business_days_to_add -= 1
return current_date
#demo:
print '10 business days from today:'
print date_by_adding_business_days(datetime.date.today(), 10)
假日的问题在于,它们因国家、地区、宗教等而有很大差异。您需要为您的用例提供一个假日列表/集合,然后以类似的方式跳过它们。一个起点可能是苹果为iCal发布的日历提要(采用ics格式),而美国则是
您可以使用该模块来解析它。谢谢基于omz代码,我做了一些小改动……这可能对其他用户有帮助:
import datetime
def date_by_adding_business_days(from_date, add_days,holidays):
business_days_to_add = add_days
current_date = from_date
while business_days_to_add > 0:
current_date += datetime.timedelta(days=1)
weekday = current_date.weekday()
if weekday >= 5: # sunday = 6
continue
if current_date in holidays:
continue
business_days_to_add -= 1
return current_date
#demo:
Holidays =[datetime.datetime(2012,10,3),datetime.datetime(2012,10,4)]
print date_by_adding_business_days(datetime.datetime(2012,10,2), 10,Holidays)
如果您不介意使用第三方库,那么它很方便
您还可以查看
rruleset
并使用.exdate()
提供假期以跳过计算中的假期,还可以选择使用缓存
选项来避免重新计算,这可能值得一看。我想要一个不是O(N)的解决方案,它看起来像是一个有趣的代码高尔夫。这是我写的,以防有人感兴趣。适用于正数和负数。如果我遗漏了什么,请告诉我
def add_business_days(d, business_days_to_add):
num_whole_weeks = business_days_to_add / 5
extra_days = num_whole_weeks * 2
first_weekday = d.weekday()
remainder_days = business_days_to_add % 5
natural_day = first_weekday + remainder_days
if natural_day > 4:
if first_weekday == 5:
extra_days += 1
elif first_weekday != 6:
extra_days += 2
return d + timedelta(business_days_to_add + extra_days)
希望这有帮助。它不是
O(N)
,而是O(假日)
。此外,假日仅在偏移为正时有效
def add_working_days(start, working_days, holidays=()):
"""
Add working_days to start start date , skipping weekends and holidays.
:param start: the date to start from
:type start: datetime.datetime|datetime.date
:param working_days: offset in working days you want to add (can be negative)
:type working_days: int
:param holidays: iterator of datetime.datetime of datetime.date instances
:type holidays: iter(datetime.date|datetime.datetime)
:return: the new date wroking_days date from now
:rtype: datetime.datetime
:raise:
ValueError if working_days < 0 and holidays
"""
assert isinstance(start, (datetime.date, datetime.datetime)), 'start should be a datetime instance'
assert isinstance(working_days, int)
if working_days < 0 and holidays:
raise ValueError('Holidays and a negative offset is not implemented. ')
if working_days == 0:
return start
# first just add the days
new_date = start + datetime.timedelta(working_days)
# now compensate for the weekends.
# the days is 2 times plus the amount of weeks are included in the offset added to the day of the week
# from the start. This compensates for adding 1 to a friday because 4+1 // 5 = 1
new_date += datetime.timedelta(2 * ((working_days + start.weekday()) // 5))
# now compensate for the holidays
# process only the relevant dates so order the list and abort the handling when the holiday is no longer
# relevant. Check each holiday not being in a weekend, otherwise we don't mind because we skip them anyway
# next, if a holiday is found, just add 1 to the date, using the add_working_days function to compensate for
# weekends. Don't pass the holiday to avoid recursion more then 1 call deep.
for hday in sorted(holidays):
if hday < start:
# ignore holidays before start, we don't care
continue
if hday.weekday() > 4:
# skip holidays in weekends
continue
if hday <= new_date:
# only work with holidays up to and including the current new_date.
# increment using recursion to compensate for weekends
new_date = add_working_days(new_date, 1)
else:
break
return new_date
def添加工作日(开始、工作日、假日=()):
"""
将工作日添加到开始日期,跳过周末和节假日。
:param start:起始日期
:键入start:datetime.datetime | datetime.date
:param working_days:要添加的工作日中的偏移量(可以为负数)
:键入工作日:int
:param holidays:datetime.datetime的迭代器datetime.date实例
:类型假日:iter(datetime.date | datetime.datetime)
:return:从现在开始的新日期
:rtype:datetime.datetime
:提高:
如果工作日<0和节假日,则值错误
"""
断言isinstance(start,(datetime.date,datetime.datetime)),'start应该是datetime实例'
断言isinstance(工作日,整数)
如果工作日<0和节假日:
raise VALUE ERROR('未执行节假日和负偏移量')
如果工作日=0:
回程起动
#首先加上天数
新建日期=开始+日期时间.timedelta(工作日)
#现在补偿周末的费用。
#天数为2倍,加上周数,包含在添加到周中某一天的补偿中
#从一开始。这补偿了向星期五添加1,因为4+1//5=1
new_date+=datetime.timedelta(2*((工作日+开始.weekday())//5))
#现在补偿假期
#仅处理相关日期,以便在假期结束时对列表进行排序并中止处理
#相关的。检查每个假期是否在周末,否则我们不介意,因为我们无论如何都会跳过它们
#接下来,如果找到假日,只需在日期上添加1,使用add_working_days函数来补偿
#周末。不要放假,以免递归超过1次调用深度。
对于已排序的hday(假日):
如果hday<开始:
#在开始之前忽略假期,我们不在乎
持续
如果hday.weekday()大于4:
#周末不去度假
持续
如果hday如果某人需要增加/减少天数,则扩展@omz的答案:
def add_business_days(from_date, ndays):
business_days_to_add = abs(ndays)
current_date = from_date
sign = ndays/abs(ndays)
while business_days_to_add > 0:
current_date += datetime.timedelta(sign * 1)
weekday = current_date.weekday()
if weekday >= 5: # sunday = 6
continue
business_days_to_add -= 1
return current_date
类似于@omz解决方案,但递归:
def add_days_skipping_weekends(start_date, days):
if not days:
return start_date
start_date += timedelta(days=1)
if start_date.weekday() < 5:
days -= 1
return add_days_skipping_weekends(start_date, days)
def添加天数跳过周末(开始日期,天数):
如果不是三天:
返回开始日期
开始日期+=timedelta(天数=1)
如果开始日期.工作日()小于5:
天数-=1天
返回添加天数跳过周末(开始日期,天)
我使用以下代码处理业务日期增量。对于假期,您需要创建自己的列表以跳过
today = datetime.now()
t_1 = today - BDay(1)
t_5 = today - BDay(5)
t_1_str = datetime.strftime(t_1,"%Y%m%d")
我知道它不能处理假期,但我发现这个解决方案更有用,因为它在时间上是恒定的。它包括计算整周的数量,加上假期就有点复杂了。我希望它能帮助某人:)
你能告诉我们到目前为止你已经尝试了什么吗?还有,你希望代码如何工作?函数?或者仅仅是怎么做?你必须硬编码假期的日期(我想无论如何)你的答案是有用的,但我不能标记:(我没有足够的声望点!!谢谢!是的,我们需要的不是O(N)的东西),但这真的有可能吗?如何避免中间的假期?很棒的解决方案,尽管它不能像OP所希望的那样处理假期。此外,您应该使用math.floor(business\u days\u to\u add/5)
在第二行,因此它在Python3中也可以工作。看起来可能有一个dateutil
的扩展提供了这种支持:我看到一些错误。如果您从星期天开始,并添加1天。自开始。工作日()=6,所以它将增加2天,即使只需要1天。它应该是周一而不是周三。此外,当您为周末添加天数时,您不会检查是否有任何周末将被忽略
today = datetime.now()
t_1 = today - BDay(1)
t_5 = today - BDay(5)
t_1_str = datetime.strftime(t_1,"%Y%m%d")
def add_days(days):
today = datetime.date.today()
weekday = today.weekday() + ceil(days)
complete_weeks = weekday // 7
added_days = weekday + complete_weeks * 2
return today + datetime.timedelta(days=added_days)