Upgrade to Pro — share decks privately, control downloads, hide ads and more …

タイムゾーンの奥地は思ったよりも闇深いかもしれない

 タイムゾーンの奥地は思ったよりも闇深いかもしれない

タイムゾーンは、開発において日付や時刻を扱う際に不可欠な要素ですが、その仕組みや実装について深く考える機会は少ないですよね!本セッションは、各主要OS(Linux、Windows、macOS)がどのようにタイムゾーンを管理・参照するのか、内部構造や関連ファイルなどについて紐解きます。また、PHPがタイムゾーンを扱う際に参照する仕組み(設定など)についても触れ、タイムゾーンの基本概念から実践的なトラブルシューティング方法まで幅広くカバーします。さらに、タイムゾーンデータの更新(IANAタイムゾーンデータベースの利用やPHPのアップデートとの関係)についても取り上げ、国際化対応や開発の安定性向上に寄与する知識を提供します。タイムゾーンの奥地へ踏み込み、日常の開発をより強固に支える技術を一緒に探っていきましょう!

Suguru Ohki

April 12, 2025
Tweet

More Decks by Suguru Ohki

Other Decks in Programming

Transcript

  1. OSごとにはどういう形式? データフォーマット: Linux/macOS: 標準的なtzfileフォーマット Windows: 独自形式(Time Zone Information Registry Format)

    識別子の違い: Linux/macOS: "Asia/Tokyo" (IANA形式) Windows: "Tokyo Standard Time" (Windows独自形式) 2025-04-12 スー | PHP Conference Odawara 2025 21
  2. Linux /usr/share/zoneinfo/ ディレクトリにデータ /etc/localtime へのシンボリックリンクで設定 tzdata パッケージで更新 # タイムゾーン設定確認 ls

    -la /etc/localtime # タイムゾーン設定変更 sudo timedatectl set-timezone Asia/Tokyo 2025-04-12 スー | PHP Conference Odawara 2025 23
  3. NTPにおける大問題 - 2036年問題 1900年1月1日 0時0分0秒 (UTC)を起点とする 「何秒たったか」を積算する32ビットの積算秒数で表現する 「およそ2の32乗秒」までしか計算ができないため、42億9496万7295秒までしか 表現ができず、起点から42億9496万7295秒経過 2036年2月7日

    6時28分15秒 (UTC) の次の秒、2036年2月7日 6時28分16秒には桁 あふれを起こす 「新たな起点になった」とリセットされてしまい、NTPが誤動作すると予想する 2025-04-12 スー | PHP Conference Odawara 2025 35
  4. NTP設定と確認方法 # Linuxで現在のNTP状態確認 timedatectl status # NTPサーバーリストの確認(Linux, macOS) cat /etc/ntp.conf

    # server time.asia.apple.com ← macOSの場合 # Windowsで同期設定確認 w32tm /query /status # 手動での時刻同期(Linux) sudo ntpdate pool.ntp.org とこんなふうに確認できます! ぜひお手元でmacOSの部分を試してみてください! 2025-04-12 スー | PHP Conference Odawara 2025 42
  5. 同期エラーの検出方法 # Linuxでのログ確認 journalctl -u systemd-timesyncd grep -i ntp /var/log/syslog

    # Windowsでの診断 w32tm /query /status w32tm /stripchart /computer:pool.ntp.org /samples:5 # ntpstatコマンド(要インストール) ntpstat ntpq -p 2025-04-12 スー | PHP Conference Odawara 2025 46
  6. 同期エラーの判定基準 接続状態: NTPサーバーに到達できない No server suitable for synchronization found Stratum値が16(同期未確立)

    僕は本番で出会ったことはないので、もし戦士の方がいらっしゃいましたら 教えてください 2025-04-12 スー | PHP Conference Odawara 2025 47
  7. 同期エラーの判定基準 同期状態フラグ: unsynchronised または unreachable w32tm /query /status で同期状態が unsynchronized

    ntpstat で "unsynchronised" と表示 高いjitter値: 時刻値の揺らぎが大きい 通常は数msが正常、100ms以上で問題の可能性 2025-04-12 スー | PHP Conference Odawara 2025 49
  8. タイムゾーンファイルの実体 データセクション 移行時間 (transition times) タイプインデックス (indices) タイプテーブル (types with

    UTC offsets) 略称文字列 (abbreviation strings) うるう秒情報 (leap seconds) 2025-04-12 スー | PHP Conference Odawara 2025 56
  9. tzfileの中身を見る # hexdumpでバイナリ内容を確認 hexdump -C /usr/share/zoneinfo/Asia/Tokyo # zdumpでタイムゾーンの移行時間を確認 zdump -v

    /usr/share/zoneinfo/Asia/Tokyo | head # PHPでタイムゾーン情報を詳細表示 php -r 'print_r((new DateTimeZone("Asia/Tokyo"))->getTransitions());' 2025-04-12 スー | PHP Conference Odawara 2025 58
  10. PHPにおけるタイムゾーンの内部実装 TimeZoneクラス: C言語実装の timelib_tzinfo 構造体をラップ OS依存のTZデータベースをパース タイムゾーンキャッシュ: パフォーマンス向上のためメモリ内キャッシュを使用 date.timezone_cache の影響(php.ini設定)

    データの取得元: デフォルト: システムの /usr/share/zoneinfo PHP組み込みのデータ( timezonedb.h ) 拡張モジュール( timezonedb )からの上書き 2025-04-12 スー | PHP Conference Odawara 2025 59
  11. PHP内部でのタイムゾーン処理フロー 1. 文字列識別子からの解決: $tz = new DateTimeZone('Asia/Tokyo'); → 内部で timelib_fetch_timezone_offset()

    呼び出し 2. タイムスタンプの変換: $dt = new DateTime('2025-03-23 14:00:00', $tz); → timelib_get_time_zone_info() で特定時刻のオフセット計算 3. タイムゾーンプロパティの取得: $transitions = $tz->getTransitions(); 2025-04-12 スー | PHP Conference Odawara 2025 60
  12. PHP-src: タイムゾーン実装の実態 ext/date/lib/timelib.h での定義: /* タイムゾーン情報を格納する構造体 */ typedef struct _timelib_tzinfo

    { char *name; /* タイムゾーン名 */ struct { uint32_t ttisgmtcnt; /* UTC/標準フラグの数 */ uint32_t ttisstdcnt; /* 標準/夏時間フラグの数 */ uint32_t leapcnt; /* うるう秒情報の数 */ uint32_t timecnt; /* 移行時間の数 */ uint32_t typecnt; /* タイプの数 */ uint32_t charcnt; /* 略称の文字数 */ } bit32; /* ... 移行時間やタイプデータへのポインタなど ... */ } timelib_tzinfo; 2025-04-12 スー | PHP Conference Odawara 2025 61
  13. タイムゾーン情報の読み込み ext/date/lib/parse_tz.c から読み込み処理: /* タイムゾーンファイルを解析する関数 */ timelib_tzinfo *timelib_parse_tzfile( char *timezone,

    const timelib_tzdb *tzdb) { /* ... */ /* tzfileからデータを読み込み */ if (memcmp(buffer, "TZif", 4) == 0) { /* マジックナンバーの検証 */ version = *(buffer + 4); /* ... */ /* 移行時間の読み込み */ for (i = 0; i < header->timecnt; i++) { /* ... タイムスタンプとタイプインデックスの解析 ... */ } } /* ... */ } 2025-04-12 スー | PHP Conference Odawara 2025 62
  14. php-src: DateTimeZone実装 ext/date/php_date.c からのPHPクラスインターフェース: /* DateTimeZoneクラスのメソッドテーブル */ static const zend_function_entry

    date_timezone_methods[] = { PHP_ME(DateTimeZone, __construct, arginfo_timezone_construct, ZEND_ACC_PUBLIC) PHP_ME(DateTimeZone, getName, arginfo_timezone_void, ZEND_ACC_PUBLIC) PHP_ME(DateTimeZone, getOffset, arginfo_timezone_getoffset, ZEND_ACC_PUBLIC) PHP_ME(DateTimeZone, getTransitions, arginfo_timezone_gettransitions, ZEND_ACC_PUBLIC) /* ... */ }; /* getTransitionsメソッドの実装 */ PHP_METHOD(DateTimeZone, getTransitions) { /* ... */ /* タイムゾーン情報からすべての移行情報を取得 */ for (i = 0; i < tz->timecnt; i++) { /* 各移行時間を配列として返す */ array_init(&element); add_assoc_long(&element, "ts", tz->trans[i]); add_assoc_string(&element, "time", timelib_time_to_string(dummy)); /* ... */ 2025-04-12 スー | PHP Conference Odawara 2025 63
  15. date.timezone設定の解決順序 ext/date/php_date.c での解決順序: /* タイムゾーン設定の取得ロジック */ static timelib_tzinfo *get_timezone_info(void) {

    /* 1. システムのタイムゾーン環境変数 */ if ((env = getenv("TZ")) != NULL) { /* ... */ } /* 2. php.ini設定 */ if (DATEG(default_timezone) && *DATEG(default_timezone)) { /* ... */ } /* 3. PHPが推測したタイムゾーン */ if (!found_guess) { /* ... */ php_error_docref(NULL, E_WARNING, "date.timezone設定がありません。システムから自動検出が必要です。"); } 2025-04-12 スー | PHP Conference Odawara 2025 65
  16. タイムゾーン取り扱いの推奨される方法: php.ini で date.timezone を適切に設定する: サーバー環境全体で一貫したデ フォルトタイムゾーンを提供します。 DateTime および DateTimeZone

    オブジェクトを使用する: 特定のタイムゾーン で日時を扱いたい場合、これらのオブジェクトを使うことで、デフォルト設定に 依存せず、より明確かつ柔軟にタイムゾーンを管理できます。 2025-04-12 スー | PHP Conference Odawara 2025 67
  17. コンテナ環境での時刻確認 アプリケーションレベルでの確認 PHPのログに時刻情報を出力 error_log('Current time: ' . date('Y-m-d H:i:s.u T'));

    ヘルスチェックエンドポイントに時刻情報を追加 2025-04-12 スー | PHP Conference Odawara 2025 70
  18. コンテナ環境での時刻確認 CloudWatch Logs Insights での分析 複数コンテナのログから時刻パターンを抽出 filter @message like /Current

    time:/ | parse @message "Current time: * " as timestamp | display timestamp ※ 出力形式によって上記のようなクエリでできるという例 2025-04-12 スー | PHP Conference Odawara 2025 71
  19. コンテナ環境での時刻確認 ECS Exec(AWS CLI)の活用 aws ecs execute-command --cluster your-cluster \

    --task your-task-id \ --container your-container \ --command "date" \ --interactive 2025-04-12 スー | PHP Conference Odawara 2025 72
  20. 分散システムの時刻同期診断 サーバー間時刻差の可視化: // 各サーバーで実行して結果を比較 $server_time = date('Y-m-d H:i:s.u T'); $db_time

    = $pdo->query('SELECT NOW() AS time')->fetch()['time']; error_log("Server time: $server_time, DB time: $db_time"); 2025-04-12 スー | PHP Conference Odawara 2025 74
  21. 参考資料 PHP公式ドキュメント - タイムゾーン IANAタイムゾーンデータベース PHPでのDateTimeのベストプラクティス RFC 6557 - Procedures

    for Maintaining the Time Zone Database タイムゾーン呪いの書 (知識編) すごくありがたい記事でした! 2025-04-12 スー | PHP Conference Odawara 2025 86
  22. NTPとPTPの比較 項目 NTP PTP 精度 ミリ秒〜マイクロ秒 マイクロ秒〜ナノ秒 実装 主にソフトウェア ハードウェア支援が望ましい

    複雑さ 比較的シンプル より複雑 導入コスト 低い 高い(特にハードウェア対応機器) Webアプリでの利用 一般的 特殊用途(高頻度取引等)のみ PHPアプリケーションでは通常NTPで十分 マイクロ秒以上の精度が必要な場合はPTPを検討 2025-04-12 スー | PHP Conference Odawara 2025 89
  23. PTP (Precision Time Protocol) IEEE 1588標準の高精度時刻同期プロトコル NTPよりさらに高精度(ナノ秒〜マイクロ秒レベル) 主な用途: 金融取引システム(高頻度取引) 産業オートメーション

    電力網制御システム 通信ネットワーク 特徴: ハードウェアタイムスタンプ対応 ネットワーク遅延の非対称性を考慮 2025-04-12 スー | PHP Conference Odawara 2025 104
  24. PHP内部でのタイムゾーン処理のフロー 1. date_default_timezone_set() 関数による設定: スクリプトの実行中に date_default_timezone_set() 関数が呼び出される と、その設定が最優先されます。 ここで指定されたタイムゾーンが、それ以降の date()

    関数や DateTime オブジェクト(明示的にタイムゾーンが指定されない場合)などの日付・時 刻関連の処理で使用されるデフォルトのタイムゾーンになります。 例: date_default_timezone_set('Asia/Tokyo'); 2025-04-12 スー | PHP Conference Odawara 2025 106
  25. 2. php.ini ファイルの date.timezone 設定: date_default_timezone_set() がスクリプト中で呼び出されなかった場 合、PHPは php.ini ファイル内の

    date.timezone ディレクティブの設定 値を使用しようとします。 これはサーバー全体、または特定のディレクトリ/アプリケーションに対す るデフォルトのタイムゾーン設定として機能します。サーバー管理者が設定 するのが一般的です。 例 (php.ini内): date.timezone = "Asia/Tokyo" 現在の設定値は ini_get('date.timezone') で確認できます。 2025-04-12 スー | PHP Conference Odawara 2025 107
  26. 4. PHPによるシステム設定の推測 (非推奨・エラー発生): 上記1、2、3のいずれも利用できない場合、PHPはOS(オペレーティングシ ステム)のタイムゾーン設定を推測しようと試みることがありました (古い バージョン)。 しかし、この推測は信頼性が低く、また、近年のPHPバージョン (PHP 5.1.0

    以降) では、 date_default_timezone_set() または php.ini でタイムゾー ンが明示的に設定されていない場合、日付/時刻関数を使用するたびに E_WARNING レベルのエラー(またはバージョンによっては E_NOTICE) が発生します。これは、開発者にタイムゾーン設定を強制するためです。 2025-04-12 スー | PHP Conference Odawara 2025 109
  27. フローの要約: PHPは以下の順序でデフォルトタイムゾーンを決定します。 1. date_default_timezone_set() (スクリプト実行中の設定) 2. php.ini の date.timezone (サーバー/アプリケーション設定)

    3. (警告/エラー発生) PHPがタイムゾーン設定を見つけられない場合、警告またはエ ラーが発生します。 4. (最終手段: UTC) 上記で決まらずエラーも発生しない特殊な場合、UTCになる可 能性があります。 推奨される方法: php.ini で date.timezone を適切に設定する: サーバー環境全体で一貫したデ フォルトタイムゾーンを提供します。 DateTime および DateTimeZone オブジェクトを使用する: 特定のタイムゾーン 2025-04-12 スー | PHP Conference Odawara 2025 111
  28. <?php // デフォルトタイムゾーンを取得 (php.iniの設定などに依存) echo "Default timezone before set: "

    . date_default_timezone_get() . "\n"; echo "Current time (default): " . date('Y-m-d H:i:s') . "\n\n"; // スクリプト内でデフォルトタイムゾーンを上書き date_default_timezone_set('America/New_York'); echo "Default timezone after set: " . date_default_timezone_get() . "\n"; echo "Current time (New York): " . date('Y-m-d H:i:s') . "\n\n"; // DateTimeオブジェクトで特定のタイムゾーンを指定 (デフォルト設定に影響されない) $tokyoTime = new DateTime('now', new DateTimeZone('Asia/Tokyo')); echo "Current time (Tokyo using DateTime): " . $tokyoTime->format('Y-m-d H:i:s P') . "\n"; $londonTime = new DateTime('now', new DateTimeZone('Europe/London')); echo "Current time (London using DateTime): " . $londonTime->format('Y-m-d H:i:s P') . "\n"; // DateTimeオブジェクトでタイムゾーンを指定しない場合、デフォルトタイムゾーンが使われる $defaultTimeObj = new DateTime('now'); echo "Current time (Default using DateTime): " . $defaultTimeObj->format('Y-m-d H:i:s P') . "\n"; ?> 2025-04-12 スー | PHP Conference Odawara 2025 112