Python Django datetime naiveとawareに関するエラーが出たとき

プログラミング
Masa
Masa

こんにちは!Masaです。

今回はPythonでの時間の概念”naive”と”aware”について解説していきます

先日、Python、Djangoにて日付を扱う操作をしていたら以下のエラーが出ました。

typeerror: can’t subtract offset-naive and offset-aware datetimes

直訳だと「オフセット非対応とオフセット対応のデータの引き算ができない」という意味になります。

この問題について解説していきます!



naiveとawareとは

まずこの問題は、Pythonのdatetimeモジュールを使用した際に発生しました。

datetimeモジュールは

  • 日付表現のdateオブジェクト
  • 時間表現のtimeオブジェクト
  • 日付と時間表現のdatetimeオブジェクト

の3種類があります。

そしてdatetimeモジュールには2つの種類があります。(Pythonドキュメントより

  • aware:タイムゾーンや夏時間など、時間調節に関する知識を持っている実時刻を表現
  • naive:タイムゾーンなどの時間調整に関する知識持たず、時間表現だけを行う。

つまり、awareはタイムゾーンや夏時間など、上手に利用すれば便利だが扱いにくいです。
一方、naiveはawareにある時間調節に関する知識を外しているため、簡単に理解でき、上手に利用することができます。

  • date型のオブジェクトは常にnaive
  • time型あるいはdatetime型のオブジェクトはawareかnaiveのどちらか

であり、awareとnaiveの判別には、タイムゾーン情報の属性tzinfoを持つかどうかで見極めます。



can’t subtract offset-naive and offset-aware datetimesの原因

そしてエラーの原因は、naiveのdatetimeオブジェクトとawarのdatetimeオブジェクトで引き算をしてしまったために発生しました

以下のサンプルプログラムから見ていきましょう。

import datetime

dt1 = datetime.datetime.now() #naive
dt2 = datetime.datetime.now(datetime.timezone.utc) #aware

上記のコードはどちらも現在時刻を取得しています。

dt1がnaiveなオブジェクト、dt2がawareなオブジェクトとなります。
awareなオブジェクトを取得したい場合は、タイムゾーン情報を指定する必要があります。

この状態で以下の命令をすると、エラーの原因になります。

delta = dt1 - dt2

この場合の解決方法の1つとして、どちらかの形に合わせてあげましょう。

解決法

両方ともnaiveに合わせる場合

import datetime

dt1 = datetime.datetime.now() #naive
dt2 = datetime.datetime.now(datetime.timezone.utc) #aware

dt2 = dt2.replace(tzinfo=None)

delta = dt1 - dt2

awareに合わせる場合

import datetime
import pytz

dt1 = datetime.datetime.now() #naive
dt2 = datetime.datetime.now(datetime.timezone.utc) #aware

dt1 = pytz.utc.localize(dt1)

delta = dt1 - dt2

この場合、タイムゾーンを表すライブラリーである、pytzを使用しています。
pipでpytzをインストールしてから使用して下さい。



Djangoバージョン

自分自身はDjangoにてこのエラーが発生したため、Djangoでの解決方法も載せておきます。

両方ともnaiveに合わせる場合

import datetime
from django.utils.timezone import make_naive

dt1 = datetime.datetime.now() #naive
dt2 = datetime.datetime.now(datetime.timezone.utc) #aware

dt2 = make_naive(dt2)

delta = dt1 - dt2

両方ともnaiveに合わせる場合

import datetime
from django.utils.timezone import make_aware

dt1 = datetime.datetime.now() #naive
dt2 = datetime.datetime.now(datetime.timezone.utc) #aware

dt1 = make_aware(dt1)

delta = dt1 - dt2



まとめ

本日はdatetimeモジュールのawareとnaiveについてでしたが、いかがだったでしょうか。

日付は協定時間や夏時間などがあり、細かく見ていかないと不都合が生じる可能性があるので、気を付けましょう!

最後まで読んでいただきありがとうございます。
自己学習に役立てていただけたら嬉しく思います
また、SNSやブログでシェアしていただくことや、誤植などをコメントで指摘いただけたら幸いです。

コメント

タイトルとURLをコピーしました