Dato un intervallo di date come calcolare il numero di week-end parzialmente o totalmente all'interno di tale intervallo?

StackOverflow https://stackoverflow.com/questions/883615

  •  22-08-2019
  •  | 
  •  

Domanda

Dato un intervallo di date come calcolare il numero di week-end parzialmente o totalmente all'interno di tale intervallo?

(Alcune definizioni come richiesto: prendere 'fine settimana' a significare Sabato e Domenica. Tale intervallo è compreso cioè la data finale è parte della gamma 'Totalmente o parzialmente' significa che qualsiasi parte del fine settimana rientrano nell'intervallo di date significa che l'intero weekend è contato.)

Per semplificare immagino solamente effettivamente bisogno di conoscere la durata e che giorno della settimana del giorno iniziale è ...

I maledettamente bene ora sta andando a coinvolgere facendo divisione intera per 7 e una logica di aggiungere 1 a seconda del resto, ma io non riesco a capire cosa ...

punti extra per risposte in Python ;-)

Modifica

Ecco il mio codice finale.

I fine settimana sono (notti come Contiamo soggiornato) Venerdì e Sabato e giorni sono 0-indicizzato a partire da Lunedi. Ho usato l'algoritmo di onebyone e il layout del codice di Tom. Grazie mille ragazzi.

def calc_weekends(start_day, duration):
    days_until_weekend = [5, 4, 3, 2, 1, 1, 6]
    adjusted_duration = duration - days_until_weekend[start_day]
    if adjusted_duration < 0:
        weekends = 0
    else:
        weekends = (adjusted_duration/7)+1
    if start_day == 5 and duration % 7 == 0: #Saturday to Saturday is an exception
        weekends += 1
    return weekends

if __name__ == "__main__":
    days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    for start_day in range(0,7):
        for duration in range(1,16):
            print "%s to %s (%s days): %s weekends" % (days[start_day], days[(start_day+duration) % 7], duration, calc_weekends(start_day, duration))
        print
È stato utile?

Soluzione

Approccio generale per questo genere di cose:

Per ogni giorno della settimana, capire quanti giorni sono necessari prima di un periodo che inizia in quel giorno "contiene un week-end". Per esempio, se "contiene un week-end" significa "contiene sia il Sabato e la Domenica", allora abbiamo la seguente tabella:

Domenica: 8 Lunedi: 7 Martedì: 6 Mercoledì: 5 Giovedi: 4 Venerdì: 3 Sabato: 2

Per "parzialmente o totalmente", abbiamo:

Domenica: 1 Lunedi: 6 Martedì: 5 Mercoledì: 4 Giovedi: 3 Venerdì: 2 Sabato: 1

Ovviamente questo non deve essere codificato come un tavolo, ora che è ovvio come sembra.

Quindi, dato il giorno della settimana di inizio del periodo, sottrarre [*] il valore magico dalla durata del periodo in giorni (probabilmente start-end + 1, per includere sia fenceposts). Se il risultato è inferiore a 0, contiene 0 week-end. Se è uguale o maggiore di 0, allora esso contiene (almeno) 1 settimana.

Poi bisogna fare i conti con i giorni rimanenti. Nel primo caso questo è facile, un fine settimana in più per intero 7 giorni. Questo è vero anche nel secondo caso per ogni giorno a partire tranne la Domenica, che richiede solo 6 giorni in più per includere un altro week-end. Quindi, nel secondo caso per periodi di Domenica si poteva contare 1 week-end all'inizio del periodo, quindi sottrarre 1 dalla lunghezza e ricalcolare da Lunedi.

Più in generale, cosa sta succedendo qui per il fine settimana "intero o in parte" è che stiamo controllando per vedere se iniziamo a metà il bit interessante (il "week-end"). Se è così, si può:

  • 1) il conte uno, spostare la data di inizio e la fine del pezzo interessante, e ricalcolare.
  • 2) Spostare la data di inizio di nuovo all'inizio del pezzo interessante, e ricalcolare.

Nel caso del fine settimana, c'è solo un caso speciale che inizia a metà strada, in modo da (1) sembra buono. Ma se si stavano diventando la data come data + tempo in secondi piuttosto che giorno, o se foste interessati a settimane lavorative di 5 giorni, piuttosto che il fine settimana 2 giorni, poi (2) potrebbe essere più semplice da capire.

[*] A meno che non si sta utilizzando tipi unsigned, naturalmente.

Altri suggerimenti

