我试图在指定日期添加n(整数)工作日,添加日期必须避免假期和周末(工作日内不包括)
答案 0 :(得分:13)
跳过周末会很容易做到这样的事情:
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)
节假日的问题在于,它们因国家或地区,宗教等而有很大差异。您需要一个列表/假期用于您的用例,然后以类似的方式跳过它们。起点可能是Apple为iCal发布的日历源(采用ics格式),美国的日历源为http://files.apple.com/calendars/US32Holidays.ics
您可以使用icalendar模块解析此问题。
答案 1 :(得分:12)
如果您不介意使用第三方库,则dateutil便于使用
from dateutil.rrule import *
print "In 4 business days, it's", rrule(DAILY, byweekday=(MO,TU,WE,TH,FR))[4]
您还可以查看rruleset
并使用.exdate()
提供假期以跳过计算中的假期,并且可选择使用cache
选项以避免重新计算可能值得期待着。
答案 2 :(得分:5)
没有真正的捷径可以做到这一点。试试这种方法:
skip(self, d)
的类,该方法返回True
表示应跳过的日期。datetime
或类似的东西,因为一天的分数会杀了你。True
d.weekday() >= 5
醇>
要添加N天,请使用以下方法:
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
答案 3 :(得分:5)
感谢基于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)
答案 4 :(得分:5)
我想要一个不是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)
答案 5 :(得分:1)
这将需要一些工作,因为在任何库中没有任何已定义的假期构造(至少据我所知)。您需要创建自己的枚举。
通过在日期时间对象上调用.weekday() < 6
,可以轻松查看周末日。
答案 6 :(得分:0)
希望这会有所帮助。它不是O(N)
而是O(holidays)
。此外,假期仅在偏移为正时才有效。
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
答案 7 :(得分:0)
如果某人需要增加/减少天数,请扩展@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
答案 8 :(得分:0)
类似于@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)
答案 9 :(得分:0)
我知道它不能处理假期,但是我发现此解决方案更有用,因为它的时间是恒定的。它包括计算整周数,增加假期则稍微复杂一些。我希望它可以帮助某人:)
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)
答案 10 :(得分:-1)
我正在使用以下代码来处理营业日期差额。假期,您需要创建自己的列表以跳过。
l1 = [1,3,2,4,3,4,5,3,2,1]
l_uni = []
for ii, item in enumerate(l1):
if ii==0:
l_uni.append(item)
elif l1[ii] != l1[ii-1]:
l_uni.append(item)
for jj in range(0,len(l_uni)):
try:
if l_uni[jj] == 3:
if l_uni[jj+1] == 4:
if l_uni[jj+2] == 5:
print('found sequence')
break
except IndexError:
print('no sequence')