我想编写一个函数,该函数接受一个字符串并返回True
,如果它是有效的ISO-8601日期时间 - 精确到微秒,包括时区偏移量 -False
其他方式。
我发现了其他问题,这些问题提供了解析日期时间字符串的不同方法,但我想仅在 ISO-8601 格式的情况下返回True
。解析对我没有帮助,除非我可以让它为与 ISO-8601 不匹配的格式抛出错误。
(我在代码的其他地方使用了漂亮的箭头库。欢迎使用arrow
的解决方案。
编辑:似乎"此字符串是有效的ISO 8601日期时间"的通用解决方案在常见的Python日期时间包中不存在。
因此,为了使这个问题更窄、更具体、更易于回答,我将采用一种格式字符串来验证以下形式的日期时间字符串:
'2016-12-13T21:20:37.593194+00:00'
目前我正在使用:
format_string = '%Y-%m-%dT%H:%M:%S.%f%z'
datetime.datetime.strptime(my_timestamp, format_string)
这给出了:
ValueError: time data '2016-12-13T21:20:37.593194+00:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
问题似乎出在 UTC 偏移量 (+00:00
) 中的冒号上。如果我使用不带冒号的偏移量(例如'2016-12-13T21:20:37.593194+0000'
),这将按预期正确解析。这显然是因为datetime
的%z
令牌不尊重有冒号的UTC偏移量形式,只尊重没有冒号的形式,即使两者都根据规范有效。
最新版本的 Python(从 3.7 开始)在datetime
标准库中有一个fromisoformat()
函数。请参阅:https://docs.python.org/3.7/library/datetime.html
所以这将解决问题:
from datetime import datetime
def datetime_valid(dt_str):
try:
datetime.fromisoformat(dt_str)
except:
return False
return True
更新:
我了解到Python不承认"Z"后缀是有效的。 由于我想在我的 API 中支持这一点,我现在正在使用(在合并 Matt 的反馈之后):
from datetime import datetime
def datetime_valid(dt_str):
try:
datetime.fromisoformat(dt_str.replace('Z', '+00:00'))
except:
return False
return True
https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch04s07.html
给出许多变体,用于以ISO8601格式验证日期和时间(例如,2008-08-30T01:45:36 或 2008-08-30T01:45:36.123Z)。XML 架构日期时间类型的正则表达式给出如下:
>>> regex = r'^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$'
因此,为了验证,您可以执行以下操作:
import re
match_iso8601 = re.compile(regex).match
def validate_iso8601(str_val):
try:
if match_iso8601( str_val ) is not None:
return True
except:
pass
return False
一些例子:
>>> validate_iso8601('2017-01-01')
False
>>> validate_iso8601('2008-08-30T01:45:36.123Z')
True
>>> validate_iso8601('2016-12-13T21:20:37.593194+00:00')
True
这是一个粗略但有效的解决方案(对于较窄的问题),使用datetime.strptime()
:
import datetime
def is_expected_datetime_format(timestamp):
format_string = '%Y-%m-%dT%H:%M:%S.%f%z'
try:
colon = timestamp[-3]
if not colon == ':':
raise ValueError()
colonless_timestamp = timestamp[:-3] + timestamp[-2:]
datetime.datetime.strptime(colonless_timestamp, format_string)
return True
except ValueError:
return False
鉴于您对问题施加的约束,您可以使用正则表达式轻松解决它。
>>> import re
>>> re.match(r'^d{4}-dd-ddTdd:dd:dd.d{6}[+-]dd:dd$', '2016-12-13T21:20:37.593194+00:00')
<_sre.SRE_Match object; span=(0, 32), match='2016-12-13T21:20:37.593194+00:00'>
如果您需要传递ISO 8601的所有变体,这将是一个更复杂的正则表达式,但仍然可以完成。如果您还需要验证数值范围,例如验证小时是否介于 0 和 23 之间,则可以在正则表达式中加上括号以创建匹配组,然后验证每个组。
In [1] import dateutil.parser as dp
In [2]: import re
...: def validate_iso8601_us(str_val):
...: try:
...: dp.parse(str_val)
...: if re.search('.dddddd',str_val):
...: return True
...: except:
...: pass
...: return False
...:
In [3]: validate_iso8601_us('2019/08/15T16:03:5.12345')
Out[3]: False
In [4]: validate_iso8601_us('2019/08/15T16:03:5.123456')
Out[4]: True
In [5]: validate_iso8601_us('2019/08/15T16:03:5.123456+4')
Out[5]: True
In [6]: validate_iso8601_us('woof2019/08/15T16:03:5.123456+4')
Out[6]: False