やりたいこと
zoneinfoを使ってdatetimeのタイムゾーンを適切に扱えるようにしたい
事例
ローカル環境のPythonはしばしばJSTで動いているので
タイムゾーン付けずに開発してるとUTCのサーバーで動かしたときにバグります
>>> datetime.datetime.now()
datetime.datetime(2025, 12, 3, 6, 0, 40, 372929)
>>> datetime.datetime.now()
datetime.datetime(2025, 12, 2, 21, 0, 40, 372929)
説明
nativeとaware
datetime.datetime.Datetime型のオブジェクトにはタイムゾーンが指定されたaware状態のものと、タイムゾーンが指定されていないnative状態のものがあります。nativeとawareで比較したり時間の差をとろうとするとエラーが出ますのでうっかり混ぜることはありませんが、nativeで統一してしまうと上記のように環境によって時間や日付が違ってしまいますので、常にawareにすることが推奨されています
タイムゾーンの扱い方
単純に時間差を固定で適用する方法と、タイムゾーン変更も含めて過去の現地時間を適切に扱える方法があります。日本はUTCに対して+9時間で長らく固定されているので単純に固定時間差を適用するので良いように思えてしまいますが、実際にはUTCに対するオフセットが変更されるケースがちょくちょくある為、固定値だと正確な取り扱いができなくなることがあります
方法1: datetime.timezoneを使う
固定時差を設定する機能がdatetime本体にあります
datetimeパッケージはPython2.3 (2003年) からありますが、datetime.timezoneはPython3.2 (2011年) から追加されています。
方法2: pytzを使う
タイムゾーンを適切に扱うことができるパッケージです
2005年からありZoneinfoが登場するまではこれがスタンダードでした。IANA DBの情報をパッケージに内包していて、最新のタイムゾーン情報を扱うためにはパッケージの更新が必要になりますが、当時はIANA DBをOS経由で参照する仕組みがなかった為、これが最善の選択肢でした
方法3: zoneinfo.Zoneinfoを使う (推奨)
タイムゾーンを適切に扱うことができるパッケージです
Python 3.9 (2020年) 以降、IANA DBをOS経由で参照してタイムゾーンを扱えるパッケージがPythonの標準機能で入りました。OSから最新のIANA DBが参照できる為、パッケージの更新を行わなくても最新のタイムゾーン情報が取得できますので、pytzより機能面でも優れています。今から新たに作成するものはこちらを用いることが推奨されます
使い方
ZoneInfoを付ければタイムゾーンを指定できます
最初からつける場合
from zoneinfo import ZoneInfo
JST = ZoneInfo("Asia/Tokyo")
now = datetime.datetime.now(JST)
from zoneinfo import ZoneInfo
UTC = ZoneInfo("UTC")
now = datetime.datetime.now(UTC)
途中でつける場合
import datetime
from zoneinfo import ZoneInfo
now = datetime.datetime.now()
jst = ZoneInfo('Asia/Tokyo')
now.astimezone(jst)
まとめ
サマータイムがある国が結構多いので、必要になる機会は意外と多いと思います
DSTなんか滅ぼしてしまえばいいのに (過激派)