python 用datetime和pytz來轉(zhuǎn)換時(shí)區(qū)原文:http://www./2010/12/14/%E7%94%A8datetime%E5%92%8Cpytz%E6%9D%A5%E8%BD%AC%E6%8D%A2%E6%97%B6%E5%8C%BA
Python標(biāo)準(zhǔn)庫里提供了time、datetime和calendar這3個(gè)模塊來進(jìn)行時(shí)間和日期的處理,其中應(yīng)用最廣的是datetime,而轉(zhuǎn)換時(shí)區(qū)也是靠它來做的。 時(shí)區(qū)這個(gè)玩意非常抽象,處理它時(shí)經(jīng)常弄得我頭暈,只好記錄下來,免得以后再犯暈。 首先要知道時(shí)區(qū)之間的轉(zhuǎn)換關(guān)系,其實(shí)這很簡(jiǎn)單:把當(dāng)?shù)貢r(shí)間減去當(dāng)?shù)貢r(shí)區(qū),剩下的就是格林威治時(shí)間了。 例如北京時(shí)間的18:00就是18:00+08:00,相減以后就是10:00+00:00,因此就是格林威治時(shí)間的10:00。 而把格林威治時(shí)間加上當(dāng)?shù)貢r(shí)區(qū),就能得到當(dāng)?shù)貢r(shí)間了。 例如格林威治時(shí)間的10:00是10:00+00:00,轉(zhuǎn)換成太平洋標(biāo)準(zhǔn)時(shí)間就是加上-8小時(shí),因此是02:00-08:00。 而太平洋標(biāo)準(zhǔn)時(shí)間轉(zhuǎn)換成北京時(shí)間轉(zhuǎn)換也一樣,時(shí)區(qū)相減即可。 例如太平洋標(biāo)準(zhǔn)時(shí)間的02:00-08:00,與北京時(shí)間相差-16小時(shí),因此結(jié)果是18:00+08:00。 而Python的datetime可以處理2種類型的時(shí)間,分別為offset-naive和offset-aware。前者是指沒有包含時(shí)區(qū)信息的時(shí)間,后者是指包含時(shí)區(qū)信息的時(shí)間,只有同類型的時(shí)間才能進(jìn)行減法運(yùn)算和比較。 不幸的是datetime模塊的函數(shù)在默認(rèn)情況下都只生成offset-naive類型的datetime對(duì)象,例如now()、utcnow()、fromtimestamp()、utcfromtimestamp()和strftime()。 其中now()和fromtimestamp()可以接受一個(gè)tzinfo對(duì)象來生成offset-aware類型的datetime對(duì)象,但是標(biāo)準(zhǔn)庫并不提供任何已實(shí)現(xiàn)的tzinfo類,只能自己動(dòng)手豐衣足食了… 下面就是實(shí)現(xiàn)格林威治時(shí)間和北京時(shí)間的tzinfo類的例子:
一個(gè)tzinfo類需要實(shí)現(xiàn)utcoffset、dst和tzname這3個(gè)方法。其中utcoffset需要返回夏時(shí)令的時(shí)差調(diào)整;tzname需要返回時(shí)區(qū)名,如果你不需要用到的話,也可以不實(shí)現(xiàn)。 一旦生成了一個(gè)offset-aware類型的datetime對(duì)象,我們就能調(diào)用它的astimezone()方法,生成其他時(shí)區(qū)的時(shí)間(會(huì)根據(jù)時(shí)差來計(jì)算)。 而如果拿到的是offset-naive類型的datetime對(duì)象,也是可以調(diào)用它的replace()方法來替換tzinfo的,只不過這種替換不會(huì)根據(jù)時(shí)差來調(diào)整其他時(shí)間屬性。 因此,如果拿到一個(gè)格林威治時(shí)間的offset-naive類型的datetime對(duì)象,直接調(diào)用replace(tzinfo=UTC())即可轉(zhuǎn)換成offset-aware類型,然后再調(diào)用astimezone()生成其他時(shí)區(qū)的datetime對(duì)象。 而如果是+6:00時(shí)區(qū)的offset-naive類型的datetime對(duì)象,則可以創(chuàng)建一個(gè)+6:00時(shí)區(qū)的tzinfo類,然后用上述方式轉(zhuǎn)換。 而反過來要將offset-aware類型轉(zhuǎn)換成offset-naive類型時(shí),為了不至于弄混,建議先用astimezone(UTC())生成格林威治時(shí)間,然后再replace(tzinfo=None)。 看上去一切都很簡(jiǎn)單,但不知道你還是否記得上文所述的夏時(shí)令。 提起夏時(shí)令這個(gè)玩意,真是讓我頭疼,因?yàn)樗鼪]有規(guī)則可循:有的國家實(shí)行夏時(shí)令,有的國家不實(shí)行,有的國家只在部分地區(qū)實(shí)行夏時(shí)令,有的地區(qū)只在某些年實(shí)行 夏時(shí)令,每個(gè)地區(qū)實(shí)行夏時(shí)令的起止時(shí)間都不一定相同,而且有的地方TMD還不是用幾月幾日來指定夏時(shí)令的起止時(shí)間的,而是用某月的第幾個(gè)星期幾這種形 式,。 所以說要寫這樣一個(gè)通用的dst方法估計(jì)能把人氣死,于是我只好找來了pytz這個(gè)第三方庫。(注意不要去sourceforge下載,那個(gè)版本4年沒更新了,有嚴(yán)重bug。) 這個(gè)pytz的文檔初看起來很簡(jiǎn)單,用pytz.country_timezones('國家代碼')可以拿到這個(gè)國家的時(shí)區(qū)名列表,而用pytz.timezone('時(shí)區(qū)名')就能獲取一個(gè)tzinfo對(duì)象。 例如取中國的第一個(gè)時(shí)區(qū)來生成datetime對(duì)象:
可是它有個(gè)很奇怪的陷阱,注意看那個(gè)tz,它實(shí)際上是+8:06:00,比北京時(shí)間快了6分鐘。更扯淡的是,中國的時(shí)區(qū)里找不到北京時(shí)間,只能拿上海時(shí)間來湊數(shù)… 平時(shí)使用時(shí)可能沒什么問題,但是構(gòu)造datetime對(duì)象,或調(diào)用replace方法時(shí)就會(huì)莫名其妙地差6分鐘了:
用同一個(gè)tz生成的datetime對(duì)象,居然會(huì)出現(xiàn)不相等的結(jié)果,而且連它們的tzinfo都不一樣… 要解決這個(gè)問題,最簡(jiǎn)單的方法就是使用臺(tái)北(Asia/Taipei)時(shí)間,它正好是+08:00。不過這樣治標(biāo)不治本,畢竟還有那么多不是整點(diǎn)的時(shí)區(qū),不可能一一去找替代時(shí)區(qū)。 實(shí)際上limodou在《關(guān)于pytz的一些記錄(續(xù))》這篇文章中也提到了這個(gè)問題,他指出只要構(gòu)造時(shí)生成offset-naive類型的datetime對(duì)象,再用tz.localize(dt)就能生成正確的時(shí)間了:
最后就是關(guān)鍵的處理夏時(shí)令的代碼,需要用到normalize方法:
可以看到,normalize以后,就能正確處理夏時(shí)令的變更了。 來自:http://blog.csdn.net/wujingwen1111/article/details/8551957 |
|
|