記事一覧へ戻る
読了 約 5

「日付が1日ズレる」の正体 - タイムゾーンの落とし穴と実務での扱い方

#development

執筆: デジタル道具屋 編集部 / バックエンド担当

海外チームを含むサービス開発で、ユーザー時刻のローカライズ実装と分析基盤への保存形式を統一する作業を担当してきた編集チームです。

本記事の執筆方針と編集ポリシーについてはAbout ページをご覧ください。

導入:「お客様の集計だけ1日ズレている」事件

男の子
男の子
「博士、ヤバいぜ! 北米のお客様から『売上レポートが日本時間で集計されてて、月初の1日ぶん違う』ってクレームが来たんだぜ!」
博士
博士
「ふむ。バックエンドで日付を YYYY-MM-DD として保存しておったか、もしくは new Date() を JST のサーバーで実行して日付丸め込みをしておったのではないかね?」
女の子
女の子
「タイムゾーンを意識せず日付文字列を切り出すと、UTC では 23:30 だけど JST では翌日 8:30、みたいな境界で必ず事故るのよね。」

1. UTC、JST、ローカル時刻の関係を整理

博士
博士
「まず用語を整理しておこう。」
必ず押さえる4つの用語
  • UTC:協定世界時。世界標準。サーバー保存の事実上のデファクト
  • JST:日本標準時。常に UTC+9(日本では夏時間が無い)
  • ローカル時刻:実行環境(OS / ブラウザ)が認識しているタイムゾーンでの時刻
  • DST(Daylight Saving Time):夏時間。米国の多くの州や欧州では年に2回切り替え。日本には無い

「日本に住んでるユーザー向けだから JST 固定でいい」と決めても、ユーザーが海外旅行中に予約を取るケースなどで容易に破綻します。 原則は「保存は UTC、表示時にユーザーのタイムゾーンへ変換」です。

2. JavaScript で日付がズレる典型パターン

パターン1:new Date('2026-05-08') は UTC として解釈される

男の子
男の子
「えっ、文字列で日付指定するとローカル時刻じゃないのかよ?」

ECMAScript の仕様(ISO 8601 拡張)では、YYYY-MM-DD 形式(時刻なし)は UTC として扱われます。 一方で YYYY/MM/DD2026-05-08T00:00:00(タイムゾーン無し)はローカル時刻と解釈されます。 この差で、JST 環境では1日ズレる事故が頻発します。

// JST 環境
new Date('2026-05-08').toString()
// → "Fri May 08 2026 09:00:00 GMT+0900 (JST)"
// 内部的には UTC 2026-05-08 00:00 なので、JST では 09:00

new Date('2026/05/08').toString()
// → "Fri May 08 2026 00:00:00 GMT+0900 (JST)"
// こちらはローカル時刻として 0:00

パターン2:toISOString() は常に UTC を返す

JST 環境で new Date('2026-05-08T08:00:00+09:00').toISOString()'2026-05-07T23:00:00.000Z' となり、日付部分が前日になります。 これを .split('T')[0] で日付だけ取り出すと、5/8 のはずが 5/7 として保存されてしまいます。

女の子
女の子
「日付だけ欲しいなら toLocaleDateString('sv-SE') がおすすめ。YYYY-MM-DD 形式でローカル時刻のまま取れるわよ。」

3. 実務で守るべき設計ルール

API・DB 設計の基本ルール
  1. サーバー・DB には UTC で保存TIMESTAMP WITH TIME ZONEDATETIME(UTC)
  2. API のレスポンスは ISO 8601 + Z 表記2026-05-08T12:34:56.789Z
  3. ユーザーのタイムゾーンはプロフィールに保存Asia/Tokyo のような IANA 名で
  4. 表示はユーザータイムゾーンでIntl.DateTimeFormatdate-fns-tz を活用
  5. 「日付だけ」のフィールドは特に注意:誕生日や祝日は UTC ではなくローカル日付として扱う設計に

サマータイム(DST)が刺さるケース

米国・欧州の DST 切り替え当日は、ローカル時刻が「1時間飛ぶ」または「同じ時刻が2回ある」事態になります。 例えば米国西海岸では 3月 第2 日曜の 02:00 が突然 03:00 になります。 毎日 02:30 にバッチを動かすシステムは、その日だけバッチが「存在しない時刻」になり実行されません。 バッチ系は 必ず UTC で時刻を指定するのが鉄則です。

博士
博士
「IANA Time Zone Database は年に数回更新されるのも知っておくのじゃ。たとえばトルコは2016年に DST 廃止、ロシアも複数回タイムゾーンを変更しておる。」

4. 試しながら理解する

「いま UTC で何時?」「東京と NY とロンドンの時差はいくつ?」を即座に確認するなら、当サイトのタイムゾーン変換ツールが便利です。 打ち合わせの設定や、ログのタイムスタンプを別ロケールで読み解くときに使ってください。

関連ツール: UNIX 時間変換日数計算年齢計算

🚨 現場での失敗あるある

日本のチームだけで開発・リリースしたサービスを海外展開する直前で、 「全 API レスポンスの日時が JST タイムゾーン無し文字列だった」と発覚するパターンが頻出します。 サービス開始時から ISO 8601 + Z 表記で統一しておけば、後からの追加コストはほぼゼロです。

参考にした一次情報

本記事の内容は、以下の公式仕様や一次情報を参照して執筆しています。

この記事の内容を実際に試す

解説した内容は、以下のツールでブラウザ上から無料で試せます。

タイムゾーン変換

世界各国の時間を比較・変換

UNIX時間変換

UNIXタイムスタンプと日時の相互変換

日数計算

2つの日付の間の日数・週数を計算

年齢計算

生年月日から現在の正確な年齢を計算