Reputation: 807
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
Reputation: 655
With activesupport:
Time.current.advance(minutes: 1).change(sec: 0, usec: 0)
Upvotes: 1
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
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
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
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
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