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)


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/ffi-icu/duration_formatting.rb', line 61

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 { |error|
            Lib.ulistfmt_openForType(@locale, :units, list_join_format, error)
        },
        Lib.method(:ulistfmt_close)
    )
end

Instance Method Details

#format(fields) ⇒ Object



82
83
84
85
86
87
88
89
90
91
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
# File 'lib/ffi-icu/duration_formatting.rb', line 82

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] < 0
        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