3

Consider the following example, where I take a naive datetime, make it timezone aware in UTC, and then convert to UTC-5:

d1 = datetime.datetime(2019,3,7, 7,45)

d2 = pytz.utc.localize(d1)
print(f'UTC  : {d2}')

d3 = d2.astimezone(pytz.timezone('Etc/GMT-5'))
print(f'UTC-5: {d3}')

The output of this is:

UTC  : 2019-03-07 07:45:00+00:00
UTC-5: 2019-03-07 12:45:00+05:00

I would have expected the UTC-5 time to be 02:45, but the 5 hour offset is being added to UTC, rather than subtracted.

Questions:

  • Why is the 'Etc/GMT-5' offset applied to UTC +5 hours instead of -5 hours?
  • How can I convert from UTC to UTC-5?
Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • 1
    Related: [Timezone offset sign reversed by Python dateutil?](https://stackoverflow.com/a/31081559/190597) – unutbu Feb 23 '19 at 14:19
  • Certainly seems to be, but the top answer in your linked question says the behavior is different between `dateutil` and `datetime`, while my observed behavior is counter to that – Steve Lorimer Feb 23 '19 at 14:29

2 Answers2

10

You are using pytz, not just Python's datetime. Like dateutil, pytz uses the Olson tz database.

The Olson tz database defines Etc/GMT+N timezones which conform with the POSIX style:

those zone names beginning with "Etc/GMT" have their sign reversed from the standard ISO 8601 convention. In the "Etc" area, zones west of GMT have a positive sign and those east have a negative sign in their name (e.g "Etc/GMT-14" is 14 hours ahead of GMT.)


So, to convert UTC to a timezone with offset -5 you could use Etc/GMT+5:

import datetime as DT
import pytz

naive = DT.datetime(2019, 3, 7, 7, 45)
utc = pytz.utc
gmt5 = pytz.timezone('Etc/GMT+5')
print(utc.localize(naive).astimezone(gmt5))

# 2019-03-07 02:45:00-05:00
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
1

Apparently, in posix style systems, you have to use the inverse of the timezone offset. That means if you want to get -5, you have to use GMT+5.

d3 = d2.astimezone(pytz.timezone('Etc/GMT+5'))

prints

UTC-5: 2019-03-07 02:45:00-05:00

Otherwise, you have to pass the posix_offset as true. This is in dateutil documentation;

There is one notable exception, which is that POSIX-style time zones use an inverted offset format, so normally GMT+3 would be parsed as an offset 3 hours behind GMT. The tzstr time zone object will parse this as an offset 3 hours ahead of GMT. If you would like to maintain the POSIX behavior, pass a True value to posix_offset.

https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzstr

Laksitha Ranasingha
  • 4,321
  • 1
  • 28
  • 33