Какой лучший способ найти обратное значение datetime.isocalendar ()? - PullRequest
43 голосов
/ 20 ноября 2008

Метод Python datetime.isocalendar() возвращает кортеж (ISO_year, ISO_week_number, ISO_weekday) для данного объекта datetime. Есть ли соответствующая обратная функция? Если нет, есть ли простой способ вычислить дату с указанием года, номера недели и дня недели?

Ответы [ 8 ]

64 голосов
/ 09 ноября 2009

Мне недавно пришлось самому решить эту проблему, и я нашел решение:

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)

Несколько тестовых случаев:

>>> 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)
10 голосов
/ 13 октября 2015
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)

Это было адаптировано из очень хорошего ответа @ BenJames. Вам не нужно знать первый день года. Вам просто нужно знать пример даты, которая определенно совпадает с годом ISO, и календарную неделю ISO и день этой даты.

4 января - просто один из примеров, потому что, как указал Бен, 4 января всегда относится к одному и тому же году ISO и григорианскому году и является первым днем ​​года, в котором это делается.

Поскольку недели имеют одинаковую длину, вы можете просто вычесть дни и недели между ISO даты, которую вы хотите, и ISO даты, которую вы знаете в обеих формах, и добавить к этому числу дней и недель. , (Неважно, являются ли эти числа положительными или отрицательными, поэтому вы можете выбрать другой «фиксированный день», например, 28 декабря.)

Редактировать

Я исправил это, потому что, как подсказал @JoSo, первый день григорианского года, который также принадлежит году ISO, - это 4 января, а не 5 января. Как говорится в объяснении, не имеет значения, какая дата выбрана в качестве ориентира, но выбор 4 января делает этот выбор менее «волшебным».

9 голосов
/ 09 марта 2016

Начиная с Python 3.6, вы можете использовать новые директивы %G, %u и %V. См. выпуск 12006 и обновленную документацию :

%G
ISO 8601 год, в котором год представляет большую часть недели ISO (%V).

%u
Рабочий день ISO 8601 в виде десятичного числа, где 1 - понедельник.

%V
ISO 8601 неделя в виде десятичного числа с понедельником в качестве первого дня недели. 01 неделя - неделя, содержащая 4 января.

Учитывая строку с номером года, номером недели и номером дня недели, их легко разобрать на дату с помощью:

from datetime import datetime

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

или как функция с целочисленными входами:

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()
3 голосов
/ 09 июля 2016

Для следующих людей, пришедших сюда, более короткая версия хорошего решения Бена с одним разрешением:

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)
3 голосов
/ 03 февраля 2016

Начиная с Python 3.6, datetime.strptime() будет поддерживать директивы %G, %V и %u, поэтому вы можете просто выполнить datetime.strptime('2015 1 2', '%G %V %u').date(). Смотри: https://hg.python.org/cpython/rev/acdebfbfbdcf

0 голосов
/ 02 мая 2019

Я придумал решение, похожее на то, которое опубликовал Бен Джеймс, но использовал одну функцию:

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)

Я протестировал его с граничными значениями, такими как последняя неделя 2020 года и первая неделя 2021 года, и это сработало довольно хорошо.

0 голосов
/ 20 декабря 2008

Обратите внимание, что% W - это неделя № (0-53), которая НЕ ЖЕ, как неделя ISO (1-53). В некоторых случаях% W не будет работать.

0 голосов
/ 20 ноября 2008

РЕДАКТИРОВАТЬ: игнорировать это, крайние случаи являются болью. Перейти с решением Бена.

Хорошо, при ближайшем рассмотрении я заметил, что strptime имеет параметры %W и %w, поэтому работает следующее:

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

Несколько ошибок: номер недели ISO начинается с 1, а %W начинается с 0. День недели ISO начинается с 1 (понедельник), что совпадает с %w, поэтому, вероятно, воскресенье должно быть 0, а не 7 ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...