JSON のタイムスタンプはどの形式か
要点:エポック数と ISO 文字列にはそれぞれトレードオフがありますが、実際に壊すのは混在です。オフセット付き ISO 8601 を使い、UTC で保存し、ユーザーのタイムゾーンで表示します。

要点:回線では明示的なオフセット付き ISO 8601 を使い、末尾 Z の UTC で保存し、表示時だけ変換しましょう。エポック数を選ぶなら、秒かミリ秒かを全体で固定し、単位をフィールド名に入れます。
2026-10-30 14:30:00 では足りない理由
その文字列はゾーンを運びません。上海の 14:30 とロンドンの 14:30 は八時間離れており、どちらを意味するか判断できません。new Date('2026-10-30 14:30:00') はランタイムのローカルゾーンでパースするので、サーバーとブラウザが一つのペイロードから二つの異なる時刻点を導きます。再現しない種類のバグです。
裸の 2026-10-30 は時刻点ですらありません。誕生日や請求期間には適し、作成時刻には誤りです。
二つの実用的な形式
ISO 8601 文字列
{
"createdAt": "2026-10-30T14:30:00+08:00",
"updatedAt": "2026-10-30T06:30:00Z"
}
読みやすく、形式が一貫していればソート可能で、必要ならミリ秒精度を持ち、自己記述的です。
エポック数
{ "createdAt": 1793519400 }
コンパクトで曖昧さがありませんが、読めず、秒とミリ秒が恒久的な危険です。エポックを選ぶなら単位を標準化し、フィールド名に入れます。createdAtMs は createdAt よりずっと安全です。
早く決める価値のある慣習
| 項目 | 慣習 |
|---|---|
| 回線形式 | Z か +08:00 付き ISO 8601 |
| 保存ゾーン | UTC。DST が決して入り込まない |
| 精度 | ミリ秒で十分 |
| フィールド命名 | createdAt、updatedAt、expiresAt |
| 純粋な日付 | 別の date フィールド。例:birthday: "1990-01-01" |
| 期間 | 整数と単位。例:timeoutMs: 3000 |
表示
new Intl.DateTimeFormat('en-GB', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Europe/London',
}).format(new Date('2026-10-30T06:30:00Z'));
表示がデバイスに従ってはならないときは timeZone を明示します。ブラウザの既定に頼るのは、ユーザー向けの時刻には正しく、レポートには誤りです。
繰り返す三つのバグ
裸の日付は UTC 深夜としてパースされる。 ECMAScript 仕様では、日付のみの文字列は UTC、日時文字列はローカルです。その不整合は一日ずれるバグの確実な源です。
データベースのゾーンがシリアライザと食い違う。 MySQL の datetime はゾーンを持ちません。ローカルで書いて UTC で読めば、すべてがオフセット分ずれます。
夏時間。 未来のローカル時刻を保存すると、存在しない、または二度起きる時刻点を作ります。UTC にそのような隙間はありません。
FAQ
ISO 8601 とエポック数はどう選べばよいですか?
人間がペイロードを読むとき、辞書順のソートが欲しいとき、値が自分のゾーンを運ぶ必要があるときは ISO 8601 を使います。コストは十数バイトとパースライブラリです。コンパクトさと言語をまたぐ扱いの単純さがより重要なときはエポック数を使いますが、秒とミリ秒を全体で固定し、フィールドがどの単位かを明示してください。
裸の日付とタイムスタンプは同じフィールドを共有すべきですか?
いいえ。裸の 2026-10-30 は時刻もゾーンもない暦日で、タイムスタンプフィールドは正確な時刻点を運ぶと期待されます。誕生日や請求期間は別の date フィールドに保ち、作成や更新時刻には ISO 8601 の At フィールドを使ってください。