How to get system timezone setting and pass it to pytz.timezone?

user805627 picture user805627 · Nov 4, 2012 · Viewed 73.7k times · Source

We can use time.tzname get a local timezone name, but that name is not compatible with pytz.timezone.

In fact, the name returned by time.tzname is ambiguous. This method returns ('CST', 'CST') in my system, but 'CST' can indicate four timezones:

  • Central Time Zone (North America) - observed in North America's Central Time Zone
  • China Standard Time
  • Chungyuan Standard Time - the term "Chungyuan Standard Time" is now rarely in use in Taiwan
  • Australian Central Standard Time (ACST)

Answer

jfs picture jfs · Jun 28, 2013

tzlocal module returns pytz tzinfo's object corresponding to the local timezone:

import time
from datetime import datetime

import pytz # $ pip install pytz
from tzlocal import get_localzone # $ pip install tzlocal

# get local timezone    
local_tz = get_localzone() 

# test it
# utc_now, now = datetime.utcnow(), datetime.now()
ts = time.time()
utc_now, now = datetime.utcfromtimestamp(ts), datetime.fromtimestamp(ts)

local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
assert local_now.replace(tzinfo=None) == now

It works even during daylight savings time transitions when local time may be ambiguous.

local_tz also works for past dates even if utc offset for the local timezone was different at the time. dateutil.tz.tzlocal()-based solution fails in this case e.g., in Europe/Moscow timezone (example from 2013):

>>> import os, time
>>> os.environ['TZ'] = 'Europe/Moscow'
>>> time.tzset()
>>> from datetime import datetime
>>> from dateutil.tz import tzlocal
>>> from tzlocal import get_localzone
>>> dateutil_tz = tzlocal()
>>> tzlocal_tz = get_localzone()
>>> datetime.fromtimestamp(0, dateutil_tz)                              
datetime.datetime(1970, 1, 1, 4, 0, tzinfo=tzlocal())
>>> datetime.fromtimestamp(0, tzlocal_tz)
datetime.datetime(1970, 1, 1, 3, 0, tzinfo=<DstTzInfo 'Europe/Moscow' MSK+3:00:00 STD>)

dateutil returns wrong UTC+4 offset instead of the correct UTC+3 on 1970-01-01.

For those bumping into this in 2017 dateutil.tz.tzlocal() is still broken. The above example works now because the current utf offset is UTC+3 in Moscow (that by accident is equal to the utc offset from 1970). To demonstrate the error we can choose a date when utc offset is UTC+4:

>>> import os, time
>>> os.environ['TZ'] = 'Europe/Moscow'
>>> time.tzset()
>>> from datetime import datetime
>>> from dateutil.tz import tzlocal
>>> from tzlocal import get_localzone
>>> dateutil_tz = tzlocal()
>>> tzlocal_tz = get_localzone()
>>> ts = datetime(2014, 6,1).timestamp() # get date in 2014 when gmtoff=14400 in Moscow
>>> datetime.fromtimestamp(ts, dateutil_tz)
datetime.datetime(2014, 5, 31, 23, 0, tzinfo=tzlocal())
>>> datetime.fromtimestamp(ts, tzlocal_tz)
datetime.datetime(2014, 6, 1, 0, 0, tzinfo=<DstTzInfo 'Europe/Moscow' MSK+4:00:00 STD>)

dateutil returns wrong UTC+3 offset instead of the correct UTC+4 on 2014-06-01.