Python: How do you convert a datetime/timestamp from one timezone to another timezone?

If you know your origin timezone and the new timezone that you want to convert it to, it turns out to be very straightforward:

  1. Make a pytz.timezoneobject for both the current timezone and the new timezone e.g. pytz.timezone("US/Pacific"). If you don't know a timezone's official name, you can find a list of all official timezones by simply calling pytz.all_timezones

  2. Call .localize() using the current timezone's pytz object with your datetime/timestamp as input to localize it to the current timezone. e.g. current_timezone.localize(timestamp)

  3. Finally, call .astimezone() on the newly localized datetime/timestamp from step 2 with the desired new timezone's pytz object as input e.g. localized_timestamp.astimezone(new_timezone).

Done!

As a full example:

import datetime
import pytz

# a timestamp I'd like to convert
my_timestamp = datetime.datetime.now()

# create both timezone objects
old_timezone = pytz.timezone("US/Eastern")
new_timezone = pytz.timezone("US/Pacific")

# two-step process
localized_timestamp = old_timezone.localize(my_timestamp)
new_timezone_timestamp = localized_timestamp.astimezone(new_timezone)

# or alternatively, as an one-liner
new_timezone_timestamp = old_timezone.localize(my_timestamp).astimezone(new_timezone) 

Bonus: but if all you need is the current time in a specific timezone, you can conveniently pass that timezone directly into datetime.now() to get the current times directly:

datetime.datetime.now(new_timezone)

When it comes to needing timezones conversions generally, I would strongly advise that one should store all timestamps in your database in UTC, which has no daylight savings time (DST) transition. And as a good practice, one should always choose to enable time zone support (even if your users are all in a single time zone!). This will help you avoid the DST transition problem that plagues so much software today.

Beyond DST, time in software can be generally quite tricky. To get a sense of just how difficult it is to deal with time in software in general, here is a potentially enlightening resource: http://yourcalendricalfallacyis.com

Even a seemingly simple operation as converting a datetime/timestamp into a date can become non-obvious. As this helpful documentation points out:

A datetime represents a point in time. It’s absolute: it doesn’t depend on anything. On the contrary, a date is a calendaring concept. It’s a period of time whose bounds depend on the time zone in which the date is considered. As you can see, these two concepts are fundamentally different.

Understanding this difference is a key step towards avoiding time-based bugs. Good luck.


How do you convert datetime/timestamp from one timezone to another timezone?

There are two steps:

  1. Create an aware datetime objects from the system time and timezone e.g., to get the current system time in the given timezone:

    #!/usr/bin/env python
    from datetime import datetime
    import pytz
    
    server_timezone = pytz.timezone("US/Eastern")
    server_time = datetime.now(server_timezone) # you could pass *tz* directly
    

    Note: datetime.now(server_timezone) works even during ambiguous times e.g., during DST transitions while server_timezone.localize(datetime.now()) may fail (50% chance).

    If you are sure that your input time exists in the server's timezone and it is unique then you could pass is_dst=None to assert that:

    server_time = server_timezone.localize(naive_time, is_dst=None)
    

    It raises an exception for invalid times.
    If it is acceptable to ignore upto a day error (though typically an error due to DST is around an hour) then you could drop is_dst parameter:

    server_time = server_timezone.normalize(server_timezone.localize(naive_time))
    

    .normalize() is called to adjust non-existing times (local time in the gap, during "spring forward" transitions). If the time zone rules haven't changed; your server shouldn't generate non-existing times. See "Can I just always set is_dst=True?"

  2. Convert an aware datetime object to the target timezone tz:

    tz = pytz.timezone("US/Pacific")
    server_time_in_new_timezone = server_time.astimezone(tz)