轉自:http://hi.baidu.com/limodou/item/72da32384b25c7ff97f88df1
http://hi.baidu.com/limodou/item/4a13b3107fca86f89d778af1
如果你的程序要考慮時區,可以使用pytz。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/Harbin', 'Asia/Chongqing', 'Asia/Urumqi', 'Asia/Kashgar']
可以看到,中國的時區可能有:'Asia/Shanghai', 'Asia/Harbin', 'Asia/Chongqing',最后兩個不知道是什么。我們可能使用的'Asia/Shanghai'比較多。
因此可以創建一個時區對象:
>>> 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)
559
>>> from pytz import common_timezones
>>> len(common_timezones)
393
可以看到有很多。
在昨天與了關於pytz的東西后,還是發現一些問題。
>>> 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中看到的。
