Python 時間/時區處理

Python 時間/時區處理筆記

很常用到的 function,可是三不五時都要去尻以前的 Code 來用有點麻煩
乾脆直接寫在這,以後來這邊尻!

取得 timestamp 與轉換

1
2
3
4
5
6
7
8
9
10
11
12
import time
import datetime
# 取得 timestamp
ts = time.time()
ts_str = datetime.strftime('%s')
# 用 datetime 物件轉成 timestamp
time.mktime(datetime.now().timetuple())
# 轉成 datetime 物件
dt_obj = datetime.datetime.fromtimestamp(ts)

用字串轉成 datetime 物件

1
dt_obj = datetime.datetime.strptime('2016-08-20 11:21:00','%Y-%m-%d %H:%M:%S')

datetime object 變換/加減

1
2
3
4
5
6
from datetime import datetime, timedelta
new_date = datetime.now().replace(days=1, hours=2, minutes=3)
offset_dt = datetime.now() + timedelta(days=2, hours=5, minutes=30)
offset_dt = datetime.now() - timedelta(days=0, hours=5, minutes=30)

用 pytz 做時區轉換

1
2
3
4
5
6
7
8
9
10
11
import pytz
from datetime import datetime
datetime_obj = datetime.strptime('2016-08-20 11:21:00', '%Y-%m-%d %H:%M:%S')
# 先將 naive datetime datetime_obj 掛上 tzinfo
# 並取得巴西聖保羅時間 2016-08-20 11:21:00 GMT-3
br_tz = pytz.timezone('America/Sao_Paulo')
br_local_date = br_tz.localize(datetime_obj, is_dst=None)
# datetime.datetime(2016, 8, 20, 11, 21, tzinfo=<DstTzInfo 'America/Sao_Paulo' BRT-1 day, 21:00:00 STD>)

要注意 python 的 datetime.timetuple 物件並沒有包含 timezone 資訊,
所以如果想把帶有 tzinfo 的 datetime 物件轉出 utc 會有一些問題。

看看以下例子。

1
2
3
4
5
6
br_local_date.timetuple()
# time.struct_time(tm_year=2016, tm_mon=8, tm_mday=20, tm_hour=11, tm_min=21, tm_sec=0, tm_wday=5, tm_yday=233, tm_isdst=0)
br_local_date.utctimetuple()
# time.struct_time(tm_year=2016, tm_mon=8, tm_mday=20, tm_hour=14, tm_min=21, tm_sec=0, tm_wday=5, tm_yday=233, tm_isdst=0)
# 要使用 utctimetuple 才能知道這個 datetime 物件的 utc 時間

所以我們來做個實驗

1
2
3
4
5
6
7
test_date = datetime.strptime('2016-08-20 11:21:00', '%Y-%m-%d %H:%M:%S')
time.mktime(test_date.timetuple())
# 1471663260
time.mktime(br_local_date.timetuple())
# 1471663260

疑?兩個一樣捏,我不是已經把 br_local_date 掛上 tzinfo 了嗎?
我不是應該拿到台灣當地時間 2016-08-20 11:21:00 的 utc
跟巴西聖保羅當地時間 2016-08-20 11:21:00 的 utc 嗎?

因為 time.mktime 會拿你本地系統時區去轉 timestamp,不是看 tzinfo。
而 calendar.timegm 不會自動幫你加時區。
所以做法就是要先把帶有 tzinfo 的 datetime 物件轉到 utc timezone,
再透過 calendar + datetime.utctimetuple() 把正確的 timestamp 轉出來!
這個雷踩過幾次,要特別小心。

想把巴西當地時間的 2016-08-20 11:21:00 轉成 utc 的完整範例:

1
2
3
4
5
6
7
8
9
10
11
12
import calendar
import pytz
from datetime import datetime
datetime_obj = datetime.strptime('2016-08-20 11:21:00', '%Y-%m-%d %H:%M:%S')
br_tz = pytz.timezone('America/Sao_Paulo')
br_local_date = br_tz.localize(datetime_obj, is_dst=None)
br_utc = br_local_date.astimezone(pytz.timezone('utc'))
calendar.timegm(br_utc.utctimetuple())
# 1471702860 = GMT: Sat, 20 Aug 2016 14:21:00 GMT

Refs

卡西哥的文章