RelativeTimeFormat.cpp (5190B)
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> 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 75 RelativeTimeFormat::RelativeTimeFormat( 76 RelativeTimeFormatOptions::Numeric aNumeric, 77 URelativeDateTimeFormatter* aFormatter, 78 UFormattedRelativeDateTime* aFormattedRelativeDateTime) 79 : mNumeric(aNumeric), 80 mFormatter(aFormatter), 81 mFormattedRelativeDateTime(aFormattedRelativeDateTime) {} 82 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 95 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 119 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 = !std::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