Display duration in human-readable format, such as "X hours, Y minutes"

I am using Rails 4, Ruby 2.1 with PostgreSQL.

I have a database field called duration, which is the datatype of an interval.

When retrieving data in that column, it is returned in hh: mm: ss format, for example. 1:30:00.

I am trying to find a way to show it like: 1 hour, 30 minutes

Other examples: 02:00:00 to 2:00 02:15:00 to 2:00, 15 minutes 02:01:00 to 2:00, 1 minute

Any advice is appreciated.

+3


source to share


5 answers


I would start with something like this:



def duration_of_interval_in_words(interval)
  hours, minutes, seconds = interval.split(':').map(&:to_i)

  [].tap do |parts|
    parts << "#{hours} hour".pluralize(hours)       unless hours.zero?
    parts << "#{minutes} minute".pluralize(minutes) unless minutes.zero?
    parts << "#{seconds} hour".pluralize(seconds)   unless seconds.zero?
  end.join(', ')
end

duration_of_interval_in_words('02:00:00')
# => '2 hours'

duration_of_interval_in_words('02:01:00')
# => '2 hours, 1 minute'

duration_of_interval_in_words('02:15:00')
# => '2 hours, 15 minutes'

      

+6


source


See also ActionView :: Helpers :: DateHelper distance_of_time_in_words (and related)

eg.

0 <-> 29 secs # => less than a minute
30 secs <-> 1 min, 29 secs # => 1 minute
1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
... etc

      



https://apidock.com/rails/ActionView/Helpers/DateHelper/distance_of_time_in_words

Perhaps there is no need to include model validation in the error? (this is my use case)

0


source


You can try the following method for display, for example:

  • minutes_to_human (45) # 45 minutes
  • minutes_to_human (120) # 2 hours
  • minutes_to_human (75) # 2.5 hours
  • minutes_to_human (75) # 1.15 hours

def minutes_to_human(minutes)
  result = {}

  hours = minutes / 60
  result[:hours] = hours if hours.positive?
  result[:minutes] = ((minutes * 60) - (hours * 60 * 60)) / 60 if minutes % 60 != 0

  result[:minutes] /= 60.0 if result.key?(:hours) && result.key?(:minutes)

  return I18n.t('helper.minutes_to_human.hours_minutes', time: (result[:hours] + result[:minutes]).round(2)) if result.key?(:hours) && result.key?(:minutes)
  return I18n.t('helper.minutes_to_human.hours', count: result[:hours]) if result.key?(:hours)
  return I18n.t('helper.minutes_to_human.minutes', count: result[:minutes].round) if result.key?(:minutes)
  ''
end

      


Translations:

en:
  helper:
    minutes_to_human:
      minutes:
        zero: '%{count} minute'
        one: '%{count} minute'
        other: '%{count} minutes'
      hours:
        one: '%{count} hour'
        other: '%{count} hours'
      hours_minutes: '%{time} hours'

      

0


source


Just use duration

+inspect

seconds = 86400 + 3600 + 15
ActiveSupport::Duration.build(seconds).inspect
=> "1 day, 1 hour, and 15.0 seconds"

      

Or it can be customized a little

ActiveSupport::Duration.build(seconds).parts.map do |key, value|
  [value.to_i, key].join 
end.join(' ')
=> "1days 1hours 15seconds"

      

PS

You can get seconds with

1.day.to_i
=> 86400

      

Time can only be analyzed in ISO8601 format

ActiveSupport::Duration.parse("PT2H15M").inspect
=> "2 hours and 15 minutes"

      

0


source


I find duration.inspect serves the purpose well.

> 10.years.inspect
=> "10 years"

      

-1


source







All Articles