やる気がストロングZERO

やる気のストロングスタイル

python3のdatetimeとtimezoneとpytzについて

django

datetime.datetime.now()

で取得したdatetimeオブジェクトを使ってDBに値を保存しようとしたら
以下のwarningがでた。

RuntimeWarning: DateTimeField ItemList.created received a naive datetime (2018-06-30 15:27:40.224351) while time zone support is active.

これきっかけでpython3のdatetimeについて調べた。
結果的には取得したdatetimeオブジェクトにtimezone情報がなかったことが原因であることはわかったが、
このあたりの全体像がわかるまでちょっと時間がかかった。

python3のdatetimeにはnativeとawareが存在する

nativeとはタイムゾーン情報を持たないdatetimeオブジェクトで、
awareとはタイムゾーン情報を持ったdatetimeオブジェクト。

例えばawareでtimezoneがutcで2018年1月1日12時10分10秒を表している場合、
日本時間は+9時間であるとわかる。

nativeで2018年1月1日12時10分10秒を表している場合、
そもそもこれがどこのtimezoneで表された時刻なのか不明なため、日本時間もわからない。

例えば日本のみで使われる前提であれば「日本時間を表す」という前提でnativeで扱ってもいいが、
内部的にutcのawareで扱い適宜現地のtimezoneに変換して表示する、というのがベストプラクティスらしい。

awareのdatetimeオブジェクトの作り方

このあたりがなかなか理解できなかったのだが、
python3の公式ドキュメントによるとawareの現在時刻を取得するには、datetime.datetime.now()の引数にdatetime.tzinfoを継承したタイムゾーン情報を定義したクラスを自分で定義し、このクラスのインスタンスを渡す必要があるらしい。

で、このクラスを自分で定義するのが結構めんどうなので、このあたりをまるっと提供してくれているライブラリとしてpytzがあるので、これをつかうのがデファクトスタンダードになっているらしい。

import pytz
import datetime
now = datetime.datetime.now(pytz.timezone('UTC'))

# 上記nowを日本時間で扱いたければ
now.astimezone(pytz.timezone('Asia/Tokyo'))

# 特定の時間のdatetimeがほしければ
my_time = datetime.datetime(2018,6,30,12,0,0,0,pytz.timezone('Asia/Tokyo'))

djangoであれば、

from django.utils import timezone

utc_now = timezone.now() # utc
local_now = timezone.localtime() # local

で、それぞれutcとlocalでの現在時刻が取得できる。
※local時間の取得方法についてchihiroyさんにお教えいただきました。ありがとうございます。