class ActiveSupport::Duration::ISO8601Serializer

:nodoc:
Serializes duration to string according to ISO 8601 Duration format.

def format_seconds(seconds)

def format_seconds(seconds)
  if @precision
    sprintf("%0.0#{@precision}f", seconds)
  else
    seconds.to_s
  end
end

def initialize(duration, precision: nil)

def initialize(duration, precision: nil)
  @duration = duration
  @precision = precision
end

def normalize

If all parts are negative it will negate all of them and return minus as a sign.
Zero parts are removed as not significant.
Parts are summarized (as they can become repetitive due to addition, etc).
Return pair of duration's parts and whole duration sign.
def normalize
  parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p|
    p[k] += v  unless v.zero?
  end
  # Convert weeks to days and remove weeks if mixed with date parts
  if week_mixed_with_date?(parts)
    parts[:days] += parts.delete(:weeks) * SECONDS_PER_WEEK / SECONDS_PER_DAY
  end
  parts
end

def serialize

Builds and returns output string.
def serialize
  parts = normalize
  return "PT0S" if parts.empty?
  output = +"P"
  output << "#{parts[:years]}Y"   if parts.key?(:years)
  output << "#{parts[:months]}M"  if parts.key?(:months)
  output << "#{parts[:days]}D"    if parts.key?(:days)
  output << "#{parts[:weeks]}W"   if parts.key?(:weeks)
  time = +""
  time << "#{parts[:hours]}H"     if parts.key?(:hours)
  time << "#{parts[:minutes]}M"   if parts.key?(:minutes)
  if parts.key?(:seconds)
    time << "#{format_seconds(parts[:seconds])}S"
  end
  output << "T#{time}" unless time.empty?
  output
end

def week_mixed_with_date?(parts)

def week_mixed_with_date?(parts)
  parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
end