Skip to content
ブログに戻る
チュートリアル

PostgreSQLのtimestampカラムには一体何が格納されているのか?

PostgreSQLのtimestampとtimestamptzの内部構造をわかりやすく解説。タイムゾーンの落とし穴と、ユースケースに応じた正しい型の選び方を紹介します。

6 分で読めます

PostgreSQL の timestamp vs timestamptz:内部で実際に何が保存されているのか?

PostgreSQLの timestamptimestamptz は、どちらも1つの64ビット整数として格納されています。その値は1970-01-01 00:00:00 UTCからのマイクロ秒数です。違いが現れるのは、データを人間が読める形式にフォーマットするときだけです。

なぜハマりやすいのか?

  • 2つのカラムに同じ日付を入れたのに、クエリ結果が違う
  • あなたのアプリは 2025-07-29 10:00 を挿入したのに、別のチームでは 02:00 と表示される
  • フロントエンドのISO文字列がバックエンドのログと一致しない

2つの桃缶:ラベルなしとラベル付き

データ型正式名称格納値SELECT時の振る舞い
timestamptimestamp without time zone生のマイクロ秒カウントそのまま返却 ― Postgresはタイムゾーンを推測しない
timestamptztimestamp with time zone同じマイクロ秒カウントPostgresがセッションの TimeZone 設定を適用してからテキストを送信

たとえ話

  • timestamp = 産地ラベルのない桃の缶詰。中身がフルーツだとわかっても、どこで缶詰にされたかはわからない。
  • timestamptz = 「原産地 UTC+9(JST)」と印刷された桃の缶詰。開ける人は自分で栄養表示を換算するかどうか決められる。

内部構造:ただの巨大な数値

2000-01-01 00:00:00 UTC  → 0
2000-01-01 00:00:01 UTC  → 1 000 000
  • 単位:マイクロ秒(100万分の1秒)
  • 範囲:紀元前4713年〜西暦294276年
  • timestamptimestamptz のストレージは完全に同一。解釈の仕方だけが異なる

15秒で試せるデモ

-- クライアントは東京時間(JST)を使用
SET TimeZone = 'Asia/Tokyo';

CREATE TABLE demo (
  created_ts timestamp,
  created_tz timestamptz
);

INSERT INTO demo VALUES ('2025-07-29 10:00', '2025-07-29 10:00');
クエリ結果理由
SELECT created_ts FROM demo;2025-07-29 10:00:00生の値がそのまま返される。タイムゾーン計算なし
SELECT created_tz FROM demo;2025-07-29 10:00:00+09出力時にタイムゾーンラベルが付与される
SET TimeZone = 'UTC'; のあとSELECT2025-07-29 01:00:00+00同じ瞬間を、別のレンズで見ている

よくある落とし穴と対処法

1. ユーザーごとに違う時刻が見える

  • 原因:クライアントごとに異なる TimeZone 設定で timestamptz を読んでいる
  • 対処:すべて timestamp にして1つのタイムゾーンで統一するか、接続初期化時に SET TimeZone = 'UTC' を強制する

2.「壁時計の時刻」を保存するのに型を間違えた

  • 業務カレンダー(営業時間、締切日)には timestamp を使う
  • 国際的なワークフロー(注文、ログ)は UTCtimestamptz に格納する

3. APIのタイムゾーンがずれる

  • timestamptz は常にISO-8601形式でオフセット付き(Z または +09:00)の文字列として送信する
  • UIはユーザーのローカルタイムゾーンに合わせてフォーマットする

早見表:どちらを使うべき?

ローカルの予定表だけ → timestamp
グローバルに扱うもの → timestamptz(UTCで格納)
  • 会計レポート、授業スケジュール → timestamp
  • 監査ログ、ECの注文データ → timestamptz

Go Toolsで手軽に検証

やりたいことツール使い方
SQLのエポック値を確認タイムスタンプ変換ツール1690622400 を貼り付けて変換
2つのタイムゾーンをひと目で比較タイムゾーン変換ツール10:00 Asia/Tokyo と入力
時間フィールドを含むJSONを整形JSONフォーマッターデータを貼り付けて整形・確認

すべてのツールはブラウザ上で完全に動作します。データがあなたのマシンを離れることはありません。

よくある質問

PostgreSQL の timestamp と timestamptz の違いは何ですか?

timestamp(タイムゾーンなし)は日時値をそのまま保存し、タイムゾーン情報を持ちません。timestamptz(タイムゾーン付き)は入力を UTC に変換して保存し、取得時にセッションのタイムゾーンに再変換します。ほぼすべてのケースで timestamptz を使いましょう — 分散システムにおけるタイムゾーン関連のバグを防げます。

PostgreSQL は timestamptz にタイムゾーンを実際に保存していますか?

いいえ — 名前に反して、PostgreSQL はタイムゾーン自体を保存しません。入力を UTC に変換し、UTC 値(2000-01-01 からのマイクロ秒カウント)のみを保存します。取得時には、セッションの timezone 設定に基づいて UTC から変換します。元のタイムゾーン情報は破棄されます。

PostgreSQL セッションのタイムゾーンを変更するにはどうすればよいですか?

SET timezone = 'America/New_York'; を実行するとセッションのタイムゾーンが変更されます。これは timestamptz 値の表示と解釈に影響します。サーバー全体のデフォルトを変更するには postgresql.conftimezone を設定します。曖昧さを避けるため、略称(JST など)ではなく IANA タイムゾーン名(Asia/Tokyo など)を常に使用してください。

イベント時刻の保存には timestamp と timestamptz のどちらを使うべきですか?

ほぼすべてのケースで timestamptz を使いましょう — ユーザー操作、API 呼び出し、監査ログ、スケジュールイベントなどに最適です。timestamp(タイムゾーンなし)は、特定の瞬間に紐づかない抽象的な時刻にのみ使用します。例えば「店舗は 09:00 に開店」はローカルタイムゾーンの午前 9 時を意味し、特定の UTC 時刻ではありません。

PostgreSQL の timestamptz は夏時間(DST)をどう処理しますか?

PostgreSQL は内部的にすべてを UTC で保存するため、timestamptz で夏時間を正しく処理します。値を取得する際、セッションタイムゾーンの現在の夏時間ルールに基づいて UTC から変換します。これにより、同じ UTC 時刻でも夏時間の切り替え前後で正しく異なるローカル時刻が表示されます。

まとめ

  • PostgreSQLの2つの時間型はどちらもマイクロ秒カウンター。ラベルの有無だけが違い
  • 型を間違えると、混乱するタイムスタンプと誤った計算の原因になる
  • 適切なツールでテスト・変換・検証すれば、何時間ものデバッグ時間を節約できる