How to get system timezone setting and pass it to pytz.timezone?
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)
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.
Use the tzlocal
function from the python-dateutil
package:
from dateutil.tz import tzlocal
localtimezone = tzlocal()
Internally, this is a class that uses time.timezone
and time.altzone
(switching based on time.daylight
), but creates a suitable timezone object from that.
You use this instead of a pytz
timezone.
The alternative is to read the currently configured timezone from the operating system instead, but this differs widely from OS to OS. On Mac OS X you need to read the output of systemsetup -gettimezone
:
$ systemsetup -gettimezone
Time Zone: Europe/Copenhagen
On Debian and Ubuntu systems, you can read /etc/timezone
:
$ cat /etc/timezone
Europe/Oslo
On RedHat and direved systems, you'll need to read it from /etc/sysconfig/clock
:
$ grep ZONE /etc/sysconfig/clock
ZONE="Europe/Oslo"