How to convert a localized timestamp to UTC using Python?
Question:
I need to convert a “localized” timestamp in an arbitrary timezone to a unix timestamp (in UTC).
>>> import pytz
# This represents 2019-09-11T16:14:00 (US/Central) or 2019-09-11T21:14:00 UTC!
>>> local_timestamp = 1568218440
>>> tz = pytz.timezone("US/Central")
>>> my_unix_timestamp = unix_timestamp(local_timestamp, tz)
>>> print(my_unix_timestamp)
1568236440 # 2019-09-11T21:14:00
I know this has been asked many times before, but I was getting hung up on the initial conversion of the timestamp from an arbitrary timezone, because you must explicitly set the tz
when you construct your initial datetime
object (as noted in my answer).
Answers:
As noted in the pytz
documentation, the standard Python datetime.replace()
method does not properly account for daylight-savings time. You should use pytz’s timezone.localize()
method.
The other gotcha is that you must explicitly define the tz
info when creating a datetime
object from the input timestamp. Otherwise, it is assumed that your timestamp is in local (system) time. As noted in the datetime
documentation, you should avoid datetime.utcfromtimestamp()
for this reason.
So:
from datetime import datetime, timezone
def unix_timestamp(local_timestamp, local_timezone):
"""turn the input timestamp into a UTC `datetime` object, even though
the timestamp is not in UTC time, we must do this to construct a datetime
object with the proper date/time values"""
dt_fake_utc = datetime.fromtimestamp(local_timestamp, tz=timezone.utc)
"""remove the (incorrect) timezone info that we supplied """
dt_naive = dt_fake_utc.replace(tzinfo=None)
"""localize the datetime object to our `timezone`. You cannot use
datetime.replace() here, because it does not account for daylight savings
time"""
dt_local = local_timezone.localize(dt_naive)
"""Convert our datetime object back to a timestamp"""
return int(dt_local.timestamp())
Unix time should not be localized; it should always refer to UTC.
Only the datetime
object should be localized (be made tz-aware). EX:
from datetime import datetime
from zoneinfo import ZoneInfo
datetime.fromisoformat('2019-09-11T21:14:00').replace(tzinfo=ZoneInfo("UTC")).timestamp()
# 1568236440.0
# US/Central is at UTC-5, so Unix time is the same as the above:
datetime.fromisoformat('2019-09-11T16:14:00').replace(tzinfo=ZoneInfo("America/Chicago")).timestamp()
# 1568236440.0
Sidenote: pytz
uses a different time zone model than the datetime
module (Python standard lib). This is why you can’t safely use the replace
method of a datetime object to set a timezone. However, replace
is safe to use with Python 3.9’s zoneinfo
(and also dateutil
) timezone objects.
I need to convert a “localized” timestamp in an arbitrary timezone to a unix timestamp (in UTC).
>>> import pytz
# This represents 2019-09-11T16:14:00 (US/Central) or 2019-09-11T21:14:00 UTC!
>>> local_timestamp = 1568218440
>>> tz = pytz.timezone("US/Central")
>>> my_unix_timestamp = unix_timestamp(local_timestamp, tz)
>>> print(my_unix_timestamp)
1568236440 # 2019-09-11T21:14:00
I know this has been asked many times before, but I was getting hung up on the initial conversion of the timestamp from an arbitrary timezone, because you must explicitly set the tz
when you construct your initial datetime
object (as noted in my answer).
As noted in the pytz
documentation, the standard Python datetime.replace()
method does not properly account for daylight-savings time. You should use pytz’s timezone.localize()
method.
The other gotcha is that you must explicitly define the tz
info when creating a datetime
object from the input timestamp. Otherwise, it is assumed that your timestamp is in local (system) time. As noted in the datetime
documentation, you should avoid datetime.utcfromtimestamp()
for this reason.
So:
from datetime import datetime, timezone
def unix_timestamp(local_timestamp, local_timezone):
"""turn the input timestamp into a UTC `datetime` object, even though
the timestamp is not in UTC time, we must do this to construct a datetime
object with the proper date/time values"""
dt_fake_utc = datetime.fromtimestamp(local_timestamp, tz=timezone.utc)
"""remove the (incorrect) timezone info that we supplied """
dt_naive = dt_fake_utc.replace(tzinfo=None)
"""localize the datetime object to our `timezone`. You cannot use
datetime.replace() here, because it does not account for daylight savings
time"""
dt_local = local_timezone.localize(dt_naive)
"""Convert our datetime object back to a timestamp"""
return int(dt_local.timestamp())
Unix time should not be localized; it should always refer to UTC.
Only the datetime
object should be localized (be made tz-aware). EX:
from datetime import datetime
from zoneinfo import ZoneInfo
datetime.fromisoformat('2019-09-11T21:14:00').replace(tzinfo=ZoneInfo("UTC")).timestamp()
# 1568236440.0
# US/Central is at UTC-5, so Unix time is the same as the above:
datetime.fromisoformat('2019-09-11T16:14:00').replace(tzinfo=ZoneInfo("America/Chicago")).timestamp()
# 1568236440.0
Sidenote: pytz
uses a different time zone model than the datetime
module (Python standard lib). This is why you can’t safely use the replace
method of a datetime object to set a timezone. However, replace
is safe to use with Python 3.9’s zoneinfo
(and also dateutil
) timezone objects.