隨著得物越做越大越做越強, 國際化的腳步也越來越快. 國際化給程序帶來2大類需要處理的問題: 語言 和 日期時間. 這篇主要聊 日期時間處理.
詞匯 abbr
很多問題往往只是語言的迷霧, 穿透語言表達的概念和定義, 就能解決很多問題.
- area.地區(qū) i18n.國際化.
zh_CN
.語言代碼(iso639).國家代碼(iso3166) i10n.本地化 - dt.datetime.日期時間 date.日期 time.時間
-
tz
.timezone.時區(qū) UTC.協(xié)調(diào)世界時=GMT.格林尼治標準時.gmt
loc
.localtime.localWallClockTime DST.夏令時.人為- CST.中國標準時間=PRC=
Asia/Shanghai
=+0800
=東八區(qū) - EST.美國東部標準時間 EDT.美國東部夏令時
- CST.中國標準時間=PRC=
-
ts
.timestamp.時間戳 -
fmt
.fromat.日期時間的顯示格式
后文遇到相關詞匯可以直接對照來看.
最佳實踐 BestPractice
The preferred way of dealing with times is to always work in UTC, converting to localtime only when generating output to be read by humans. -- https://pypi.org/project/pytz
- 簡單概括: 在處理時間問題時, 一直使用 UTC, 只在需要展示時按照對應地區(qū)進行處理. gmt? loc?
- 只使用包含時區(qū)的時期時間數(shù)據(jù)
-
ts.時間戳
即 UTC, 需要增加 tz 信息進行 fmt - 處理時保持一致性: gmt 歸 gmt, loc 歸 loc
py 中相關包與函數(shù)
在進入代碼之前, 約定 本地地區(qū) 為 CST.中國標準時間
.
- 包: time datetime pytz calendar
- strptime: str-parse-time
- strftime: str-format-time
- 相關日期時間表示: ts.時間戳
class time.struct_time
(后面簡寫為st
)class datetime.datetime
(后面簡寫為dt
) str.fmt- ts
- st>ts:
calendar.timegm(gmt_st) time.mktime(loc_st)
- dt>ts:
dt.timestamp()
- st>ts:
- st
- ts>st:
gmt_st=time.gmtime() loc_st=time.localtime()
- dt>st:
dt.timetuple()
- str>st:
time.strptime(str, fmt)
- ts>st:
- dt
- ts>dt:
datetime.fromtimestamp(ts, tz=utc) datetime.fromtimestamp(ts, tz=cst
)-
datetime.now(tz=utc)
使用當前時間作為ts -
datetime.today()
也使用當前時間作為ts, 但是無時區(qū)信息, 不建議使用
-
- str>dt:
dt.strptime(str, fmt)
- ts>dt:
- str
- st>str:
time.strftime(fmt, st)
- dt>str:
dt.strftime(fmt)
- st>str:
- ts
- gmt 歸 gmt
time.gmtime() calendar.timegm()
utc=pytz.utc
- loc 歸 loc: 以 CST 為例
time.localtime() time.mktime()
-
time.zone=-28800 cst=pytz.timezone('PRC')
關于時期時間的顯示 format
查看 formt 文檔:
- 相關文檔: https://docs.python.org/3.6/library/time.html#time.strftime
- pycharm 在
strftime()/strptime()
上按F1
[圖片上傳失敗...(image-90d990-1634529818435)]
簡單示例:
import time
fmt = '%Y-%m-%d %H:%M:%S %Z%z' # 'date time timezone`
s = time.strftime(fmt, time.localtime()) # '2021-10-17 01:11:22 CST+0800'
print(s)
關于時間戳
- 簡單處理, 約定:
ts.時間戳
使用int
, 精確到秒
- 重要的事情再強調(diào)一遍:
ts.時間戳
都是 UTC 下的, 需要時區(qū)信息 fmt 成 datetime str
-0800 | UTC | CST=+0800 | |
---|---|---|---|
ts.offset | 28800s | 0 | -28800s |
同一時刻ts相等 | ts | ts | ts |
相同日期時間的ts | ts+offset | ts+offset | ts+offset |
當前 ts:
import calendar
import time
ts = int(time.time()) # 1634447629
loc_ts = int(time.mktime(time.localtime())) # 1634447629
print(ts, loc_ts)
指定時刻的 ts:
import calendar
import time
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
st = time.strptime('2021-10-17 01:11:22 CST+0800', fmt)
ts = calendar.timegm(st) # 1634433082
loc_ts = int(time.mktime(st)) # 1634433082
print(ts, loc_ts)
同一時刻
- 同一個時刻: ts 相等, st/dt 表示的時刻相同, str 根據(jù) fmt 變化
import calendar
import time
from datetime import datetime
import pytz
utc = pytz.utc # utc.zone
cst = pytz.timezone('PRC') # CST.中國標準時間=PRC=Asia/Shanghai
fmt_datetime = '%Y-%m-%d %H:%M:%S'
# 同一個時刻: ts 相等, st/dt 表示的時刻相同, str 根據(jù) fmt 變化
# ts>st
gmt_st = time.gmtime()
loc_st = time.localtime()
# st>ts ts=gmt_ts=loc_ts
ts = int(time.time())
gmt_ts = calendar.timegm(gmt_st)
loc_ts = int(time.mktime(loc_st))
# ts>dt
gmt_dt = datetime.now(tz=utc)
loc_dt = datetime.now(tz=cst)
gmt_dt2 = datetime.today().astimezone(tz=utc)
loc_dt2 = datetime.today().astimezone(tz=cst)
gmt_ts2dt = datetime.fromtimestamp(ts, tz=utc)
loc_ts2dt = datetime.fromtimestamp(ts, tz=cst)
# dt>ts
gmt_dt_ts = int(gmt_dt.timestamp())
loc_dt_ts = int(loc_dt.timestamp())
# dt>st
gmt_dt_st = gmt_dt.timetuple()
loc_dt_st = loc_dt.timetuple()
# fmt_datetime: 不含時區(qū)的日期時間字符串
# st/dt>str
gmt_str = time.strftime(fmt_datetime, gmt_st)
loc_str = time.strftime(fmt_datetime, loc_st)
gmt_dt_str = gmt_dt.strftime(fmt_datetime)
loc_dt_str = loc_dt.strftime(fmt_datetime)
# str>st: 無時區(qū), 不建議使用
gmt_str2st = time.strptime(gmt_str, fmt_datetime)
loc_str2st = time.strptime(loc_str, fmt_datetime)
# str>ts
gmt_str2ts = calendar.timegm(time.strptime(gmt_str, fmt_datetime))
loc_str2ts = int(time.mktime(time.strptime(loc_str, fmt_datetime)))
# str>dt
gmt_str2dt = datetime.strptime(loc_str, fmt_datetime).astimezone(tz=utc) # ?
gmt_str2dt2 = datetime.strptime(gmt_str, fmt_datetime).astimezone(tz=utc) # ?
loc_str2dt = datetime.strptime(loc_str, fmt_datetime).astimezone(tz=cst)
相同日期時間
- 相同零點時間, 比如 本地時間零點
2021-10-18 00:00:00
import time
from datetime import datetime
import pytz
utc = pytz.utc # utc.zone
cst = pytz.timezone('PRC') # CST.中國標準時間=PRC=Asia/Shanghai
fmt_date = '%Y-%m-%d'
# 相同日期時間: 當日零點
# fmt_datetime 相同: 當日零點
ts = int(time.time())
offset = time.timezone
gmt_ts_zero = ts - ts % 86400
loc_ts_zero = gmt_ts_zero + time.timezone
gmt_dt_zero = datetime.fromtimestamp(gmt_ts_zero).astimezone(tz=utc)
loc_dt_zero = datetime.fromtimestamp(loc_ts_zero).astimezone(tz=cst)
gmt_str_zero = time.strftime(fmt_date, time.gmtime(gmt_ts_zero))
loc_str_zero = time.strftime(fmt_date, time.localtime(loc_ts_zero))
loc_dt2str_zero = datetime.now(tz=cst).date()
- 相同零點時間: 某日零點時的 date 與 ts
import calendar
import time
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
fmt_date = '%Y-%m-%d'
st = time.strptime('2021-10-17 01:11:22 CST+0800', fmt)
ts = calendar.timegm(st) # 1634433082
print(ts)
ts = ts - ts % 86400 # '2021-10-17 00:00:00 +0000'
d = time.strftime(fmt_date, time.gmtime(ts))
loc_d = time.strftime(fmt_date, time.localtime(ts))
print(ts, d, loc_d)
offset = time.timezone # -28800
loc_date_ts = ts - offset # '2021-10-16 16:00:00 +0000' '2021-10-17 00:00:00 +0800'
print(offset, loc_date_ts)
- 任一相同日期時間字符串
import calendar
import time
from datetime import datetime
import pytz
utc = pytz.utc # utc.zone
cst = pytz.timezone('PRC') # CST.中國標準時間=PRC=Asia/Shanghai
s = '2021-10-18 10:57:10'
fmt_datetime = '%Y-%m-%d %H:%M:%S'
st = time.strptime(s, fmt_datetime) # 無時區(qū)信息, 不建議使用
gmt_ts = calendar.timegm(st)
loc_ts = int(time.mktime(st)) # loc_ts=gmt_ts+time.zone
gmt_dt = datetime.strptime(s, fmt_datetime).astimezone(tz=utc)
loc_dt = datetime.strptime(s, fmt_datetime).astimezone(tz=cst)
時區(qū)的那些事兒
- py 中使用 pytz 設置時區(qū)
import pytz
utc = pytz.utc # utc.zone
cst = pytz.timezone('PRC') # CST.中國標準時間=PRC=Asia/Shanghai
print(pytz.all_timezones) # 查看時區(qū)
更詳細的 timezone 信息: https://en.wikipedia.org/wiki/Time_zone
- 時間差(秒)轉timezone
import time
from datetime import datetime, timezone, timedelta
seconds = 28800
ts = int(time.time())
tz = timezone(timedelta(seconds=seconds))
dt = datetime.fromtimestamp(ts, tz)
s = dt.strftime('%Y-%m-%d %H:%M:%S')
- py 中修改本地時區(qū)
import os
import time
import pytz
cst_off = time.timezone # -28800
os.environ['TZ'] = 'UTC'
time.tzset()
off = time.timezone
print(cst_off, off) # 0
寫在最后
再強調(diào)下最佳實踐:
- 所有處理都在 UTC 下, 只在需要 fmt 時轉成對應時區(qū)
- ts 即 UTC, 需要 tz 信息進行 fmt
- 不建議使用不帶時區(qū)信息的日期時間表示格式(st dt str)