This article was written 10 seconds ago
You’ve undoubtedly used programs where the timing of something is described as “{integer} {units} ago”. (Apple Mail, Reddit, uh… HeavyInk) In these programs, the integer counts up and the units become larger as time passes.
Getting to the point, I’ve seen a few implementations of this. However,
they all involve lots of silly copy/paste type code in if or
case constructs. So, for something I was working on over at HeavyInk I implemented my own variation of
this, without the drivel.
TIME_UNITS = %w(year month week day hour minute)
def time_ago(time)
diff = Time.now - time
TIME_UNITS.select{ |unit|
diff > 1.send(unit) ? units_ago(unit,diff) : nil
} || "Less than a minute ago"
end
def units_ago(unit,seconds)
in_units = seconds / 1.send(unit)
"#{in_units} #{in_units != 1 ? unit.to_s.pluralize : unit} ago"
end
This was a little bit strange looking to me when I first came back to it,
so I think an explanation is in order. First off, TIME_UNITS
contains all of the units that we’re interested in, ordered from largest to
smallest. We start off by passing a Time object to
time_ago.
time_ago(10.minutes.ago) #=> "10 minutes ago"
(Yeah, I know its a pretty silly example.) How it works becomes fairly obvious. Iterate through each of the time units, and find the first one we’ve exceeded at least one of. Divide our number of seconds of difference by the number of seconds in whatever unit we’ve chosen. (In this case, minutes, so 60.) So then we have our unit and the number of those units that we have.
Simple, and in my opinion, pretty elegant. Certainly better than copy/pasting lots of lines of code