Vercel(UTC)で JST 日時処理にハマった話

2026.02.02
Share:

はじめに

日本向けの Web サービスを Vercel にデプロイすると、必ずと言っていいほどハマる問題がある。タイムゾーンだ。

Vercel のサーバーは UTC(協定世界時)で動作する。一方、日本は JST(UTC+9)。この9時間のズレが、日時処理のあらゆる場面で問題を引き起こす。

問題の発見

2026年1月26日、Wakulier のスケジュール一括調整機能を Claude Code で実装していたときのこと。開発日記にはこう書いた。

JSTタイムゾーン問題との戦い(最も苦労した) VercelはUTCサーバーで動作するため、JavaScriptのDateメソッドがUTC時刻を返す問題に何度もハマった。

ローカル開発環境(JST)では正しく動作するのに、Vercel にデプロイすると日付がズレる。典型的な「本番環境でのみ発生する」バグだった。

なぜ問題が起きるのか

JavaScript の Date オブジェクトは、サーバーのローカルタイムゾーンに依存する。

const date = new Date();
date.setHours(10, 0, 0, 0);

このコードは「10時」を設定しているように見えるが、実際には「サーバーのローカルタイムの10時」を設定している。

  • ローカル開発(JST): 10:00 JST
  • Vercel(UTC): 10:00 UTC = 19:00 JST

同じコードなのに、環境によって9時間もズレる。

具体的なハマりパターン

パターン1: setHours() の罠

開発日記に記録した問題のコード。

// ❌ 問題のコード
date.setHours(10, 0, 0, 0);  // UTC 10:00 = JST 19:00

朝10時の予定を設定したつもりが、夜7時になってしまう。

パターン2: setDate() の罠

日付を1日進めるコードも問題だった。

// ❌ 問題のコード
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);

深夜0時 JST = 前日15時 UTC のタイミングで実行すると、「明日」の計算結果が想定と異なる。

解決策: 明示的なタイムゾーン指定

Claude Code と原因を追跡した結果、解決策は「明示的にJST(+09:00)オフセットを付与」することだとわかった。

解決策1: ISO 8601 形式でオフセットを指定

// ✅ 解決策
const jstDateStr = "2026-01-26";
const jstDateTimeStr = `${jstDateStr}T10:00:00+09:00`;
const date = new Date(jstDateTimeStr);  // 確実に JST 10:00

+09:00 を明示することで、どのサーバーで実行しても同じ結果になる。

解決策2: ミリ秒計算で日付操作

setDate() の代わりにミリ秒加算を使う。

// ✅ 解決策
const tomorrow = new Date(date.getTime() + 24 * 60 * 60 * 1000);

タイムゾーンに依存しない純粋な時間計算になる。

解決策3: 日付ライブラリの活用

複雑な日時処理には date-fns-tzdayjs のタイムゾーンプラグインを使うのも手だ。

import { formatInTimeZone } from 'date-fns-tz';

const jstDate = formatInTimeZone(
  new Date(),
  'Asia/Tokyo',
  'yyyy-MM-dd HH:mm:ss'
);

クライアント側の注意点

サーバー側だけでなく、クライアント側にも罠がある。

datetime-local 入力の処理

HTML の <input type="datetime-local"> はタイムゾーン情報を含まない。

<input type="datetime-local" value="2026-01-26T10:00">

この値をそのままサーバーに送ると、サーバー側で UTC として解釈される可能性がある。

対策: クライアント側でタイムゾーンを付与してから送信する。

const localValue = "2026-01-26T10:00";
const jstValue = `${localValue}:00+09:00`;

学んだこと

開発日記には3つの教訓を書いた。

  • Vercel(UTC)でのJST日時処理は明示的なオフセット付与が必須
  • setDate(), setHours()などの破壊的メソッドはサーバーのローカルタイムゾーンに依存するため避ける
  • ドキュメント化が大事(JSTタイムゾーン処理パターンをCLAUDE.mdに追記)

特に3つ目が重要だ。同じ問題を何度も踏まないよう、プロジェクトのドキュメントにパターンを記録しておく。

まとめ

Vercel で日本向けサービスを開発するなら、タイムゾーン問題は避けて通れない。

対処の基本方針。

  1. 明示的なオフセット指定: +09:00 を必ず付与
  2. 破壊的メソッドを避ける: setHours(), setDate() は使わない
  3. ミリ秒計算を活用: タイムゾーンに依存しない計算
  4. ドキュメント化: プロジェクト固有のパターンを記録

ローカルでは動くのに本番で動かない。その原因の多くはタイムゾーンにある。


関連記事

Zeronova avatar

Zeronovaゼロノバ

Product Manager / AI-Native Builder

Web/IT業界19年以上・20以上のWebサービスを担当したPdM。東証プライム上場企業の子会社代表として事業経営を経験。現在はAIを駆使して企画から実装まで完結させる個人開発を実践中。

関連プロダクト

Wakulier(ワクリア)

継続案件の依頼管理ツール