shad0w_wa1k3r
shad0w_wa1k3r

Reputation: 13372

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.

Upvotes: 2

Views: 2719

Answers (2)

Paul
Paul

Reputation: 10873

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.

Upvotes: 2

shad0w_wa1k3r
shad0w_wa1k3r

Reputation: 13372

I found a workaround that I'm not a fan of (it should be little more trivial to compare timezones, but yes, it's a hard domain), based on this answer.

>>> tz = pytz.timezone("Asia/Dubai")
>>> datetime.utcoffset(dt) == tz.utcoffset(dt.replace(tzinfo=None))
True

Upvotes: 0

Related Questions