English-friendly timespan in Rails

   By chris on April 4th 2007 in /dev/rails, /dev/ruby | 928 views

Awhile ago I was looking for a function that would convert a number of seconds into a human-readable timespan string. For instance: convert 86400 into 1 day and 172800 into 2 days, accomodating intervals ranging from weeks to minutes.

I didn't find one. Instead, this is what I came up with this, which at its fanciest will output something like 2 days, 14 hours and 15 mins.

RUBY:
  1. # Takes a period of time in seconds and returns it in human-readable form (down to minutes)
  2. def time_period_to_s time_period       
  3.   out_str = ''
  4.      
  5.   interval_array = [ [:weeks, 604800], [:days, 86400], [:hours, 3600], [:mins, 60] ]
  6.   interval_array.each do |sub|
  7.     if time_period>= sub[1] then
  8.       time_val, time_period = time_period.divmod( sub[1] )
  9.          
  10.       time_val == 1 ? name = sub[0].to_s.singularize : name = sub[0].to_s
  11.            
  12.       ( sub[0] != :mins ? out_str += ", " : out_str += " and " ) if out_str != ''
  13.       out_str += time_val.to_s + " #{name}"
  14.     end
  15.   end
  16.  
  17.   return out_str 
  18. end

Obviously it's hard-coded only to support english and ignores seconds (too fine-grained for me) but over-coming either of those limitations should be fairly straight-forward.

Update: I terminated the upper value at weeks because things get complicated when we get to months. We can all agree that a day is 24 hours, a week is 7 days, but how long is a month? Not four weeks, not five weeks.... Predictability dies at months, where the actual timespan becomes relevant, and that's more work than is worthwhile at this point. Feel free to embrace and extend :)

2 Responses to “English-friendly timespan in Rails”

  1. sam responded on 04 Apr 2007 at 8:41 pm #

    I hacked something like this up once, too, when I wanted to print event durations in something other than seconds.

    http://vpim.rubyforge.org/

    see Vpim::Duration

    looking at the code now, its totally brute force :-) obviously done in a hurry and never looked at again.

  2. chris responded on 04 Apr 2007 at 9:16 pm #

    Your as_str() method looks a bit like my first pass at this. The switch case approach - unwieldiness aside - made it a bit tricky to do things like have “and” if minutes were at the end (too many ugly conditions), or add localization (something planned for this version at some point In The Future).

    Note that the use of singularize() means this only works in Rails and not with stock Ruby (as singularize() is part of ActiveSupport::CoreExtensions). A bit more hackery would be required to make it portable.

Trackback URI | Comments RSS

Leave a Reply