PostgreSQL の日時型には、タイムゾーンを持たない timestamp(正式名 timestamp without time zone)と、持つ timestamptz(timestamp with time zone)があります。どちらも 8 バイトで、違いは「入れた値をどう解釈し、どう表示するか」です。この記事の出力は PostgreSQL 17.6 で採りました。
まず見る(結論)
- 時刻を記録する列は
timestamptzにする。表示はセッションのtimezoneに合わせて変わる timestampは入れた文字列をそのまま持つだけで、どの地域の時刻かは分からないnow()はトランザクションの開始時刻。同じトランザクションの中では何度呼んでも同じ値- 日付で範囲を切るときは
BETWEENではなく>= '2026-10-01' AND < '2026-11-01'
timestamp と timestamptz の違い
表 t_ts(a timestamp, b timestamptz) に、どちらも '2026-10-17 10:00:00' を入れました。入れたときのセッションの timezone は UTC です。表示するセッションの timezone を変えると、b だけが変わります。
SET timezone = 'Asia/Tokyo';
SELECT a, b FROM t_ts;
a | b
---------------------+------------------------
2026-10-17 10:00:00 | 2026-10-17 19:00:00+09
SET timezone = 'UTC';
SELECT a, b FROM t_ts;
a | b
---------------------+------------------------
2026-10-17 10:00:00 | 2026-10-17 10:00:00+00
timestamptz は、入れた時点の timezone で値を解釈し、UTC に直して保存します。取り出すときはセッションの timezone に直して表示します。同じ瞬間を指していることは変わりません。
サーバーやアプリケーションのタイムゾーン設定が混ざる環境で timestamp を使うと、どの列が日本時間でどの列が UTC か分からなくなります。
timezone の既定値を確かめる
SHOW timezone;
TimeZone
----------
Etc/UTC
公式のコンテナイメージでは UTC になっていました。日本時間で表示したいなら、セッションで SET timezone = 'Asia/Tokyo';、データベース単位なら ALTER DATABASE 名前 SET timezone = 'Asia/Tokyo'; で変えます。
特定のタイムゾーンでの時刻を取り出すには AT TIME ZONE を使います。結果はタイムゾーンを持たない timestamp になります。
SELECT b, b AT TIME ZONE 'Asia/Tokyo' AS tokyo FROM t_ts; -- timezone = UTC で実行
b | tokyo
------------------------+---------------------
2026-10-17 10:00:00+00 | 2026-10-17 19:00:00
現在時刻を取る
SELECT now(), current_timestamp, localtimestamp, clock_timestamp();
now | current_timestamp | localtimestamp | clock_timestamp
------------------------------+------------------------------+---------------------------+-------------------------------
2026-10-17 10:00:49.27298+00 | 2026-10-17 10:00:49.27298+00 | 2026-10-17 10:00:49.27298 | 2026-10-17 10:00:49.273365+00
now() と current_timestamp は同じもので、トランザクションが始まった時刻を返します。実際の今の時刻を返すのは clock_timestamp() です。トランザクションの中で2秒待つと、違いが分かります。
BEGIN;
SELECT now(), clock_timestamp();
2026-10-17 10:00:49.273555+00 | 2026-10-17 10:00:49.273633+00
SELECT pg_sleep(2);
SELECT now(), clock_timestamp();
2026-10-17 10:00:49.273555+00 | 2026-10-17 10:00:51.285077+00
COMMIT;
1つのトランザクションで入れた行に同じ時刻を付けたいなら now()、処理時間を測るなら clock_timestamp() を使います。
書式を変える・切り捨てる・足し引きする
SELECT to_char(now(), 'YYYY-MM-DD HH24:MI:SS'); -- 2026-10-17 10:00:51
SELECT date_trunc('day', now()); -- 2026-10-17 00:00:00+00
SELECT now() - interval '1 day'; -- 2026-10-16 10:00:51.567941+00
SELECT '2026-10-17'::timestamp + interval '1 month'; -- 2026-11-17 00:00:00
時は HH24 と書きます。HH だけだと12時間制になります。
秒未満を持たない timestamp(0) は丸める
timestamp(0) の列に '2026-10-17 10:00:00.789' を入れると、2026-10-17 10:00:01 になりました。切り捨てではなく四捨五入です。秒で切り捨てたいなら、入れる前に date_trunc('second', ...) を通します。
BETWEEN で月末の行が漏れる
10月の行を BETWEEN '2026-10-01' AND '2026-10-31' で取ると、2026-10-31 13:00 の行が入りません。'2026-10-31' は 2026-10-31 00:00:00 として比べられるためです。
SELECT b FROM t_ts WHERE b BETWEEN '2026-10-01' AND '2026-10-31';
b
---
(0 rows)
SELECT b FROM t_ts WHERE b >= '2026-10-01' AND b < '2026-11-01';
b
------------------------
2026-10-31 13:00:00+09
(1 row)
この書き方なら、秒未満の値があっても漏れません。