在Python中验证ISO-8601日期时间字符串?

时间:2016-12-13 20:22:12

标签: python validation datetime iso8601 arrow-python

我想写一个带字符串的函数,如果它是有效的ISO-8601日期时间则返回True - 精确到微秒,包括时区偏移 - 否则为False

我发现other questions提供了解析日期时间字符串的不同方法,但我希望在ISO-8601格式的情况下返回True只要。解析对我没有帮助,除非我能够为不符合ISO-8601的格式抛出错误。

(我在我的代码中的其他地方使用了很好的arrow库。欢迎使用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令牌does not respect the UTC offset form that has a colon,只有表单没有,即使是both are valid per the spec

5 个答案:

答案 0 :(得分:7)

Python的最新版本(从3.7开始)在fromisoformat()标准库中具有datetime函数。请参阅: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中支持此功能时,我现在正在使用:

from datetime import datetime

def datetime_valid(dt_str):
    try:
        datetime.fromisoformat(dt_str)
    except:
        try:
            datetime.fromisoformat(dt_str.replace('Z', '+00:00'))
        except:
            return False
        return True
    return True

答案 1 :(得分:3)

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 Schema dateTime类型的正则表达式为:

>>> 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

答案 2 :(得分:2)

这是一个粗略但功能性的解决方案(针对较窄的问题)使用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

答案 3 :(得分:1)

考虑到你对这个问题的限制,你可以用正则表达式轻松解决它。

>>> import re
>>> re.match(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{6}[+-]\d\d:\d\d$', '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之间,则可以将括号放入正则表达式以创建匹配组,然后验证每个组。

答案 4 :(得分:0)

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('\.\d\d\d\d\d\d',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