特定の日付にn(整数)の稼働日を追加しようとしています。日付の追加では、休日と週末を回避する必要があります(稼働日には含まれていません)。
12 に答える
週末をスキップするのは、次のようなことを行うのは非常に簡単です。
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 形式で) 公開しているカレンダー フィードである可能性があります。
これを解析するには、 icalendarモジュールを使用できます。
サードパーティのライブラリを使用してもかまわない場合は、dateutilが便利です
from dateutil.rrule import *
print "In 4 business days, it's", rrule(DAILY, byweekday=(MO,TU,WE,TH,FR))[4]
また、計算でそれらをスキップするために休日を提供するために見てrruleset
使用することもできます。オプションで、検討する価値のある再計算を回避するオプションがあります。.exdate()
cache
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)
これを行うための本当の近道はありません。このアプローチを試してください:
- スキップする必要がある日付
skip(self, d)
を返すメソッドを持つクラスを作成します。True
- すべての休日を日付オブジェクトとして含むクラスに辞書を作成します。
datetime
1日の分数があなたを殺すので、使用しないでください。 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
(少なくとも私の知る限り)どのライブラリにも休日の定義された構成要素がないため、これには多少の作業が必要です。それらの独自の列挙を作成する必要があります。
.weekday() < 6
週末のチェックは、datetime オブジェクトを呼び出すことで簡単に実行できます。
お役に立てれば。ではありません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