PostgreSQLのタイムゾーン 'Asia/Tokyo' はサマータイムを考慮する
PostgreSQLでtimestamp without time zone型のカラムのデータ1を使う生クエリを書くとき、日本人は日本時間に変換したいことが多い。この変換をするにはtimestamp with time zone型にキャストしてからタイムゾーンを変更する必要がある。
この方法については昔に記事を書いていた。

次のように、タイムゾーンとして'JST'を使うよう指定する。
timezone('JST', events.occured_at::timestamptz)しかし、扱う日時によっては'JST'ではなく'Asia/Tokyo'を使うほうがいい、というのを同僚に教えてもらった。具体的には、日本でサマータイムが実施されていた1948年から1951年を含む日時を扱う場合は、次の方法を使うほうがよい。
-- こちらのほうがよいtimezone('Asia/Tokyo', events.occured_at::timestamptz)理由
'JST'だと固定のオフセット(UTC+9時間)になるが、'Asia/Tokyo'は日本で1948年から1951年にかけて実施されていたサマータイム(夏時刻法 - Wikipedia)も考慮されるため2。
America/New_Yorkなどの完全な時間帯名称。…PostgreSQLはこの目的のためによく使用されているIANA時間帯データを使用します。…
PSTなどの時間帯省略形。こうした指定は、単に特定のUTCからのオフセットを定義します。一方、完全な時間帯名称では夏時間遷移規則群も組み込まれます。…
検証
PostgreSQL 18で確かめた。
DROP TABLE IF EXISTS tz_dst_test;
CREATE TABLE tz_dst_test ( id serial PRIMARY KEY, name text, local_ts timestamp without time zone);
INSERT INTO tz_dst_test (name, local_ts)VALUES ('1948年サマータイム開始前', '1948-05-01 14:59:00'), ('1948年サマータイム中', '1948-05-01 15:00:00'), ('1951年サマータイム中', '1951-09-08 14:59:59'), ('1951年サマータイム終了後', '1951-09-09 15:00:00');
SELECT name, local_ts::timestamptz AS utc_ts, timezone('Asia/Tokyo', local_ts::timestamptz) AS asia_tokyo, timezone('JST', local_ts::timestamptz) AS jst, timezone('Asia/Tokyo', local_ts::timestamptz) - timezone('JST', local_ts::timestamptz) AS diffFROM tz_dst_testORDER BY id;結果:
-[ RECORD 1 ]------------------------name | 1948年サマータイム開始前utc_ts | 1948-05-01 14:59:00+00asia_tokyo | 1948-05-01 23:59:00jst | 1948-05-01 23:59:00diff | 00:00:00-[ RECORD 2 ]------------------------name | 1948年サマータイム中utc_ts | 1948-05-01 15:00:00+00asia_tokyo | 1948-05-02 01:00:00jst | 1948-05-02 00:00:00diff | 01:00:00-[ RECORD 3 ]------------------------name | 1951年サマータイム中utc_ts | 1951-09-08 14:59:59+00asia_tokyo | 1951-09-09 00:59:59jst | 1951-09-08 23:59:59diff | 01:00:00-[ RECORD 4 ]------------------------name | 1951年サマータイム終了後utc_ts | 1951-09-08 15:00:00+00asia_tokyo | 1951-09-09 00:00:00jst | 1951-09-09 00:00:00diff | 00:00:00サマータイム期間に入ると時間を1時間進め、期間が終わると1時間戻すので、asia_tokyoのカラムの値は次のように当時の正しい時間に変換できている。
- 1948-05-01から1948-05-02になるときに1時間進み、1948-05-02は01:00から始まる(1948-05-02は23時間しかない)
- 1951-09-09は00:59:59から01:00になるときに1時間巻き戻る(1951-09-09は25時間ある)
一方jstのカラムの値は、UTC時間に対して9時間加えただけの時間になっていて、当時のサマータイムを考慮できていない。
脚注
-
これ自体がそもそも推奨されていないが、そういうテーブルがあるとする ↩
-
「IANA Time Zone Databaseで日本標準時はJSTで表わすが、夏時刻の適用期間はJDTとなる」と記載があるが、pg_timezone_abbrevsにJDTは入っていなかった ↩