【Django+PostgreSQL】TimeZone の扱い | SEの道標
django

【Django+PostgreSQL】TimeZone の扱い

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 バイトになっています。

8.5. 日付/時刻データ型

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",
    }
}

コメント

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