😽

Laravel -> MySqlへ.Timestamp型でタイムゾーンが合わない原因を解明へ

2024/10/01に公開

困ったこと

LaravelでMySQLにデータを挿入する際、TIMESTAMP型フィールドのタイムゾーンが異なると、保存される日時データが期待と異なる場合があります。
具体的には、

  • Laravelの config/app.phpのタイムゾーンは Asia/Tokyo ( ORMでのcreated_atなどはJST時刻で生成 )
  • MySqlのタイムゾーンは UTC
  • MySqlで確認できるcreated_atなどはJSTの時刻(UTCとして、UTC+9hourの時刻)

想定としては、MySqlにはUTCの時刻が登録されるイメージでした。
ポイントは、各アプリケーションでのタイムゾーンの設定と、Timestamp型の仕組みでした。

MySqlサーバーのタイムゾーンの種類

MySQLには3種類のタイムゾーン設定があり、それぞれがどのように影響するかを理解することが重要です。

セッションタイムゾーン:

  • 現在のクライアントセッションに対して適用されているタイムゾーン(@@session.time_zone)。
SELECT @@session.time_zone;

グローバルタイムゾーン:

  • データベース全体に対して適用されるタイムゾーン設定(@@global.time_zone)。
SELECT @@global.time_zone;

サーバーのシステムタイムゾーン:

  • MySQLサーバーが使用するデフォルトタイムゾーン(@@system_time_zone)。
SELECT @@system_time_zone;

セッションタイムゾーンがSYSTEMの場合

セッションタイムゾーンがSYSTEMと設定されている場合、MySQLはサーバーのシステムタイムゾーンを使用します。もしサーバーのタイムゾーンがUTCに設定されていると、LaravelでAsia/Tokyoの日時を扱っていても、MySQLがUTCで日時を処理してしまうため、データの保存時にズレが生じる可能性があります。

Timestamp型の特徴(Datetime型との比較)

TIMESTAMP型

  • タイムゾーンに依存して動作します。データを保存すると、MySQLはセッションのタイムゾーンを基にUTCに変換して保存し、取得時にはセッションタイムゾーンに基づいてローカルの時刻に変換します。
  • メリット: UTCで一貫したデータ管理ができ、異なるタイムゾーン間でも一貫したデータの表示が可能です。

DATETIME型

  • タイムゾーンに依存しません。保存された時刻がそのまま保持され、変換は行われません。
  • メリット: タイムゾーンを考慮せずに、そのままの時刻を扱いたい場合に便利です。

Laravelのタイムゾーン設定

Laravelの設定ファイルにおいて、アプリケーションとデータベースのタイムゾーンを個別に設定できます。

config/app.php
ここで設定されたtimezoneは、Laravelアプリケーション全体のタイムゾーンを定義します。例えば、Asia/Tokyoに設定されていれば、Laravel内で生成される日時(Carbon::now()など)はすべてJSTで処理されます。

config/database.php
MySQLに接続する際のセッションタイムゾーンを設定します。この設定により、LaravelからMySQLにデータを挿入する際に使用されるタイムゾーンが指定されます。

問題の原因

問題は config/app.php はタイムゾーンをAsia/Tokyoに設定していたが、 config/database.php は何も設定していませんでした。
そのためLaravel -> MySqlのセッションタイムゾーンがUTCで設定されている状況でした。
つまり、Laravel --> MySqlへcreated_atが登録される際にはUTCとしてJSTの時刻(UTC +9 hour)が登録されている状況でした。

Laravel->MySqlのセッションタイムゾーン確認方法

php artisan tinker

DB::select("SELECT @@session.time_zone");
OR
DB::select("SELECT @@system_time_zone");

解決方法

config/database.php にタイムゾーンを設定する

        'mysql' => [

            'driver' => 'mysql',
            // 省略
            'timezone' => 'Asia/Tokyo',
        ],

(補足)Laravelを動かしているEC2のタイムゾーン

EC2インスタンスのタイムゾーンは、デフォルトでUTCに設定されていますが、アプリケーションの設定とは独立して動作します。

sh-5.2$ timedatectl
               Local time: Tue 2024-10-01 07:24:39 UTC
           Universal time: Tue 2024-10-01 07:24:39 UTC
                 RTC time: Tue 2024-10-01 07:24:39
                Time zone: n/a (UTC, +0000)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

まとめ

Discussion