Class: ICU::DurationFormatting::DurationFormatter

Inherits:
Object
  • Object
show all
Defined in:
lib/ffi-icu/duration_formatting.rb

Instance Method Summary collapse

Constructor Details

#initialize(locale:, style: :long) ⇒ DurationFormatter

Returns a new instance of DurationFormatter.

Raises:

  • (ArgumentError)


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/ffi-icu/duration_formatting.rb', line 71

def initialize(locale:, style: :long)
  if !Lib.respond_to?(:unumf_openForSkeletonAndLocale) || !Lib.respond_to?(:ulistfmt_openForType)
    raise('ICU::DurationFormatting requires ICU >= 67')
  end

  raise(ArgumentError, "Unknown style #{style}") unless VALID_STYLES.include?(style)

  @locale = locale
  @style = style
  # These are created lazily based on what parts are actually included
  @number_formatters = {}

  list_join_format = STYLES_TO_LIST_JOIN_FORMAT.fetch(style)
  @list_formatter = FFI::AutoPointer.new(
    Lib.check_error do |error|
      Lib.ulistfmt_openForType(@locale, :units, list_join_format, error)
    end,
    Lib.method(:ulistfmt_close)
  )
end

Instance Method Details

#format(fields) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/ffi-icu/duration_formatting.rb', line 92

def format(fields)
  fields.each_key do |field|
    raise("Unknown field #{field}") unless VALID_FIELDS.include?(field)
  end
  fields = fields.dup # we might modify this argument.

  # Intl.js spec says that rounding options affect only the smallest unit, and only
  # if that unit is sub-second. All other fields therefore need to be truncated.
  smallest_unit = VALID_FIELDS[fields.keys.map { |k| VALID_FIELDS.index(k) }.max]
  fields.each_key do |k|
    raise(ArgumentError, 'Negative durations are not yet supported') if fields[k].negative?

    fields[k] = fields[k].to_i unless k == smallest_unit && ROUNDABLE_FIELDS.include?(smallest_unit)
  end

  formatted_hms = nil
  if @style == :digital
    # icu::MeasureFormat contains special casing for hours/minutes/seconds formatted
    # at numeric width, to render it as h:mm:s, essentially. This involves using
    # a pattern called durationUnits defined in the ICU data for the locale.
    # If we have data for this combination of hours/mins/seconds in this locale,
    # use that and emulate ICU's special casing.
    formatted_hms = format_hms(fields)
    if formatted_hms
      # We've taken care of all these fields now.
      HMS_FIELDS.each do |f|
        fields.delete(f)
      end
    end
  end

  formatted_fields = VALID_FIELDS.map do |f|
    next unless fields.key?(f)
    next unless fields[f] != 0

    format_number(fields[f], [
      UNIT_FORMAT_STRINGS[f], STYLES_TO_NUMBER_FORMAT_WIDTH[@style],
      ('.#########' if f == smallest_unit)
    ].compact.join(' '))
  end
  formatted_fields << formatted_hms
  formatted_fields.compact!

  format_list(formatted_fields)
end