RailsのTime.zone.nowとDateを比較するとfalseになる原因と解決法
この記事で伝えたいこと
Railsで日時比較をするときには日付の型を確認しようと言う話をします。
日時に応じて挙動が変わる処理の実装
WEBアプリケーションを開発していると、特定期間のみキャンペーンのバナーを表示させたいというような、期間などの時刻で動作する機能を開発する必要があります。 かつての自分は「その時間になったら手動でデプロイするのかな」と思っていたこともあったのですが、コードの中に時間による分岐を入れれば自動で機能がアクティブになることを教えてもらいました。
そんな自分が書いたコードが下記のような分岐です。
Time.zone.now.between?(Date.new(2022, 12, 01), Date.new(2022, 12, 31))
しかし、2022年12月1日0:00(JST)になってもこれではうまく動作しませんでした。
[2] pry(main)> Time.zone.now.between?(Date.new(2022, 12, 01), Date.new(2022, 12, 31))
=> false
RubyとRailsぞれぞれの日時の型
どうしてこうなるのか少し調べてみると、RailsやRubyにはそれぞれたくさんの日時の型があることがわかりました。
Railsアプリケーションでは「システムまたは環境変数に設定されたタイムゾーン」と「application.rbに設定されたタイムゾーン」の2種類があります。 どちらも同じ設定になっている場合は問題が起きることは少ないですが、設定が異なっていると予期せぬ結果になる場合があります。

RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い - Qiita
2021.2.11追記:DateTimeクラスは非推奨なクラスになりました DateTimeクラスは非推奨なクラスとなり、DateTimeクラスではなくTimeクラスを使うよう、公式にアナウンスされました。 参考1 But we consider use of Date...
冒頭の処理で使ったTime.zone.nowとDateの型をそれぞれ見てみると異なる型を使っていました。
[1] pry(main)> Time.zone.class
=> ActiveSupport::TimeZone
[2] pry(main)> Date.new(2022,12,01).class
=> Date
型を揃えた分岐にすることで解消
比較する型をどちらかにそろえれば解消できました。
Time.zone.localに合わせる
Time.zone.nowに合わせてActiveSupport::TimeWithZoneを使うようにすれば解消できました。
[1] pry(main)> Time.zone.now.between?(Time.zone.local(2022, 12, 01), Time.zone.local(2022, 12, 31)
=> true
[2] pry(main)> Time.zone.local(2022, 12, 01).class
=> ActiveSupport::TimeWithZone
Dateに合わせる
またはDateに合わせる方法もあります。
[1] pry(main)> Time.zone.today.between?(Date.new(2022, 12, 01), Date.new(2022, 12, 31))
=> true
[2] pry(main)> Time.zone.today.class
=> Date
こうしてどちらかに揃えたことで意図するシステムの動作を担保することができました。
感想
今回はタイムゾーンがDate型に反映されなかったためエラーになってしまったようですが、どこでその分岐が起きたのかはまだ調査中になります。
仮説としてRailsのTimeWithZoneでのbetweenメソッド内部でutcによる比較になっている可能性がありそうでした。
rails/activesupport/lib/active_support/time_with_zone.rb at 8015c2c2cf5c8718449677570f372ceb01318a32 · rails/rails
Ruby on Rails. Contribute to rails/rails development by creating an account on GitHub.
ちょっとこのあたりだいぶ複雑でまだまだ理解ができていないと感じています。 もしもこのあたりご知見があるかたいらっしゃればコメントいただけると幸いですmm
参考

#Ruby と #Rails の Time や Time.zone の扱いががマジで分からないし混沌としてるのでちょっとだけ整理したい。 - Qiita
Try at この時間に試しました JST 2019-11-28 hour 19 UTC 2019-11-28 hour 10 For Ruby require ‘active_support/core_ext’ No Time zone setting タイムゾ...
記事の更新をメールで受け取る
質問・リクエストを送る
記事についての質問や、取り上げてほしいテーマがあればお気軽にどうぞ。いただいた質問はブログ記事として回答し、Q&Aページで公開することがあります。