Currently, there is no direct way to format ActiveSupport::Duration objects in Rails
. You'd have to resort to using the #inspect, which provides limited configuration options. Also, the locale support in the #inspect
was removed around Rails 5.1
.
You could do something like the following to format an ActiveSupport::Duration
object by hand in a view helper:
def duration_as_sentence(duration)
parts = duration.parts
units = [:days, :hours, :minutes]
map = {
:days => { :one => :d },
:hours => { :one => :h, :other => :hrs },
:minutes => { :one => :m, :other => :mins }
}
parts.
sort_by { |unit, _| units.index(unit) }.
map { |unit, val| "#{val} #{val == 1 ? map[unit][:one].to_s : map[unit][:other].to_s}" }.
to_sentence
end
With that said, I wrote a small gem named duration_in_words to help with this that is a bit more configurable and with locale support.
duration_in_words
provides a view helper to convert ActiveSupport::Duration
objects into concise string like 1h 20m and 30s
, with locale support.
With duration_in_words
you could do the following:
include ActionView::Helpers::DurationHelper
>> duration = 2.hours
>> duration_in_words(duration)
=> "2h"
>> duration = 1.day + 2.hours + 30.minutes
>> duration_in_words(duration)
=> "1d 2h and 30m"
Using :format option:
There are two formats available, :compact
, and :full
. :compact
being the default.
>> duration = 1.day + 2.hours + 30.minutes
>> duration_in_words(duration, format: :full)
=> "1 day 2 hours and 30 minutes"
Using :locale option:
Given this locale dictionary:
de:
duration:
in_words:
format:
compact:
years:
one: '%{count}J'
other: '%{count}J'
months:
one: '%{count}M'
other: '%{count}M'
days:
one: '%{count}T'
other: '%{count}T'
hours:
one: '%{count}Std.'
other: '%{count}Std.'
minutes:
one: '%{count}Min'
other: '%{count}Min'
seconds:
one: '%{count}s'
other: '%{count}s'
support:
words_connector: ' '
two_words_connector: ' und '
last_word_connector: ' und '
full:
years:
one: '%{count} Jahr'
other: '%{count} Jahre'
months:
one: '%{count} Monat'
other: '%{count} Monate'
days:
one: '%{count} Tag'
other: '%{count} Tage'
hours:
one: '%{count} Stunde'
other: '%{count} Stunden'
minutes:
one: '%{count} Minute'
other: '%{count} Minuten'
seconds:
one: '%{count} Sekunde'
other: '%{count} Sekunden'
support:
words_connector: ', '
two_words_connector: ' und '
last_word_connector: ', und '
>> duration = 1.day + 2.hours + 30.minutes
>> duration_in_words(duration, locale: :de)
=> "1T 2Std. und 30s"
>> duration_in_words(duration, format: :full, locale: :de)
>> "1 Tag, 2 Std., und 30 Sekunden"
Give it a try next time you need to format ActiveSupport::Duration
objects.