【问题标题】:How do I validate a date string format in python?如何在 python 中验证日期字符串格式?
【发布时间】:2013-05-28 01:33:09
【问题描述】:

我有一个 python 方法,它接受 日期输入作为字符串

如何添加验证以确保传递给方法的日期字符串在 ffg.格式:

'YYYY-MM-DD'

如果不是,方法应该引发某种错误

【问题讨论】:

标签: python date


【解决方案1】:

出于好奇,我对上面发布的两个相互竞争的答案进行了计时。
我得到了以下结果:

dateutil.parser (valid str): 4.6732222699938575
dateutil.parser (invalid str): 1.7270505399937974
datetime.strptime (valid): 0.7822393209935399
datetime.strptime (invalid): 0.4394566189876059

这是我使用的代码(Python 3.6)


from dateutil import parser as date_parser
from datetime import datetime
from timeit import timeit


def is_date_parsing(date_str):
    try:
        return bool(date_parser.parse(date_str))
    except ValueError:
        return False


def is_date_matching(date_str):
    try:
        return bool(datetime.strptime(date_str, '%Y-%m-%d'))
    except ValueError:
        return False



if __name__ == '__main__':
    print("dateutil.parser (valid date):", end=' ')
    print(timeit("is_date_parsing('2021-01-26')",
                 setup="from __main__ import is_date_parsing",
                 number=100000))

    print("dateutil.parser (invalid date):", end=' ')
    print(timeit("is_date_parsing('meh')",
                 setup="from __main__ import is_date_parsing",
                 number=100000))

    print("datetime.strptime (valid date):", end=' ')
    print(timeit("is_date_matching('2021-01-26')",
                 setup="from __main__ import is_date_matching",
                 number=100000))

    print("datetime.strptime (invalid date):", end=' ')
    print(timeit("is_date_matching('meh')",
                 setup="from __main__ import is_date_matching",
                 number=100000))

