How to compute the time difference between two time zones in python?

Question:

How can I compute the time differential between two time zones in Python? That is, I don’t want to compare TZ-aware datetime objects and get a timedelta; I want to compare two TimeZone objects and get an offset_hours. Nothing in the datetime library handles this, and neither does pytz.

Asked By: coriolinus

||

Answers:

The first thing you have to know is that the offset between two time zones depends not only on the time zones in question, but on the date you’re asking about. For example, the dates on which Daylight Savings Time began and ended changed in the US in 2007. While fundamental time zone logistics change only infrequently in any single location, the rate of change globally is impossible to ignore. Therefore, you have to incorporate the date in question into your function.

Having completed the necessary preface, the actual function isn’t too hard to write if you take advantage of the pendulum library. It should look something like this:

import pendulum

def tz_diff(home, away, on=None):
    """
    Return the difference in hours between the away time zone and home.

    `home` and `away` may be any values which pendulum parses as timezones.
    However, recommended use is to specify the full formal name.
    See https://gist.github.com/pamelafox/986163

    As not all time zones are separated by an integer number of hours, this
    function returns a float.

    As time zones are political entities, their definitions can change over time.
    This is complicated by the fact that daylight savings time does not start
    and end on the same days uniformly across the globe. This means that there are
    certain days of the year when the returned value between `Europe/Berlin` and
    `America/New_York` is _not_ `6.0`.

    By default, this function always assumes that you want the current
    definition. If you prefer to specify, set `on` to the date of your choice.
    It should be a `Pendulum` object.

    This function returns the number of hours which must be added to the home time
    in order to get the away time. For example,
    ```python
    >>> tz_diff('Europe/Berlin', 'America/New_York')
    -6.0
    >>> tz_diff('Europe/Berlin', 'Asia/Kabul')
    2.5
    ```
    """
    if on is None:
        on = pendulum.today()
    diff = (on.set(tz=home) - on.set(tz=away)).total_hours()

    # what about the diff from Tokyo to Honolulu? Right now the result is -19.0
    # it should be 5.0; Honolulu is naturally east of Tokyo, just not so around
    # the date line
    if abs(diff) > 12.0:
        if diff < 0.0:
            diff += 24.0
        else:
            diff -= 24.0

    return diff

As stated in the documentation, you may not get a stable result for this between any two given locations as you sweep across the days of the year. However, implementing a variant which chooses the median result over the days of the current year is an exercise left for the reader.

Answered By: coriolinus

Here is a code snippet to get the difference between UTC and US/Eastern, but it should work for any two timezones.

# The following algorithm will work no matter what is the local timezone of the server,
# but for the purposes of this discussion, let's assume that the local timezone is UTC.
local_timestamp = datetime.now()
# Assume that utc_timestamp == 2019-01-01 12:00.
utc_timestamp = pytz.utc.localize(local_timestamp)
# If it was 12:00 in New York, it would be 20:00 in UTC. So us_eastern_timestamp is a UTC
# timestamp with the value of 2019-01-01 20:00.
us_eastern_timestamp = timezone("US/Eastern").localize(local_timestamp).astimezone(pytz.utc)
# delta is a Python timedelta object representing the interval between the two timestamps,
# which, in our example, is -8 hours.
delta = utc_timestamp - us_eastern_timestamp
# In the last line, we convert the timedelta into an integer representing the number of
# hours.
print round(delta.total_seconds() / 60.0 / 60.0)
Answered By: Daniel Libicki

Here’s another solution:

from datetime import datetime
from pytz import timezone
from dateutil.relativedelta import relativedelta

utcnow = timezone('utc').localize(datetime.utcnow()) # generic time
here = utcnow.astimezone(timezone('US/Eastern')).replace(tzinfo=None)
there = utcnow.astimezone(timezone('Asia/Ho_Chi_Minh')).replace(tzinfo=None)

offset = relativedelta(here, there) 
offset.hours

