Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validate an ISO-8601 datetime string in Python?

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.

like image 843
Stew Avatar asked Dec 13 '16 20:12

Stew


People also ask

How do I check if a date is ISO in Python?

if variable_name is str: print('string format! ') else: print('not a string! ') you can print the information and then check if the date is formatted in accordance with the ISO standard(en.wikipedia.org/wiki/ISO_8601).

How do I read the ISO 8601 date format?

ISO 8601 Formats ISO 8601 represents date and time by starting with the year, followed by the month, the day, the hour, the minutes, seconds and milliseconds. For example, 2020-07-10 15:00:00.000, represents the 10th of July 2020 at 3 p.m. (in local time as there is no time zone offset specified—more on that below).

Is valid ISO date string?

Yes, it is valid. It's basically extended in terms of subsecond support, but that's allowed by the standard.

How do I convert datetime to ISO 8601 in Python?

To convert datetime to ISO 8601 format use the isoformat() method. It returns a string representing the date in ISO 8601 format. this ISO string contains the date, time, and UTC offset to the corresponding time zone.


2 Answers

https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch04s07.html

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
like image 157
user2549818 Avatar answered Oct 03 '22 20:10

user2549818


Recent versions of Python (from 3.7 onwards) have a fromisoformat() function in the datetime standard library. See: https://docs.python.org/3.7/library/datetime.html

So this will do the trick:

from datetime import datetime

def datetime_valid(dt_str):
    try:
        datetime.fromisoformat(dt_str)
    except:
        return False
    return True

Update:

I learned that Python does not recognize the 'Z'-suffix as valid. As I wanted to support this in my API, I'm now using:

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
like image 42
Hans Bouwmeester Avatar answered Oct 03 '22 21:10

Hans Bouwmeester