ma11hew28
ma11hew28

Reputation: 126329

Ruby: Convert seconds to natural language

How can we convert a wait-time in seconds into a string that naturally expresses the wait-time to a human?

Example Input & Output

Upvotes: 1

Views: 223

Answers (1)

Cary Swoveland
Cary Swoveland

Reputation: 110675

Code

require 'time'

FMT = [[s =  60,         "%-S seconds",                   :NO_ROUND],
       [s *= 60,         "%-M minutes",                   :ROUND_MINS],
       [s *= 24,         "until %-l:%M %p today",         :ROUND_UP_MINS_15],
       [s *   7,         "until %A %-l:%M %p",            :ROUND_UP_MINS_15],
       [Float::INFINITY, "until %-_m/%d/%y at %-l:%M %p", :ROUND_UP_MINS_15]]

def fmt_duration(start_time = Date.today.to_time, elapsed_seconds)
  secs = start_time.to_i + elapsed_seconds
  _, fmt, round = FMT.find { |max_secs_plus_1, *_| elapsed_seconds < max_secs_plus_1 }
  rnd_secs =
  case round
  when :NO_ROUND
    secs
  when :ROUND_MINS
    mins = (secs/60.0).round
    mins -= 1 if mins % 60 == 0
    60 * mins
  when :ROUND_UP_MINS_15
    900 * (secs/900.0).ceil
  end
  Time.at(rnd_secs).strftime(fmt)
end

Note that

puts "#{FMT}"
  #=> [[60,       "%-S seconds",                   :NO_ROUND],
  #    [3600,     "%-M minutes",                   :ROUND_MINS],
  #    [86400,    "until %-l:%M %p today",         :ROUND_UP_MINS_15],
  #    [604800,   "until %A %-l:%M %p",            :ROUND_UP_MINS_15],
  #    [Infinity, "until %-_m/%d/%y at %-l:%M %p", :ROUND_UP_MINS_15]]

See Time#strftime for a listing of Date and Time formatting codes in the second column of FMT.

Examples

def time_to_secs(days, hours, minutes, seconds)
  seconds + 60 * (minutes + 60 * (hours + 24 * days))
end

secs = time_to_secs( 0,  0,  0, 37) #=> 37
fmt_duration(secs)                  #=> "37 seconds"

secs = time_to_secs( 0,  0, 41, 37) #=> 2_497
fmt_duration(secs)                  #=> "42 minutes"

secs = time_to_secs( 0, 13, 41, 37) #=> 49_297
fmt_duration(secs)                  #=> "until 1:45 PM today"

secs = time_to_secs( 6, 23, 36, 37) #=> 603_397
fmt_duration(secs)                  #=> "until Tuesday 11:45 PM"

secs = time_to_secs(24, 13, 41, 37) #=> 2_122_897
fmt_duration(secs)                  #=> "until 7/22/17 at 1:45 PM"

Upvotes: 1

Related Questions