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で取得した日時を渡す。
まとめると以下の図になる。