可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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).