Vol.10

タイムゾーン問題の落とし穴

はじめに

日時の処理は、システム開発で最も見落としやすい問題の一つです。私たちも実際に、タイムゾーンに関する問題でトラブルを経験しました。本コラムでは、その教訓を共有します。

発生した問題

【症状】
「今日の学習記録」を表示しているはずが、
昨日の記録も混ざって表示される

【原因】
サーバー(UTC)とクライアント(JST)で
日付の境界がずれていた

UTC 00:00 = JST 09:00
→ 朝9時までは「昨日」として扱われていた

タイムゾーンの基本

UTCとJST

UTC(協定世界時):世界標準
JST(日本標準時):UTC + 9時間

例:
UTC 2025-01-15 00:00:00
= JST 2025-01-15 09:00:00

問題が起きやすいケース

  • 日付の境界をまたぐ処理
  • 「今日」「昨日」の判定
  • 日付でのフィルタリング
  • 定期実行タスクのスケジュール

解決策

原則:保存はUTC、表示はローカル

【データベース保存時】
UTC形式で保存
→ 2025-01-15T00:00:00Z

【画面表示時】
ユーザーのタイムゾーンに変換
→ 2025年1月15日 09:00(JST)

実装例

// 保存時:UTCで保存
const utc_now = new Date().toISOString();
// → "2025-01-15T00:00:00.000Z"

// 表示時:JSTに変換
function format_jst(utc_string) {
    const date = new Date(utc_string);
    return date.toLocaleString('ja-JP', {
        timeZone: 'Asia/Tokyo'
    });
}

「今日」の判定

// JSTベースで「今日」を判定
function get_today_range_jst() {
    const now = new Date();

    // JSTの今日の開始(00:00 JST = 15:00 UTC前日)
    const start = new Date(now.toLocaleString('en-US', {
        timeZone: 'Asia/Tokyo'
    }));
    start.setHours(0, 0, 0, 0);

    // JSTの今日の終了
    const end = new Date(start);
    end.setDate(end.getDate() + 1);

    return { start, end };
}

仕様書への記載

## 日時処理規約

### 保存形式
- データベース:UTC(ISO 8601形式)
- 例:2025-01-15T00:00:00Z

### 表示形式
- 画面表示:JST(日本標準時)
- フォーマット:YYYY年MM月DD日 HH:mm

### 日付フィルタリング
- 「今日」はJST基準で判定
- APIパラメータで日付を指定する場合はJSTで受け取る

チェックリスト

  • □ 日時はUTCで保存しているか
  • □ 表示時にJSTに変換しているか
  • □ 日付フィルタの境界は正しいか
  • □ テストは日付境界でも行ったか

まとめ

タイムゾーン問題は、仕様書に明記することで防げます。「日時をどう扱うか」を事前に決めておくことが重要です。


次回:「セキュリティを最初から考慮する開発」