こんばんはエンジニアの眠れない夜です。
Djangoで時間を扱うプログラムを書いている時に下記のようなエラーに出くわすことがあります。
RuntimeWarning: DateTimeField received a naive datetime * to while time zone support is active.
そんな時サクッと解決する方法をご紹介します。
【Django】make_aware()で変換
私も同じエラーに出くわし、調べてみたところDjangoにすごく便利な関数があることに気づきました。
datetime.datetime.now() はナイーブタイムなので、これをmake_awareの中に入れるアウェアータイムが取得できるというすぐれものです。
from django.utils.timezone import make_aware
import datetime
aware_time = make_aware(datetime.datetime.now())
使い方はたったこれだけなのですが、この際タイムゾーンについてもう少しきちんと理解しておきたいという人はこの先も読み進めてください。
今後、時間を扱うプログラムで引っかかった時に役立つはずです。
ナイーブタイム(naive)とアウェアータイム(aware)
そもそもnaiveとかawareってなんなの?というところから始めます。
- naive time はタイムゾーンを含まない時間
- aware time はタイムゾーンを意識した時間
という違いがあります。
aware という単語の意味は「認識のある、意識のある」なので aware time はタイムゾーンを認識(意識)するということです。
naive time では例えば「2019/10/28 13:07」という時間があったとしてもこれが日本時間なのか標準時なのかさっぱり分かりません。ただ、「2019/10/28 13:07」という時間だけを示しています。
一方、aware time は「2019/10/28 13:07 (日本時間)」というタイムゾーンを意識した時間になります。つまり、タイムゾーンの設定を変えれば、同じ瞬間の別の場所の時間に変換可能です。
例えばイギリスなら9時間の時差があるので「2019/10/28 4:07 (イギリス時間)」となります。時間的には9時間ずれていますが時差があるので同じ時を表しています。
naive timeとaware time の違いがわかったところでこれをPythonのプログラム上ではどのように表しているかを見ていきましょう。
【Python】naive timeとaware time の扱い方
Pythonでnaive timeとaware timeではこのように表示されることが確認できます。
datetime.datetime.now()
> datetime.datetime(2019, 10, 28, 4, 13, 0, 763462)
make_aware(datetime.datetime.now())
> datetime.datetime(2019, 10, 28, 13, 13, 0, 763462, tzinfo=JST)
よく見れば、先程「2019/10/28 13:07 (日本時間)」と書いたのと対して違いはありませんね。
(クラスが変われば表示のされ方も変わりますが、基本は同じです。)
※ make_aware()で取得されるタイムゾーンはsetting.pyのTIME_ZONE
をもとに選択されます。
タイムゾーンを変更した時間を取得したい場合
aware timeはタイムゾーンを変更できるので実際にやってみましょう。タイムゾーンの扱いはpytz
というタイムゾーンを扱うモジュールを使うと便利です。
(PythonTimeZone いかにもPythonのモジュールらしい名前です。)
pip install pytz
先程のmake_aware()された時間をアメリカの東海岸の時間に変更する場合はastimezone()にpytz.timezone(‘ローカルエリア’)を渡せばOKです。
import pytz
aware_time = make_aware(datetime.datetime.now())
aware_time.astimezone(pytz.timezone('US/Eastern'))
> datetime.datetime(2019, 10, 28, 0, 11, 46, 838753, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)
選択したいローカルエリアはpytz.common_timezones
で取得できます。
pytz.common_timezones
> ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'US/Arizona', 'US/Central', 'US/Eastern', 'US/Hawaii', 'US/Mountain', 'US/Pacific', 'UTC']
まとめ
- naive time タイムゾーンを意識しない時間
- aware time タイムゾーンを意識した時間
naive timeをaware timeに変換
from django.utils.timezone import make_aware
import datetime
aware_time = make_aware(datetime.datetime.now())
aware time のタイムゾーンを変更
import pytz
aware_time = make_aware(datetime.datetime.now())
aware_time.astimezone(pytz.timezone('US/Eastern'))
これで時間を扱う時に問題が起こっても基本が理解できたので問題解決の糸口を見つけるのも簡単になるはずです。
ここまで理解したうえで始めエラーメッセージを超意訳すると
RuntimeWarning: DateTimeField received a naive datetime * to while time zone support is active.
aware timeで動いてるのにnaive timeを突っ込むな。バカ!
です(笑)
最後まで読んでいただきありがとうございます。
もっと詳しい日付に関することをまとめた記事があるのでぜひこちらもご参考ください。
今回取り上げられなかった事が他にもたくさん詰まっています。
それでは素敵なPythonライフを!
nativeではなくnaiveです…
yさん
ご指摘ありがとうございます。
完全に読み間違えていました。。。
記事を修正しましたm(_ _)m
[…] 参考: ・Pythonで現在時刻・日付・日時を取得 ・PythonでISO 8601形式の文字列と日時datetimeを相互変換 ・Pythonで日付操作を完全マスター!! ・【Django】naive timeをaware timeに変換する方法 […]