Python で datetime の naive と aware を比較できず TypeError になる
can't compare offset-naive and offset-aware datetimes は tzinfo の有無が違う datetime を比較・減算したときに起きる。
両方を aware(zoneinfo / timezone.utc)に揃えると解決する。
公開: 更新:
要約
TypeError: can't compare offset-naive and offset-aware datetimes は、片方に tzinfo が付いて aware、もう片方が naive のまま比較・減算した場合に発生する。
両方を aware に揃えるのが基本方針で、Python 3.9 以降は標準モジュールの zoneinfo を使うのが推奨。
よくある原因
datetime.now()とdatetime.now(timezone.utc)の混在: 引数なしのnow()は naive、tzinfo を渡すと aware で結果の型が異なる。- 外部ライブラリは aware: SQLAlchemy / pydantic / Django ORM などは多くの場合 aware で値を返すため、自前の naive と比較すると落ちる。
- ISO 文字列のタイムゾーン欠落:
datetime.fromisoformat("2025-01-01T12:00:00")はタイムゾーン情報なしで naive、末尾に+09:00が付くと aware。 pytzとzoneinfoの混在: 古いpytz由来の tzinfo はlocalize経由でないと正しく扱えず、zoneinfoと挙動が違う場面がある。
解決策
1. 両方を aware にする
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
now = datetime.now(ZoneInfo("Asia/Tokyo"))
later = datetime.now(timezone.utc)
# どちらも aware なので比較・減算が可能
print(later > now)datetime 公式ドキュメント(新しいタブで開く) のとおり、tzinfo を持つかどうかで naive / aware が決まる。
2. naive を aware に昇格
from datetime import datetime
from zoneinfo import ZoneInfo
naive = datetime(2025, 1, 1, 12, 0)
aware = naive.replace(tzinfo=ZoneInfo("Asia/Tokyo"))replace(tzinfo=...) は時刻の数字をそのまま保ったまま「これは JST だ」と注釈を付ける操作。
UTC に変換したいなら astimezone(timezone.utc) を続けて呼ぶ。
3. aware を naive に揃える(推奨しない)
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
aware = datetime.now(timezone.utc)
naive = aware.astimezone(ZoneInfo("Asia/Tokyo")).replace(tzinfo=None)DB の制約で naive しか保存できない場合の最終手段。
意図しないタイムゾーン差を生むので、可能ならスキーマ側を aware に直す方が安全。
4. プロジェクト全体のルール統一
実用上は「内部表現は UTC aware、ユーザー表示で ZoneInfo 変換」が事故が少ない。ZoneInfo 自体は zoneinfo モジュール公式(新しいタブで開く) のとおり標準ライブラリに含まれており、pytz は新規採用しなくてよい。
実行例
実際に上記の手順を python:3.12(Debian GNU/Linux 13 trixie)環境で動かすと、まず aware > naive の比較で TypeError: can't compare offset-naive and offset-aware datetimes が終了コード 1 で発生し、続いて解消策1・2 の実行でそれぞれ aware 同士の比較と replace(tzinfo=...) による昇格・UTC 変換が正常に完了することを確認できる。
$ python bad.py
Traceback (most recent call last):
File "/tmp/tmp.U9cZOrL23Q/bad.py", line 5, in <module>
print(aware > naive)
^^^^^^^^^^^^^
TypeError: can't compare offset-naive and offset-aware datetimes
終了コード: 1$ python solve_aware.py
aware 同士: False / 差分: 3:00:00$ python solve_replace.py
昇格後: 2025-01-01 12:00:00+09:00 / UTC 変換: 2025-01-01 03:00:00+00:00— 2026-08-02 時点の出力