使用零填充格式的 python 日期时间 strptime 的意外行为

Unexpected behavior of python datetime strptime with zero-padded formats

我有代表不同格式的日期时间的字符串列表。即:

list_date_str = ['2021010112', '202101011210']

第一个应翻译为 2021-01-01 12:00,第二个应翻译为 2021-01-01 12:10。 我没有多想就写了这个片段:

import datetime as dt

for date_str in list_date_str:
    try:
        date = dt.datetime.strptime(date_str, '%Y%m%d%H%M')
    except ValueError:
        date = dt.datetime.strptime(date_str, '%Y%m%d%H') 
    print(date)

经过艰苦的错误搜索后,我意识到第一个字符串没有按预期解析。代码给出:

2021-01-01 01:02:00
2021-01-01 12:10:00

我明白发生了什么:从未到达过块。相反,“2021010112”的倒数第二个字符被解释为小时数字,最后一个字符被解释为分钟数字。

这是应该的行为吗? datetime doc 明确指出 %H 表示 零填充 十进制数以及 %M.

我是没听懂还是文档误导了我?为什么 try 块不引发 ValueError?

有没有方便可靠的方法来解决这个问题?我知道在这种特殊情况下,可以通过交换 try- 和 expect-block 来修复代码。但这不是正确的做法。

PS:这个问题也适用于pd.to_datetime。

我怀疑文档更准确地反映了字符串格式,而不是字符串解析。

在您的情况下,实际问题是您的数据格式不一致。我不会依赖解析尝试失败来确定应该解析的格式。相反,您应该明确检查例如你的字符串的长度决定你想用什么格式来解析它。这也使您能够优雅地处理不仅仅是您在此处描述的两种情况。

使用len从字典获取字符串长度和时间格式

例如:

import datetime
list_date_str = ['2021010112', '202101011210']

frmt = {10: '%Y%m%d%H', 12: '%Y%m%d%H%M'}
for date_str in list_date_str:
    try:
        print(datetime.datetime.strptime(date_str, frmt.get(len(date_str))))
    except:
        raise Exception("Date Format Not Found.")

也许最简单的方法是在需要时对日期时间字符串进行零填充:

list_date_str = ['2021010112', '202101011210']

for date_str in list_date_str:
    try:
        date = dt.datetime.strptime(f'{date_str:0<12}', '%Y%m%d%H%M')
    except ValueError:
        print(f'Failed to convert {date_str!r}')
        continue 
    print(date)

这里的 fstring f'{date_str:0<12}' 用于使用字段宽度 12 对字符串的末尾进行零填充。这也允许解析可能根本没有时间分量的较短字符串:

>>> list_date_str = ['2021010112', '202101011210', 'baddate', '20210101', '2021']
>>> for date_str in list_date_str:
...     try:
...         date = dt.datetime.strptime(f'{date_str:0<12}', '%Y%m%d%H%M')
...     except ValueError:
...         print(f'Failed to convert {date_str!r}')
...         continue 
...     print(date)
... 
2021-01-01 12:00:00
2021-01-01 12:10:00
Failed to convert 'baddate'
2021-01-01 00:00:00
Failed to convert '2021'