【发布时间】:2010-09-23 04:28:33
【问题描述】:
Python datetime.isocalendar() 方法为给定的 datetime 对象返回一个元组 (ISO_year, ISO_week_number, ISO_weekday)。有对应的反函数吗?如果没有,是否有一种简单的方法可以计算给定年份、星期数和星期几的日期?
【问题讨论】:
Python datetime.isocalendar() 方法为给定的 datetime 对象返回一个元组 (ISO_year, ISO_week_number, ISO_weekday)。有对应的反函数吗?如果没有,是否有一种简单的方法可以计算给定年份、星期数和星期几的日期?
【问题讨论】:
编辑:忽略这一点,边缘情况很痛苦。选择 Ben 的解决方案。
好的,经过仔细检查,我注意到 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...
【讨论】:
请注意,%W 是第 # 周 (0-53),它与 ISO 周 (1-53) 不同。将存在 %W 不起作用的边缘情况。
【讨论】:
Python 3.8 添加了fromisocalendar() 方法:
>>> datetime.fromisocalendar(2011, 22, 1)
datetime.datetime(2011, 5, 30, 0, 0)
Python 3.6 添加了%G, %V and %u directives:
>>> datetime.strptime('2011 22 1', '%G %V %u')
datetime.datetime(2011, 5, 30, 0, 0)
原答案
我最近不得不自己解决这个问题,并想出了这个解决方案:
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)
【讨论】:
fourth_jan 的生成对我不起作用(使用 Python 2.7),我不得不写:fourth_jan = datetime.strptime('{0}-01-04'.format(iso_year), '%Y-%m-%d')。
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 日历周和日。
1 月 4 日只是一个例子,因为正如 Ben 指出的那样,1 月 4 日总是属于同一个 ISO 年和公历年,并且是一年中的第一天。
由于周长相同,您可以简单地减去所需日期的 ISO 和您在两种形式中都知道的日期的 ISO 之间的天数和周数,然后加上该天数和周数. (这些数字是正数还是负数都没有关系,因此您可以选择其他“固定日期”,例如 12 月 28 日。)
我更正了这一点,因为正如@JoSo 所指出的,公历年的第一天也属于 ISO 年,是 1 月 4 日而不是 1 月 5 日。正如解释所说,选择哪个日期作为参考点并不重要,但选择 1 月 4 日会使这个选择不那么“神奇”。
【讨论】:
datetime 对象,该函数不是 isocalendar 的逆函数。它没有找到任何/参见github.com/jwg4/qual/blob/master/qual/tests/test_iso.py 当然这不是保证 - 我有兴趣了解任何错误。如果您仔细研究代码的逻辑,您可能会也可能不会相信它做了正确的事情。
从 Python 3.6 开始,datetime.strptime() 将支持%G、%V 和%u 指令,因此您可以简单地使用datetime.strptime('2015 1 2', '%G %V %u').date()。见:https://hg.python.org/cpython/rev/acdebfbfbdcf
【讨论】:
从 Python 3.6 开始,您可以使用新的 %G、%u 和 %V 指令。见issue 12006 和updated documentation:
%G
ISO 8601 年,世纪代表包含 ISO 周大部分时间的年份 (%V)。
%u
ISO 8601 weekday 作为十进制数,其中 1 是星期一。
%V
ISO 8601 周为十进制数,星期一为一周的第一天。第 01 周是包含 1 月 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()
【讨论】:
AttributeError: type object 'datetime.date' has no attribute 'strptime',请改用from datetime import datetime。
对于下一个来到这里的人,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)
【讨论】:
我想出了一个类似于 Ben James 发布的解决方案,但使用了一个函数:
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 年第一周等边界值对其进行了测试,结果非常好。
【讨论】: