I have a python method which accepts a date input as a string.
How do I add a validation to make sure the date string being passed to the method is in the ffg. format:
'YYYY-MM-DD'
if it's not, method should raise some sort of error
I have a python method which accepts a date input as a string.
How do I add a validation to make sure the date string being passed to the method is in the ffg. format:
'YYYY-MM-DD'
if it's not, method should raise some sort of error
>>> import datetime
>>> def validate(date_text):
try:
datetime.date.fromisoformat(date_text)
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
Note that datetime.date.fromisoformat()
obviously works only when date is in ISO format. If you need to check date in some other format, use datetime.datetime.strptime()
.
The Python dateutil
library is designed for this (and more). It will automatically convert this to a datetime
object for you and raise a ValueError
if it can't.
As an example:
>>> from dateutil.parser import parse
>>> parse("2003-09-25")
datetime.datetime(2003, 9, 25, 0, 0)
This raises a ValueError
if the date is not formatted correctly:
>>> 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
is also extremely useful if you start needing to parse other formats in the future, as it can handle most known formats intelligently and allows you to modify your specification: dateutil
parsing examples.
It also handles timezones if you need that.
Update based on comments: parse
also accepts the keyword argument dayfirst
which controls whether the day or month is expected to come first if a date is ambiguous. This defaults to False. E.g.
>>> 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
I think the full validate function should look like this:
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
Executing just
datetime.strptime(date_text, "%Y-%m-%d")
is not enough because strptime method doesn't check that month and day of the month are zero-padded decimal numbers. For example
datetime.strptime("2016-5-3", '%Y-%m-%d')
will be executed without errors.
from datetime import datetime
datetime.strptime(date_string, "%Y-%m-%d")
..this raises a ValueError
if it receives an incompatible format.
..if you're dealing with dates and times a lot (in the sense of datetime objects, as opposed to unix timestamp floats), it's a good idea to look into the pytz module, and for storage/db, store everything in UTC.
From mere curiosity, I timed the two rivalling answers posted above.
And I had the following results:
dateutil.parser (valid str): 4.6732222699938575
dateutil.parser (invalid str): 1.7270505399937974
datetime.strptime (valid): 0.7822393209935399
datetime.strptime (invalid): 0.4394566189876059
And here's the code I used (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))