Il mio approccio per questo genere di cose: non iniziare a fare in giro cercando di implementare nuovamente la propria logica della data - è difficile, vale a dire. si rovinare tutto per i casi limite e in cattiva luce. Suggerimento: se avete mod 7 aritmetica qualsiasi punto del programma, o state trattando le date come interi qualsiasi punto del programma: non si riesce . Se ho visto la "soluzione accettato" in qualsiasi parte (o anche vicino) il mio codice di base, qualcuno avrebbe bisogno di ricominciare da capo. E mendicanti la fantasia che chiunque si ritenga un programmatore avrebbe votato la risposta su.

Al contrario, utilizzare il costruito nella logica data / ora che viene fornito con Python:

In primo luogo, avere una lista di tutti i giorni che vi interessano:

from datetime import date, timedelta    
FRI = 5; SAT = 6

# a couple of random test dates
now = date.today()
start_date = now - timedelta(57)
end_date = now - timedelta(13)
print start_date, '...', end_date    # debug

days = [date.fromordinal(d) for d in  
            range( start_date.toordinal(),
                   end_date.toordinal()+1 )]

Avanti, filtrare verso il basso per pochi giorni che sono il fine settimana. Nel tuo caso siete interessati a venerdì e sabato sera, che sono 5 e 6. (Si noti come non sto cercando di rotolare questa parte nella lista precedente comprensione, dal momento che sarebbe difficile da verificare come corretto).

weekend_days = [d for d in days if d.weekday() in (FRI,SAT)]

for day in weekend_days:      # debug
    print day, day.weekday()  # debug

Infine, si vuole capire quanti fine settimana sono nella vostra lista. Questa è la parte difficile, ma ci sono in realtà solo quattro casi da considerare, uno per ogni estremità sia per Venerdì o Sabato. Esempi concreti contribuire a rendere più chiaro, più questo è davvero il genere di cosa che si desidera documentato nel codice:

num_weekends = len(weekend_days) // 2

# if we start on Friday and end on Saturday we're ok,
# otherwise add one weekend
#  
# F,S|F,S|F,S   ==3 and 3we, +0
# F,S|F,S|F     ==2 but 3we, +1
# S|F,S|F,S     ==2 but 3we, +1
# S|F,S|F       ==2 but 3we, +1

ends = (weekend_days[0].weekday(), weekend_days[-1].weekday())
if ends != (FRI, SAT):
    num_weekends += 1

print num_weekends    # your answer

Shorter, più chiaro e più facile da capire significa che si può avere più fiducia nel codice, e può andare avanti con i problemi più interessanti.

Per contare fine settimana interi, basta regolare il numero di giorni in modo che si avvia su un Lunedi, poi dividere per sette. (Si noti che se il giorno di inizio è un giorno della settimana, aggiungere giorni per passare al precedente Lunedi, e se è in un fine settimana, sottrarre giorni di tempo per passare al prossimo Lunedi dal momento che già persi questo fine settimana.)

days = {"Saturday":-2, "Sunday":-1, "Monday":0, "Tuesday":1, "Wednesday":2, "Thursday":3, "Friday":4}

def n_full_weekends(n_days, start_day):
    n_days += days[start_day]
    if n_days <= 0:
        n_weekends = 0
    else:
        n_weekends = n_days//7
    return n_weekends

if __name__ == "__main__":
    tests = [("Tuesday", 10, 1), ("Monday", 7, 1), ("Wednesday", 21, 3), ("Saturday", 1, 0), ("Friday", 1, 0),
    ("Friday", 3, 1), ("Wednesday", 3, 0), ("Sunday", 8, 1), ("Sunday", 21, 2)]
    for start_day, n_days, expected in tests:
        print start_day, n_days, expected, n_full_weekends(n_days, start_day)

Se vuoi sapere fine settimana parziali (o settimane), basta guardare la parte frazionaria della divisione per sette.

Si avrebbe bisogno logica esterna accanto matematica crudo. È necessario disporre di una libreria di calendario (o se si dispone di una discreta quantità di tempo implementare da soli) per definire ciò che un week-end, che giorno della settimana che si avvia su, fine su, ecc.

Date un'occhiata a di Python classe calendario .

Senza una definizione logica di giorni nel codice, un puro metodi matematici fallirebbe sul caso angolo, come un intervallo di 1 giorno o, credo, niente più bassa quindi una settimana (o più basso quindi 6 giorni se consentito parziali ).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top