【发布时间】:2013-05-28 01:33:09
【问题描述】:
我有一个 python 方法,它接受 日期输入作为字符串。
如何添加验证以确保传递给方法的日期字符串在 ffg.格式:
'YYYY-MM-DD'
如果不是,方法应该引发某种错误
【问题讨论】:
-
完全不检查并捕获任何由此产生的异常可能更像 Pythonic(请求宽恕,而不是许可)。
我有一个 python 方法,它接受 日期输入作为字符串。
如何添加验证以确保传递给方法的日期字符串在 ffg.格式:
'YYYY-MM-DD'
如果不是,方法应该引发某种错误
【问题讨论】:
出于好奇,我对上面发布的两个相互竞争的答案进行了计时。
我得到了以下结果:
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))
【讨论】:
我认为完整的验证功能应该是这样的:
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')
将被正确执行。
【讨论】:
datetime.strptime(date_text, "%Y-%m-%d") 就足够了吗?
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()返回格式字符串?
>>> 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
【讨论】:
from datetime import datetime
datetime.strptime(date_string, "%Y-%m-%d")
..如果收到不兼容的格式,则会引发ValueError。
..如果您经常处理日期和时间(在日期时间对象的意义上,而不是 unix 时间戳浮点数),最好查看 pytz 模块,对于 storage/db,store一切都以 UTC 为准。
【讨论】: