最近的項目需要根據(jù)用戶所屬時區(qū)制定一些特定策略,學習、應用了若干python3的時區(qū)轉(zhuǎn)換相關知識,這里整理一部分記錄下來。
下面涉及的幾個概念及知識點:
GMT時間:Greenwich Mean Time, 格林尼治平均時間
UTC時間:Universal Time Coordinated 世界協(xié)調(diào)時,可以認為是更精準的GMT時間,但兩者誤差極小,在1s以內(nèi),一般可視為等同
LMT:Local Mean Time, 當?shù)貥藴蕰r間
Python中的北京時間:Python的標準timezone中信息中并沒有Asia/Beijing,原因要追溯到國民政府期間上報給國際標準的五個時區(qū)城市沒有北京,因此一般使用Asia/Shanghai獲取東8區(qū)時間
Python使用到的時間相關函數(shù)及概念:
包含時區(qū)信息的datetime稱為: offset-aware datetime,反之稱為offset-naive datetime
pytz.timezone(x): pytz package中預定義的時區(qū)相關對象, pytz可通過 python3 -m pip install pytz 安裝
datetime(...) : 直接指定year/month/day/hour/second生成naive datetime
datetime(...tzinfo=tz) : 直接指定year/month/day/hour/second+時區(qū)信息生成offset-aware datetime
datetime.now(): 生成當前默認時區(qū)的 naive datetime
datetime.now(tzinfo=tz): 生成指定時區(qū)的offset-aware datetime
datetime.strptime(string, format) : 生成當前默認時區(qū)的string、format表示的 naive datetime
datetime.replace(tzinfo=tz): 直接替換datetime 時區(qū)信息為tz時區(qū)offset-aware datetime--不針對時區(qū)進行任何轉(zhuǎn)換
datetime.astimezone(tz): 將時間轉(zhuǎn)換為新的tz時區(qū)的offset-aware datetime
下述代碼示例中,由于云主機位于日本,所以默認時區(qū)為東9區(qū)(Asia/Tokyo)
Python中獲取當前時刻時間:
In [1]: import pytz
In [2]: from datetime import datetime, timedelta
In [3]: datetime.now() # 默認時區(qū)當前時間
Out[3]: datetime.datetime(2021, 8, 1, 18, 36, 8, 352873)
In [4]: datetime.now(pytz.timezone('Asia/Tokyo')) # 指定Tokyo時區(qū)當前時間
Out[4]: datetime.datetime(2021, 8, 1, 18, 36, 25, 421048, tzinfo=DstTzInfo 'Asia/Tokyo' JST+9:00:00 STD>)
可以看到,datetime.now()未指定時區(qū)時,獲取到的對象是offset-navie datetime,而指定時區(qū)后則是offset-aware datetime,naive和aware的datetime是不可以執(zhí)行比較、相減相關操作的,只有同類型的datetime才能求時間差值、比較大小,如下:
In [5]: datetime.now() - datetime.now(pytz.timezone('Asia/Tokyo'))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
ipython-input-5-8b6c111dc5de> in module>
----> 1 datetime.now() - datetime.now(pytz.timezone('Asia/Tokyo'))
TypeError: can't subtract offset-naive and offset-aware datetimes
In [6]: datetime.now() - datetime.now() # 只有同樣的offset-naive datetime才能求差值
Out[6]: datetime.timedelta(days=-1, seconds=86399, microseconds=999991)
In [8]: datetime.now(pytz.timezone('Asia/Tokyo')) - datetime.now(pytz.timezone('Asia/Tokyo')) # 同樣的offset-aware datetime才能求差值
Out[8]: datetime.timedelta(days=-1, seconds=86399, microseconds=999976)
這里碰到了第一個坑,比如我們想獲得北京時間2021年1月1日0點的datetime,然后將其轉(zhuǎn)換為東京時間,直覺上我們很可能這么寫:
In [19]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Asia/Shanghai')) # 這里獲取北京時間20210101 0點的datetime
Out[19]: datetime.datetime(2021, 1, 1, 0, 0, tzinfo=DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>) # 注意獲取的是LMT時間
In [21]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Asia/Shanghai')).astimezone(pytz.timezone('Asia/Tokyo')) # 將北京時轉(zhuǎn)換為東京時間
Out[21]: datetime.datetime(2021, 1, 1, 0, 54, tzinfo=DstTzInfo 'Asia/Tokyo' JST+9:00:00 STD>) # 獲取的是日本標準時間JST+9
In [22]: datetime.now(pytz.timezone('Asia/Shanghai')) # 示例獲取當前時刻北京時間
Out[22]: datetime.datetime(2021, 8, 1, 18, 11, 6, 706727, tzinfo=DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>) # 獲取的是中國標準時間(CST+8)
仔細一看,北京時間的0點轉(zhuǎn)化為東京時間卻是0:54,相差是54分鐘,而不是1個小時,這就奇怪了,仔細一看tzinfo中的信息是LMT+8:06:00 STD,表示這是LMT時間,相比UTC快8小時6分鐘,而不是東8區(qū)標準時間,而通過astimezone方法轉(zhuǎn)換后得到的就是日本標準時間(東9區(qū)),所以兩者之前的差值并不是1小時整。
第一個坑究其原因,通過datetime(..tzinfo=..)指定時區(qū)獲取的是LMT,而datetime.now(tz)、datetime.astimezone(tz) 獲取的卻是UTC(GMT)標準時間,LMT和GMT標準時間可能會有甚至十分鐘級的差值,這已經(jīng)足夠影響到程序的正常邏輯了。
所以如果要保證獲取標準時區(qū)的時間,建議避免使用Asia/Shanghai、Asia/Tokyo這類大洲/城市 字符串表示時間,而使用GMT、UTC這些無歧義的標準時區(qū),如下:
In [45]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT-9'))
Out[45]: datetime.datetime(2021, 1, 1, 0, 0, tzinfo=StaticTzInfo 'Etc/GMT-9'>) # 東9區(qū)應使用GMT-9
這里第二個坑出現(xiàn)了,由于歷史原因,Python中timezone的表示中,時區(qū)偏移以西為正,以東為負,和我們熟悉的ISO標準剛好相反,所以東9區(qū)應該表示為Etc/GMT-9, 而Etc/GMT+9表示的其實是西9區(qū),如下可以驗證GMT-9與JST相差0, GMT+9與JST相差18小時(64800s):
In [50]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT-9')) - datetime(2021, 1, 1).astimezone(pytz.timezone('Asia/Tokyo'))
Out[50]: datetime.timedelta(0)
In [51]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT+9')) - datetime(2021, 1, 1).astimezone(pytz.timezone('Asia/Tokyo'))
Out[51]: datetime.timedelta(seconds=64800)
最后,獲取指定時區(qū)2021年1月1日datetime的方式,以北京時間為例:
In [56]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT-8'))
Out[56]: datetime.datetime(2021, 1, 1, 0, 0, tzinfo=StaticTzInfo 'Etc/GMT-8'>)
In [58]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT-8')).astimezone(pytz.timezone('Asia/Shanghai'))
Out[58]: datetime.datetime(2021, 1, 1, 0, 0, tzinfo=DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>) # 可見GMT-8和東八區(qū)標準時間(CST+8)一致
進一步如果要獲取指定時區(qū)零點的時間戳就很簡單了:
In [44]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT0')).timestamp() # 獲取格林尼治時區(qū)2021年1月1日0點時間戳
Out[44]: 1609459200.0
另外兩種獲取指定時區(qū)時刻的方法,此三種方式彼此等價:
In [51]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT0')) == datetime(2021, 1, 1).replace(tzinfo=pytz.timezone('Etc/GMT0'))
Out[51]: True
In [53]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT0')) == datetime.strptime('20210101', '%Y%m%d').replace(tzinfo=pytz.timezone('Etc/GMT0'))
到此這篇關于淺談Python3中datetime不同時區(qū)轉(zhuǎn)換介紹與踩坑的文章就介紹到這了,更多相關Python3 datetime不同時區(qū)轉(zhuǎn)換 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- Python time.time()方法
- 一篇文章帶你了解python標準庫--time模塊
- 一篇文章帶你了解python標準庫--datetime模塊
- python標準庫之time模塊的語法與簡單使用
- python常見模塊之OS模塊和time模塊
- Python time庫的時間時鐘處理
- python語言time庫和datetime庫基本使用詳解
- 關于python time庫整理匯總