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

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)

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.


I thought I would post another solution that provides rounding up and down to the nearest number of seconds given. Oh, and this does not change the time zone like some of the other solutions.

class Time
  def round(sec=1)
    down = self - (self.to_i % sec)
    up = down + sec

    difference_down = self - down
    difference_up = up - self

    if (difference_down < difference_up)
      return down
    else
      return up
    end
  end
end

t = Time.now                             # => Mon Nov 15 10:18:29 +0200 2010
t.round(15.minutes)                      # => Mon Nov 15 10:15:00 +0200 2010
t.round(20.minutes)                      # => Mon Nov 15 10:20:00 +0200 2010
t.round(60.minutes)                      # => Mon Nov 15 10:00:00 +0200 2010

ActiveSupport was used in the examples for the x.minutes feature. You can use 15 * 60 instead.

Methods floor and ceil can be easily implemented based on this solution.