【Django】native timeをaware timeに変換する方法

Python date time native aware

こんばんはエンジニアの眠れない夜です。

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())

使い方はたったこれだけなのですが、この際タイムゾーンについてもう少しきちんと理解しておきたいという人はこの先も読み進めてください。

今後、時間を扱うプログラムで引っかかった時に役立つはずです。

ネイティブタイム(native)とアウェアータイム(aware)

そもそもnativeとかawareってなんなの?というところから始めます。

  • native time はタイムゾーンを意識しない時間
  • aware time はタイムゾーンを意識した時間

という違いがあります。そもそもaware timeは「認識のある、意識のある」という意味なのでタイムゾーンを認識(意識)する。ということですね。

native 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時間ずれていますが時差があるので同じ時を表しています。

native timeとaware time の違いがわかったところでこれをPythonのプログラム上ではどのように表しているかを見ていきましょう。

【Python】native timeとaware time の扱い方

Pythonでnative 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']

まとめ

  • native time タイムゾーンを意識しない時間
  • aware time タイムゾーンを意識した時間

native 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で動いてるのにnative timeを突っ込むな。バカ!

です(笑)

最後まで読んでいただきありがとうございます。
それでは、楽しいPython開発ライフを(^^)ノシ

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください