什么是最好的方式来finddatetime.isocalendar()的逆?

Python datetime.isocalendar()方法为给定的datetime对象返回一个元组(ISO_year, ISO_week_number, ISO_weekday) 。 是否有相应的反函数? 如果没有,是否有一个简单的方法来计算给定一年的date,星期数和星期几?

我最近不得不自己解决这个问题,并提出了这个解决scheme:

 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) 

几个testing用例:

 >>> 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) 
 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年份的date的例子,以及那个date的ISO日历周和date。

1月4日只是一个例子,因为正如本指出的,1月4日总是属于同一个ISO年和公历年,是今年的第一天。

由于周数都是相同的长度,因此您可以简单地减去所需date的ISO和两种表单中已知date的ISO之间的date和星期,并添加date和星期数。 (这些数字是正数还是负数无关紧要,所以可以select其他“固定date”,如12月28日)。

编辑

我纠正了这个问题,因为正如@JoSo所指出的那样,公历年的第一天也属于ISO年,是1月4日和1月5日。 正如解释所说,select哪个date作为参考点并不重要,但是select1月4日会使这个select变得不那么“神奇”。

从Python 3.6开始(目前正在开发 ),您可以使用新的%G%u%V指令。 请参阅问题12006和更新的文档 :

%G
世纪ISO 8601年代表包含ISO周的大部分的年份( %V )。

%u
ISO 8601星期一作为十进制数,其中1是星期一。

%V
ISO 8601周作为十进制数,星期一作为一周的第一天。 第一周是包含1月4日的一周。

给定一个包含年份,周数和周日数的string,很容易通过以下方式parsing出date:

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

或者作为整数input的函数:

 from datetime import date def date_from_isoweek(iso_year, iso_weeknumber, iso_weekday): return date.strptime( '{:04d} {:02d} {:d}'.format(iso_year, iso_weeknumber, iso_weekday) '%G %V %u') 

对于下一个来到这里的人来说,Ben的一个更好的解决scheme是一个简短的单一的版本:

 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) 

从Python 3.6开始, datetime.strptime()将支持%G%V%u指令,所以你可以简单地使用datetime.strptime('2015 1 2', '%G %V %u').date() 。 请参阅: https : //hg.python.org/cpython/rev/acdebfbfbdcf

请注意,%W是与ISO周(1-53)不同的星期#(0-53)。 %W将无法工作的边缘情况。

编辑:忽略这一点,边缘情况是一个痛苦。 去Ben的解决scheme。

好的,仔细观察,我注意到strptime%W%w参数,所以下面的工作:

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

几个问题:ISO周编号从1开始,而%W0开始。 ISO周日从1 (星期一)开始,与%w相同,所以星期天可能必须是0 ,而不是7