2012-07-18 17 views
8

Próbuję utworzyć funkcję generatora do iteracji w dni robocze (dni powszednie), pomijając weekendy (a wakacje też byłyby miłe!). Do tej pory mam tylko funkcję, która po prostu iteracje nad dniach:Generator pasma czasu w języku Python w dniach roboczych

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

Ja usiłuje dowiedzieć się czyste, sprawne i pythonic sposób, aby generator przeskoczyć weekendów i świąt. Z góry dziękuję!

+1

Zobacz to pytanie na święta: http://stackoverflow.com/questions/1986207/holiday-calendars-file -formats-et-al –

Odpowiedz

23

będę silny zalecamy użycie dateutil biblioteki dla takich zadań. Podstawowy (nie ignorując święta) iterator ciągu dni roboczych po prostu jest:

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

Dobry przykład! +1 –

+0

Czy to rzeczywiście robi to, co mówisz? Próbowałem go na Pythonie 2.7 i 3.3 na Linux i Mac OS, i we wszystkich przypadkach zwraca wszystkie dni, w tym weekendy. Jeśli spojrzysz na "dateutil.rrule.WDAYMASK", możesz zauważyć, że jest to lista od 0 do 6, to znaczy od wszystkich dni, nie tylko od poniedziałku do piątku. –

+0

@JohnZwinck W porządku, WDAYMASK jest rzeczywiście niepoprawny (przynajmniej w przypadku aktualnych wersji dateutil). Zaktualizowałem odpowiedź, aby to odzwierciedlić. – earl

6

Zakładając, że startDate i endDate są obiektami datetime lub datami, można użyć numeru the weekday method, aby uzyskać dzień tygodnia, a następnie pominąć go, jeśli jest to sobota lub niedziela. Wystarczy zrobić:

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) 

na wakacje trzeba będzie sprawdzić ręcznie dla każdego wypoczynku chcesz. Niektóre wakacje są określone w sposób złożony, więc może to być nieco trudne.

7

Istnieje przydatna biblioteka o nazwie dateutil, która może zrobić tego rodzaju rzeczy dla ciebie. Może generować przedziały dat (lub daty oparte na regułach niestandardowych), z wyłączeniem niektórych dni, rozważać tydzień zaczynający się od dnia itd ... Ma również nieco bardziej elastyczny timedelta niż wbudowana biblioteka datetime.

Docs w http://labix.org/python-dateutil/ - i jest dostępny na 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