Quelle est la meilleure façon de trouver l'inverse de datetime.isocalendar ()?

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

  •  08-07-2019
  •  | 
  •  

Question

Le Python datetime.isocalendar () renvoie un tuple (ISO_an, ISO_number_number, ISO_weekday) pour l'objet datetime donné. Existe-t-il une fonction inverse correspondante? Sinon, existe-t-il un moyen facile de calculer une date en fonction d’une année, d’un numéro de semaine et d’un jour de la semaine?

Était-ce utile?

La solution

J'ai récemment eu à résoudre ce problème moi-même et j'ai trouvé cette solution:

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)

Quelques cas de 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)

Autres conseils

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)

Ceci a été adapté de la très bonne réponse de @ BenJames. Vous n'êtes pas obligé de connaître le premier jour de l'année. Vous devez simplement connaître un exemple de date qui se trouve certainement dans la même année ISO, ainsi que la semaine et le jour du calendrier ISO de cette date.

Le 4 janvier n'est qu'un exemple parmi d'autres car, comme Ben l'a souligné, le 4 janvier appartient toujours à la même année ISO et à la même année grégorienne et constitue le premier jour de l'année pour le faire.

Étant donné que les semaines ont la même durée, vous pouvez simplement soustraire les jours et les semaines entre l'ISO de la date souhaitée et l'ISO de la date que vous connaissez dans les deux formulaires, et ajouter ce nombre de jours et de semaines. . (Peu importe que ces chiffres soient positifs ou négatifs, vous pouvez donc choisir un autre "jour fixe" tel que le 28 décembre.)

Modifier

J'ai corrigé cela parce que, comme l'a gentiment souligné @JoSo, le premier jour de l'année grégorienne, qui appartient également à l'année ISO, est le 4 janvier et non le 5 janvier. Comme l'explication l'indique, peu importe la date choisie comme point de référence, le choix du 4 janvier rend ce choix moins "magique".

À partir de Python 3.6, vous pouvez utiliser les nouvelles directives % G , % u et % V . Voir le numéro 12006 et le documentation mise à jour :

  

% G
  ISO 8601 année avec siècle représentant l'année qui contient la plus grande partie de la semaine ISO (% V ).

     

% u
  Jour ISO 8601 sous forme de nombre décimal, où 1 correspond à lundi.

     

% V
  ISO 8601 semaines sous forme de nombre décimal, lundi étant le premier jour de la semaine. La semaine 01 est la semaine du 4 janvier.

Étant donné une chaîne avec l'année, le numéro de la semaine et le numéro du jour de la semaine, il est facile d'analyser ceux-ci jusqu'à une date avec:

from datetime import datetime

datetime.strptime('2002 01 1', '%G %V %u').date()

ou en tant que fonction avec des entrées entières:

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()

À partir de Python 3.6, datetime.strptime () prend en charge les % G , le % V et le % u , vous pouvez donc simplement faire datetime.strptime ('2015 1 2', '% G% V% u'). date () . Voir: https://hg.python.org/cpython/rev/acdebfbfbdcf

Pour les prochains visiteurs, version plus courte et à définition unique de la bonne solution de 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)

Notez que% W est le numéro de la semaine (0-53), qui n'est PAS identique à la semaine ISO (1-53). Il y aura des cas extrêmes où% W ne fonctionnera pas.

J'ai proposé une solution similaire à celle publiée par Ben James, mais utilisant une seule fonction:

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)

Je l'ai testé avec des valeurs limites telles que la dernière semaine de 2020 et la première semaine de 2021, et cela a très bien fonctionné.

EDIT: ignorez ceci, les cas extrêmes sont pénibles. Allez avec la solution de Ben.

Ok, en y regardant de plus près, j'ai remarqué que les paramètres strptime comportent les paramètres % W et % w . Le code suivant fonctionne donc:

def fromisocalendar(y,w,d):
   return datetime.strptime( "%04dW%02d-%d"%(y,w-1,d), "%YW%W-%w")

Quelques pièges: le numéro de semaine ISO commence à 1 , tandis que % W commence à 0 . Le jour de la semaine ISO commence à 1 (lundi), ce qui est identique à % w , de sorte que dimanche devrait probablement être 0 , et non 7 ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top