在 Python 的世界,時間是什么
不光是在 Python 的世界,在計算機世界,時間的表示都是混亂的。
首先,有Unix時間戳
, 然后還有 UTC
( 雖然只是一種標準 ), GMT
等等, 不僅如此, 還有各種時區的概念( 世界上有好幾百種時區, 比如美國, 各個州定義各種時區 )。 另外, 矯情的美國人還定義了夏令時( DST )這種奇怪的東西。
怎一個亂字了得。
與時間相關的標準庫 & 三方庫
在 Python 的標準庫里面,與時間相關的標準庫有:
- time
- datetime
- calendar
處理時間的第三方庫有:
- dateutil (推薦)
- pytz
其中,標準庫提供的 API 繁多且混亂,各種不一致,每次使用都需要臨時去查文檔,每次使用,給人帶來的都是痛苦。
處理時間(元數據的思想)
什么是元數據呢?
最純粹,最干凈的數據。舉個例子,在 Excel 中,你認為最重要的是什么呢? 肯定是那些一行行干干凈凈的數據。有了它們,我們就可以做出各種漂亮的圖表,以及在這一堆數據上面做各種分析。這樣的數據有什么特點?一是干凈,應該是純粹的,不帶其他修飾的;二是粒度足夠細,不能在往下進行拆分。
在 Python 中表示某個時間,你可以用 Unix時間戳
, 也可以用datetime
這種面向對象感十足的,也可以用「適合人類閱讀的字符串形式」。對應到上面的例子,誰是我們的選擇?
當然是Unix時間戳
了。因為它有如下特點:
- 足夠純粹(從 1970/1/1 以來的秒數,而且是絕對偏移量)
- 沒有任何多余的含義(就是一個絕對偏移量)
使用時間戳,從一開始就避免各種時區問題,因為它是絕對的。另外,處理時間戳是一件方便且容易的事情。
特別是存儲數據庫,直接存成時間戳就行了(BITINT 就可以搞定), 再也不用和各種 DateTime 打交道了,避免了許許多多麻煩。
如何用呢?
- 在程序中,使用時間戳;
- 當需要展示給用戶時,轉化成字符串;
- 在其他需要的情況,才轉化成類似 datetime 這種對象
下面結合標準庫 API 來說明,順便給混亂的標準庫 API 總結下,梳理出那些重要的 API 。
# 保持和 JS 一致,統一放大到毫秒級別
def now_timestamp():
return int(time.time() * 1000)
# 時間戳轉化成時間字符串,展示給用戶
def timestamp2str(ts, fmt=None):
if fmt is None:
fmt = '%Y/%m/%d %H:%M:%S'
timetuple = time.localtime(ts)
return time.strftime(fmt, timetuple)
def datetime2timestamp(dt, is_utc=True):
if is_utc:
ts = time.mktime(dt.utctimetuple())
else:
ts = time.mktime(dt.timetuple())
return ts
# datetime 對象轉化成 ISO8601 格式的時間字符串
def datetime2iso8601(dt):
return dt.isoformat()
def timestamp2iso8601(ts):
dt = timestamp2datetime(ts)
return datetime2iso8601(dt)
def timestamp2datetime(ts):
return datetime.fromtimestamp(ts)
def datetime2str(dt, fmt):
return dt.strftime(fmt)
def timestr2datetime(time_str, fmt='%Y-%m-%dT%H:%M:%S'):
return datetime.strptime(time_str, fmt)
def timestr2timestamp(time_str):
dt = timestr2datetime(time_str)
return datetime2timestamp(dt)
第三方庫又能干什么
第三方庫中,只推薦 dateutil (主要因為使用得熟),主要有以下原因:
- 強大(你所考慮的事情,它都考慮好了)
- API 簡潔且一致,能讓人減少很多心智負擔
API 誰都能寫,但是 API 的法則從來都是宜精不宜多
,寫出容易使用,符合直覺,拓展性好,抽象性強
的 API 可不是容易的事情,少許的幾個 API 就能滿足你全部的需要,還有比這更爽的事情嗎?
如果用標準庫不爽了,那就用 dateutil 吧。
關于 dateutil 的使用,其官方文檔有非常詳盡的例子,在此不贅述了。