仕組みから学ぶ Web
応用読了目安 14#時刻#タイムゾーン#UTC#日付#夏時間

時刻とタイムゾーン — 保存した「0時」は、どこの0時か

毎日0時から9時までの注文が、前日の売上に計上されていた。時刻には「瞬間」「壁の時計の時刻」「日付」の3種類があり、混ぜた瞬間に9時間ずれる。

この記事の進み方

What — 時刻には3つの種類がある

「時刻」とひとまとめに呼んでいる値には、性質の違う3つの種類があります。混ぜると、ずれます。

Compare時刻の3つの種類

世界中で同じ1点を指す

2026-11-30T15:00:00Z = 2026-12-01T00:00:00+09:00
注文した時刻、ログの時刻、決済の時刻
タイムゾーン
**値に含まれる(または UTC で表す)**
比較・計算
そのままできる
表示
見る人のタイムゾーンに変換して出す
注意
保存
  • 上の2つは、書き方が違うだけで同じ瞬間
  • UTC で保存しておけば、どこから見ても同じ1点を指す
  • 冒頭の注文の時刻はこれ。値は正しく保存されていた

どれも「日時っぽい値」に見えるが、意味が違う。保存する前に、その値がどれなのかを決める。

UTC
協定世界時。世界の時刻の基準。日本時間は UTC より9時間進んでいる(+09:00)。
オフセット
UTC からの差。+09:00 など。ある瞬間の差を表すだけで、夏時間の決まりは含まない。
タイムゾーン
Asia/Tokyo のような地域の名前。その地域の時差と夏時間の決まり、その変更の歴史を含む。
夏時間
季節によって時計を1時間進める制度。切り替わる日には、存在しない時刻と2回来る時刻がある。

冒頭の事故で、何が失われたか

Figure日本時間 12月1日 8時の注文が、11月の売上になるまで
  1. 1利用者が注文する日本時間 12月1日 8:00軽い
  2. 2サーバーが時刻を記録するUTC 11月30日 23:00軽い
  3. 3DB に保存するタイムゾーンの情報が無い型
  4. 4日付を取り出すDATE(created_at) = 11月30日重い
  5. 5日次の売上に数える11月30日の売上重い

集計の段で「日本の暦で日付を取り出す」と決めていれば、ほかの段はそのままでずれない。

1/5
利用者が注文する利用者の手元の時計は12月1日の朝。本人にとってこれは12月の買い物。

どの段も、受け取った値を正しく扱っている。「どこの時刻か」という情報だけが、どこかで落ちて、最後の段で UTC の暦が使われた。

確認 — ここまで読めたか

注文時刻 created_at は UTC で保存しています。日本時間の11月30日の売上を集計するつもりで WHERE DATE(created_at) = '2026-11-30' と書きました。集計に入るのはどの注文ですか?

まず選ぶ(解答例は a〜d の記号で説明します)

Why — なぜ時刻はずれるのか

時刻の値がずれる原因は、ほとんどの場合、計算の誤りではなく、「どこの時刻か」という情報が、途中のどこかで落ちることです。

値は受け渡しのたびに、文字列になり、型が変わり、別の言語に渡ります。その途中で、

  • タイムゾーンを持たない文字列に変換される("2026-11-30 23:00"
  • タイムゾーンを持たない DB の型に保存される
  • 受け取った側が、自分のタイムゾーン(サーバーの設定、ブラウザの設定)で解釈する

と、同じ文字列が、受け取った場所によって別の瞬間を指すようになります。

同じ文字列が、書き方しだいで9時間ずれる

JavaScript の Date は、この「どこの時刻か」の扱いが、文字列の書き方によって変わります

new Date("2026-11-30")        // 日付だけ    → UTC の 0時 として解釈
new Date("2026-11-30T00:00")  // 時刻付き    → 実行環境のタイムゾーンの 0時 として解釈

日本時間の環境では、この2つは9時間違う瞬間になります。仕様どおりの動きですが、見た目がほとんど同じなので、画面の入力をそのまま渡すと気づきません。

確認 — ここまで読めたか

日本時間に設定されたブラウザで、new Date('2026-11-30') と new Date('2026-11-30T00:00') を作りました。2つの瞬間の差はどれですか?

まず選ぶ(解答例は a〜d の記号で説明します)

夏時間のある地域では、存在しない時刻がある

日本には夏時間がありませんが、利用者や取引先が海外にいると避けられません。

ニューヨークでは、2027年3月14日の午前2時に時計が3時に進みます。その日の 2:30 は存在しません。 秋に戻る日には、1:30 が2回来ます。

  • 「毎日 2:30 に実行」は、その日は実行されないか、3:30 にずれる
  • 「24時間後」と「翌日の同じ時刻」は、その日だけ一致しない
  • 先の日付の予約を、いまの決まりで UTC に変換して保存すると、国が夏時間の制度を変えたときに、予約の時刻が1時間ずれる

演習 — まず自分で判断する

解説を読む前に、まず自分で判断してみてください。ここで一度詰まっておくと、 次の節の判断軸が「なるほど」ではなく「そう来たか」に変わります。

演習 — 設計判断を問う

「11月30日 23時59分まで」のキャンペーンの終了を、どう保存し、どう判定しますか?

与えられた条件
  • キャンペーンは日本の利用者向け。終了は日本時間で決まっている
  • 運営者は管理画面から、日付と時刻を入力して終了を設定する
  • サーバーは UTC で動いている。複数の言語のサービスが同じ値を読む
  • 利用者は海外から日本のサイトを使うこともある
  • 「23時59分まで」の最後の1分に注文した人にも割引を適用したい
この軸で考える
  • · 終了は3つの種類(瞬間・壁の時計・日付)のどれか
  • · 入力された「23:59」が、どこの時計の時刻かを、どこで確定させるか
  • · 終わりの境界を「以下」で持つか「未満」で持つか
まず選ぶ(解答例は a〜d の記号で説明します)

演習 — 説明できるか

経理の担当者に、日次売上が毎日ずれていた理由と、過去の数字をどう読めばよいかを説明してください

与えられた条件
  • 相手は会計に詳しいが、システムの時刻の仕組みは知らない
  • ずれは毎日0時から8時59分の注文が、前日に入っていたこと
  • 修正は来週入り、過去の数字も作り直せる
  • 今月の締めの作業は今週中に行う必要がある

読み終わりましたか?

読了にすると、これを前提とする記事がロードマップで開放されます。