at.
at.

Reputation: 52560

rounding a Rails DateTime to the nearest 15 minute interval

I need to round a DateTime and also a Time to the nearest 15 minute interval. My thought is to zero out the seconds and the milliseconds (do those exist in a DateTime or Time?) and maybe even nanoseconds? And then divide the number of minutes by 15, round that, then multiply the result by 15 and set that to be the minutes:

# zero out the seconds
time -= time.sec.seconds
# zero out the milliseconds (does that exist?)
# zero out the nanoseconds (less likely this exists)

minutes_should_be = (time.min / 15.to_f).round * 15
time += (minutes_should_be - time.min).minutes

So I guess my question is if there is a better way to do this and if milliseconds and nanoseconds exist in a DateTime or Time? There is a nsec method for nanoseconds, but I think that's the total nanoseconds since epoch.

Upvotes: 4

Views: 6476

Answers (3)

mahemoff
mahemoff

Reputation: 46489

A generic rounding solution for DateTime, based on Tessi's answer:

class DateTime

  def round(granularity=1.hour)
    Time.at((self.to_time.to_i/granularity).round * granularity).to_datetime
  end

end

Example usage:

DateTime.now.round 15.minutes
> Fri, 15 May 2015 11:15:00 +0100

Upvotes: 4

tessi
tessi

Reputation: 13574

The following should do the trick:

##
# rounds a Time or DateTime to the neares 15 minutes
def round_to_15_minutes(t)
  rounded = Time.at((t.to_time.to_i / 900.0).round * 900)
  t.is_a?(DateTime) ? rounded.to_datetime : rounded
end

The function converts the input to a Time object, which can be converted to the seconds since the epoch with to_i (this automatically strips nano-/milliseconds). Then we divide by 15 minutes (900 seconds) and round the resulting float. This automatically rounds the time to the nearest 15 minutes. Now, we just need to multiply the result by 15 minutes and convert it to a (date)time again.

Example values:

round_to_15_minutes Time.new(2013, 9, 13, 0, 7, 0, "+02:00")
#=> 2013-09-13 00:00:00 +0200
round_to_15_minutes Time.new(2013, 9, 13, 0, 8, 0, "+02:00")
#=> 2013-09-13 00:15:00 +0200
round_to_15_minutes Time.new(2013, 9, 13, 0, 22, 29, "+02:00")
#=> 2013-09-13 00:15:00 +0200
round_to_15_minutes Time.new(2013, 9, 13, 0, 22, 30, "+02:00")
#=> 2013-09-13 00:30:00 +0200
round_to_15_minutes DateTime.now
#=> #<DateTime: 2013-09-13T01:00:00+02:00 ((2456548j,82800s,0n),+7200s,2299161j)>

Upvotes: 8

maček
maček

Reputation: 77786

I think this will work

def nearest15 minutes
  ((minutes / 60.0 * 4).round / 4.0 * 60).to_i
end

The idea, is

  • get your minutes in terms hours (decimal)
  • round to the nearest quarter
  • convert back to minutes

Some sample output

10.times do
  n = [*1..200].sample
  puts "%d => %d" % [n, nearest15(n)]
end

Output

85 => 90
179 => 180
54 => 60
137 => 135
104 => 105
55 => 60
183 => 180
184 => 180
46 => 45
92 => 90

Upvotes: 2

Related Questions