Я надеялся, что будет более простой способ, чем использовать divmod, но это самый СУХОЙ и многократно используемый способ, который я нашел для этого:
def seconds_to_units(seconds)
'%d days, %d hours, %d minutes, %d seconds' %
# the .reverse lets us put the larger units first for readability
[24,60,60].reverse.inject([seconds]) {|result, unitsize|
result[0,0] = result.shift.divmod(unitsize)
result
}
end
Метод легко настраивается путем изменения строки формата и первого встроенного массива (т. Е. [24,60,60]).
Улучшенная версия
class TieredUnitFormatter
# if you set this, '%d' must appear as many times as there are units
attr_accessor :format_string
def initialize(unit_names=%w(days hours minutes seconds), conversion_factors=[24, 60, 60])
@unit_names = unit_names
@factors = conversion_factors
@format_string = unit_names.map {|name| "%d #{name}" }.join(', ')
# the .reverse helps us iterate more effectively
@reversed_factors = @factors.reverse
end
# e.g. seconds
def format(smallest_unit_amount)
parts = split(smallest_unit_amount)
@format_string % parts
end
def split(smallest_unit_amount)
# go from smallest to largest unit
@reversed_factors.inject([smallest_unit_amount]) {|result, unitsize|
# Remove the most significant item (left side), convert it, then
# add the 2-element array to the left side of the result.
result[0,0] = result.shift.divmod(unitsize)
result
}
end
end
Примеры:
fmt = TieredUnitFormatter.new
fmt.format(270921) # => "3 days, 3 hours, 15 minutes, 21 seconds"
fmt = TieredUnitFormatter.new(%w(minutes seconds), [60])
fmt.format(5454) # => "90 minutes, 54 seconds"
fmt.format_string = '%d:%d'
fmt.format(5454) # => "90:54"
Обратите внимание, что format_string
не позволит вам изменить порядок частей (это всегда наиболее значимое значение по меньшей мере). Для более точного контроля вы можете использовать split
и манипулировать значениями самостоятельно.