[Feature Proposal] Add ActiveSupport::Duration#in_specific_parts to break down durations into chosen units

Motivation

ActiveSupport::Duration#parts returns all built in units (years, months, days, etc.) without folding larger units into smaller ones. While often this is what we need, I found myself repeatedly in a situation where I wanted only specific units and larger units to be converted down into them.

For example:

(1.day + 1.hour + 10.minutes).in_specific_parts(:hours, :minutes)
# => { hours: 25, minutes: 10 }

This is useful for reporting and simplifying time calculations where parts is too granular.


Behavior

  • Accepts one or more units from ActiveSupport::Duration::PARTS
  • Converts larger units into smaller ones using PARTS_IN_SECONDS
  • Drops any leftover smaller than the smallest requested unit
  • Preserves the sign for negative durations

Proposed Implementation

This is how I am currently extending ActiveSupport::Duration in my project.

def in_specific_parts(*requested_parts)
  raise ArgumentError, "You must specify at least one component" if requested_parts.empty?

  requested_parts = requested_parts.flatten.map(&:to_sym)

  # Validate units
  valid_parts = ActiveSupport::Duration::PARTS
  unknown_parts = requested_parts - valid_parts
  raise ArgumentError, "Unknown components: #{unknown_parts.join(', ')}" if unknown_parts.any?

  sign = to_i.negative? ? -1 : 1
  remaining_seconds = to_i.abs

  # Sort largest → smallest
  sorted_parts = requested_parts.sort_by { |u| -ActiveSupport::Duration::PARTS_IN_SECONDS[u] }

  output_parts = {}
  sorted_parts.each do |unit|
    part_value, remaining_seconds = remaining_seconds.divmod(ActiveSupport::Duration::PARTS_IN_SECONDS[unit])
    output_parts[unit] = part_value * sign
  end
  output_parts
end


Discussion Points

  • Would this be broadly useful enough to warrant inclusion in core Rails?
  • Any naming alternatives that feel more Rails-like?
  • Any improvements on the method itself that you identify?

If there’s interest, I’m happy to submit a PR with tests and documentation (Would be my first open source contribution, so I would probably need a bit of guidance though :wink: )