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

问题描述 投票:0回答:1

我喜欢

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

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

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

python datetime parsing python-dateutil
1个回答
5
投票

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.split(timestr)

,最终调用_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 解析器期望的格式重新格式化数据。


关于

fuzzy 

是否允许模糊解析,允许类似“Today is January 1, 2047 at 8:21:00AM”这样的字符串。

它实际上做的是在
_parse()

函数中,它尝试找到当前标记的匹配项,如果没有匹配,则不会引发

ValueError
if
fuzzy
。因此,对于任何有效的非模糊输入,模糊结果将是相同的。
在这种情况下,它不会改变任何内容(但它可能对您收到的其他 date_strings 有用,我不知道)。

© www.soinside.com 2019 - 2024. All rights reserved.