【讨论】:

    【解决方案2】:

    我认为完整的验证功能应该是这样的:

    from datetime import datetime
    
    def validate(date_text):
        try:
            if date_text != datetime.strptime(date_text, "%Y-%m-%d").strftime('%Y-%m-%d'):
                raise ValueError
            return True
        except ValueError:
            return False
    

    只执行

    datetime.strptime(date_text, "%Y-%m-%d") 
    

    是不够的,因为 strptime 方法不会检查月份和月份中的日期是否是零填充的十进制数字。例如

    datetime.strptime("2016-5-3", '%Y-%m-%d')
    

    将被正确执行。

    【讨论】:

    • “你在技术上是正确的——最好的正确。”我需要在我的字符串中确保这一点。
    • 这对我的测试很好,但是我的文档似乎不正确,因为它指出:“%d -> 一个月中的一天作为一个零填充的十进制数 -> 01, 02, ..., 31 " 和 %m -> Month 一样,作为一个零填充的十进制数。 -> 01, 02, ..., 12 docs.python.org/2/library/…
    • 如果您需要检查月份和日期是否填充为零,仅检查字符串的长度和datetime.strptime(date_text, "%Y-%m-%d") 就足够了吗?
    【解决方案3】:

    Python dateutil 库是为此(以及更多)而设计的。它会自动为您将其转换为datetime 对象,如果不能,则引发ValueError

    举个例子:

    >>> from dateutil.parser import parse
    >>> parse("2003-09-25")
    datetime.datetime(2003, 9, 25, 0, 0)
    

    如果日期格式不正确,这会引发ValueError

    >>> parse("2003-09-251")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/Users/jacinda/envs/dod-backend-dev/lib/python2.7/site-packages/dateutil/parser.py", line 720, in parse
        return DEFAULTPARSER.parse(timestr, **kwargs)
      File "/Users/jacinda/envs/dod-backend-dev/lib/python2.7/site-packages/dateutil/parser.py", line 317, in parse
        ret = default.replace(**repl)
    ValueError: day is out of range for month
    

    如果您以后开始需要解析其他格式,dateutil 也非常有用,因为它可以智能地处理大多数已知格式并允许您修改规范:dateutil parsing examples

    如果您需要,它还可以处理时区。

    基于 cmets 的更新parse 还接受关键字参数 dayfirst,该参数控制如果日期不明确,则应将日期或月份排在第一位。这默认为 False。例如

    >>> parse('11/12/2001')
    >>> datetime.datetime(2001, 11, 12, 0, 0) # Nov 12
    >>> parse('11/12/2001', dayfirst=True)
    >>> datetime.datetime(2001, 12, 11, 0, 0) # Dec 11
    

    【讨论】:

    • 它可能接受太多,例如,parse('13/12/2001') 是“12 月 13 日”,但 parse('11/12/2001') 是“12 月”(第一个结果会建议“12 月”)。
    • parse 实际上采用dayfirst 关键字参数,允许您控制它。 parse('11/12/2001', dayfirst=True) 将返回“12 月 11 日”。 dateutil 的默认值为dayfirst=False
    • 您错过了datetutil.parser.parse() 接受太多时间格式的观点(您可以找到其他输入不明确的示例)。如果您想验证您的输入是 YYYY-MM-DD 格式,那么 parse() 函数是错误的工具。
    • 这是一个完全正确的观点——如果你真的想限制在那个特定的格式上,这不会这样做,并且在这种情况下,接受的答案已经很好地完成了正确的事情。我想当我写答案时,我更多地考虑指出如何验证它是否是有效日期,而不是作者要求的特定格式,当人们遇到这个问题时,他们经常寻找。
    • 除了datetime对象之外,有没有办法让.parse()返回格式字符串?
    【解决方案4】:
    >>> import datetime
    >>> def validate(date_text):
        try:
            datetime.datetime.strptime(date_text, '%Y-%m-%d')
        except ValueError:
            raise ValueError("Incorrect data format, should be YYYY-MM-DD")
    
    
    >>> validate('2003-12-23')
    >>> validate('2003-12-32')
    
    Traceback (most recent call last):
      File "<pyshell#20>", line 1, in <module>
        validate('2003-12-32')
      File "<pyshell#18>", line 5, in validate
        raise ValueError("Incorrect data format, should be YYYY-MM-DD")
    ValueError: Incorrect data format, should be YYYY-MM-DD
    

    【讨论】:

    • 有没有办法做到这一点而无需尝试/除外?当引发和捕获异常时,Python 往往会显着变慢。
    • @chiffa 您可以匹配日期格式正则表达式,但不推荐使用它,因为它不太健壮并且异常更清晰。您确定日期验证是您的瓶颈吗?
    • 不是真的,所以最后我将把 throw-except 结构包装在一个函数中。我很惊讶没有返回布尔值的验证函数会触发 datetime 库中的异常抛出。
    • 对于那些想要在日期中填充零的人来说,这个解决方案将不起作用,因为 strptime 对零填充并不严格。实现您自己的正则表达式或在去除空格后检查结果字符串的长度,然后使用此解决方案。
    • 是的,同意@Suparshva 例如这个字符串将被假定为正确的日期,没有ValueError:“2018-10-1”
    【解决方案5】:
    from datetime import datetime
    
    datetime.strptime(date_string, "%Y-%m-%d")
    

    ..如果收到不兼容的格式,则会引发ValueError

    ..如果您经常处理日期和时间(在日期时间对象的意义上,而不是 unix 时间戳浮点数),最好查看 pytz 模块,对于 storage/db,store一切都以 UTC 为准。

    【讨论】:

    • 你速度更快,我会自己发布的 (ideone.com/vuxDDf)。点赞。
    • ..刚发布后就看到了,今天正好在使用 datetime 对象。
    猜你喜欢
    • 1970-01-01
    • 2021-01-21
    • 2018-06-26
    • 2017-11-19
    • 2012-11-06
    • 2022-08-15
    • 1970-01-01
    • 2019-04-16
    • 1970-01-01
    相关资源
    最近更新 更多