tags:

views:

1280

answers:

7

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)
A: 

You could do:

Time.at(t.to_i/(15*60)*(15*60))
Shalmanese
+3  A: 

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.

cmsjr
A: 

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
Kent Fredric
He did say round *down*, so his implementation would be correct.
dancavallaro
+10  A: 

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 and floor methods to the Time class. The added benefit is that you can more easily round by any time partition.

require 'rubygems'
require 'activesupport'

class Time
  def round(seconds = 60)
    Time.at((self.to_f / seconds).round * seconds)
  end

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

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

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

Ryan McGeary
+1  A: 

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
Chuck
+2  A: 

Ask the Rails core team to implement the method

time.at_beginning_of_last_fifteen_minutes

It's the kind of thing they go for.

Daniel Lucraft
lol. TimeWithZone#at_beginning_of_last_<xx>_minutes would be more general, where <xx> may be any integer divisor of 60 (other values to be defined). It lets them get more creative with method_missing. I'm slightly concerned to find that I'm only about 80% joking, now that I think about it...
Mike Woodhouse
A: 
# this is an extension of Ryan McGeary's solution, specifically for Rails.
# Note the use of utc, which is necessary to keep Rails time zone stuff happy.
# put this in config/initializers/time_extensions

require 'rubygems'
require 'active_support'

module TimeExtensions
  %w[ round floor ceil ].each do |_method|
    define_method _method do |*args|
      seconds = args.first || 60
      Time.at((self.to_f / seconds).send(_method) * seconds).utc
    end
  end
end

Time.send :include, TimeExtensions
David Lowenfels