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.
source to share
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'
source to share
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)
source to share
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'
source to share
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"
source to share