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).

Asked By: spatialaustin

||

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())
Answered By: spatialaustin

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.

Answered By: FObersteiner
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.