How to round a time down to the nearest 15 minutes in Ruby?

Viewed 33979

Is there an easy way to round a Time down to the nearest 15 minutes?

This is what I'm currently doing. Is there an easier way to do it?

t = Time.new
rounded_t = Time.local(t.year, t.month, t.day, t.hour, t.min/15*15)
13 Answers

You said "round down", so I'm not sure if you're actually looking for the round or the floor, but here's the code to do both. I think something like this reads really well if you add round_off and floor methods to the Time class. The added benefit is that you can more easily round by any time partition.

require 'active_support/core_ext/numeric' # from gem 'activesupport'

class Time
  # Time#round already exists with different meaning in Ruby 1.9
  def round_off(seconds = 60)
    Time.at((self.to_f / seconds).round * seconds).utc
  end

  def floor(seconds = 60)
    Time.at((self.to_f / seconds).floor * seconds).utc
  end
end

t = Time.now                    # => Thu Jan 15 21:26:36 -0500 2009
t.round_off(15.minutes)         # => Thu Jan 15 21:30:00 -0500 2009
t.floor(15.minutes)             # => Thu Jan 15 21:15:00 -0500 2009

Note: ActiveSupport was only necessary for the pretty 15.minutes argument. If you don't want that dependency, use 15 * 60 instead.

I am not very familiar with the syntax of ruby but you can round down to the nearest 15 minutes using modulo. (i.e. x - (x modulo 15)). I would guess the syntax would be something like

t.min - ( t.min % 15)

This will make your set of possible values 0, 15, 30, and 45. Assuming 0 <= t.min <= 59.

Since Ruby allows arithmetic (in seconds) on Times, you can just do this:

t = Time.new
rounded_t = t-t.sec-t.min%15*60

You could do:

Time.at(t.to_i/(15*60)*(15*60))

Chuck's answer, while elegant, will run you into trouble if you try to compare values derived in this way; the usecs are not zeroed out.

Shalmanese' answer takes care of that, or Chuck's can be modified as:

t = Time.new
truncated_t = Time.at(t.to_i - t.sec - t.min % 15 * 60)

Your current evaluation using

min / 15 * 15 

is only truncating the min, so

15 => 15
16 => 15
..
29 => 15
30 => 30 

Which is not 'rounding'.

You can approximate rounding in a bad-way with

(( min + 7.5 ) / 15).to_i * 15 

Or, using internals:

( min.to_f / 15 ).round * 15

If you're using ActiveSupport, then you can use some of its time helpers.

t = Time.now
t.at_beginning_of_hour + (t.min - t.min % 15).minutes
Related