Laravel -> MySqlへ.Timestamp型でタイムゾーンが合わない原因を解明へ
困ったこと
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