2012-10-02 16 views
11

Sto tentando di aggiungere n (numero intero) giorni lavorativi a una determinata data, l'aggiunta data deve evitare le festività e i fine settimana (non è inclusa nel giorni lavorativi)Aggiungere n giorni lavorativi a una determinata data ignorando le festività ei fine settimana in python

+2

puoi mostrarci cosa hai provato fino ad ora - e anche, come vuoi che il codice funzioni? una funzione?o semplicemente come farlo? –

+0

dovresti inserire il codice di sicurezza delle date delle vacanze (penso comunque) –

risposta

7

Saltare i fine settimana sarebbe abbastanza facile fare qualcosa di simile:

import datetime 
def date_by_adding_business_days(from_date, add_days): 
    business_days_to_add = add_days 
    current_date = from_date 
    while business_days_to_add > 0: 
     current_date += datetime.timedelta(days=1) 
     weekday = current_date.weekday() 
     if weekday >= 5: # sunday = 6 
      continue 
     business_days_to_add -= 1 
    return current_date 

#demo: 
print '10 business days from today:' 
print date_by_adding_business_days(datetime.date.today(), 10) 

Il problema delle vacanze è che variano molto per nazione o anche per regione, religione, ecc. visualizzare una lista/una serie di festività per il proprio caso d'uso e poi saltarle in un modo simile. Un punto di partenza potrebbe essere il feed di calendario che Apple pubblica per iCal (nel formato ics), quello per gli Stati Uniti sarebbe http://files.apple.com/calendars/US32Holidays.ics

È possibile utilizzare il modulo icalendar per analizzarlo.

0

Questo richiederà un po 'di lavoro dal momento che non esiste alcun costrutto definito per le vacanze in nessuna libreria (almeno a mia conoscenza). Dovrai creare la tua enumerazione di quelli.

Il controllo dei giorni del fine settimana viene effettuato facilmente chiamando .weekday() < 6 sull'oggetto datetime.

2

Non esiste una vera scorciatoia per farlo. Prova questo approccio:

  1. creare una classe che ha un metodo che restituisce skip(self, d)True per le date che dovrebbero essere ignorati.
  2. Creare un dizionario nella classe che contiene tutte le festività come date objects. Non usare datetime o simili perché le frazioni di un giorno ti uccideranno.
  3. ritorno True per qualsiasi data che è nel dizionario o d.weekday() >= 5

Per aggiungere N giorni, utilizzare questo metodo:

def advance(d, days): 
    delta = datetime.timedelta(1) 

    for x in range(days): 
     d = d + delta 
     while holidayHelper.skip(d): 
      d = d + delta 

    return d 
+0

La tua risposta è stata utile ma non posso contrassegnarla :(non ho abbastanza punti reputazione !! grazie! – cyberbrain

4

Grazie basati sul codice OMZ ho fatto alcuni piccoli cambiamenti ... è forse utile per gli altri utenti:

import datetime 
def date_by_adding_business_days(from_date, add_days,holidays): 
    business_days_to_add = add_days 
    current_date = from_date 
    while business_days_to_add > 0: 
     current_date += datetime.timedelta(days=1) 
     weekday = current_date.weekday() 
     if weekday >= 5: # sunday = 6 
      continue 
     if current_date in holidays: 
      continue 
     business_days_to_add -= 1 
    return current_date 

#demo: 
Holidays =[datetime.datetime(2012,10,3),datetime.datetime(2012,10,4)] 
print date_by_adding_business_days(datetime.datetime(2012,10,2), 10,Holidays) 
11

Se non ti dispiace utilizzando una libreria 3rd party allora dateutil è a portata di mano

from dateutil.rrule import * 
print "In 4 business days, it's", rrule(DAILY, byweekday=(MO,TU,WE,TH,FR))[4] 

potete anche guardare rruleset e utilizzando .exdate() offrire le vacanze di saltare quelli nel calcolo e, facoltativamente, c'è un'opzione cache per evitare di ri-calcolo che potrebbe essere la pena di guardare a.

+0

Sembra che ci possa essere un'estensione di 'dateutil' che fornisce questo supporto: https://pypi.python.org/pypi/bdateutil/0.1 – Blairg23

+0

o panda: https://stackoverflow.com/a/19036752/2097 – BlackShift

4

Volevo una soluzione che non fosse O (N) e sembrava un po 'divertente del codice golf. Ecco cosa ho sbattuto fuori nel caso qualcuno fosse interessato. Funziona con numeri positivi e negativi. Fammi sapere se mi sono perso qualcosa.

def add_business_days(d, business_days_to_add): 
    num_whole_weeks = business_days_to_add/5 
    extra_days  = num_whole_weeks * 2 

    first_weekday = d.weekday() 
    remainder_days = business_days_to_add % 5 

    natural_day  = first_weekday + remainder_days 
    if natural_day > 4: 
     if first_weekday == 5: 
      extra_days += 1 
     elif first_weekday != 6: 
      extra_days += 2 

    return d + timedelta(business_days_to_add + extra_days) 
+0

Sì, abbiamo bisogno di qualcosa non O (N), ma è davvero possibile? Come evitare le festività intermedie? – Ethan

+1

Soluzione impressionante, sebbene non gestisca le vacanze come l'OP voleva. Inoltre, dovresti usare 'math.floor (business_days_to_add/5)' nella seconda riga quindi funziona anche in Python3. – lufte

0

Spero che questo aiuti. Non è O(N) ma O(holidays). Inoltre, le festività funzionano solo quando l'offset è positivo.

def add_working_days(start, working_days, holidays=()): 
    """ 
    Add working_days to start start date , skipping weekends and holidays. 

    :param start: the date to start from 
    :type start: datetime.datetime|datetime.date 
    :param working_days: offset in working days you want to add (can be negative) 
    :type working_days: int 
    :param holidays: iterator of datetime.datetime of datetime.date instances 
    :type holidays: iter(datetime.date|datetime.datetime) 
    :return: the new date wroking_days date from now 
    :rtype: datetime.datetime 
    :raise: 
     ValueError if working_days < 0 and holidays 
    """ 
    assert isinstance(start, (datetime.date, datetime.datetime)), 'start should be a datetime instance' 
    assert isinstance(working_days, int) 
    if working_days < 0 and holidays: 
     raise ValueError('Holidays and a negative offset is not implemented. ') 
    if working_days == 0: 
     return start 
    # first just add the days 
    new_date = start + datetime.timedelta(working_days) 
    # now compensate for the weekends. 
    # the days is 2 times plus the amount of weeks are included in the offset added to the day of the week 
    # from the start. This compensates for adding 1 to a friday because 4+1 // 5 = 1 
    new_date += datetime.timedelta(2 * ((working_days + start.weekday()) // 5)) 
    # now compensate for the holidays 
    # process only the relevant dates so order the list and abort the handling when the holiday is no longer 
    # relevant. Check each holiday not being in a weekend, otherwise we don't mind because we skip them anyway 
    # next, if a holiday is found, just add 1 to the date, using the add_working_days function to compensate for 
    # weekends. Don't pass the holiday to avoid recursion more then 1 call deep. 
    for hday in sorted(holidays): 
     if hday < start: 
      # ignore holidays before start, we don't care 
      continue 
     if hday.weekday() > 4: 
      # skip holidays in weekends 
      continue 
     if hday <= new_date: 
      # only work with holidays up to and including the current new_date. 
      # increment using recursion to compensate for weekends 
      new_date = add_working_days(new_date, 1) 
     else: 
      break 
    return new_date 
Problemi correlati