2012-07-18 52 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