Qual è il modo migliore per trovare l'inverso di datetime.isocalendar ()?
Domanda
The Python datetime.isocalendar ()
restituisce un datetime
specificato. Esiste una funzione inversa corrispondente? In caso contrario, esiste un modo semplice per calcolare una data data un anno, il numero della settimana e il giorno della settimana?
Soluzione
Di recente ho dovuto risolvere questo problema da solo, e ho trovato questa soluzione:
import datetime
def iso_year_start(iso_year):
"The gregorian calendar date of the first day of the given ISO year"
fourth_jan = datetime.date(iso_year, 1, 4)
delta = datetime.timedelta(fourth_jan.isoweekday()-1)
return fourth_jan - delta
def iso_to_gregorian(iso_year, iso_week, iso_day):
"Gregorian calendar date for the given ISO year, week and day"
year_start = iso_year_start(iso_year)
return year_start + datetime.timedelta(days=iso_day-1, weeks=iso_week-1)
Alcuni casi di test:
>>> iso = datetime.date(2005, 1, 1).isocalendar()
>>> iso
(2004, 53, 6)
>>> iso_to_gregorian(*iso)
datetime.date(2005, 1, 1)
>>> iso = datetime.date(2010, 1, 4).isocalendar()
>>> iso
(2010, 1, 1)
>>> iso_to_gregorian(*iso)
datetime.date(2010, 1, 4)
>>> iso = datetime.date(2010, 1, 3).isocalendar()
>>> iso
(2009, 53, 7)
>>> iso_to_gregorian(*iso)
datetime.date(2010, 1, 3)
Altri suggerimenti
import datetime
def iso_to_gregorian(iso_year, iso_week, iso_day):
"Gregorian calendar date for the given ISO year, week and day"
fourth_jan = datetime.date(iso_year, 1, 4)
_, fourth_jan_week, fourth_jan_day = fourth_jan.isocalendar()
return fourth_jan + datetime.timedelta(days=iso_day-fourth_jan_day, weeks=iso_week-fourth_jan_week)
Questo è stato adattato dall'ottima risposta di @ BenJames. Non devi conoscere il primo giorno dell'anno. Devi solo conoscere un esempio di una data che è certamente nello stesso anno ISO e la settimana e il giorno del calendario ISO di quella data.
Il 4 gennaio è semplicemente un esempio, perché, come ha sottolineato Ben, il 4 gennaio appartiene sempre allo stesso anno ISO e anno gregoriano, ed è il primo giorno dell'anno a farlo.
Poiché le settimane hanno tutte la stessa lunghezza, puoi semplicemente sottrarre i giorni e le settimane tra l'ISO della data desiderata e l'ISO della data che conosci in entrambi i moduli e aggiungere quel numero di giorni e settimane . (Non importa se questi numeri sono positivi o negativi, quindi puoi scegliere qualche altro "giorno fisso" come il 28 dicembre.)
Modifica
L'ho corretto perché, come è stato utile sottolineato da @JoSo, il primo giorno dell'anno gregoriano che appartiene anche all'anno ISO è il 4 gennaio e non il 5 gennaio. Come dice la spiegazione, non importa quale data sia stata scelta come punto di riferimento, ma la scelta del 4 gennaio rende questa scelta meno "magica".
A partire da Python 3.6, puoi usare le nuove direttive % G
, % u
e % V
. Vedi numero 12006 e documentazione aggiornata :
% G
ISO 8601 anno con secolo che rappresenta l'anno che contiene la maggior parte della settimana ISO (% V
).
% u
ISO 8601 giorno della settimana come numero decimale dove 1 è lunedì.
% V
Settimana ISO 8601 come numero decimale con lunedì come primo giorno della settimana. La settimana 01 è la settimana contenente il 4 gennaio.
Data una stringa con anno, numero della settimana e numero del giorno della settimana, è facile analizzarli fino a una data con:
from datetime import datetime
datetime.strptime('2002 01 1', '%G %V %u').date()
o come funzione con input interi:
from datetime import datetime
def date_from_isoweek(iso_year, iso_weeknumber, iso_weekday):
return datetime.strptime(
'{:04d} {:02d} {:d}'.format(iso_year, iso_weeknumber, iso_weekday),
'%G %V %u').date()
A partire da Python 3.6, datetime.strptime ()
supporterà % G
, % V
e % u
direttive, quindi puoi semplicemente fare datetime.strptime ('2015 1 2', '% G% V% u'). date ()
. Vedi: https://hg.python.org/cpython/rev/acdebfbfbdcf
Per le prossime persone che verranno qui, una versione più breve, a definizione singola, della buona soluzione di Ben:
def iso_to_gregorian(iso_year, iso_week, iso_day):
jan4 = datetime.date(iso_year, 1, 4)
start = jan4 - datetime.timedelta(days=jan4.isoweekday()-1)
return start + datetime.timedelta(weeks=iso_week-1, days=iso_day-1)
Nota che% W è la settimana # (0-53) che NON È LO STESSO della settimana ISO (1-53). Ci saranno casi limite in cui% W non funzionerà.
Ho trovato una soluzione simile a quella pubblicata da Ben James, ma usando una sola funzione:
import datetime
def getDateFromWeek(year,week,day):
"""Method to retrieve the date from the specified week, year and weekday"""
year_start = datetime.date(year,1,1)
ys_weekday = year_start.weekday()
delta = (week*7)+(day-ys_weekday)
if ys_weekday<4:
delta -= 7
return year_start + datetime.timedelta(days=delta)
L'ho provato con valori limite come l'ultima settimana del 2020 e la prima settimana del 2021, e ha funzionato abbastanza bene.
EDIT: ignoralo, i casi limite sono un dolore. Vai con la soluzione di Ben.
Ok, a un esame più attento ho notato che strptime
ha i parametri % W
e % w
, quindi i seguenti lavori:
def fromisocalendar(y,w,d):
return datetime.strptime( "%04dW%02d-%d"%(y,w-1,d), "%YW%W-%w")
Un paio di gotcha: il numero della settimana ISO inizia da 1
, mentre % W
inizia da 0
. Il giorno della settimana ISO inizia a 1
(lunedì), che equivale a % w
, quindi probabilmente domenica dovrebbe essere 0
, non 7
...