I was trying to convert a datetime from one timezone to another. I'm in the process of updating our Python codebase to stop relying on utilities we don't need anymore. In particular, I'm deprecating our use of arrow
and pytz
. In doing so, I noticed some strange behavior from ZoneInfo("UTC")
.
from datetime import datetime, timezone
jan1_in_utc = datetime.fromisoformat('2022-01-01T08:00').replace(tzinfo=ZoneInfo("UTC"))
# This gives datetime.datetime(2022, 1, 1, 8, 0, tzinfo=zoneinfo.ZoneInfo(key='UTC'))
# Let's say I try to convert it to America/Toronto timezone
jan1_in_utc.astimezone(ZoneInfo("America/Toronto"))
# This gives me the SAME date time ?!?!?
# datetime.datetime(2022, 1, 1, 8, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Toronto'))
# However, if I use timezone.utc instead
jan1_in_utc = datetime.fromisoformat('2022-01-01T08:00').replace(tzinfo=timezone.utc)
# This works as expected
jan1_in_utc.astimezone(ZoneInfo("America/Toronto"))
# this correctly calculates a -5 offset
# datetime.datetime(2022, 1, 1, 3, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Toronto'))
I'm not sure what I'm doing wrong. "UTC" is in the list of zoneinfo.available_timezones()
. Using "utc" raises an error.
I also noticed this oddity. Calculating the utcoffset
from the ZoneInfo("UTC")
isn't 0
.
jan1_in_utc = datetime.fromisoformat('2022-01-01T08:00').replace(tzinfo=ZoneInfo("UTC"))
ZoneInfo("UTC").utcoffset(jan1_in_utc)
Where as if I use timezone.utc
, there's no time difference.
jan1_in_utc = datetime.fromisoformat('2022-01-01T08:00').replace(tzinfo=timezone.utc)
timezone.utc.utcoffset(jan1_in_utc)
# This gives datetime.timedelta(0)
Now I'm unsure if I should use ZoneInfo
at all, or if I should still rely on pytz
and arrow
. Any thoughts? Clearly, I'm missing something!
CodePudding user response:
Cannot reproduce. Did you make sure tzdata is installed and up-to-date?
On
Python 3.9.15 (main, Oct 30 2022, 10:17:28)
[GCC 11.3.0] on linux
I get Toronto time at UTC-5 as expected for both options:
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
jan1_in_utc = datetime.fromisoformat('2022-01-01T08:00').replace(tzinfo=ZoneInfo("UTC"))
jan1_in_utc = jan1_in_utc.astimezone(ZoneInfo("America/Toronto"))
print(repr(jan1_in_utc))
# datetime.datetime(2022, 1, 1, 3, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Toronto'))
jan1_in_utc = datetime.fromisoformat('2022-01-01T08:00').replace(tzinfo=timezone.utc)
jan1_in_utc = jan1_in_utc.astimezone(ZoneInfo("America/Toronto"))
print(repr(jan1_in_utc))
# datetime.datetime(2022, 1, 1, 3, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Toronto'))
CodePudding user response:
I'm not entirely sure why, but I've narrowed the scope of the issue. When we're building our Docker containers, we added a line to set /etc/timezone
to be America/Toronto
. That causes ZoneInfo.utcoffset
to behave differently.
Because of this, the utcoffset
method appears to pick up that timezone and calculate offsets differently.
>>> jan1_in_utc = datetime.fromisoformat('2022-01-01T08:00').replace(tzinfo=ZoneInfo("UTC"))
>>> ZoneInfo("UTC").utcoffset(jan1_in_utc)
datetime.timedelta(days=-1, seconds=68400)
It returns 1 day minus 19 hours which is -5 hours. That's the offset for America/Toronto. I believe this explains why no adjustment is made to the time. ZoneInfo
seems to think I'm converting from America/Toronto to America/Toronto, so no change is made to the datetime
.
Python's datetime.timezone.utc
doesn't exhibit this behavior. Even with /etc/timezone
set to America/Toronto
it calculates the offset correctly.
>>> jan1_in_utc = datetime.fromisoformat('2022-01-01T08:00').replace(tzinfo=ZoneInfo("UTC"))
>>> ZoneInfo("UTC").utcoffset(jan1_in_utc)
datetime.timedelta(0)