我在python中发现了一个错误 datetime.strptime
功能。
我创造了 datetime
对象基于 周数 (%W
) 年 (%Y
)和 星期几 (%w
)。 2015年第一周的星期二日期是错误的:
>>> 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
我该怎么处理这些信息?
我看了很多年,我得到了同样令人费解的行为,但我发现了一些逻辑。
看完之后 文档,我理解它好一点:
%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)
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)
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
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)
因此,在实际开始这一年之后,行为似乎是可预测的并且与文档一致。
我能够确认这是一个错误。我研究了 _strptime.py
模块并且可以确认它是如何处理朱利安日期计算的边缘条件。
问题源于呼叫的事实 _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
我已经将这个补丁应用到我的本地机器上,现在我看到我认为OP想要的东西:
>>> datetime.strptime('%s %s %s' % (0, 2015, 2), '%W %Y %w').date()
datetime.date(2014, 12, 30)
提交的错误报告 http://bugs.python.org/issue23136