Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate over Ruby Time object with delta

Tags:

ruby

Is there a way to iterate over a Time range in Ruby, and set the delta?

Here is an idea of what I would like to do:

for hour in (start_time..end_time, hour)
    hour #=> Time object set to hour
end

You can iterate over the Time objects, but it returns every second between the two. What I really need is a way to set the offset or delta (such as minute, hour, etc.)

Is this built in to Ruby, or is there a decent plugin available?

like image 753
Josiah I. Avatar asked Feb 01 '09 17:02

Josiah I.


3 Answers

Prior to 1.9, you could use Range#step:

(start_time..end_time).step(3600) do |hour|
  # ...
end

However, this strategy is quite slow since it would call Time#succ 3600 times. Instead, as pointed out by dolzenko in his answer, a more efficient solution is to use a simple loop:

hour = start_time
while hour < end_time
  # ...
  hour += 3600
end

If you're using Rails you can replace 3600 with 1.hour, which is significantly more readable.

like image 92
Zach Langley Avatar answered Nov 20 '22 09:11

Zach Langley


If your start_time and end_time are actually instances of Time class then the solution with using the Range#step would be extremely inefficient since it would iterate over every second in this range with Time#succ. If you convert your times to integers the simple addition will be used but this way you will end up with something like:

(start_time.to_i..end_time.to_i).step(3600) do |hour|
  hour = Time.at(hour)     
  # ...
end

But this also can be done with simpler and more efficient (i.e. without all the type conversions) loop:

hour = start_time
begin
  # ...      
end while (hour += 3600) < end_time
like image 40
dolzenko Avatar answered Nov 20 '22 07:11

dolzenko


Range#step method is very slow in this case. Use begin..end while, as dolzenko posted here.

You can define a new method:

  def time_iterate(start_time, end_time, step, &block)
    begin
      yield(start_time)
    end while (start_time += step) <= end_time
  end

then,

start_time = Time.parse("2010/1/1")
end_time = Time.parse("2010/1/31")
time_iterate(start_time, end_time, 1.hour) do |t|
  puts t
end

if in rails.

like image 18
Xenofex Avatar answered Nov 20 '22 09:11

Xenofex