datetimeを別のタイムゾーンに変更する
UTCの文字列をdatetimeに変換し、JSTに変える
例えば「2013-12-31T23:55:58Z」という、ISO 8061拡張表記の文字列をJSTに変換してみる。
やりたいことを図示すると、こうなる。
※時分秒の後ろに"Z"と付けるとUTCらしい(以下引用)
ISO 8601 - Wikipedia
タイムゾーン指定子
協定世界時(UTC)
時刻の後ろに Z を添えることで協定世界時(UTC)での時刻をそのまま示すことができる。
例:
2004-04-01T12:00Z (20040401T1200Z)2004年04月01日12時00分(正午)(UTC)
文字列->datetime
python-dateutilを使うとISO8061の文字列を簡単にパースできるので実際にやってみる。
>>> from dateutil.parser import parse >>> x = parse("2013-12-31T23:55:58Z") >>> x datetime.datetime(2013, 12, 31, 23, 55, 58, tzinfo=tzutc()) >>> x.isoformat() '2013-12-31T23:55:58+00:00'
datetimeのastimezoneを使ってJSTに変換
タイムゾーンを変えるときはpytzのastimezoneを使う。
さっきの処理にこのように続けてみる。
>>> import pytz >>> JST = pytz.timezone("Japan") >>> y = x.astimezone(JST) >>> y datetime.datetime(2014, 1, 1, 8, 55, 58, tzinfo=<DstTzInfo 'Japan' JST+9:00:00 STD>) >>> y.isoformat() '2014-01-01T08:55:58+09:00'
astimezoneはaware-datetimeからしか呼べないので注意。
このように、naive-datetimeから呼ぶとエラーになる。
>>> now = datetime.now() >>> now.astimezone(JST) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: astimezone() cannot be applied to a naive datetime
時差が入っている文字列をJSTに変換
>>> z = parse("2013-12-31T23:55:48+0900") >>> z datetime.datetime(2013, 12, 31, 23, 55, 48, tzinfo=tzoffset(None, 32400)) >>> z.astimezone(JST) datetime.datetime(2013, 12, 31, 23, 55, 48, tzinfo=<DstTzInfo 'Japan' JST+9:00:00 STD>) >>> z == z.astimezone(JST) True
サマータイムを考慮するときはnormalize
>>> NY = pytz.timezone("America/New_York") >>> NY.normalize(NY.localize(datetime(2014, 3, 9, 2, 1, 0))) datetime.datetime(2014, 3, 9, 3, 1, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>) >>> NY.normalize(NY.localize(datetime(2014, 3, 9, 2, 1, 0))).dst() datetime.timedelta(0, 3600)
Pythonのdatetime・utcnowとnowの違い
Pythonのタイムゾーンに関する区別
UTCとかタイムゾーンの扱いで色々考えていて、この二つは何が違うのか混乱してきたので整理した。
まず、Pythonのdatetimeには「タイムゾーンを持つ・持たない」の区別がある。
そして、タイムゾーンを持つaware-datetimeの中で、UTCとそれ以外という区別があると考えると良い(以下の図)。
datetimeのutcnowとnowは何が違うのか?
Pythonのdatetimeにはutcnowとnowという二つのメソッドがある。
これは、現在日時を取得するとき、UTCを基準に日時算出するか・コンピュータに設定されているタイムゾーンから算出するかの違いを区別するため分かれている。
例えば、コンピュータに日本時間が設定されていれば、utcnowとnowが返す日時は9時間離れている。
>>> from datetime import datetime >>> datetime.utcnow() datetime.datetime(2013, 12, 11, 7, 10, 42, 37689) >>> datetime.now() datetime.datetime(2013, 12, 11, 16, 10, 44, 644069) >>> datetime.utcnow() == datetime.now() False
このように、utcnowもnowも両方naiveなdatetimeを返すのだが、時差があると日時の値は等しくない。
言い換えると、「naiveなdatetimeを見ただけではそれがUTCなのか日本時間なのか判らない」。
pytzのfromutcとlocalize
そんなときにpytzのタイムゾーンを使ってnaive-datetimeをaware-datetimeに変換する必要が生じる。
>>> import pytz >>> from datetime import datetime >>> UTC, JST = pytz.utc, pytz.timezone("Japan") >>> x = UTC.fromutc(datetime(2013, 12, 11, 10, 15, 22)) >>> y = JST.localize(datetime(2013, 12, 11, 19, 15, 22)) >>> x == y True
余談だが、Pythonのdatetimeのコンストラクタに直接tzinfoを渡すのはサマータイムを考慮する場合は避けた方が無難らしい。
Pythonのdatetimeで夏時間を扱う - nekoya press
夏時間を考慮してdatetimeオブジェクトを作る場合は、以下を基本方針とするのがいいでしょう。
コンストラクタにはtzinfoを渡さず、naiveなdatetimeオブジェクトをlocalizeする
存在しない時刻が渡る可能性がある時はnormalizeするかUTCに変換する
fromutcとlocalizeを間違えて使わないよう注意
naiveなdatetimeをawareに変換するときは、そのnaiveがUTC準拠なのかローカルタイムゾーン準拠なのかによって利用するメソッドが違う。
from datetime import datetime import pytz now1 = datetime.utcnow() now2 = datetime.now() utc, japan = pytz.utc, pytz.timezone("Japan") template = """\ {0} [tz({0}).localize(utcnow)] {1} [tz({0}).localize(now)] {2} [tz({0}).fromutc(utcnow)] {3} [tz({0}).fromutc(now)] {4} """ for tz in (utc, japan): print(template.format(tz, tz.localize(now1), tz.localize(now2), tz.fromutc(now1), tz.fromutc(now2)))
試しにこういうプログラムを実行してみると以下の結果になり、間違っているケースが存在することが判る。
UTC
[tz(UTC).localize(utcnow)] 2013-12-10 02:56:08.186243+00:00
[tz(UTC).localize(now)] 2013-12-10 11:56:08.186250+00:00 ←間違い
[tz(UTC).fromutc(utcnow)] 2013-12-10 02:56:08.186243+00:00
[tz(UTC).fromutc(now)] 2013-12-10 11:56:08.186250+00:00 ←間違い
Japan
[tz(Japan).localize(utcnow)] 2013-12-10 02:56:08.186243+09:00 ←間違い
[tz(Japan).localize(now)] 2013-12-10 11:56:08.186250+09:00
[tz(Japan).fromutc(utcnow)] 2013-12-10 11:56:08.186243+09:00
[tz(Japan).fromutc(now)] 2013-12-10 20:56:08.186250+09:00 ←間違い
間違いケース
- fromutcにutcnowではなくnowで取得した日時を渡す。
- utcのtzinfoを使う場合にlocalizeにnowで取得した日時を渡す。
- ローカルタイムゾーンのtzinfoを使ってlocalizeにutcnowで取得した日時を渡す。
まとめると以下の図になる。