I want to write a function that takes a string and returns True
if it is a valid ISO-8601 datetime--precise to microseconds, including a timezone offset--False
otherwise.
I have found other questions that provide different ways of parsing datetime strings, but I want to return True
in the case of ISO-8601 format only. Parsing doesn't help me unless I can get it to throw an error for formats that don't match ISO-8601.
(I am using the nice arrow library elsewhere in my code. A solution that uses arrow
would be welcome.)
EDIT: It appears that a general solution to "is this string a valid ISO 8601 datetime" does not exist among the common Python datetime packages.
So, to make this question narrower, more concrete and answerable, I will settle for a format string that will validate a datetime string in this form:
'2016-12-13T21:20:37.593194+00:00'
Currently I am using:
format_string = '%Y-%m-%dT%H:%M:%S.%f%z'
datetime.datetime.strptime(my_timestamp, format_string)
This gives:
ValueError: time data '2016-12-13T21:20:37.593194+00:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
The problem seems to lie with the colon in the UTC offset (+00:00
). If I use an offset without a colon (e.g. '2016-12-13T21:20:37.593194+0000'
), this parses properly as expected. This is apparently because datetime
's %z
token does not respect the UTC offset form that has a colon, only the form without, even though both are valid per the spec.
give many variants for validating date and times in ISO8601 format (e.g., 2008-08-30T01:45:36 or 2008-08-30T01:45:36.123Z). The regex for the XML Schema dateTime type is given as:
>>> 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])?$'
So in order to validate you could do:
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
Some examples:
>>> 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