Search code examples
pythondatetimepytzpython-dateutil

Python: Check if parsed datetime has correct timezone


So, I have an aware datetime in ISO format - 2020-03-15T18:00:00+04:00.

I want to parse it and validate that the tzinfo is "Asia/Dubai" (UTC+04:00). Unfortunately, dateutil & datetime don't give me a good enough tzinfo attribute on the parsed object, that I can easily verify.

>>> from datetime import datetime
>>> from dateutil.parser import isoparse
>>> import pytz
>>> 
>>> dts = "2020-03-15T18:00:00+04:00"
>>> dt = datetime.fromisoformat(dts)
>>> dt.tzinfo
datetime.timezone(datetime.timedelta(seconds=14400))
>>> dt.tzinfo == pytz.timezone("Asia/Dubai")
False
>>> 
>>> dt = isoparse(dts)
>>> dt.tzinfo
tzoffset(None, 14400)
>>> dt.tzinfo == pytz.timezone("Asia/Dubai")
False

I couldn't find how to deal with the tzoffset object so that I can compare it easily to the timezone string.

Any suggestions or comments or a nudge in the right direction are welcome.


Solution

  • The answer to the question, "Is the parsed datetime 'Asia/Dubai'", particularly when your concept of 'Asia/Dubai' means pytz.timezone("Asia/Dubai") is always "no", because dateutil will never attach that time zone to something you have parsed from an ISO 8601 datetime.

    One reason for this is that the string you are parsing does not have enough information to determine what set of rules generated it - consider that "Asia/Tibilisi" and "Asia/Dubai" both use UTC+4, but they do not represent the same time zone.

    So the question is: why do you need to know this information?

    If you simply want to work with datetimes in "Asia/Dubai", the best thing to do is to convert them to it with .astimezone:

    from dateutil import tz
    from dateutil.parser import isoparse
    
    _DUBAI = tz.gettz("Asia/Dubai")
    def parse_as_dubai(dt_str):
        dt = isoparse(dt_str)
        if dt.tzinfo is None:
            raise ValueError(f"No time zone offset in {dt_str}")
        return dt.astimezone(_DUBAI)
    

    Note that as long as you have parsed a time zone, this will always be accurate, whether or not the input string was originally in "Asia/Dubai". If no time zone was given, using astimezone will use the system local time of the system that is parsing the datetime, which is not what you want.

    If for some reason you need to validate that the original datetime had the same offset as "Asia/Dubai" (not sure why you'd need this, but I suppose it could be indicative of a problem in the source of the datetimes), then converting to "Asia/Dubai" and checking that the offset is your best bet. You can add an additional error check in the code above:

    from dateutil import tz
    from dateutil.parser import isoparse
    
    _DUBAI = tz.gettz("Asia/Dubai")
    def parse_as_dubai(dt_str):
        dt = isoparse(dt_str)
        if dt.tzinfo is None:
            raise ValueError(f"No time zone offset in {dt_str}")
        rv = dt.astimezone(_DUBAI)
        if dt.utcoffset() != rv.utcoffset():
            raise ValueError(f"{dt_str} not generated in Asia/Dubai!")
        return rv
    

    As a final note, I would urge you not to use pytz and instead to use dateutil.tz, particularly since you are already using dateutil for the parsing. You can read more about why in this article.