Skip to content

range of Time has trouble seeing if Date object is included #2588

Closed
pivotalcommon opened this Issue Aug 19, 2011 · 24 comments
@pivotalcommon

rails 3.0.9

Example:

range = 10.day.ago..2.days.ago
range.include?(Date.today) #should be false

We get an infinite loop of the following stacktrace:

./gems/activesupport-3.0.9/lib/active_support/time_with_zone.rb:322: warning: Time#succ is obsolete; use time + 1
./gems/activesupport-3.0.9/lib/active_support/core_ext/object/acts_like.rb:8:in `call'
./gems/activesupport-3.0.9/lib/active_support/core_ext/object/acts_like.rb:8:in `acts_like?'
./gems/activesupport-3.0.9/lib/active_support/core_ext/time/calculations.rb:278:in `compare_with_coercion'
./gems/activesupport-3.0.9/lib/active_support/time_with_zone.rb:183:in `<=>'
./gems/activesupport-3.0.9/lib/active_support/core_ext/range/include_range.rb:16:in `each'
./gems/activesupport-3.0.9/lib/active_support/core_ext/range/include_range.rb:16:in `include?'
./gems/activesupport-3.0.9/lib/active_support/core_ext/range/include_range.rb:16:in `include?'
./gems/activesupport-3.0.9/lib/active_support/core_ext/range/include_range.rb:16:in `include_with_range?'

Are we to expect the a Time range includes a Date?

NOTE: We are using home_run, but we were able to verify that the same error occurs with and without home_run.

@tekin
tekin commented Nov 14, 2011

This also bit me today. What's the best way to write a test that catches an infinite loop?

@tekin
tekin commented Nov 14, 2011

LOL.

Having looked into this, I'm not sure it is actually infinite looping - it's just checking every second between 10 days and 2 days ago incrementally, which isn't a particularly efficient manner in which to test if a date is in a range. The obvious solution is to build your range with Dates rather than TimeWithZones, that'll give you what you want and not take forever.

@tekin
tekin commented Nov 15, 2011

This does bring up the issue of what to do about Time ranges though - Time#succ is now obsolete in Rails 1.9.2+ and Range relies on the #succ to step through a range.

@ahawkins
range = 10.day.ago.to_i..2.days.ago.to_i
range.include?(Date.today.to) #will be false

I think this can be closed now.

@steveklabnik
Ruby on Rails member

@twinturbo without the .to_i, it does still blow up.

@ahawkins

@steveklabnik it should work using to_i. Are you saying it doesn't?

@steveklabnik
Ruby on Rails member

With the to_i: no crash. Without it: crash.

@ahawkins

So the question is: what should we do? Should we patch Date/Time/DateTime ranges to use .to_i for include? or what?

cc @rafaelfranca

@rafaelfranca
Ruby on Rails member

I'll leave this decision to the Rails core cc/ @fxn @jeremy

@jeremy
Ruby on Rails member
jeremy commented Apr 30, 2012

Think this should "just work" without converting to_i or hitting an infinite loops, for sure!

@tekin
tekin commented Apr 30, 2012

I haven't checked recently, but if I recall correctly, the issue was that it took an age to return (giving the appearance of having crashed) if you passed in a Time Range because the underlying code was iterating through every second between the two limits of the range, creating a new Time object each time.

@jeremy
Ruby on Rails member
jeremy commented Apr 30, 2012

Yeah - that's insane ;)

@ahawkins

I don't think there is anyway to fix without changing the way the objects work with ranges. Using to_i seems easy enough IMO. I'd like to see it "just work", but I have no clue how that can happen.

@ozgor
ozgor commented May 7, 2012

using to_i is not ideal. It stops you from using chained method such as 2.days.ago.beginning_of_day..2.days.ago.end_of_day

@ahawkins
ahawkins commented May 7, 2012

@ozgor no it doesn't You chose to when to use #to_i. 2.days.ago.beginning_of_day.to_i...2.days.ago.end_of_day.to_i

@tekin
tekin commented May 7, 2012

@twinturbo all the same, calling #to_i is not really an optimal solution - it relies on user knowledge of this bug rather than it simply "just working".

The question I'm wondering: Is it even valid to call #include? on a Time range? Time is not a discrete unit, but somewhere in the belly of either ActiveSupport, tzinfo or the Standard Lib itself, a time range gets enumerated through if you call #include?, which raises lots of warnings (because Time#succ is deprecated) and ends up taking forever.

I've got around this issue by calling #cover? instead (http://www.ruby-doc.org/core-1.9.3/Range.html#method-i-cover-3F) which seems a more sensible method, but all the same, maybe it should be expected that #include? should just work, even for a TimeWithZone range..

@frodsan
frodsan commented Sep 12, 2012

@jeremy can we close this?

@tomrossi7

I just ran into this today as well with tests that compare date/times to see if something is included. Example:

assert (1.minutes.ago..Time.now).include?(object.created_at)
@parndt
parndt commented Mar 25, 2013

This is a really long running issue with many workarounds and no accepted solution.

Should it remain open as an issue with Rails? If so, what sort of patch are we looking for?

@adamthedeveloper

I've had this come up recently on this line:

gems/activesupport-3.2.13.rc1/lib/active_support/time_with_zone.rb:332: warning: Time#succ is obsolete; use time + 1

Same issue?

@wyaeld
wyaeld commented Jun 12, 2013

yes, its the same issue, not a loop, but its checking for every second in the range. main workarounds seem to be to call to_i or to_date beforehand depending on your comparison.

@rafaelfranca
Ruby on Rails member

See #11463

@pixeltrix
Ruby on Rails member

Note if you want to use Range#cover? to check if a Date is inside a ActiveSupport::TimeWithZone you'll need to convert it to a ActiveSupport::TimeWithZone first, e.g:

# Rails 3.2
>> range = 10.days.ago..10.days.from_now
=> Sun, 07 Jul 2013 04:42:42 BST +01:00..Sat, 27 Jul 2013 04:42:42 BST +01:00
>> range.cover?(Date.current.to_time_in_current_zone)
=> true

# Rails 4.0
>> range = 10.days.ago..10.days.from_now
=> Sun, 07 Jul 2013 04:42:42 BST +01:00..Sat, 27 Jul 2013 04:42:42 BST +01:00
>> range.cover?(Date.current.in_time_zone)
=> true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.