Метод Python datetime.isocalendar()
возвращает кортеж (ISO_year, ISO_week_number, ISO_weekday)
для данного объекта datetime
. Существует ли соответствующая обратная функция? Если нет, есть ли простой способ вычислить дату с указанием года, номера недели и дня недели?
Какой лучший способ найти обратный datetime.isocalendar()?
Ответ 1
Мне недавно пришлось решить эту проблему самостоятельно и придумал это решение:
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)
Ответ 2
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 даты, которую вы знаете в обеих формах, и добавить в это количество дней и недель, (Не имеет значения, являются ли эти цифры положительными или отрицательными, поэтому вы можете выбрать другой "фиксированный день", такой как 28 декабря.)
Изменить
Я исправил это, потому что, как было показано в @JoSo, первый день григорианского года, который также относится к году ISO, - 4 января, а не 5 января. Как говорится в объяснении, не имеет значения, какая дата выбрана в качестве ориентира, но выбор 4-го января делает этот выбор менее "волшебным".
Ответ 3
Начиная с 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()
Ответ 4
Начиная с Python 3.6, datetime.strptime()
будет поддерживать директивы %G
, %V
и %u
, поэтому вы можете просто сделать datetime.strptime('2015 1 2', '%G %V %u').date()
. См.: https://hg.python.org/cpython/rev/acdebfbfbdcf
Ответ 5
Для следующих людей, приезжающих сюда, более короткая, одна-четкая версия 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)
Ответ 6
Обратите внимание, что% W - это неделя # (0-53), которая НЕ является такой же, как неделя ISO (1-53). Будут граничные случаи, когда% W не будет работать.
Ответ 7
Я придумал решение, похожее на то, которое опубликовал Бен Джеймс, но с использованием одной функции:
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 года, и это сработало довольно хорошо.
Ответ 8
РЕДАКТИРОВАТЬ: проигнорируйте это, краевые случаи - боль. Идите с решением Бен.
Хорошо, при ближайшем рассмотрении я заметил, что 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
...