dateutil.parser 解析格式为“%Y:%m:%d”的日期时出现奇怪的结果

Not*_*ame 2 python datetime parsing python-dateutil

我喜欢dateutil.parser并经常使用它,因此我不必担心日期时间格式不一致,并且在大多数情况下它对我来说非常可靠。

但今天我花了一个小时调试试图理解为什么我在数据库中填充的日期都是 2022 年。日期格式是:2009:05:03 08:12:37存储在照片元数据中并使用exifpackage.json 提取。

感觉它应该是一种相当简单的解析格式,但这就是我分别使用dateutildatetime模块得到的:

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)

Out:
2022-07-25 08:12:37
2009-05-03 08:12:37
Run Code Online (Sandbox Code Playgroud)

正常解析和模糊解析都给出相同的结果。很奇怪,我什至不明白它是如何得出这个精确结果的。

看起来它尝试解析并成功完成时间,但无法解析日期并默认设置当前日期。这感觉像是一种非常危险的行为。它应该引发异常。

Len*_*mju 5

参数说dateutil.parser.parsedefault

\n
\n

默认日期时间对象,如果这是日期时间对象而不是None,则timestr替换默认对象中指定的元素。

\n
\n

Github 上的代码非常parser.parse()清楚:

\n
        if default is None:\n            default = datetime.datetime.now().replace(hour=0, minute=0,\n                                                      second=0, microsecond=0)\n
Run Code Online (Sandbox Code Playgroud)\n

但记录下来的行为并没有表明这一点,我同意你的观点,即结果令人惊讶。

\n

至于为什么失败,稍后会_timelex.split(timestr)最终调用_timelex.get_token()。它是一个用纯 Python 实现的状态机,用于解析时间字符串,并正确返回 token [\'2009\', \':\', \'05\', \':\', \'03\', \' \', \'08\', \':\', \'12\', \':\', \'37\']
\n但随后_parse()迭代它们并尝试解释它们,因此它调用_parse_numeric_token()它。此函数尝试将标记与特定情况进行匹配。在某些情况下,“ymd”(年/月/日)被设置(您想要的):

\n
    \n
  • YYMMDD(第一个标记没有任何空格或点 ( .))
  • \n
  • YYYYMMDD[HHMM[ss]](第一个没有空格的标记,包括时间)
  • \n
  • 令牌后跟其中之一-/.
  • \n
  • 最后一个标记或后跟任何 .,;-/\'(或任何单词at/on/and/ad/m/t/of/st/nd/rd/th
  • \n
  • 令牌可能是一天
  • \n
\n

但它与“后跟:”的情况匹配,然后被解释为HH:MM:SSinto _result(hour=2009, minute=5, second=3, microsecond=0)。它消耗了前 5 个代币(整个预期的 YMD)。之后,它会跳过空格,返回到_parse_numeric_token(),匹配完全相同的大小写并将 HMS 覆盖到_result(hour=8, minute=12, second=37, microsecond=0)
\n它没有找到任何 YMD,因此 没有执行任何操作ymd.resolve_ymd()

\n

返回到parse()(没有前导下划线),它通过将结果字段替换为 来构建一个简单的日期时间(无时区),最终成为最终结果。parse()default

\n

我认为这可能是在 GitHub 上提出问题的原因,但我担心它可能会被视为“无法修复”,因为您提供的数据很奇怪:时间部分通常由冒号分隔,而日期部分(基本上)从未被它们分隔(参见ISO 8601)。我以前从未见过这种日期时间格式。

\n

相反,我建议您修复生成此格式错误的数据的代码,或者添加类似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)}"\n 的内容以将数据重新格式化为 dateutil 解析器所需的格式。

\n
\n

关于fuzzy\xc2\xa0

\n
\n

是否允许模糊解析,允许像\xe2\x80\x9c这样的字符串今天是2047年1月1日上午8:21:00\xe2\x80\x9d。

\n
\n

它实际上做的是在_parse()函数中,它尝试找到当前标记的匹配项,如果没有匹配项,它不会引发ValueErrorif fuzzy。因此,对于任何有效的非模糊输入,模糊结果将是相同的。
\n在这种情况下,它不会改变任何内容(但它可能对您收到的其他 date_strings 有用,我不知道)。

\n