为什么`datetime.strptime`在2015年第0周的星期二得到错误的日期?

tor*_*orm 11 python datetime

我在python datetime.strptime函数中发现了一个错误.

我已经在周数(),()和星期几()创建了datetime对象.2015年第一周的星期二日期是错误的:%W%Y%w

>>> from datetime import datetime

>>> datetime.strptime('%s %s %s' % (0, 2015, 1), '%W %Y %w').date()
datetime.date(2014, 12, 29) # OK

>>> datetime.strptime('%s %s %s' % (0, 2015, 2), '%W %Y %w').date()
datetime.date(2015, 1, 1) # WRONG !!!

>>> datetime.strptime('%s %s %s' % (0, 2015, 3), '%W %Y %w').date()
datetime.date(2014, 12, 31) # OK

>>> datetime.strptime('%s %s %s' % (0, 2015, 4), '%W %Y %w').date()
datetime.date(2015, 1, 1) # OK

>>> datetime.strptime('%s %s %s' % (0, 2015, 5), '%W %Y %w').date()
datetime.date(2015, 1, 2) # OK

>>> datetime.strptime('%s %s %s' % (0, 2015, 6), '%W %Y %w').date()
datetime.date(2015, 1, 3) # OK

>>> datetime.strptime('%s %s %s' % (0, 2015, 0), '%W %Y %w').date()
datetime.date(2015, 1, 4) # OK
Run Code Online (Sandbox Code Playgroud)

我该怎么处理这些信息?

Reu*_*ani 6

我看了很多年,我得到了同样令人费解的行为,但我发现了一些逻辑.

阅读文档后,我理解它更好一点:

%W - 一年中的周数(星期一作为一周的第一天)作为十进制数.在第一个星期一之前的新年中的所有日子被认为是在第0周.

因此,%W只在第0周填写新年中的正确值!这与以下结果完全一致:

2015年:

>>> for i in range(7):
...     datetime.strptime('%s %s %s' % (0, 2015, i), '%W %Y %w').date()
... 
datetime.date(2015, 1, 4)
datetime.date(2014, 12, 29)
datetime.date(2015, 1, 1)
datetime.date(2014, 12, 31)
datetime.date(2015, 1, 1) # start of year
datetime.date(2015, 1, 2)
datetime.date(2015, 1, 3)
Run Code Online (Sandbox Code Playgroud)

2016年

>>> for i in range(7):
...     datetime.strptime('%s %s %s' % (0, 2016, i), '%W %Y %w').date()
... 
datetime.date(2016, 1, 3)
datetime.date(2015, 12, 28)
datetime.date(2015, 12, 29)
datetime.date(2016, 1, 1)
datetime.date(2015, 12, 31)
datetime.date(2016, 1, 1) # start of year
datetime.date(2016, 1, 2)
Run Code Online (Sandbox Code Playgroud)

2017年:

>>> for i in range(7):
...     datetime.strptime('%s %s %s' % (0, 2017, i), '%W %Y %w').date()
... 
datetime.date(2017, 1, 1)
datetime.date(2016, 12, 26)
datetime.date(2016, 12, 27)
datetime.date(2016, 12, 28)
datetime.date(2016, 12, 29)
datetime.date(2017, 1, 1)
datetime.date(2016, 12, 31)
# ... start of year
Run Code Online (Sandbox Code Playgroud)

2018:

>>> for i in range(7):
...     datetime.strptime('%s %s %s' % (0, 2018, i), '%W %Y %w').date()
... 
datetime.date(2018, 1, 7)
datetime.date(2018, 1, 1) # start of year
datetime.date(2018, 1, 2)
datetime.date(2018, 1, 3)
datetime.date(2018, 1, 4)
datetime.date(2018, 1, 5)
datetime.date(2018, 1, 6)
Run Code Online (Sandbox Code Playgroud)

因此,在实际开始这一年之后,行为似乎是可预测的并且与文档一致.

  • 我认为情况可能就是这样.给出一个0的周数和在给定年份开始之前的一天数可能被认为超出了该函数的预期范围.虽然,我认为应该为这种情况抛出某种错误而不是返回坏数据... (2认同)

use*_*028 4

我能够确认这是一个错误。我研究了该_strptime.py模块,并可以确认它是边缘条件及其处理朱利安日期计算的方式。

该问题源于这样一个事实:调用 to_calc_julian_from_U_or_W()可以返回 -1,这在正常情况下是无效的。该strptime()函数在 Julian 值为 -1 时进行测试和更正...但当 week_of_year 为零时,不应执行此操作。

顺便说一句:它仅测试 -1 的事实就是您在 2015 年遇到问题的原因。只有当一年的第一天比您测试的日期早两天时,这种情况才存在。

以下补丁修正了边缘条件

--- _strptime.py.orig   2014-12-30 15:47:05.069835336 -0500
+++ _strptime.py        2014-12-30 15:47:21.509139500 -0500
@@ -441,7 +441,7 @@
     # Cannot pre-calculate datetime_date() since can change in Julian
     # calculation and thus could have different value for the day of the week
     # calculation.
-    if julian == -1:
+    if julian == -1 and week_of_year != 0:
         # Need to add 1 to result since first day of the year is 1, not 0.
         julian = datetime_date(year, month, day).toordinal() - \
                   datetime_date(year, 1, 1).toordinal() + 1
Run Code Online (Sandbox Code Playgroud)

我已将此补丁应用到我的本地计算机上,现在我看到了我认为 OP 想要的内容:

>>> datetime.strptime('%s %s %s' % (0, 2015, 2), '%W %Y %w').date()
datetime.date(2014, 12, 30)
Run Code Online (Sandbox Code Playgroud)

提交错误报告http://bugs.python.org/issue23136