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

blog comments powered by Disqus