Here what we’re doing is converting a time to two different time zones. Then, we remove the time zone information so that when you calculate the difference between the two using relativedelta, we trick it into thinking that these are two different moments in time instead of the same moment in different time zones.

The above result will return -11, however this amount can change throughout the year since US/Eastern observes DST and Asia/Ho_Chi_Minh does not.

Answered By: Scott Guthart

(tz_from.localize(date) - tz_to.localize(date)).seconds/3600.0

Where tz_from and tz_to are the starting and ending timezones. You must specify a particular date.

Answered By: storn

I created two functions to deal with timezone.

import datetime

import pytz

def diff_hours_tz(from_tz_name, to_tz_name, negative=False):
    """
    Returns difference hours  between timezones
    res = diff_hours_tz("UTC", "Europe/Paris") : 2
    """
    from_tz = pytz.timezone(from_tz_name)
    to_tz = pytz.timezone(to_tz_name)

    utc_dt = datetime.datetime.now(datetime.timezone.utc)
    dt_from = dt_to = datetime.datetime.utcnow()

    dt_from = from_tz.localize(dt_from)
    dt_to = to_tz.localize(dt_to)

    from_d = dt_from - utc_dt
    if from_d.days < 0:
        return diff_hours_tz(to_tz_name, from_tz_name, True)

    dt_delta = dt_from - dt_to

    negative_int = -1 if negative else 1

    return int(dt_delta.seconds/3600)*negative_int

def dt_tz_to_tz(dt, from_tz_name, to_tz_name):
    """
    Apply difference hours between timezones to a datetime object
    dt_new = dt_tz_to_tz(datetime.datetime.now(), "UTC", "Europe/Paris")
    """
    hours = diff_hours_tz(from_tz_name, to_tz_name)
    return dt+datetime.timedelta(hours=hours)

# Usage example

res = diff_hours_tz("Europe/Paris", "America/New_York")
# Result : -6
res = diff_hours_tz("UTC", "Europe/Paris")
# Result : 2

now = datetime.datetime.now()
# Result : 2019-06-18 15:10:31.720105
dt_new = dt_tz_to_tz(now, "UTC", "Europe/Paris")
# Result : 2019-06-18 17:10:31.720105
dt_new = dt_tz_to_tz(now, "Europe/Paris", "America/New_York")
# Result : 2019-06-18 09:10:31.720105
dt_new = dt_tz_to_tz(now, "America/New_York", "Europe/Paris")
# Result : 2019-06-18 21:10:31.720105

I hope it will help !

Answered By: Samuel Dauzon

Here is a solution using the Python library Pytz which solves the issue of ambiguous times at the end of daylight saving time.


from pytz import timezone
import pandas as pd

def tz_diff(date, tz1, tz2):
    '''
    Returns the difference in hours between timezone1 and timezone2
    for a given date.
    '''
    date = pd.to_datetime(date)
    return (tz1.localize(date) - 
            tz2.localize(date).astimezone(tz1))
            .seconds/3600

The examples below calculate the difference in hours between UTC and Australia time for the first of January and first of June respectively. Notice how daylight savings are taken into consideration.

utc = timezone('UTC')
aus = timezone('Australia/Sydney')

tz_diff('2017-01-01', utc, aus)

# 11.0

tz_diff('2017-06-01', utc, aus)

# 10.0

Thanks

Answered By: Harry Daniels
from datetime import datetime
from zoneinfo import ZoneInfo

dt = datetime.now() # 2020-09-13
tz0, tz1 = "Europe/Berlin", "US/Eastern" # +2 vs. -4 hours rel. to UTC

utcoff0, utcoff1 = dt.astimezone(ZoneInfo(tz0)).utcoffset(), dt.astimezone(ZoneInfo(tz1)).utcoffset()

print(f"hours offset between {tz0} -> {tz1} timezones: {(utcoff1-utcoff0).total_seconds()/3600}")
>>> hours offset between Europe/Berlin -> US/Eastern timezones: -6.0
  • a way to do this with Python 3.9‘s standard library.
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.