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

匿名 (未验证) 提交于 2019-12-03 02:11:02

问题:

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)

回答1:

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.



回答2:

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"


回答3:

A very simple method to solve this question:

import time  def localTzname():     offsetHour = time.timezone / 3600     return 'Etc/GMT%+d' % offsetHour

Update: @MartijnPieters said 'This won't work with DST / summertime.' So how about this version?

import time  def localTzname():     if time.daylight:         offsetHour = time.altzone / 3600     else:         offsetHour = time.timezone / 3600     return 'Etc/GMT%+d' % offsetHour


回答4:

i don't know if this is useful for you or not, but i think it answers your more general problem:

if you have a date that is in an ambiguous timezone, like CST, simple-date (python 3.2+ only, sorry) can automate the search, and allows you to do things like prefer certain countries.

for example:

>>> SimpleDate('2013-07-04 18:53 CST') Traceback [... simpledate.AmbiguousTimezone: 3 distinct timezones found: <DstTzInfo 'Australia/Broken_Hill' CST+9:30:00 STD>; <DstTzInfo 'America/Regina' LMT-1 day, 17:01:00 STD>; <DstTzInfo 'Asia/Harbin' LMT+8:27:00 STD> (timezones=('CST',), datetime=datetime.datetime(2013, 7, 4, 18, 53), is_dst=False, country=None, unsafe=False) >>> SimpleDate('2013-07-04 18:53 CST', country='CN') SimpleDate('2013-07-04 18:53 CST') >>> SimpleDate('2013-07-04 18:53 CST', country='CN').utc SimpleDate('2013-07-04 10:53 UTC', tz='UTC')

note how, by specifying a country you reduce the range of possible values sufficiently to allow conversion to UTC.

it's implemented by doing a search over the timezones in PyTZ:

>>> SimpleDate('2013-07-04 18:53 CST', country='CN', debug=True) ... PyTzFactory: Have country code CN PyTzFactory: Country code CN has 5 timezones PyTzFactory: Expanded country codes to 5 timezones PyTzFactory: Expanding ('CST',) PyTzFactory: Name lookup failed for CST PyTzFactory: Found CST using Asia/Shanghai PyTzFactory: Found CST using Asia/Harbin PyTzFactory: Found CST using Asia/Chongqing PyTzFactory: Found CST using Asia/Urumqi PyTzFactory: Found CST using Asia/Kashgar PyTzFactory: Expanded timezone to 5 timezones PyTzFactory: New offset 8:00:00 for Asia/Shanghai PyTzFactory: Known offset 8:00:00 for Asia/Harbin PyTzFactory: Known offset 8:00:00 for Asia/Chongqing PyTzFactory: Known offset 8:00:00 for Asia/Urumqi PyTzFactory: Known offset 8:00:00 for Asia/Kashgar PyTzFactory: Have 1 distinct timezone(s) PyTzFactory: Found Asia/Shanghai ... SimpleDate('2013-07-04 18:53 CST')

finally, to answer the question asked directly, it also wraps tzlocal, as mentioned in another answer here, so will automatically do what you expect if you don't give a timezone. for example, i live in chile, so

>>> SimpleDate() SimpleDate('2013-07-04 19:21:25.757222 CLT', tz='America/Santiago') >>> SimpleDate().tzinfo <DstTzInfo 'America/Santiago' CLT-1 day, 20:00:00 STD>

gives my locale's timezone (ambiguous or not).



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!