1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 #include "mozilla/intl/RelativeTimeFormat.h"
5 #include "mozilla/FloatingPoint.h"
6
7 #include "unicode/unum.h"
8
9 #include "NumberFormatFields.h"
10 #include "ICU4CGlue.h"
11 #include "ScopedICUObject.h"
12
13 namespace mozilla::intl {
14
15 /*static*/ Result<UniquePtr<RelativeTimeFormat>, ICUError>
TryCreate(const char * aLocale,const RelativeTimeFormatOptions & aOptions)16 RelativeTimeFormat::TryCreate(const char* aLocale,
17 const RelativeTimeFormatOptions& aOptions) {
18 UErrorCode status = U_ZERO_ERROR;
19
20 UFormattedRelativeDateTime* formattedRelativeDateTime =
21 ureldatefmt_openResult(&status);
22 if (U_FAILURE(status)) {
23 return Err(ToICUError(status));
24 }
25 ScopedICUObject<UFormattedRelativeDateTime, ureldatefmt_closeResult>
26 closeFormattedRelativeDate(formattedRelativeDateTime);
27
28 UNumberFormat* nf =
29 unum_open(UNUM_DECIMAL, nullptr, 0, IcuLocale(aLocale), nullptr, &status);
30 if (U_FAILURE(status)) {
31 return Err(ToICUError(status));
32 }
33 ScopedICUObject<UNumberFormat, unum_close> closeNumberFormatter(nf);
34
35 // Use the default values as if a new Intl.NumberFormat had been constructed.
36 unum_setAttribute(nf, UNUM_MIN_INTEGER_DIGITS, 1);
37 unum_setAttribute(nf, UNUM_MIN_FRACTION_DIGITS, 0);
38 unum_setAttribute(nf, UNUM_MAX_FRACTION_DIGITS, 3);
39 unum_setAttribute(nf, UNUM_GROUPING_USED, true);
40 unum_setAttribute(nf, UNUM_MINIMUM_GROUPING_DIGITS,
41 UNUM_MINIMUM_GROUPING_DIGITS_AUTO);
42
43 UDateRelativeDateTimeFormatterStyle relDateTimeStyle;
44 switch (aOptions.style) {
45 case RelativeTimeFormatOptions::Style::Short:
46 relDateTimeStyle = UDAT_STYLE_SHORT;
47 break;
48 case RelativeTimeFormatOptions::Style::Narrow:
49 relDateTimeStyle = UDAT_STYLE_NARROW;
50 break;
51 case RelativeTimeFormatOptions::Style::Long:
52 relDateTimeStyle = UDAT_STYLE_LONG;
53 break;
54 }
55
56 URelativeDateTimeFormatter* formatter =
57 ureldatefmt_open(IcuLocale(aLocale), nf, relDateTimeStyle,
58 UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
59
60 if (U_FAILURE(status)) {
61 return Err(ToICUError(status));
62 }
63
64 // Ownership was transferred to mFormatter.
65 closeNumberFormatter.forget();
66
67 UniquePtr<RelativeTimeFormat> rtf = MakeUnique<RelativeTimeFormat>(
68 aOptions.numeric, formatter, formattedRelativeDateTime);
69
70 // Ownership was transferred to rtf.
71 closeFormattedRelativeDate.forget();
72 return rtf;
73 }
74
RelativeTimeFormat(RelativeTimeFormatOptions::Numeric aNumeric,URelativeDateTimeFormatter * aFormatter,UFormattedRelativeDateTime * aFormattedRelativeDateTime)75 RelativeTimeFormat::RelativeTimeFormat(
76 RelativeTimeFormatOptions::Numeric aNumeric,
77 URelativeDateTimeFormatter* aFormatter,
78 UFormattedRelativeDateTime* aFormattedRelativeDateTime)
79 : mNumeric(aNumeric),
80 mFormatter(aFormatter),
81 mFormattedRelativeDateTime(aFormattedRelativeDateTime) {}
82
~RelativeTimeFormat()83 RelativeTimeFormat::~RelativeTimeFormat() {
84 if (mFormattedRelativeDateTime) {
85 ureldatefmt_closeResult(mFormattedRelativeDateTime);
86 mFormattedRelativeDateTime = nullptr;
87 }
88
89 if (mFormatter) {
90 ureldatefmt_close(mFormatter);
91 mFormatter = nullptr;
92 }
93 }
94
ToURelativeDateTimeUnit(FormatUnit unit) const95 URelativeDateTimeUnit RelativeTimeFormat::ToURelativeDateTimeUnit(
96 FormatUnit unit) const {
97 switch (unit) {
98 case FormatUnit::Second:
99 return UDAT_REL_UNIT_SECOND;
100 case FormatUnit::Minute:
101 return UDAT_REL_UNIT_MINUTE;
102 case FormatUnit::Hour:
103 return UDAT_REL_UNIT_HOUR;
104 case FormatUnit::Day:
105 return UDAT_REL_UNIT_DAY;
106 case FormatUnit::Week:
107 return UDAT_REL_UNIT_WEEK;
108 case FormatUnit::Month:
109 return UDAT_REL_UNIT_MONTH;
110 case FormatUnit::Quarter:
111 return UDAT_REL_UNIT_QUARTER;
112 case FormatUnit::Year:
113 return UDAT_REL_UNIT_YEAR;
114 };
115 MOZ_ASSERT_UNREACHABLE();
116 return UDAT_REL_UNIT_SECOND;
117 }
118
formatToParts(double aNumber,FormatUnit aUnit,NumberPartVector & aParts) const119 Result<Span<const char16_t>, ICUError> RelativeTimeFormat::formatToParts(
120 double aNumber, FormatUnit aUnit, NumberPartVector& aParts) const {
121 UErrorCode status = U_ZERO_ERROR;
122
123 if (mNumeric == RelativeTimeFormatOptions::Numeric::Auto) {
124 ureldatefmt_formatToResult(mFormatter, aNumber,
125 ToURelativeDateTimeUnit(aUnit),
126 mFormattedRelativeDateTime, &status);
127 } else {
128 ureldatefmt_formatNumericToResult(mFormatter, aNumber,
129 ToURelativeDateTimeUnit(aUnit),
130 mFormattedRelativeDateTime, &status);
131 }
132 if (U_FAILURE(status)) {
133 return Err(ToICUError(status));
134 }
135
136 const UFormattedValue* formattedValue =
137 ureldatefmt_resultAsValue(mFormattedRelativeDateTime, &status);
138 if (U_FAILURE(status)) {
139 return Err(ToICUError(status));
140 }
141
142 bool isNegative = !IsNaN(aNumber) && IsNegative(aNumber);
143
144 // Necessary until all of intl is using Span (Bug 1709880)
145 return FormatResultToParts(formattedValue, Nothing(), isNegative,
146 false /*formatForUnit*/, aParts)
147 .andThen([](std::u16string_view result)
148 -> Result<Span<const char16_t>, ICUError> {
149 return Span<const char16_t>(result.data(), result.length());
150 });
151 }
152
153 } // namespace mozilla::intl
154