PostgreSQL 9 min read

PostgreSQLのtimestamp ― timestamptzとの違い、now()とタイムゾーン、日付の範囲指定

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)

この書き方なら、秒未満の値があっても漏れません。