我喜欢
dateutil.parser
并且经常使用它,所以我不必担心日期时间格式不一致,并且在大多数情况下它对我来说非常可靠。
但是今天我花了一个小时进行调试,试图理解为什么我在数据库中填充的日期全部来自 2022 年。日期格式为:
2009:05:03 08:12:37
,存储在照片元数据中并使用 exif
包提取。
感觉它应该是一种相当简单的解析格式,但这就是我分别使用
dateutil
和 datetime
模块得到的结果:
from dateutil.parser import parse
import datetime as dt
date_string = '2009:05:03 08:12:37'
wrong = parse(date_string, fuzzy=True)
correct = dt.datetime.strptime(date_string, "%Y:%m:%d %H:%M:%S")
print(wrong)
print(correct)
出:
2022-07-25 08:12:37
2009-05-03 08:12:37
正常解析和模糊解析都给出相同的结果。很奇怪,我什至不明白它是如何得出这个精确结果的。
它看起来尝试解析并成功完成时间,但无法解析日期并默认设置当前日期。这感觉像是一种非常危险的行为。它应该引发异常。
dateutil.parser.parse
参数default
说:
默认日期时间对象,如果这是日期时间对象而不是
,则None
中指定的元素将替换默认对象中的元素。timestr
Github 上的parser.parse()
代码非常清楚:
if default is None:
default = datetime.datetime.now().replace(hour=0, minute=0,
second=0, microsecond=0)
但是记录的行为并没有表明这一点,我同意你的观点,结果是令人惊讶的。至于为什么失败,稍后会,最终调用
_timelex.get_token()
。它是一个用纯Python实现的状态机,用于解析时间字符串,并且它正确返回标记['2009', ':', '05', ':', '03', ' ', '08', ':', '12', ':', '37']
。但随后
_parse()
会迭代它们并尝试解释它们,因此它会调用
_parse_numeric_token()
。该函数尝试将标记与特定情况进行匹配。在某些情况下,“ymd”(年/月/日)被设置(你想要的):
YYMMDD
(第一个没有任何空格或点的标记 (
.
))
YYYYMMDD[HHMM[ss]]
(第一个没有空格的标记,包括时间)
-/.
之一
.,;-/'
(或
at/on/and/ad/m/t/of/st/nd/rd/th
的任何单词)
:
”的情况匹配,然后将其解释为
HH:MM:SS
为
_result(hour=2009, minute=5, second=3, microsecond=0)
。它消耗了前 5 个代币(整个预期的 YMD)。之后,它会跳过空白,返回到
_parse_numeric_token()
,匹配完全相同的大小写并将 HMS 覆盖到
_result(hour=8, minute=12, second=37, microsecond=0)
。它没有找到任何YMD,所以
ymd.resolve_ymd()
没有做任何事情。回到
parse()
(没有前导下划线),它通过将
parse()
结果字段替换为 default
,构建了一个简单的日期时间(无时区),最终成为最终结果。我认为这可能是在 GitHub 上打开问题的原因
,但我担心它可能会被视为“无法修复”,因为您提供的数据很奇怪:时间部分通常由冒号分隔,而日期部分是(基本上)永远不会被它们分隔(参见ISO 8601)。我以前从未见过这种日期时间格式。 我建议您要么修复生成此格式错误的数据的代码,要么添加类似
if malformed := re.match("(\d)+:(\d+):(\d+) (\d+:\d+:\d+)", date_string): date_string = f"{malformed.group(0)}-{malformed.group(1)}-{malformed.group(2)} {malformed.group(3)}"
的内容 以 dateutil 解析器期望的格式重新格式化数据。
关于:是否允许模糊解析,允许类似“Today is January 1, 2047 at 8:21:00AM”这样的字符串。
它实际上做的是在
_parse()
函数中,它尝试找到当前标记的匹配项,如果没有匹配,则不会引发
ValueError
if fuzzy
。因此,对于任何有效的非模糊输入,模糊结果将是相同的。在这种情况下,它不会改变任何内容(但它可能对您收到的其他 date_strings 有用,我不知道)。