I will construct a method relative_time
that has two arguments:
date_time
an instance of DateTime that specifies a time in the past; and
time_unit
, one of :SECONDS
, :MINUTES
, :HOURS
, :DAYS
, :WEEKS
, :MONTHS
or :YEARS
, specifying the unit of time for which the difference in time between the current time and date_time
is to be expressed.
Code
require 'date'
TIME_UNIT_TO_SECS = { SECONDS:1, MINUTES:60, HOURS:3600, DAYS:24*3600,
WEEKS: 7*24*3600 }
TIME_UNIT_LBLS = { SECONDS:"seconds", MINUTES:"minutes", HOURS:"hours",
DAYS:"days", WEEKS: "weeks", MONTHS:"months",
YEARS: "years" }
def relative_time(date_time, time_unit)
now = DateTime.now
raise ArgumentError, "'date_time' cannot be in the future" if
date_time > now
v = case time_unit
when :SECONDS, :MINUTES, :HOURS, :DAYS, :WEEKS
(now.to_time.to_i-date_time.to_time.to_i)/
TIME_UNIT_TO_SECS[time_unit]
when :MONTHS
0.step.find { |n| (date_time >> n) > now } -1
when :YEARS
0.step.find { |n| (date_time >> 12*n) > now } -1
else
raise ArgumentError, "Invalid value for 'time_unit'"
end
puts "#{v} #{TIME_UNIT_LBLS[time_unit]} ago"
end
Examples
date_time = DateTime.parse("2020-5-20")
relative_time(date_time, :SECONDS)
5870901 seconds ago
relative_time(date_time, :MINUTES)
97848 minutes ago
relative_time(date_time, :HOURS)
1630 hours ago
relative_time(date_time, :DAYS)
67 days ago
relative_time(date_time, :WEEKS)
9 weeks ago
relative_time(date_time, :MONTHS)
2 months ago
relative_time(date_time, :YEARS)
0 years ago
Explanation
If time_unit
equals :SECONDS
, :MINUTES
, :HOURS
, :DAYS
or :WEEKS
I simply compute the number of seconds elapsed between date_time
and the current time, and divide that by the number of seconds per the given unit of time. For example, if time_unit
equals :DAYS
the elapsed time in seconds is divided by 24*3600
, as there are that many seconds per day.
If time_unit
equals :MONTHS
, I use the method Date#>> (which is inherited by DateTime
) to determine the number of months that elapse from date_time
until a time is reached that is after the current time, then subtract 1
.
The calculation is similar if time_unit
equals :YEARS
: determine the number of years that elapse from date_time
until a time is reached that is after the current time, then subtract 1
.
One could require the user to enter a Time instance (rather than a DateTime
instance) as the first argument. That would not simplify the method, however, as the Time
instance would have to be converted to a DateTime
instance when time_unit
equals :MONTH
or :YEAR
, to use the method Date#>>
.