1. 程式人生 > >python pytz時區設定模組

python pytz時區設定模組

如果你的程式要考慮時區,可以使用pytz。
pytz官方文件:http://pytz.sourceforge.net/
我使用的python版本:3.7.1

datetime模組中有tzinfo相關的東西,但是它是一個抽象類,文件上說:
tzinfo is an abstract base clase, meaning that this class should not be instantiated directly. You need to derive a concrete subclass, and (at least) supply implementations of the standard tzinfo methods needed by the datetime methods you use. The datetime module does not supply any concrete subclasses of tzinfo.

上面是說tzinfo是一個抽象類,不應該被直接例項化。你需要派生子類,提供相應的標準方法。datetime模組並不提供tzinfo的任何子類。
所以你可能會使用pytz這個模組。通過easy_install可以安裝。

關於時區使用的幾點想法:
1. 如果你的網站可能有來自其它時區的,可能你要考慮這個問題。都是一個地區的話,還要看伺服器是否與使用者在一個地區,如果不在,也要考慮。
2. 因此,基本上要考慮伺服器時區與使用者時區。伺服器時區可以配置在系統中,全域性生效。而使用者時區則與使用者相關,可以由使用者自已進行設定。
3. 在生成相關時間物件時要加入時區的資訊,並在輸出時進行合適的轉換。


而pytz提供了建立某個時區物件的方法,如:


# 檢視中國時區
>>> import pytz
>>> pytz.country_timezones('cn')
['Asia/Shanghai', 'Asia/Urumqi']

可以看到,中國的時區有:'Asia/Shanghai', 'Asia/Urumqi'。

# 建立一個時區物件:
>>> tz = pytz.timezone('Asia/Shanghai')
# 然後在建立時間物件時進行指定:
>>> import datetime
>>> datetime.datetime.now(tz)
datetime.datetime(2009, 2, 21, 15, 12, 33, 906000, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)
>>> datetime.datetime(2009, 2, 21, 15, 12, 33, tzinfo=tz)
datetime.datetime(2009, 2, 21, 15, 12, 33, tzinfo=<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>)
>>> datetime.date(2009, 2, 21, tzinfo=tz)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tzinfo' is an invalid keyword argument for this function
>>> datetime.time(15, 12, 33, tzinfo=tz)
datetime.time(15, 12, 33, tzinfo=<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>)

從上面可以看出now(), datetime(), time()都是可以指定tzinfo資訊的,而date是不行的,不知道為什麼。所以最好的方法是內部使用datetime物件,需要時進行時區轉換,然後再輸出。

時區轉換:
>>> utc = pytz.utc
>>> n = datetime.datetime.now(tz)
>>> n
datetime.datetime(2009, 2, 21, 15, 16, 41, 843000, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)
>>> new = n.astimezone(utc)
>>> new
datetime.datetime(2009, 2, 21, 7, 16, 41, 843000, tzinfo=<UTC>)
>>> utc.normalize(n.astimezone(utc))
datetime.datetime(2009, 2, 21, 7, 16, 41, 843000, tzinfo=<UTC>)

utc是世界標準時間。
上面的程式碼通過astimezone(utc)將中國時間轉為utc標準時間,可以看到不同的時區時間已經不一樣了。不過在pytz的文件上說:
Converting between timezones also needs special attention. This also needs to use the normalize method to ensure the conversion is correct.

要注意什麼呢?是 daylight savings time,中文叫【日光節約時間】或【夏令時】。對於有采用了夏時制的要使用normzlize來處理,不採用的,直接使用astimezone來處理。所以在通常情況下使用astimezone()就足夠了。

另外pytz還提供了全部的timezone資訊,如:

>>> from pytz import all_timezones
>>> len(all_timezones)
591
>>> from pytz import common_timezones
>>> len(common_timezones)
439

時區轉換時發現的問題:
>>> import pytz, datetime
>>> tz = pytz.timezone('Asia/Shanghai')
>>> tz
<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>

可以看到,它有一個LMT,這是Local Mean Time的縮寫,網上查一查意思是本地平均時。而且時間是+8:06,說明與UTC的時差不是8個小時整。先不管它,讓我們轉換一下試試。

>>> d = datetime.datetime(2009,2,21,23,18,5,tzinfo=tz)
>>> d
datetime.datetime(2009, 2, 21, 23, 18, 5, tzinfo=<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>)

這時,時區與tz是一樣的,沒問題。

>>> x = d.astimezone(pytz.utc)
>>> x
datetime.datetime(2009, 2, 21, 15, 12, 5, tzinfo=<UTC>)

我們轉為UTC時區,時間上有差異,沒問題。

再轉回來。

>>> x.astimezone(tz)
datetime.datetime(2009, 2, 21, 23, 12, 5, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)

奇怪,看到了吧,變成了CST了。時差也成了+8:00了。CST就是Central Standard Time的意思。但這樣就造成了轉換的不一致。我們應該使用CST標準才對。

讓我們再看一下:

>>> datetime.datetime.now(tz)
datetime.datetime(2009, 2, 22, 11, 11, 2, 125000, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)
>>> datetime.time(23, 18, 5, tzinfo=tz)
datetime.time(23, 18, 5, tzinfo=<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>)

可以看到now()函式得到的是CST的,而time傳入tzinfo是LMT的。(date不支援tzinfo引數)所以我們要進行修訂,怎麼做,使用timezone物件的localize()方法,如:

>>> d = datetime.datetime(2009,2,21,23,18,5)
>>> tz.localize(d)
datetime.datetime(2009, 2, 21, 23, 18, 5, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)

所以我才明白pytz的文件上說的:

Creating localtimes is also tricky, and the reason why working with local times is not recommended. Unfortunately, you cannot just pass a ‘tzinfo’ argument when constructing a datetime.

所以我的建議是生成帶時區的時間時,一定要使用timezone.localize()來生成。不要在時間物件的建構函式中傳入tzinfo的方式來實現,為些我封裝了一些函式放在了uliweb/utils/date.py中。

另外關於北京時間。在pytz中,我無法找到Asia/Beijing和GMT+8這樣的時區設定,但是有些時間轉換的工具卻有。按理說pytz使用的是標準的時區資料庫,我特意下載了檢視,的確是沒有。

時區處理的確是挺麻煩的事。象有些資料庫也支援這樣的功能,如postgres支援set timezone的命令,這是在django中看到的。

 

done!