¿Cuál es la mejor manera de encontrar el inverso de datetime.isocalendar ()?

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

  •  08-07-2019
  •  | 
  •  

Pregunta

El Python datetime.isocalendar () devuelve una tupla (ISO_year, ISO_week_number, ISO_weekday) para el objeto dado datetime . ¿Hay una función inversa correspondiente? Si no, ¿hay una manera fácil de calcular una fecha dado un año, número de semana y día de la semana?

¿Fue útil?

Solución

Recientemente tuve que resolver este problema yo mismo, y se me ocurrió esta solución:

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)

Algunos casos de prueba:

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

Otros consejos

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)

Esto fue adaptado de la muy buena respuesta de @ BenJames. No tienes que saber el primer día del año. Solo tiene que conocer un ejemplo de una fecha que ciertamente está en el mismo año ISO, y la semana del calendario ISO y el día de esa fecha.

El 4 de enero es simplemente un ejemplo, porque, como señaló Ben, el 4 de enero siempre pertenece al mismo año ISO y año gregoriano, y es el primer día del año para hacerlo.

Dado que las semanas tienen la misma duración, simplemente puede restar los días y las semanas entre el ISO de la fecha que desea y el ISO de la fecha que conoce en ambos formularios, y agregar esa cantidad de días y semanas . (No importa si estos números son positivos o negativos, por lo que puede elegir otro 'día fijo' como el 28 de diciembre).

Editar

Corregí esto porque, como lo señaló útilmente @JoSo, el primer día del año gregoriano que también pertenece al año ISO es el 4 de enero y no el 5 de enero. Como dice la explicación, no importa qué fecha se elija como punto de referencia, pero elegir el 4 de enero hace que esta elección sea menos 'mágica'.

A partir de Python 3.6, puede usar las nuevas directivas % G , % u y % V . Consulte número 12006 y documentación actualizada :

  

% G
  Año ISO 8601 con siglo que representa el año que contiene la mayor parte de la semana ISO (% V ).

     

% u
  Día de la semana ISO 8601 como un número decimal donde 1 es lunes.

     

% V
  ISO 8601 semana como un número decimal con lunes como primer día de la semana. La semana 01 es la semana que contiene el 4 de enero.

Dada una cadena con año, número de semana y número de día de la semana, es fácil analizarlos a una fecha con:

from datetime import datetime

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

o como una función con entradas enteras:

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 partir de Python 3.6, datetime.strptime () admitirá % G , % V y % u directivas, por lo que puede simplemente hacer datetime.strptime ('2015 1 2', '% G% V% u'). date () . Ver: https://hg.python.org/cpython/rev/acdebfbfbdcf

Para las próximas personas que vienen aquí, una versión más corta, de una sola definición, de la buena solución 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)

Tenga en cuenta que% W es la semana # (0-53) que NO ES LA MISMA que la semana ISO (1-53). Habrá casos extremos donde% W no funcionará.

Se me ocurrió una solución similar a la publicada por Ben James, pero usando una sola función:

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)

Lo probé con valores límite como la última semana de 2020 y la primera semana de 2021, y funcionó bastante bien.

EDITAR: ignore esto, los casos extremos son un dolor. Ve con la solución de Ben.

Ok, en una inspección más cercana, noté que strptime tiene los parámetros % W y % w , así que lo siguiente funciona:

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

Un par de problemas: el número de semana ISO comienza en 1 , mientras que % W comienza en 0 . El día de la semana ISO comienza en 1 (lunes), que es lo mismo que % w , por lo que el domingo probablemente tendría que ser 0 , no 7 ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top