TFrazee
TFrazee

Reputation: 807

Ruby - Get time at start of next minute

I'm looking for a concise way to get a Ruby Time object representing the top of the next minute (and hour/day/month/year, if possible). I want this to work in a pure Ruby environment, so the Rails function Time.change or similar doesn't fit the bill.

At first this seems simple - just add 1 to Time.now, but there are edge cases where if, for example, you try to instantiate a Time object with Time.now.min + 1 when the current minute is 59, you get an ArgumentError: min out of range. This goes for hour, day, and month as well.

I have some lengthy code that does the job. It's ugly, but I'm just experimenting:

def add_minute

    now = Time.local
    year = now.year
    month = now.month
    day = now.day
    hour = now.hour
    min = now.min

    if now.min == 59
        if now.hour == 23
            if now.day == Date.civil(now.year, now.month, -1).day
                if month == 12
                    year = year + 1
                    month = 1
                    day = 1
                    hour = 0
                    min = 0
                else
                    month = now.month + 1
                    day = 1
                    hour = 0
                    min = 0
                end
            else
                day = now.day + 1
                hour = 0
                min = 0
            end
        else
            hour = now.hour + 1
            min = 0
        end
    else
        min = now.min + 1
    end

    Time.local year, month, day, hour, min, 0
end

This seems absurdly verbose for what seems like it should be a simple or built-in task, but I haven't found a native Ruby solution. Does one exist?

Upvotes: 2

Views: 2745

Answers (6)

silva96
silva96

Reputation: 655

With activesupport:

Time.current.advance(minutes: 1).change(sec: 0, usec: 0)

Upvotes: 1

Bobby Brown
Bobby Brown

Reputation: 11

We can make this calculation easier to understand by getting the current number of seconds we are through the day. (optional)

DateTime.current.to_i gives us the number of seconds since 1970 DateTime.current.to_i - DateTime.current.beginning_of_day.to_i gives us the number of seconds since the start of the day.

(((number_of_seconds_through_the_day + 60)/60) * 60) gives us the number of seconds we will be at when the next minute starts

Then we subtract the two to give us the number of seconds until the top of the next minute.

If we want the exact time at start of the next minute then we can do:

DateTime.current + seconds_until_start_of_the_next_minute.seconds

def seconds_until_start_of_the_next_minute
  number_of_seconds_through_the_day = DateTime.current.to_i - DateTime.current.beginning_of_day.to_i
  number_of_seconds_through_the_day_at_next_minute = (((number_of_seconds_through_the_day + 60)/60) * 60)
  seconds_until_next_minute_starts = number_of_seconds_through_the_day_at_next_minute - number_of_seconds_through_the_day
  return seconds_until_next_minute_starts
end

Upvotes: 0

iGian
iGian

Reputation: 11193

Other option, given one second to midnight:

require 'time'
now = Time.strptime('2018-12-31 23:59:59', '%Y-%m-%d %H:%M:%S')

Within one minute:

Time.at(now + 60) #=> 2019-01-01 00:00:59 +0100
Time.at(now + 60 - now.sec) #=> 2019-01-01 00:00:00 +0100

You get: # HAPPY NEW YEAR!

Upvotes: 2

mannykary
mannykary

Reputation: 832

You could convert the Time object to UNIX epoch time (seconds since 1970) using #to_i, add 60 s, and then convert back to a Time object.

time_unix = Time.now.to_i
time_unix_one_min_later = time_unix + 60
time_one_min_later = t = Time.at(time_unix_one_min_later)
time_one_min_later_rounded_down = Time.new(t.year, t.month, t.day, t.hour, t.min)

EDIT: Even shorter - you can just add integer seconds to Time.now directly:

time_one_min_later = t = Time.now + 60
time_one_min_later_rounded_down = Time.new(t.year, t.month, t.day, t.hour, t.min)

EDIT 2: One-liner - just subtract Time.now.sec:

time_one_min_later_rounded_down = Time.now + 60 - Time.now.sec

Upvotes: 4

Marcin Kołodziej
Marcin Kołodziej

Reputation: 5313

Not that clean without ActiveSupport:

new_date = (DateTime.now + 1.to_f / (60*24))
DateTime.new(new_date.year, new_date.month, new_date.day, new_date.hour, new_date.minute)

Upvotes: 0

steenslag
steenslag

Reputation: 80065

Ruby has built in methods for adding months (>>) and days (+). A year is 12 months, and an hour is 1/24th of a day.

require 'date'

def add_time(time, year: 0 ,month: 0, day: 0, hour: 0, minute: 0)
  time >>= 12*year
  time >>= month
  time +=  day
  time +=  Rational(hour,24)       # or (hour/24.0) if you dislike rationals
  time +=  Rational(minute, 24*60) # (minute/24.0*60) if you dislike rationals
end

p t = DateTime.now
p add_time(t, year: 1, minute: 30)

Upvotes: 1

Related Questions