4

How would I be able to get the size or count of a range made up of Time objects?

Something that would achieve the same result as my pseudo Ruby code, which doesn't work:

((Time.now.end_of_day - 31.days)..(Time.now.end_of_day - 1.day)).size == 30

currently doing the above gives an error:

NoMethodError: undefined method `size' for 2012-05-18 23:59:59 -0400..2012-06-17 23:59:59 -0400:Range

and trying to turn it into array (range).to_a :

can't iterate from Time

update

Interesting, Just tried to do

((Date.today.end_of_day - 31.days)..(Date.today.end_of_day - 1.day)).count

Users/.../gems/ruby/1.9.1/gems/activesupport-3.0.15/lib/active_support/time_with_zone.rb:322: warning: Time#succ is obsolete; use time + 1

However

((Date.today - 31.days)..(Date.today - 1.day)).count == 31

I would be willing to settle for that?

Also ((Date.today - 31.days)..(Date.yesterday)).count == 31

update 2

On the other hand, taking Mu's hint we can do:

(((Time.now.end_of_day - 31.days)..(Time.now.end_of_day - 1.day)).first.to_date..((Time.now.end_of_day - 31.days)..(Time.now.end_of_day - 1.day)).last.to_date).count == 31

Victor S
  • 5,098
  • 5
  • 44
  • 62
  • Exactly WHAT do you intend to achieve? The `.size` in your code points that you want the number of elements the range would give, but you suggest iterating. – André Medeiros Jun 18 '12 at 21:59
  • I don't suggest iterating, the ruby error suggest so, I would like to get the size/count as I mention. Someone seems to have changed the title of my post... – Victor S Jun 18 '12 at 22:00
  • Also, you are right, the above code woud give the error `NoMethodError: undefined method `size' for 2012-05-18 23:59:59 -0400..2012-06-17 23:59:59 -0400:Range`, what I did earlier is (range).to_a which gives the previously mentioned error, I will update the question. – Victor S Jun 18 '12 at 22:06
  • The probably easiest way is to subtract them, as pointed by Andrew Marshall. – André Medeiros Jun 18 '12 at 22:11
  • Similar to http://stackoverflow.com/questions/501253/iterate-over-ruby-time-object-with-delta – Justin Jun 18 '12 at 22:13
  • `Date` objects only represent whole days, so `end_of_day` is meaningless. That's why `31.days` (a Rails/ActiveSupport addition that converts to number of seconds) doesn't make any sense with dates either. – Lars Haugseth Jun 18 '12 at 22:30

3 Answers3

5

There's no such method as Range#size, try Range#count (as suggested by Andrew Marshall), though it still won't work for a range of Time objects.

If you want to perform number-of-days computations, you're better off using Date objects, either by instantiating them directly (Date.today - 31, for example), or by calling #to_date on your Time objects.

Date objects can be used for iteration too:

((Date.today - 2)..(Date.today)).to_a
=> [#<Date: 2012-06-17 ((2456096j,0s,0n),+0s,2299161j)>,
 #<Date: 2012-06-18 ((2456097j,0s,0n),+0s,2299161j)>,
 #<Date: 2012-06-19 ((2456098j,0s,0n),+0s,2299161j)>]

((Date.today - 2)..(Date.today)).map(&:to_s)
=> ["2012-06-17", "2012-06-18", "2012-06-19"]
Lars Haugseth
  • 14,721
  • 2
  • 45
  • 49
  • 1
    There is `Range#count`, though. It fails with the same error as `to_a` in the question. (I suppose a range doesn't have a "size" because it doesn't contain actual objects in a meaningful way, but it does have a "count" of the number of objects within the range it represents.) – Andrew Marshall Jun 18 '12 at 22:14
4

It's because a size for a date range doesn't make sense—it doesn't know if you want to view it as days, minutes, seconds, months, or something else. The reason the error mentions iterating is that in order to determine the size of the range, it must know how to iterate over them so that it may count the number of elements.

Since what you want is the difference in days, just do that:

date_one = Time.now.end_of_day - 31.days
date_two = Time.now.end_of_day - 1.day

((date_one - date_two) / 1.day).abs
#=> 30.0

You must divide by 1.day since a difference of Times returns seconds.

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
  • `irb(main):016:0> Time.now - Time.now+50 => 49.999998633` `irb(main):017:0> Time.now - Time.now+40` `=> 39.999998196` `irb(main):018:0>` Works fine for me, event without the /(1.day). – André Medeiros Jun 18 '12 at 22:08
  • Thanks, but I would like to get the number from the range, not rewrite the code, if possible. Just imagine that you only have to work with the range. – Victor S Jun 18 '12 at 22:10
  • @VictorS That is not possible for exactly the reasons I outlined. You *must* rewrite the code or change the endpoints of the range to something other than `Time`s. – Andrew Marshall Jun 18 '12 at 22:11
0

To have any chance of your code working you should wrap everything before .size in parentheses.

Instead of using a range, maybe you can just subtract one time object from another?

I know you make ranges out of Date objects so you could convert to that.

David Grayson
  • 84,103
  • 24
  • 152
  • 189
  • thanks, I assign it to a variable in my code, but for this example you are right, I have reformatted it. However, I don't think it makes a different practically... – Victor S Jun 18 '12 at 21:59