2012-07-18 35 views
8

Estoy intentando crear una función de generador para iterar durante los días hábiles (días laborables), omitiendo los fines de semana (¡y las vacaciones también serían agradables!). Hasta el momento, sólo tengo una función que simplemente itera sobre días:Generador de rango de fechas de Python durante días hábiles

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

estoy luchando para encontrar una manera limpia, eficiente y Pythonic para hacer que el generador de omitir los fines de semana y días festivos. ¡Gracias por adelantado!

+1

Ver esta pregunta para las vacaciones: http://stackoverflow.com/questions/1986207/holiday-calendars-file -formats-et-al –

Respuesta

23

I fuerte recomendaría el uso de la biblioteca dateutil para tales tareas. Un iterador básica (no vacaciones ignorando) durante días hábiles a continuación, simplemente es:

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

Buen ejemplo! +1 –

+0

¿Esto realmente hace lo que dices? Lo intenté en Python 2.7 y 3.3 en Linux y Mac OS, y en todos los casos, devuelve todos los días, incluidos los fines de semana. Si observa 'dateutil.rrule.WDAYMASK', puede ver que es una lista de 0-6, es decir, todos los días, no solo de lunes a viernes. –

+0

@JohnZwinck Right, WDAYMASK es de hecho incorrecto (al menos con las versiones actuales de dateutil). Actualicé la respuesta para reflejar esto. – earl

6

Suponiendo que startDate y endDate son objetos de fecha y hora o fecha, puede usar the weekday method para obtener el día de la semana, luego omítelo si es sábado o domingo. Solo hazlo:

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) 

Para las vacaciones deberás verificar manualmente para cada vacación que desees. Algunas vacaciones se definen de formas complejas, por lo que esto podría ser un poco complicado.

7

Hay una biblioteca útil llamada dateutil que puede hacer este tipo de cosas por usted. Puede generar rangos de fechas (o fechas basadas en reglas personalizadas), excluir ciertos días, considerar una semana que comienza en un día, etc. También tiene un timedelta algo más flexible que la biblioteca de fecha y hora incorporada.

Docs en http://labix.org/python-dateutil/ - Se dispone de 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 
Cuestiones relacionadas