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_DateTimePatternGenerator_h_ 5 #define intl_components_DateTimePatternGenerator_h_ 6 7 #include "unicode/udatpg.h" 8 #include "mozilla/EnumSet.h" 9 #include "mozilla/Result.h" 10 #include "mozilla/Span.h" 11 #include "mozilla/UniquePtr.h" 12 #include "mozilla/intl/ICU4CGlue.h" 13 #include "mozilla/intl/ICUError.h" 14 15 namespace mozilla::intl { 16 17 class DisplayNames; 18 19 /** 20 * The DateTimePatternGenerator is the machinery used to work with DateTime 21 * pattern manipulation. It is expensive to create one, and so generally it is 22 * created once and then cached. It may be needed to be passed in as an argument 23 * for different mozilla::intl APIs. 24 */ 25 class DateTimePatternGenerator final { 26 public: DateTimePatternGenerator(UDateTimePatternGenerator * aGenerator)27 explicit DateTimePatternGenerator(UDateTimePatternGenerator* aGenerator) 28 : mGenerator(aGenerator) { 29 MOZ_ASSERT(aGenerator); 30 }; 31 32 // Transfer ownership of the UDateTimePatternGenerator in the move 33 // constructor. 34 DateTimePatternGenerator(DateTimePatternGenerator&& other) noexcept; 35 36 // Transfer ownership of the UEnumeration in the move assignment operator. 37 DateTimePatternGenerator& operator=( 38 DateTimePatternGenerator&& other) noexcept; 39 40 // Disallow copy. 41 DateTimePatternGenerator(const DateTimePatternGenerator&) = delete; 42 DateTimePatternGenerator& operator=(const DateTimePatternGenerator&) = delete; 43 44 ~DateTimePatternGenerator(); 45 46 static Result<UniquePtr<DateTimePatternGenerator>, ICUError> TryCreate( 47 const char* aLocale); 48 49 enum class PatternMatchOption { 50 /** 51 * Adjust the 'hour' field in the resolved pattern to match the input 52 * skeleton width. 53 */ 54 HourField, 55 56 /** 57 * Adjust the 'minute' field in the resolved pattern to match the input 58 * skeleton width. 59 */ 60 MinuteField, 61 62 /** 63 * Adjust the 'second' field in the resolved pattern to match the input 64 * skeleton width. 65 */ 66 SecondField, 67 }; 68 69 /** 70 * Given a skeleton (a string with unordered datetime fields), get a best 71 * pattern that will fit for that locale. This pattern will be filled into the 72 * buffer. e.g. The skeleton "yMd" would return the pattern "M/d/y" for en-US, 73 * or "dd/MM/y" for en-GB. 74 */ 75 template <typename B> 76 ICUResult GetBestPattern(Span<const char16_t> aSkeleton, B& aBuffer, 77 EnumSet<PatternMatchOption> options = {}) { 78 return FillBufferWithICUCall( 79 aBuffer, [&](UChar* target, int32_t length, UErrorCode* status) { 80 return udatpg_getBestPatternWithOptions( 81 mGenerator.GetMut(), aSkeleton.data(), 82 static_cast<int32_t>(aSkeleton.Length()), 83 toUDateTimePatternMatchOptions(options), target, length, status); 84 }); 85 } 86 87 /** 88 * Get a skeleton (a string with unordered datetime fields) from a pattern. 89 * For example, both "MMM-dd" and "dd/MMM" produce the skeleton "MMMdd". 90 */ 91 template <typename B> GetSkeleton(Span<const char16_t> aPattern,B & aBuffer)92 static ICUResult GetSkeleton(Span<const char16_t> aPattern, B& aBuffer) { 93 // At one time udatpg_getSkeleton required a UDateTimePatternGenerator*, but 94 // now it is valid to pass in a nullptr. 95 return FillBufferWithICUCall( 96 aBuffer, [&](UChar* target, int32_t length, UErrorCode* status) { 97 return udatpg_getSkeleton(nullptr, aPattern.data(), 98 static_cast<int32_t>(aPattern.Length()), 99 target, length, status); 100 }); 101 } 102 103 /** 104 * Get a pattern of the form "{1} {0}" to combine separate date and time 105 * patterns into a single pattern. The "{0}" part is the placeholder for the 106 * time pattern and "{1}" is the placeholder for the date pattern. 107 * 108 * See dateTimeFormat from 109 * https://unicode.org/reports/tr35/tr35-dates.html#dateTimeFormat 110 * 111 * Note: 112 * In CLDR, it's called Date-Time Combined Format 113 * https://cldr.unicode.org/translation/date-time/datetime-patterns#h.x7ca7qwzh4m 114 * 115 * The naming 'placeholder pattern' is from ICU4X. 116 * https://unicode-org.github.io/icu4x-docs/doc/icu_pattern/index.html 117 */ GetPlaceholderPattern()118 Span<const char16_t> GetPlaceholderPattern() const { 119 int32_t length; 120 const char16_t* combined = 121 udatpg_getDateTimeFormat(mGenerator.GetConst(), &length); 122 return Span{combined, static_cast<size_t>(length)}; 123 } 124 125 private: 126 // Allow other mozilla::intl components to access the underlying 127 // UDateTimePatternGenerator. 128 friend class DisplayNames; 129 GetUDateTimePatternGenerator()130 UDateTimePatternGenerator* GetUDateTimePatternGenerator() { 131 return mGenerator.GetMut(); 132 } 133 134 ICUPointer<UDateTimePatternGenerator> mGenerator = 135 ICUPointer<UDateTimePatternGenerator>(nullptr); 136 toUDateTimePatternMatchOptions(EnumSet<PatternMatchOption> options)137 static UDateTimePatternMatchOptions toUDateTimePatternMatchOptions( 138 EnumSet<PatternMatchOption> options) { 139 struct OptionMap { 140 PatternMatchOption from; 141 UDateTimePatternMatchOptions to; 142 } static constexpr map[] = { 143 {PatternMatchOption::HourField, UDATPG_MATCH_HOUR_FIELD_LENGTH}, 144 #ifndef U_HIDE_INTERNAL_API 145 {PatternMatchOption::MinuteField, UDATPG_MATCH_MINUTE_FIELD_LENGTH}, 146 {PatternMatchOption::SecondField, UDATPG_MATCH_SECOND_FIELD_LENGTH}, 147 #endif 148 }; 149 150 UDateTimePatternMatchOptions result = UDATPG_MATCH_NO_OPTIONS; 151 for (const auto& entry : map) { 152 if (options.contains(entry.from)) { 153 result = UDateTimePatternMatchOptions(result | entry.to); 154 } 155 } 156 return result; 157 } 158 }; 159 160 } // namespace mozilla::intl 161 #endif 162