2012-07-18 12 views
8

Sto provando a creare una funzione generatore per iterare nei giorni lavorativi (nei giorni feriali), saltando i fine settimana (e anche le vacanze sarebbero belle!). Finora, ho solo una funzione che semplicemente itera nel corso del giorno:Generatore di intervalli di date in Python durante i giorni lavorativi

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

Sto lottando per trovare un modo pulito, efficiente, e divinatorio per rendere il generatore di saltare nel fine settimana e festivi. Grazie in anticipo!

+1

Vedere questa domanda per le vacanze: http://stackoverflow.com/questions/1986207/holiday-calendars-file -formats-et-al –

risposta

23

vorrei forte consiglia di utilizzare la libreria dateutil per questi compiti. Una base (non vacanze ignorando) iteratore su giorni lavorativi poi semplicemente è:

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

Bel esempio! +1 –

+0

Questo fa effettivamente quello che dici? Ho provato su Python 2.7 e 3.3 su Linux e Mac OS, e in tutti i casi restituisce tutti i giorni, compresi i fine settimana. Se guardi a 'dateutil.rrule.WDAYMASK' potresti vedere che si tratta di un elenco di 0-6, vale a dire tutti i giorni, non solo il lunedì-venerdì. –

+0

@JohnZwinck Giusto, WDAYMASK è effettivamente errato (almeno con le versioni correnti di dateutil). Ho aggiornato la risposta per riflettere questo. – earl

6

Supponendo che startDate e endDate siano oggetti datetime o data, è possibile utilizzare the weekday method per ottenere il giorno della settimana, quindi saltarlo se è sabato o domenica. Basta fare:

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) 

Per le vacanze è necessario controllare manualmente per ogni vacanza che si desidera. Alcune festività sono definite in modi complessi, quindi potrebbe essere un po 'complicato.

7

C'è una libreria utile chiamata dateutil che può fare questo genere di cose per voi. Può generare intervalli di date (o date in base a regole personalizzate), escludendo determinati giorni, considerare una settimana che inizia in un giorno, ecc ... Ha anche un timedelta un po 'più flessibile rispetto alla libreria datetime integrata.

Docs a http://labix.org/python-dateutil/ - e disponibili su Cheese Shop

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 
Problemi correlati