RelativeTimeFormat.h (4867B)
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 #ifndef intl_components_RelativeTimeFormat_h_ 5 #define intl_components_RelativeTimeFormat_h_ 6 7 #include "mozilla/Try.h" 8 #include "mozilla/intl/ICU4CGlue.h" 9 #include "mozilla/intl/ICUError.h" 10 #include "mozilla/intl/NumberPart.h" 11 12 #include "unicode/ureldatefmt.h" 13 #include "unicode/utypes.h" 14 15 namespace mozilla::intl { 16 17 struct RelativeTimeFormatOptions { 18 enum class Style { Short, Narrow, Long }; 19 Style style = Style::Long; 20 21 enum class Numeric { 22 /** 23 * Only strings with numeric components like `1 day ago`. 24 */ 25 Always, 26 /** 27 * Natural-language strings like `yesterday` when possible, 28 * otherwise strings with numeric components as in `7 months ago`. 29 */ 30 Auto, 31 }; 32 Numeric numeric = Numeric::Always; 33 }; 34 35 /** 36 * A RelativeTimeFormat implementation that roughly mirrors the API provided by 37 * the ECMA-402 Intl.RelativeTimeFormat object. 38 * 39 * https://tc39.es/ecma402/#relativetimeformat-objects 40 */ 41 class RelativeTimeFormat final { 42 public: 43 /** 44 * 45 * Initialize a new RelativeTimeFormat for the provided locale and using the 46 * provided options. 47 * 48 * https://tc39.es/ecma402/#sec-InitializeRelativeTimeFormat 49 */ 50 static Result<UniquePtr<RelativeTimeFormat>, ICUError> TryCreate( 51 const char* aLocale, const RelativeTimeFormatOptions& aOptions); 52 53 RelativeTimeFormat() = default; 54 55 RelativeTimeFormat(RelativeTimeFormatOptions::Numeric aNumeric, 56 URelativeDateTimeFormatter* aFormatter, 57 UFormattedRelativeDateTime* aFormattedRelativeDateTime); 58 59 RelativeTimeFormat(const RelativeTimeFormat&) = delete; 60 RelativeTimeFormat& operator=(const RelativeTimeFormat&) = delete; 61 ~RelativeTimeFormat(); 62 63 enum class FormatUnit { 64 Second, 65 Minute, 66 Hour, 67 Day, 68 Week, 69 Month, 70 Quarter, 71 Year 72 }; 73 74 /** 75 * Formats a double to the provider buffer (either utf-8 or utf-16) 76 * 77 * https://tc39.es/ecma402/#sec-FormatRelativeTime 78 */ 79 template <typename B> 80 Result<Ok, ICUError> format(double aNumber, FormatUnit aUnit, 81 B& aBuffer) const { 82 static_assert( 83 std::is_same_v<typename B::CharType, char> || 84 std::is_same_v<typename B::CharType, char16_t>, 85 "The only buffer CharTypes supported by RelativeTimeFormat are char " 86 "(for UTF-8 support) and char16_t (for UTF-16 support)."); 87 88 auto fmt = mNumeric == RelativeTimeFormatOptions::Numeric::Auto 89 ? ureldatefmt_format 90 : ureldatefmt_formatNumeric; 91 92 if constexpr (std::is_same_v<typename B::CharType, char>) { 93 mozilla::Vector<char16_t, StackU16VectorSize> u16Vec; 94 95 MOZ_TRY(FillBufferWithICUCall( 96 u16Vec, [this, aNumber, aUnit, fmt](UChar* target, int32_t length, 97 UErrorCode* status) { 98 return fmt(mFormatter, aNumber, ToURelativeDateTimeUnit(aUnit), 99 target, length, status); 100 })); 101 102 if (!FillBuffer(u16Vec, aBuffer)) { 103 return Err(ICUError::OutOfMemory); 104 } 105 return Ok{}; 106 } else { 107 static_assert(std::is_same_v<typename B::CharType, char16_t>); 108 109 return FillBufferWithICUCall( 110 aBuffer, [this, aNumber, aUnit, fmt](UChar* target, int32_t length, 111 UErrorCode* status) { 112 return fmt(mFormatter, aNumber, ToURelativeDateTimeUnit(aUnit), 113 target, length, status); 114 }); 115 } 116 } 117 118 /** 119 * Formats the relative time to a utf-16 string, and fills the provided parts 120 * vector. The string view is valid until another time is formatted. 121 * Accessing the string view after this event is undefined behavior. 122 * 123 * This is utf-16 only because the only current use case is in 124 * SpiderMonkey. Supporting utf-8 would require recalculating the offsets 125 * in NumberPartVector from fixed width to variable width, which might be 126 * tricky to get right and is work that won't be necessary if we switch to 127 * ICU4X (see Bug 1723120). 128 * 129 * https://tc39.es/ecma402/#sec-FormatRelativeTimeToParts 130 */ 131 Result<Span<const char16_t>, ICUError> formatToParts( 132 double aNumber, FormatUnit aUnit, NumberPartVector& aParts) const; 133 134 private: 135 RelativeTimeFormatOptions::Numeric mNumeric = 136 RelativeTimeFormatOptions::Numeric::Always; 137 URelativeDateTimeFormatter* mFormatter = nullptr; 138 UFormattedRelativeDateTime* mFormattedRelativeDateTime = nullptr; 139 140 static constexpr size_t StackU16VectorSize = 128; 141 142 URelativeDateTimeUnit ToURelativeDateTimeUnit(FormatUnit unit) const; 143 }; 144 145 } // namespace mozilla::intl 146 147 #endif