PostgreSQL の TimeZone
PostgreSQL は特に意識しない場合は OS の TimeZone が DB 接続時のデフォルトの TimeZone になります。
理由は、初期セットアップコマンド initdb 実行時に OS の TimeZone を読み込み、その TimeZone を postgresql.conf に設定するためです。
PostgreSQL は TimeZone 付の timestamp 型 (日付と時刻) をサポートしますが、その場合でも DB には UTC の時刻を格納します。
例えば OS の TimeZone が Asia/Tokyo の場合、DB クライアントが TimeZone を指定せずに 2024/08/03 21:00 を格納しようとした場合、PostgreSQL はこれを Asia/Tokyo (日本時間) と解釈し、UTC 時刻の 2024/08/03 12:00 で格納します。
出力時も同様で、DB クライアントから SELECT で時刻を引き出すときに TimeZone の指定がない場合は 2024/08/03 12:00 (UTC) の時間を 2024/08/03 21:00 に変換してから DB クライアントに出力します。
つまり、TimeZone 付といっても実はデータの持ち方としては TimeZone 無しの型と同じであり、入出力時に TimeZone を意識するかどうか、の違いしかありません。
実際、TimeZone 有と TimeZone 無の Timestamp はどちらも格納サイズは 8 バイトになっています。
DB 接続のセッション単位で TimeZone が決まり、TimeZone 有の Timestamp を入出力するときはそのセッションの TimeZone が使われるわけです。
Django の TimeZone
Django ではそもそも TimeZone の概念を使うかどうかの選択ができます。
USE_TZ
settings.py の USE_TZ = False の場合は TimeZone の概念を使わず、Datetime 型を扱うときは常にローカルタイムと考えます。
一方、USE_TZ = True の場合は TimeZone を意識して DB への格納時は UTC で日時を保持しようと努めます。
USE_TZ のデフォルト値は Django 5.0 以上の場合は True、5.0 未満の場合は False です。
TIME_ZONE
settings.py の TIME_ZONE は USE_TZ が False のときはローカルタイムとしてこのタイムゾーンを使います。一方 USE_TZ が True のときは入出力時のデフォルトのタイムゾーンとしてこのタイムゾーンを使います。
TIME_ZONE のデフォルト値は America/Chicago です。
DATABASES の TIME_ZONE
settings.py の DATABASES 配下にも TIME_ZONE の設定があります。これは DB 接続時の DB クライアントとしての TimeZone 指定をするパラメーターなのですが、注意点としてデフォルトは None であり、この場合は UTC での接続を行おうとします。
なので settings.py 直下の USE_TZ = True で TIME_ZONE = "Asia/Tokyo" としていても、settings.py の DATABASES 配下の TIME_ZONE を設定していないと、DB クライアントとしては UTC で接続しようとします。
実際、パケットキャプチャを見てみると DB 接続時に PostgreSQL サーバー側から TimeZone として Asia/Tokyo (postgresql.conf で指定されたデフォルト値) が返却されますが、
その直後に DB クライアント (Django) から TimeZone として UTC をセットしています。
無駄に
SELECT set_config('TimeZone', 'UTC', false)
という SQL クエリーが発生していることが分かります。
これを回避するには DATABASES の TIME_ZONE に Asia/Tokyo を設定します。
TIME_ZONE = "Asia/Tokyo"
USE_TZ = True
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "mydb",
"USER": "postgres",
"PASSWORD": "postgres",
"HOST": "127.0.0.1",
"TIME_ZONE": "Asia/Tokyo",
}
}
コメント