Kōhei Yamamoto

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

公式ドキュメントの「8.5.3 時間帯」に記載がある。

  • 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 diff
FROM tz_dst_test
ORDER BY id;

結果:

-[ RECORD 1 ]------------------------
name | 1948年サマータイム開始前
utc_ts | 1948-05-01 14:59:00+00
asia_tokyo | 1948-05-01 23:59:00
jst | 1948-05-01 23:59:00
diff | 00:00:00
-[ RECORD 2 ]------------------------
name | 1948年サマータイム中
utc_ts | 1948-05-01 15:00:00+00
asia_tokyo | 1948-05-02 01:00:00
jst | 1948-05-02 00:00:00
diff | 01:00:00
-[ RECORD 3 ]------------------------
name | 1951年サマータイム中
utc_ts | 1951-09-08 14:59:59+00
asia_tokyo | 1951-09-09 00:59:59
jst | 1951-09-08 23:59:59
diff | 01:00:00
-[ RECORD 4 ]------------------------
name | 1951年サマータイム終了後
utc_ts | 1951-09-08 15:00:00+00
asia_tokyo | 1951-09-09 00:00:00
jst | 1951-09-09 00:00:00
diff | 00:00:00

サマータイム期間に入ると時間を1時間進め、期間が終わると1時間戻すので、asia_tokyoのカラムの値は次のように当時の正しい時間に変換できている。

一方jstのカラムの値は、UTC時間に対して9時間加えただけの時間になっていて、当時のサマータイムを考慮できていない。

脚注

  1. これ自体がそもそも推奨されていないが、そういうテーブルがあるとする

  2. 「IANA Time Zone Databaseで日本標準時はJSTで表わすが、夏時刻の適用期間はJDTとなる」と記載があるが、pg_timezone_abbrevsにJDTは入っていなかった