日付の範囲判定をしたいときに 範囲.include?(現在日時) で 「TypeError: can't iterate from ActiveSupport::TimeWithZone」エラー

日付の範囲指定では Rails ActiveSupport::TimeWithZone を使いたい。

# 昨日から明日
today = Time.current
date_range = (Time.current.ago(1.day)..Time.current.since(1.day))

# 「昨日から明日」の範囲に現在時刻が含まれるか判定したとき、エラーができる
date_range.include?(today)
=> TypeError: can't iterate from ActiveSupport::TimeWithZone

Timeクラスなら使えるのに・・!

# 昨日から明日
today = Time.new(2019,1,6)
date_range = (Time.new(2019,1,5)..Time.new(2019,1,7))

# 「昨日から明日」の範囲に現在時刻が含まれるか判定したとき、エラーができる
date_range.include?(today)
=> true # エラー出ない

結論

Rails 5.1.0では対応してなかった。5.2.0(or 5.2.2 ?)対応されるようです。
Merge pull request #31081 from rails/allow-include-time-with-zone-range · rails/rails@eae65ac · GitHub

これはコミットログを流し読みされている方の記事から見つけました。流し読みってすごい。
なるようになるブログ

Active Supportの修正です。 Range#include?にActiveSupport::TimeWithZoneのインスタンスを指定出来るよう修正しています。

# before
(1.hour.ago..1.hour.from_now).include?(Time.current)
# => TypeError: can't iterate from ActiveSupport::TimeWithZone

# after
(1.hour.ago..1.hour.from_now).include?(Time.current)
# => true

Ruby 2.3から、Rangeの始点/終点ににTimeオブジェクトを指定出来るようになっており、それならばTimeWithZoneも指定出来ても良いだろう、という事で対応されたようです。

現時点で

5.1で範囲指定の際に「ActiveSupport::TimeWithZone」を使うには

date_range.cover?(today)
today.between?(date_range)

が良さそうです。


また、railsのcommitログを見て1点疑問。commitログにRailsバージョンのタグが貼られていますが、今回のコミットには複数のバージョンタグが貼られています。これは同意味なのかな。

v5.2.2 v5.2.2.rc1 v5.2.1.1 v5.2.1 v5.2.1.rc1 v5.2.0 v5.2.0.rc2 v5.2.0.rc1 v5.2.0.beta2 v5.2.0.beta1

5.2.0で対応したということか、5.2.2で対応したということ。曖昧。。

参考

rubyのrangeオブジェクトのinclude?とcover?の違い - Qiita https://qiita.com/riocampos/items/3b2db385379973859c9b