59

Specifically, given the timezone of my server (system time perspective) and a timezone input, how do I calculate the system time as if it were in that new timezone (regardless of daylight savings, etc)?

import datetime
current_time = datetime.datetime.now() #system time

server_timezone = "US/Eastern"
new_timezone = "US/Pacific"

current_time_in_new_timezone = ???
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
kfan
  • 1,641
  • 1
  • 13
  • 16

4 Answers4

76

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 two pytz.timezone objects, one for the current timezone and one for the new timezone e.g. pytz.timezone("US/Pacific"). You can find a list of all official timezones in pytz library: import pytz; pytz.all_timezones

  2. Localize the datetime/timestamp of interest to the current timezone e.g.

current_timezone = pytz.timezone("US/Eastern")
localized_timestamp = current_timezone.localize(timestamp)
  1. Convert to new timezone using .astimezone() on the newly localized datetime/timestamp from step 2 with the desired 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.

kfan
  • 1,641
  • 1
  • 13
  • 16
  • (1) if you need the current time, use `datetime.now(server_timezone)` instead of `server_timezone.localize(datetime.now())` (2) consider using `is_dst` parameter (3) You might need `server_timezone.normalize()` after `server_timezone.localize()`, to fix non-existing local times. See [more details and references in my answer](http://stackoverflow.com/a/33310542/4279) – jfs Oct 23 '15 at 20:08
  • I believe 'timezone_localized_timestamp' should be 'localized_timestamp' in your example. Or vice versa. Otherwise works perfectly. – Rivers Cuomo Aug 25 '20 at 14:25
  • 2
    Since this question/answer is a top result, I'd like to inform that `pytz` is not recommended anymore. See: https://blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html and https://talkpython.fm/episodes/show/271/unlock-the-mysteries-of-time-pythons-datetime-that-is – Denilson Sá Maia Jan 13 '22 at 12:56
28

With Python 3.9, the standard lib has all you need: zoneinfo. pytz is not needed anymore (deprecated; -> pytz deprecation shim).

Ex:

from datetime import datetime
from zoneinfo import ZoneInfo

server_timezone = "US/Eastern"
new_timezone = "US/Pacific"

current_time = datetime.now(ZoneInfo(server_timezone)) 

# current_time_in_new_timezone = ???
current_time_in_new_timezone = current_time.astimezone(ZoneInfo(new_timezone))

That gives you for example

print(current_time.isoformat(timespec='seconds'))
# 2021-10-04T02:42:54-04:00

print(repr(current_time))
# datetime.datetime(2021, 10, 4, 2, 42, 54, 40600, tzinfo=zoneinfo.ZoneInfo(key='US/Eastern'))

print(current_time_in_new_timezone.isoformat(timespec='seconds'))
# 2021-10-03T23:42:54-07:00

print(repr(current_time_in_new_timezone))
# datetime.datetime(2021, 10, 3, 23, 42, 54, 40600, tzinfo=zoneinfo.ZoneInfo(key='US/Pacific'))
FObersteiner
  • 22,500
  • 8
  • 42
  • 72
  • 2
    For anyone working in python <3.9, you can install a backport of zoneinfo https://pypi.org/project/backports.zoneinfo/ – jpobst Aug 07 '22 at 12:36
10

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)
    
Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
0

In case you want to convert a timestamp from one timezone to another, you can use this code:

from datetime import datetime
from zoneinfo import ZoneInfo


from_timezone = ZoneInfo('Europe/Moscow')  # UTC-3
to_timezone = ZoneInfo('Asia/Tbilisi')  # UTC-4

dt = datetime.fromtimestamp(timestamp, to_timezone)
result_timestamp = int(dt.replace(tzinfo=from_timezone).timestamp())

For example, if you take timestamp = 529635600 (1986-14-10 04:00:00 in Moscow) and run this code, you will get result_timestamp = 529639200 (1986-14-10 05:00:00 in Tbilisi).