Qual é a melhor maneira de encontrar o inverso de datetime.isocalendar ()?

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

  •  08-07-2019
  •  | 
  •  

Pergunta

O Python datetime.isocalendar() método retorna uma (ISO_year, ISO_week_number, ISO_weekday) tupla para o determinado objecto datetime. Existe uma função inversa correspondente? Se não, há uma maneira fácil de calcular uma data dado um ano, número de semana e dia da semana?

Foi útil?

Solução

Recentemente, tive de resolver este problema sozinho, e veio com esta solução:

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)

Alguns casos de teste:

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

Outras dicas

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)

Esta foi adaptado de @ resposta muito boa do BenJames. Você não tem que saber o primeiro dia do ano. Você apenas tem que saber um exemplo de uma data que é, certamente, no mesmo ano, ISO, e a semana do calendário ISO dia e naquela data.

A 04 de janeiro é simplesmente um exemplo, porque, como Ben apontou, a 04 de janeiro pertence sempre ao mesmo ano ISO e ano gregoriano, e é o primeiro dia do ano para fazê-lo.

Desde semanas são todas do mesmo tamanho, você pode simplesmente subtrair os dias e semanas entre o ISO da data pretendida, ea ISO da data que você sabe em ambas as formas, e adicionar esse número de dias e semanas . (Não importa se esses números são positivos ou negativos, para que você pudesse escolher algum outro 'dia fixo', como 28 de dezembro).

Editar

Eu corrigi isto porque, como foi amavelmente apontado por @JoSo, o primeiro dia do ano gregoriano que também pertence ao ano ISO é 04 de janeiro não 05 de janeiro. Como a explicação diz, não importa qual a data é escolhida como ponto de referência, mas a escolha do 04 de janeiro torna esta opção menos 'magia'.

A partir do Python 3.6, você pode usar as novas directivas %G, %u e %V. Consulte questão 12006 e documentação atualizada :

%G
ISO 8601 ano com o século que representa o ano que contém a parte maior da semana ISO (%V).

%u
ISO 8601 durante a semana como um número decimal, onde 1 é segunda-feira.

%V
ISO 8601 semana como um número decimal com segunda-feira como o primeiro dia da semana. Semana 01 é a semana que contém 4 de janeiro.

Dado uma string com o ano, WeekNumber e número da semana, é fácil de analisar os para um encontro com:

from datetime import datetime

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

ou como uma função com entradas inteiras:

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() apoiará as directivas %G, %V e %u, então você pode simplesmente fazer datetime.strptime('2015 1 2', '%G %V %u').date(). Veja: https://hg.python.org/cpython/rev/acdebfbfbdcf

Para os próximos pessoas que vêm aqui, um mais curto, single-def, versão de solução boa 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)

Note que% W é a semana # (0-53), que não é o mesmo como a semana ISO (1-53). Haverá casos extremos onde% W não vai funcionar.

Eu vim com uma solução semelhante ao publicado por Ben James, mas usando uma única função:

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)

Eu testei com valores de limite, como a última semana de 2020 e primeira semana de 2021, e funcionou muito bem.

EDIT: ignorar isso, os casos de ponta são uma dor. Vá com solução de Ben.

Ok, em uma inspeção mais eu notei que strptime tem parâmetros %W e %w, então as seguintes obras:

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

Um par de truques: o número da semana começa ISO em 1, enquanto %W começa em 0. A semana ISO dia começa às 1 (segunda-feira), que é o mesmo que %w, então domingo provavelmente teria de ser 0, não 7 ...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top