2012-10-02 37 views

回答

7

跳绳周末会很容易做这样的事情:

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格式)日历的饲料,一对美国将是http://files.apple.com/calendars/US32Holidays.ics

您可以使用icalendar模块来解析这一点。基于OMZ代码

0

这将需要一些工作,因为在任何图书馆中没有任何定义的假期构造(据我所知至少)。您将需要创建自己的枚举。

通过在您的datetime对象上调用.weekday() < 6可以轻松地检查周末日期。

2

没有真正的捷径来做到这一点。试试这个办法:

  1. 创建具有方法skip(self, d)这对于应该跳过日期返回True类。
  2. 在包含所有假期的课程中创建一本词典为date objects。不要使用datetime或类似的因为一天中的一小部分会杀死你。任何日期
  3. 返回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 
+0

你的回答是有用的,但我不能标记:(我没有足够的声望点!!谢谢! – cyberbrain

4

谢谢,我做了一些变化不大......这为其他用户也许有所帮助:

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) 
11

如果你不介意使用第三方库,然后dateutil是得心应手

from dateutil.rrule import * 
print "In 4 business days, it's", rrule(DAILY, byweekday=(MO,TU,WE,TH,FR))[4] 

您还可以查看rruleset并使用.exdate()提供假期以跳过计算中的假期,并且可以选择cache选项以避免重新计算,这可能值得您考虑。

+0

看起来像可能会有提供这种支持的'dateutil'的扩展:https://pypi.python.org/pypi/bdateutil/0.1 – Blairg23

+0

or pandas:https://stackoverflow.com/a/19036752/2097 – BlackShift

4

我想要一个不是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) 
+0

是的,我们需要一些不是O(N),但这是真的可能吗?如何避免假期之间? – Ethan

+1

真棒解决方案,虽然它不处理OP假期的假期,而且,你应该使用'math.floor(business_days_to_add/5)在第二行中,所以它也可以在Python3中使用。 – lufte

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 
相关问题