これは何?
Railsアプリ→ MySQLのTIME STAMPの保存でハマったのでメモ
前提
Ruby on Rails: 7.0.2 MySQL: 8系
Tips
- 事象
- 以下のようにscheduled_atをTime.zoneで更新をかけたところ、保存されている値が
scheduled_at
= '2024-04-03 03:00:00
とUTC時刻で保存されてしまっていた。 scheduled_at
は DATETIME型
- 以下のようにscheduled_atをTime.zoneで更新をかけたところ、保存されている値が
scheduled_time = if Time.zone.now >= Time.zone.now.beginning_of_day + 12.hours + 1.minute Time.zone.tomorrow.beginning_of_day + 12.hours else Time.zone.today.beginning_of_day + 12.hours end hoge.update!(scheduled_at: scheduled_time)
Rails側の確認
irb(main):001:0> Time.now => 2024-04-04 02:56:00.07041709 +0000 irb(main):002:0> Time.current => Thu, 04 Apr 2024 11:56:11.926433860 JST +09:00 irb(main):003:0> Time.zone.now => Thu, 04 Apr 2024 11:56:26.882822349 JST +09:00
上記のことから、RailsのタイムゾーンはJSTになっていることがわかる。 Time.nowだけUTC時刻になっているのはシステムのゾーンがUTCのママだからだとわかる。
補足 ` config/application.rb に config.time_zone = 'Tokyo' ` を記載していても、Time.nowには反映されません。 なぜなら、TimeはRubyの組み込みのクラスなので、Railsの設定の影響を受けないためです。 Rubyのタイムゾーンは以下の2つによって決まります。 ■ システムのタイムゾーン ■ 環境変数 ENV['TZ'] の値 環境変数が設定されていればそちらが優先されます。 環境変数が設定されていなければシステムのタイムゾーンが使われます。 例 Asia/Tokyo US/Central ⚠️ 無効なタイムゾーン('Tokyo'や'Hoge'など)が設定されている場合は ** 特にエラーにならず、世界標準時(UTC)がデフォルトになる ** そうなので注意が必要(システムのタイムゾーン設定にもなりません)
- config.active_record.default_timezoneの確認
- されていなかった。。。
- Railsガイドによると、
デフォルト値は:utcです。
とのこと。 Rails アプリケーションの設定項目 - Railsガイド - つまり、RailsアプリではタイムゾーンはJSTだが、DBへの書き込み時は
UTC
で行われていたということになる。
MySQLのタイムゾーンの設定を確認
- 以下のコマンドを実行すると、system_time_zoneがUTCになっていた。
SHOW VARIABLES LIKE '%time_zone%';
補足 ■システムタイムゾーン ホストマシンのタイムゾーンです。 サーバ起動時にホストマシンのタイムゾーンを特定して、system_time_zone システム変数に設定されます。 ■サーバタイムゾーン MySQLサーバの現在のタイムゾーンです。 time_zone システム変数に設定され、現在動作しているタイムゾーンを示します。time_zone の初期値は 'SYSTEM' となっており、サーバーのタイムゾーンがシステムタイムゾーンと同じであることを示します。
まとめ
これらを踏まえると、Rails側の設定の問題でDBの保存処理実行時にUTC時間に変換されてしまっている様子。 MySQLの設定は関係ない。。。?
config.active_record.default_timezoneの設定はDBを読み書きする際に、DBに記録されている時間をTime.utcで読むかTime.localで読むかを設定する。 :utcの場合DBに記録されている時間はUTC扱いで、この時DBサーバのタイムゾーン設定は考慮しない。
ActiveRecordのインスタンスが持っているTimeWithZoneの値をUTCに変換し、その時刻をDBに書き込む。 :localの場合は、DBに記録されている時間はシステムのタイムゾーンとして扱う。
ActiveRecordのインスタンスが持っているTimeWithZoneの値をシステムのタイムゾーンに変換し、その時刻をDBに書き込む。
参考
RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い #Ruby - Qiita
Rails アプリケーションの設定項目 - Railsガイド
Railsと周辺のTimeZone設定を整理する (active_record.default_timezoneの罠) #Ruby - Qiita