まず import ,UTC/JSTの定義,日付文字列の用意 [1]
from datetime import datetime, timedelta, timezone
UTC = timezone.utc
JST = timezone(timedelta(hours=+9), "JST")
val = "2020/02/15T13:59:27Z"
# 末尾のtimezone情報削るかただの文字列として扱う.strptimeに%zとか%Zとか渡さない
naive = datetime.strptime(val, "%Y/%m/%dT%H:%M:%SZ")
aware_utc = naive.replace(tzinfo=UTC)
naive = aware_utc.replace(tzinfo=None)
aware_jst = aware_utc.astimezone(JST)
aware_jst = aware_utc.replace(tzinfo=JST)
わたしは10年連続18回目の「naiveからawareへの変換をググる」をした人です.
前にQiitaに書いた 記憶は有りましたが7年前ですしunixtimeへの変換だしPython2か.
pytz使ってたんですけど油断するとLMT(+09:19)返してきたり,そもそも Stack Overflowの書き込み でviolatesだからdangerousだぜとか言われたり pytz: The Fastest Footgun in the West ではタイトルからして「西部最速の自分の足撃っちゃうやつ」とかちょっと何言ってるのかわからないこと書かれたりしててどうしたらいいの.楽に生きたい.だらけたい.
datetime モジュールだけでやります. datetime.timezone で必要なtimezone定義して使います. [1] [2]
from datetime import datetime, timedelta, timezone
UTC = timezone.utc
JST = timezone(timedelta(hours=+9), "JST")
val = "2020/02/15T13:59:27Z"
# (str -> ) aware -> naive
aware0 = datetime.strptime(val, "%Y/%m/%dT%H:%M:%S%z")
naive0 = aware0.replace(tzinfo=None)
# (str -> ) naive -> aware
naive1 = datetime.strptime(val[:-1], "%Y/%m/%dT%H:%M:%S")
aware1_utc = naive1.replace(tzinfo=UTC)
# utc -> jst
# 違うtimezoneでの時間にする
aware1_jst = aware1_utc.astimezone(JST)
# 違うtimezoneに書き換える
aware2_jst = aware1_utc.replace(tzinfo=JST)
Python3.6以前だと %z / %Z で拾えないものがあります.
naiveで読んで pytz.tzinfo.localize でawareにする.
from datetime import datetime, timedelta
from pytz import timezone
UTC = timezone("UTC")
JST = timezone("Asia/Tokyo")
val = "2020/02/15T13:59:27Z"
# (str -> ) aware -> naive
aware0 = datetime.strptime(val, "%Y/%m/%dT%H:%M:%S%z")
naive0 = aware0.replace(tzinfo=None)
# (str -> ) naive -> aware
naive1 = datetime.strptime(val[:-1], "%Y/%m/%dT%H:%M:%S")
aware1_utc = UTC.localize(naive1)
# utc -> jst
# 違うtimezoneでの時間にする
aware1_jst = aware1_utc.astimezone(JST)
# 違うtimezoneに書き換える
aware2_jst = JST.localize(aware1_utc.replace(tzinfo=None))
naive -> awareの際に,単純に replace を使うとJST(+09:00)ではなくLMT(+09:19)になってしまいます.
っていう流れでLMTになるようです. [3]
datetime(y, m, d, H, M, S, tzinfo=pytz.timezone("Asia/Tokyo")) も同様にLMTになります.
2020年とかの日付なのにLMT返されるのは困るので datetime.replace の tzinfo とか datetime の tzinfo には pytz.timezone("Asia/Tokyo") は食わすなって結論になりました.
dateutil.parser.parse つよい.
from datetime import datetime, timedelta
from dateutil.parser import parse
from dateutil.tz import gettz
UTC = gettz("UTC")
JST = gettz("Asia/Tokyo")
val = "2020/02/15T13:59:27Z"
# (str -> ) aware -> naive
aware0 = parse(val)
naive0 = aware0.replace(tzinfo=None)
# (str -> ) naive -> aware
naive1 = datetime.strptime(val[:-1], "%Y/%m/%dT%H:%M:%S")
aware1_utc = naive1.replace(tzinfo=UTC)
# utc -> jst
# 違うtimezoneでの時間にする
aware1_jst = aware1_utc.astimezone(JST)
# 違うtimezoneに書き換える
aware2_jst = aware1_utc.replace(tzinfo=None)
datetime.timezone で定義した時と同じように使えます.時代によって異なるtimezoneにも対応します.
>>> aware2_jst.strftime("%Z%z")
'JST+0900'
>>> (aware2_jst - timedelta(hours=24 * 365 * 200)).strftime("%Z%z")
'LMT+091859'
>>>
dateutil.parser.parse がらくです.つよい.
[1] | 地域によってはJSTが+9時間では無いパターンがあるので適宜変数命名に気をつけることが重要 |
[2] | 夏時間みたいなのに対応するにはtzinfoのサブクラス作らないとだめなようです |
[3] | 伝聞調なのは 私が完全には理解してないから です. >>> from datetime import datetime
>>> from datetime import timezone as tz_dt
まずは pytz.timezone でJSTを定義して tzinfo に食わせる >>> from pytz import timezone
>>> JST_PT = timezone("Asia/Tokyo")
>>> repr(JST_PT)
"<DstTzInfo 'Asia/Tokyo' LMT+9:19:00 STD>"
>>>
>>> dt_pt = datetime(2020, 2, 20, 21, 0, 0, tzinfo=JST_PT)
>>> dt_pt.strftime("%Z%z")
'LMT+0919'
>>>
>>> datetime.now(tz_dt.utc).replace(tzinfo=JST_PT)
datetime.datetime(2020, 2, 20, 12, 40, 30, 564657, tzinfo=<DstTzInfo 'Asia/Tokyo' LMT+9:19:00 STD>)
>>>
>>> datetime.now(tz_dt.utc).replace(tzinfo=JST_PT).strftime("%Z%z")
'LMT+0919'
dateutil.tz.gettz でJSTを定義して tzinfo に食わせる >>> from dateutil.tz import gettz
>>> JST_DU = gettz("Asia/Tokyo")
>>> repr(JST_DU)
"tzfile('/usr/share/zoneinfo/Asia/Tokyo')"
>>>
>>> dt_du = datetime(2020, 2, 20, 21, 0, 0, tzinfo=JST_DU)
>>> dt_du.strftime("%Z%z")
'JST+0900'
>>>
>>> datetime.now(tz_dt.utc).replace(tzinfo=JST_DU)
datetime.datetime(2020, 2, 20, 12, 40, 37, 256257, tzinfo=tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
>>>
>>> datetime.now(tz_dt.utc).replace(tzinfo=JST_DU).strftime("%Z%z")
'JST+0900'
>>>
dateutil.tz.gettz で 得られる tzfile ( tzinfo のサブクラス)を datetime なり replace なりの tzinfo に食わすと19分ズレが起こらない.なんで?tzfileがよしなに処理して返すの? |