2012-07-18 106 views
8

我正在尝试创建一个生成器函数来重复工作日(平日),跳过周末(假期也不错!)。到目前为止,我只简单地迭代函数数天:Python日期范围生成器在工作日内

def daterange(startDate, endDate): 
    for i in xrange(int((endDate - startDate).days)): 
     yield startDate + timedelta(i) 

我挣扎找出一个清洁,高效和Python的方式使发电机跳过周末和节假日。提前致谢!

+1

看到这个问题的假期:http://stackoverflow.com/questions/1986207/holiday-calendars-file -formats-et-al – 2012-07-18 21:30:06

回答

23

我会强烈建议使用dateutil库这样的任务。一个基本的(忽略节假日)迭代器来工作日内然后简单地说就是:

from dateutil.rrule import DAILY, rrule, MO, TU, WE, TH, FR 

def daterange(start_date, end_date): 
    return rrule(DAILY, dtstart=start_date, until=end_date, byweekday=(MO,TU,WE,TH,FR)) 
+0

不错的例子! +1 – 2012-07-18 21:14:41

+0

这实际上是做你说的吗?我在Linux和Mac OS上试用了Python 2.7和3.3,并且在所有情况下都会返回包括周末在内的所有日子。如果你看看'dateutil.rrule.WDAYMASK',你可能会发现它是一个0-6的列表,即所有的日子,而不仅仅是星期一到星期五。 – 2013-06-25 10:21:12

+0

@JohnZwinck对,WDAYMASK确实不正确(至少在当前版本的dateutil中)。我更新了答案以反映这一点。 – earl 2013-06-25 22:06:44

6

假设startDateendDate是日期时间或日期对象,您可以使用the weekday method来获取星期几,然后在星期六或星期天跳过它。只要做:

def daterange(startDate, endDate): 
    for i in xrange(int((endDate - startDate).days)): 
     nextDate = startDate + timedelta(i) 
     if nextDate.weekday() not in (5, 6): 
      yield startDate + timedelta(i) 

对于假期,你将不得不手动检查你想要的每个假期。有些假期是以复杂的方式定义的,所以这可能有点棘手。

7

有一个有用的库叫做dateutil,可以为你做这种事情。它可以生成日期范围(或基于自定义规则的日期),排除特定日期,考虑从一天开始的一周等等。还有比内置日期时间库更灵活的timedelta。

文档在http://labix.org/python-dateutil/ - 和可用PyPI上

0
def get_date_range(start, end, workdays=None, holidays=None, skip_non_workdays=True): 
""" 
This function calculates the durations between 2 dates skipping non workdays and holidays as specified 
:rtype : tuple 
:param start: date 
:param end: date 
:param workdays: string [Comma Separated Values, 0 - Monday through to 6 - Sunday e.g "0,1,2,3,4"] 
:param holidays: list 
:param skip_non_workdays: boolean 
:return: 
""" 
from datetime import timedelta 

duration = 0 

# define workdays 
if workdays is None: 
    workdays = [0, 1, 2, 3, 4] 
else: 
    workdays = workdays.split(",") 

# check if we need to skip non workdays 
if skip_non_workdays is False: 
    workdays = [0, 1, 2, 3, 4, 5, 6] 

# validate dates 
if end < start: 
    return False, "End date is before start date" 

# now its time for us to iterate 
i = start 
while i <= end: 

    # first let's give benefit of the doubt 
    incr = True 

    # lets see if day is in the workday array if not then fault it's existence here 
    try: 
     workdays.index(i.weekday()) 
    except ValueError: 
     incr = False 

    # lets check if day is an holiday, charge guilty if so. 
    # We are checking the index in holiday array 
    try: 
     holidays.index(i) 
     incr = False 
    except (ValueError, AttributeError): 
     pass 

    if incr: 
     duration += 1 
     print "This day passed the criterion %s" % i 

    i += timedelta(1) 

return True, duration