🕗

タイムゾーン運用の個人的ベストプラクティス

に公開

概要

Webアプリケーションを開発する際、タイムゾーンの取り扱いに悩んだことはありませんか?

本記事では、Webアプリケーションにおける「タイムゾーン運用」の個人的なベストプラクティスをまとめます。

サンプルコードの技術スタック

  • 言語: TypeScript
  • データベース: PostgreSQL

【統一方針】

🕒 DBは「timestamptz」型 + UTC で統一

created_at TIMESTAMPTZ NOT NULL DEFAULT timezone('utc', now());
  • timestamptz は内部では UTC で保存
  • timestamp はタイムゾーン情報を持たず、セッションTZに依存するため非推奨

【APIの入出力】

⬇️ 入力 (POST / PUT)

  • ユーザーがローカルで入力した日時 (JST など)を
    UTC ISO形式 (toISOString)に変換してAPIに送信
const local = new Date('2025-08-08T15:00:00')
const utc = local.toISOString() // "2025-08-08T06:00:00.000Z"

⬆️ 出力 (GET)

  • APIは UTCのISO8601文字列 (例: 2025-08-08T06:00:00Z) を返す

【表示の際にローカルに直す】

  • 表示の階層でのみ、ユーザーTZ (例: Asia/Tokyo) に変換
  • dayjs, date-fns-tzなどが便利
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'

dayjs.extend(utc)
dayjs.extend(timezone)

const jst = dayjs.utc('2025-08-08T06:00:00Z').tz('Asia/Tokyo').format('YYYY/MM/DD HH:mm')

timestamp vs timestamptz の違いと落とし穴

❌ timestampはセッションTZに依存

  • timestamp は「そのままの時刻」を保存
  • どのTZからの時刻なのか分からないため、表示や比較でずれる
  • Supabase は基本的に全セッション UTC で動作

【SET TIME ZONE について】

-- 一時的に JST の時刻表示にしたい場合
SET TIME ZONE 'Asia/Tokyo';
  • これは「セッション内だけの表示TZ」に影響
  • DB内の時刻は変わらない

【まとめ】

階層 定義
DB timestamptz + UTC で統一
API入力 UTC ISO文字列で送る
API出力 UTC ISO文字列で返す
表示 ユーザーTZに変換して表示
DB検索/比較 UTCのままではっきり関数を使える

👉 すべてのデータを UTC で扱い、表示だけローカルのタイムゾーンにするという方針が基本です。

Discussion