• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "intl_date_time_format.h"
17 
18 #include <algorithm>
19 #include "ohos/init_data.h"
20 #include "unicode/gregocal.h"
21 #include "i18n_hilog.h"
22 #include "locale_config.h"
23 #include "locale_helper.h"
24 #include "locale_info.h"
25 #include "utils.h"
26 
27 namespace OHOS {
28 namespace Global {
29 namespace I18n {
30 const std::string IntlDateTimeFormat::LOCALE_TAG = "locale";
31 const std::string IntlDateTimeFormat::LOCALE_MATCHER_TAG = "localeMatcher";
32 const std::string IntlDateTimeFormat::WEEK_DAY_TAG = "weekday";
33 const std::string IntlDateTimeFormat::ERA_TAG = "era";
34 const std::string IntlDateTimeFormat::YEAR_TAG = "year";
35 const std::string IntlDateTimeFormat::MONTH_TAG = "month";
36 const std::string IntlDateTimeFormat::DAY_TAG = "day";
37 const std::string IntlDateTimeFormat::HOUR_TAG = "hour";
38 const std::string IntlDateTimeFormat::MINUTE_TAG = "minute";
39 const std::string IntlDateTimeFormat::SECOND_TAG = "second";
40 const std::string IntlDateTimeFormat::TIME_ZONE_NAME_TAG = "timeZoneName";
41 const std::string IntlDateTimeFormat::FORMAT_MATCHER_TAG = "formatMatcher";
42 const std::string IntlDateTimeFormat::HOUR12_TAG = "hour12";
43 const std::string IntlDateTimeFormat::TIME_ZONE_TAG = "timeZone";
44 const std::string IntlDateTimeFormat::CALENDAR_TAG = "calendar";
45 const std::string IntlDateTimeFormat::DAY_PERIOD_TAG = "dayPeriod";
46 const std::string IntlDateTimeFormat::NUMBERING_SYSTEM_TAG = "numberingSystem";
47 const std::string IntlDateTimeFormat::DATE_STYLE_TAG = "dateStyle";
48 const std::string IntlDateTimeFormat::TIME_STYLE_TAG = "timeStyle";
49 const std::string IntlDateTimeFormat::HOUR_CYCLE_TAG = "hourCycle";
50 const std::string IntlDateTimeFormat::FRACTIONAL_SECOND_DIGITS_TAG = "fractionalSecondDigits";
51 const std::string DEFAULT_NUMBERING_SYSTEM = "latn";
52 const size_t NUMBER_OF_PARAMETER = 2;
53 const int32_t MAX_VALUE_OF_FRACTIONAL_SECOND_DIGITS = 3;
54 const int32_t MIN_VALUE_OF_FRACTIONAL_SECOND_DIGITS = 1;
55 static const std::string HOUR_CYCLE_11 = "h11";
56 static const std::string HOUR_CYCLE_12 = "h12";
57 static const std::string HOUR_CYCLE_23 = "h23";
58 static const std::string HOUR_CYCLE_24 = "h24";
59 
60 const std::unordered_map<std::string, icu::DateFormat::EStyle> DATE_TIME_STYLE_TO_ESTYLE = {
61     { "full", icu::DateFormat::EStyle::kFull },
62     { "long", icu::DateFormat::EStyle::kLong },
63     { "medium", icu::DateFormat::EStyle::kMedium },
64     { "short", icu::DateFormat::EStyle::kShort }
65 };
66 
67 const std::unordered_set<std::string> FORMAT_MATCHER_VALUE = {
68     "basic", "best fit"
69 };
70 
71 const std::unordered_set<std::string> DATE_TIME_STYLE_VALUE = {
72     "full", "long", "medium", "short"
73 };
74 
75 const std::unordered_set<std::string> DATE_TIME_ELEMENT = {
76     "weekday", "year", "month", "day", "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits"
77 };
78 
79 const std::unordered_map<std::string, std::pair<std::string, std::string>> HOUR_CYCLE_TO_PATTERN = {
80     { "h11", { "KK", "K" } },
81     { "h12", { "hh", "h" } },
82     { "h23", { "HH", "H" } },
83     { "h24", { "kk", "k" } }
84 };
85 
86 const std::unordered_map<char16_t, std::string> HOUR_CYCLE_MAP = {
87     { 'K', "h11" },
88     { 'h', "h12" },
89     { 'H', "h23" },
90     { 'k', "h24" }
91 };
92 
93 const std::unordered_map<UDateFormatHourCycle, std::string> UDATE_FORMAT_HOUR_CYCLE_TO_STRING = {
94     { UDAT_HOUR_CYCLE_11, "h11" },
95     { UDAT_HOUR_CYCLE_12, "h12" },
96     { UDAT_HOUR_CYCLE_23, "h23" },
97     { UDAT_HOUR_CYCLE_24, "h24" }
98 };
99 
100 const std::unordered_map<int32_t, std::string> FIELD_ID_TO_DATE_TYPE = {
101     { UDAT_YEAR_FIELD, "year" },
102     { UDAT_EXTENDED_YEAR_FIELD, "year" },
103     { UDAT_YEAR_NAME_FIELD, "yearName" },
104     { UDAT_MONTH_FIELD, "month" },
105     { UDAT_STANDALONE_MONTH_FIELD, "month" },
106     { UDAT_DATE_FIELD, "day" },
107     { UDAT_HOUR_OF_DAY1_FIELD, "hour" },
108     { UDAT_HOUR_OF_DAY0_FIELD, "hour" },
109     { UDAT_HOUR1_FIELD, "hour" },
110     { UDAT_HOUR0_FIELD, "hour" },
111     { UDAT_MINUTE_FIELD, "minute" },
112     { UDAT_SECOND_FIELD, "second" },
113     { UDAT_DAY_OF_WEEK_FIELD, "weekday" },
114     { UDAT_DOW_LOCAL_FIELD, "weekday" },
115     { UDAT_STANDALONE_DAY_FIELD, "weekday" },
116     { UDAT_AM_PM_FIELD, "dayPeriod" },
117     { UDAT_AM_PM_MIDNIGHT_NOON_FIELD, "dayPeriod" },
118     { UDAT_FLEXIBLE_DAY_PERIOD_FIELD, "dayPeriod" },
119     { UDAT_TIMEZONE_FIELD, "timeZoneName" },
120     { UDAT_TIMEZONE_RFC_FIELD, "timeZoneName" },
121     { UDAT_TIMEZONE_GENERIC_FIELD, "timeZoneName" },
122     { UDAT_TIMEZONE_SPECIAL_FIELD, "timeZoneName" },
123     { UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD, "timeZoneName" },
124     { UDAT_TIMEZONE_ISO_FIELD, "timeZoneName" },
125     { UDAT_TIMEZONE_ISO_LOCAL_FIELD, "timeZoneName" },
126     { UDAT_ERA_FIELD, "era" },
127     { UDAT_FRACTIONAL_SECOND_FIELD, "fractionalSecond" },
128     { UDAT_RELATED_YEAR_FIELD, "relatedYear" }
129 };
130 
131 const std::vector<std::string> ICU_LONG_SHORT = {
132     "long", "short",
133     "longOffset", "shortOffset",
134     "longGeneric", "shortGeneric"
135 };
136 
137 const std::vector<std::string> ICU_NARROW_LONG_SHORT = {
138     "narrow", "long", "short"
139 };
140 
141 const std::vector<std::string> ICU2_DIGIT_NUMERIC = {
142     "2-digit", "numeric"
143 };
144 
145 const std::vector<std::string> ICU_NARROW_LONG_SHORT2_DIGIT_NUMERIC = {
146     "narrow", "long", "short", "2-digit", "numeric"
147 };
148 
149 const std::vector<std::pair<std::string, std::string>> ICU_WEEKDAY_PE = {
150     { "EEEEE", "narrow" }, { "EEEE", "long" }, { "EEE", "short" },
151     { "ccccc", "narrow" }, { "cccc", "long" }, { "ccc", "short" }
152 };
153 
154 const std::vector<std::pair<std::string, std::string>> ICU_ERA_PE = {
155     { "GGGGG", "narrow" },
156     { "GGGG", "long" },
157     { "GGG", "short" }
158 };
159 
160 const std::vector<std::pair<std::string, std::string>> ICU_YEAR_PE = {
161     { "yy", "2-digit" },
162     { "y", "numeric" }
163 };
164 
165 const std::vector<std::pair<std::string, std::string>> ICU_MONTH_PE = {
166     { "MMMMM", "narrow" }, { "MMMM", "long" }, { "MMM", "short" }, { "MM", "2-digit" }, { "M", "numeric" },
167     { "LLLLL", "narrow" }, { "LLLL", "long" }, { "LLL", "short" }, { "LL", "2-digit" }, { "L", "numeric" }
168 };
169 
170 const std::vector<std::pair<std::string, std::string>> ICU_DAY_PE = {
171     { "dd", "2-digit" },
172     { "d", "numeric" }
173 };
174 
175 const std::vector<std::pair<std::string, std::string>> ICU_DAY_PERIOD_PE = {
176     { "BBBBB", "narrow" }, { "bbbbb", "narrow" }, { "BBBB", "long" },
177     { "bbbb", "long" }, { "B", "short" }, { "b", "short" }
178 };
179 
180 const std::vector<std::pair<std::string, std::string>> ICU_HOUR_PE = {
181     { "HH", "2-digit" }, { "H", "numeric" }, { "hh", "2-digit" }, { "h", "numeric" },
182     { "kk", "2-digit" }, { "k", "numeric" }, { "KK", "2-digit" }, { "K", "numeric" }
183 };
184 
185 const std::vector<std::pair<std::string, std::string>> ICU_MINUTE_PE = {
186     { "mm", "2-digit" },
187     { "m", "numeric" }
188 };
189 
190 const std::vector<std::pair<std::string, std::string>> ICU_SECOND_PE = {
191     { "ss", "2-digit" },
192     { "s", "numeric" }
193 };
194 
195 const std::vector<std::pair<std::string, std::string>> ICU_TIME_ZONE_NAME_PE = {
196     { "zzzz", "long" }, { "z", "short" },
197     { "OOOO", "longOffset" }, { "O", "shortOffset" },
198     { "vvvv", "longGeneric" }, { "v", "shortGeneric" }
199 };
200 
IcuPatternDesc(std::string property,const std::vector<std::pair<std::string,std::string>> & pairs,std::vector<std::string> allowedValues)201 IcuPatternDesc::IcuPatternDesc(std::string property, const std::vector<std::pair<std::string, std::string>> &pairs,
202     std::vector<std::string> allowedValues) : property(std::move(property)), pairs(std::move(pairs)),
203     allowedValues(std::move(allowedValues))
204 {
205     for (const auto &pair : pairs) {
206         propertyValueToPattern.emplace(pair.second, pair.first);
207     }
208 }
209 
Pattern(const std::string & twoDigitPattern,const std::string & numericPattern)210 Pattern::Pattern(const std::string& twoDigitPattern, const std::string& numericPattern)
211 {
212     IcuPatternDesc hourPatternDesc(IntlDateTimeFormat::HOUR_TAG,
213         { { twoDigitPattern, "2-digit" }, { numericPattern, "numeric" } }, { "2-digit", "numeric" });
214     std::vector<IcuPatternDesc> items = Pattern::GetIcuPatternDescs();
215     std::vector<IcuPatternDesc> result;
216     for (auto& item : items) {
217         if (item.property != IntlDateTimeFormat::HOUR_TAG) {
218             result.emplace_back(item);
219         } else {
220             result.emplace_back(hourPatternDesc);
221         }
222     }
223     data.assign(result.begin(), result.end());
224 }
225 
GetData() const226 std::vector<IcuPatternDesc> Pattern::GetData() const
227 {
228     return data;
229 }
230 
GetIcuPatternDescs()231 std::vector<IcuPatternDesc> Pattern::GetIcuPatternDescs()
232 {
233     static const std::vector<IcuPatternDesc> icuPatternDescs = {
234         IcuPatternDesc(IntlDateTimeFormat::WEEK_DAY_TAG, ICU_WEEKDAY_PE, ICU_NARROW_LONG_SHORT),
235         IcuPatternDesc(IntlDateTimeFormat::ERA_TAG, ICU_ERA_PE, ICU_NARROW_LONG_SHORT),
236         IcuPatternDesc(IntlDateTimeFormat::YEAR_TAG, ICU_YEAR_PE, ICU2_DIGIT_NUMERIC),
237         IcuPatternDesc(IntlDateTimeFormat::MONTH_TAG, ICU_MONTH_PE, ICU_NARROW_LONG_SHORT2_DIGIT_NUMERIC),
238         IcuPatternDesc(IntlDateTimeFormat::DAY_TAG, ICU_DAY_PE, ICU2_DIGIT_NUMERIC),
239         IcuPatternDesc(IntlDateTimeFormat::DAY_PERIOD_TAG, ICU_DAY_PERIOD_PE, ICU_NARROW_LONG_SHORT),
240         IcuPatternDesc(IntlDateTimeFormat::HOUR_TAG, ICU_HOUR_PE, ICU2_DIGIT_NUMERIC),
241         IcuPatternDesc(IntlDateTimeFormat::MINUTE_TAG, ICU_MINUTE_PE, ICU2_DIGIT_NUMERIC),
242         IcuPatternDesc(IntlDateTimeFormat::SECOND_TAG, ICU_SECOND_PE, ICU2_DIGIT_NUMERIC),
243         IcuPatternDesc(IntlDateTimeFormat::TIME_ZONE_NAME_TAG, ICU_TIME_ZONE_NAME_PE, ICU_LONG_SHORT)
244     };
245     return icuPatternDescs;
246 }
247 
IntlDateTimeFormat(const std::vector<std::string> & localeTags,const std::unordered_map<std::string,std::string> & configs,std::string & errMessage)248 IntlDateTimeFormat::IntlDateTimeFormat(const std::vector<std::string>& localeTags,
249     const std::unordered_map<std::string, std::string>& configs, std::string& errMessage)
250 {
251     I18nErrorCode status = I18nErrorCode::SUCCESS;
252     std::vector<std::string> requestedLocales = LocaleHelper::CanonicalizeLocaleList(localeTags, status);
253     if (status != I18nErrorCode::SUCCESS) {
254         errMessage = "invalid locale";
255         return;
256     }
257     if (!ParseConfigs(configs, errMessage)) {
258         HILOG_ERROR_I18N("IntlDateTimeFormat::IntlDateTimeFormat: Parse configs failed.");
259         return;
260     }
261     bool isValidLocale = false;
262     for (size_t i = 0; i < requestedLocales.size(); i++) {
263         std::string curLocale = requestedLocales[i];
264         UErrorCode icuStatus = U_ZERO_ERROR;
265         icuLocale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), icuStatus);
266         if (U_FAILURE(icuStatus)) {
267             HILOG_ERROR_I18N("IntlDateTimeFormat::IntlDateTimeFormat: Create icu::Locale failed, locale %{public}s.",
268                 curLocale.c_str());
269             continue;
270         }
271         if (LocaleInfo::allValidLocales.count(icuLocale.getLanguage()) > 0) {
272             isValidLocale = true;
273             IntlDateTimeFormat::InitIcuLocale();
274             break;
275         }
276     }
277     if (!isValidLocale) {
278         UErrorCode icuStatus = U_ZERO_ERROR;
279         icuLocale = icu::Locale::forLanguageTag(icu::StringPiece("en-US"), icuStatus);
280         if (U_FAILURE(icuStatus)) {
281             HILOG_ERROR_I18N("IntlDateTimeFormat::IntlDateTimeFormat: Create en-US icu::Locale failed.");
282             return;
283         }
284     }
285     if (!IntlDateTimeFormat::InitIntlDateTimeFormat(configs, errMessage)) {
286         HILOG_ERROR_I18N("IntlDateTimeFormat::IntlDateTimeFormat: Init IntlDateTimeFormat failed.");
287         return;
288     }
289     initSuccess = true;
290 }
291 
~IntlDateTimeFormat()292 IntlDateTimeFormat::~IntlDateTimeFormat()
293 {
294     if (icuDateTimePatternGenerator != nullptr) {
295         delete icuDateTimePatternGenerator;
296         icuDateTimePatternGenerator = nullptr;
297     }
298 
299     if (dateIntervalFormat != nullptr) {
300         delete dateIntervalFormat;
301         dateIntervalFormat = nullptr;
302     }
303 }
304 
Format(double milliseconds)305 std::string IntlDateTimeFormat::Format(double milliseconds)
306 {
307     if (!initSuccess || icuSimpleDateFormat == nullptr) {
308         HILOG_ERROR_I18N("IntlDateTimeFormat::Format: Init failed.");
309         return "";
310     }
311     milliseconds = LocaleHelper::TruncateDouble(milliseconds);
312     icu::UnicodeString formatResult;
313     icuSimpleDateFormat->format(milliseconds, formatResult);
314     std::string result;
315     formatResult.toUTF8String(result);
316     return result;
317 }
318 
FormatToParts(double milliseconds,std::string & errMessage)319 std::vector<std::pair<std::string, std::string>> IntlDateTimeFormat::FormatToParts(double milliseconds,
320     std::string& errMessage)
321 {
322     if (!initSuccess || icuSimpleDateFormat == nullptr) {
323         HILOG_ERROR_I18N("IntlDateTimeFormat::Format: Init failed.");
324         return {};
325     }
326     icu::FieldPositionIterator fieldPositionIter;
327     icu::UnicodeString formattedParts;
328     UErrorCode status = U_ZERO_ERROR;
329     icuSimpleDateFormat->format(milliseconds, formattedParts, &fieldPositionIter, status);
330     if (U_FAILURE(status)) {
331         errMessage = "format failed";
332         return {};
333     }
334 
335     int32_t index = 0;
336     int32_t preEdgePos = 0;
337     std::vector<CommonDateFormatPart> parts;
338     icu::FieldPosition fieldPosition;
339     while (fieldPositionIter.next(fieldPosition)) {
340         int32_t fField = fieldPosition.getField();
341         int32_t fBeginIndex = fieldPosition.getBeginIndex();
342         int32_t fEndIndex = fieldPosition.getEndIndex();
343         if (preEdgePos < fBeginIndex) {
344             parts.emplace_back(CommonDateFormatPart(fField, preEdgePos, fBeginIndex, index, true));
345             ++index;
346         }
347         parts.emplace_back(CommonDateFormatPart(fField, fBeginIndex, fEndIndex, index, false));
348         preEdgePos = fEndIndex;
349         ++index;
350     }
351     int32_t length = formattedParts.length();
352     if (preEdgePos < length) {
353         parts.emplace_back(CommonDateFormatPart(-1, preEdgePos, length, index, true));
354     }
355     std::vector<std::pair<std::string, std::string>> result(parts.size());
356     for (size_t i = 0; i < parts.size(); ++i) {
357         CommonDateFormatPart part = parts[i];
358         std::string value;
359         icu::UnicodeString tempString = formattedParts.tempSubStringBetween(part.fBeginIndex, part.fEndIndex);
360         tempString.toUTF8String(value);
361         std::string type;
362         if (part.isPreExist) {
363             type = ConvertFieldIdToDateType(-1);
364         } else {
365             type = ConvertFieldIdToDateType(part.fField);
366         }
367         result[i] = std::make_pair(type, value);
368     }
369     return result;
370 }
371 
FormatRange(double start,double end,std::string & errMessage)372 std::string IntlDateTimeFormat::FormatRange(double start, double end, std::string& errMessage)
373 {
374     if (!initSuccess) {
375         HILOG_ERROR_I18N("IntlDateTimeFormat::FormatRange: Init failed.");
376         return "";
377     }
378     if (dateIntervalFormat == nullptr) {
379         if (!InitDateIntervalFormat()) {
380             HILOG_ERROR_I18N("IntlDateTimeFormat::FormatRange: Init dateIntervalFormat failed.");
381             return "";
382         }
383     }
384     start = LocaleHelper::TruncateDouble(start);
385     end = LocaleHelper::TruncateDouble(end);
386     UErrorCode status = U_ZERO_ERROR;
387     icu::DateInterval dateInterval(start, end);
388     icu::FormattedDateInterval formatted = dateIntervalFormat->formatToValue(dateInterval, status);
389     if (U_FAILURE(status)) {
390         HILOG_ERROR_I18N("IntlDateTimeFormat::FormatRange: Format to value failed.");
391         return "";
392     }
393     icu::UnicodeString formatResult = formatted.toString(status);
394     if (U_FAILURE(status)) {
395         errMessage = "format to string failed";
396         return "";
397     }
398     bool outputRange = false;
399     icu::ConstrainedFieldPosition cfpos;
400     while (formatted.nextPosition(cfpos, status) != 0 && U_SUCCESS(status)) {
401         if (cfpos.getCategory() == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
402             outputRange = true;
403             break;
404         }
405     }
406 
407     if (!outputRange) {
408         return Format(start);
409     }
410     std::string result;
411     formatResult.toUTF8String(result);
412     return result;
413 }
414 
FormatRangeToParts(double start,double end,std::string & errMessage)415 std::vector<std::pair<std::string, std::string>> IntlDateTimeFormat::FormatRangeToParts(double start,
416     double end, std::string& errMessage)
417 {
418     if (!initSuccess) {
419         HILOG_ERROR_I18N("IntlDateTimeFormat::FormatRange: Init failed.");
420         return {};
421     }
422     if (dateIntervalFormat == nullptr) {
423         if (!InitDateIntervalFormat()) {
424             HILOG_ERROR_I18N("IntlDateTimeFormat::FormatRange: Init dateIntervalFormat failed.");
425             return {};
426         }
427     }
428     start = LocaleHelper::TruncateDouble(start);
429     end = LocaleHelper::TruncateDouble(end);
430     UErrorCode status = U_ZERO_ERROR;
431     icu::DateInterval dateInterval(start, end);
432     icu::FormattedDateInterval formatted = dateIntervalFormat->formatToValue(dateInterval, status);
433     if (U_FAILURE(status)) {
434         HILOG_ERROR_I18N("IntlDateTimeFormat::FormatRange: Format to value failed.");
435         return {};
436     }
437     icu::UnicodeString formattedParts;
438     std::vector<CommonDateFormatPart> parts = CreateDateIntervalParts(formatted, formattedParts, errMessage);
439     if (!errMessage.empty()) {
440         return {};
441     }
442     std::vector<std::pair<std::string, std::string>> result(parts.size());
443     for (size_t i = 0; i < parts.size(); ++i) {
444         CommonDateFormatPart part = parts[i];
445         std::string value;
446         icu::UnicodeString tempString = formattedParts.tempSubStringBetween(part.fBeginIndex, part.fEndIndex);
447         tempString.toUTF8String(value);
448         std::string type;
449         if (part.isPreExist) {
450             type = ConvertFieldIdToDateType(-1);
451         } else {
452             type = ConvertFieldIdToDateType(part.fField);
453         }
454         result[i] = std::make_pair(type, value);
455     }
456     return result;
457 }
458 
ResolvedOptions(std::unordered_map<std::string,std::string> & configs)459 void IntlDateTimeFormat::ResolvedOptions(std::unordered_map<std::string, std::string>& configs)
460 {
461     configs.clear();
462     if (!localeString.empty()) {
463         configs.insert(std::make_pair(LOCALE_TAG, localeString));
464     }
465     if (numberingSystem.empty()) {
466         configs.insert(std::make_pair(NUMBERING_SYSTEM_TAG, DEFAULT_NUMBERING_SYSTEM));
467     } else {
468         configs.insert(std::make_pair(NUMBERING_SYSTEM_TAG, numberingSystem));
469     }
470     IntlDateTimeFormat::ResolvedCalendarAndTimeZone(configs);
471     if (!hourCycle.empty()) {
472         configs.insert(std::make_pair(HOUR_CYCLE_TAG, hourCycle));
473         std::string hour12String = (hourCycle == HOUR_CYCLE_11 || hourCycle == HOUR_CYCLE_12) ?
474             "true" : "false";
475         configs.insert(std::make_pair(HOUR12_TAG, hour12String));
476     }
477     IntlDateTimeFormat::ResolvedDateTimeStyle(configs);
478 }
479 
SupportedLocalesOf(const std::vector<std::string> & requestLocales,const std::map<std::string,std::string> & configs,I18nErrorCode & status)480 std::vector<std::string> IntlDateTimeFormat::SupportedLocalesOf(const std::vector<std::string>& requestLocales,
481     const std::map<std::string, std::string>& configs, I18nErrorCode& status)
482 {
483     std::vector<std::string> undefined = {};
484     auto requestedLocales = LocaleHelper::CanonicalizeLocaleList(requestLocales, status);
485     if (status != I18nErrorCode::SUCCESS) {
486         HILOG_ERROR_I18N("IntlDateTimeFormat::SupportedLocalesOf: Call CanonicalizeLocaleList failed.");
487         return undefined;
488     }
489 
490     LocaleHelper::ParseOption(configs, "localeMatcher", "best fit", true, status);
491     if (status != I18nErrorCode::SUCCESS) {
492         return undefined;
493     }
494     std::set<std::string> availableLocales = LocaleInfo::GetValidLocales();
495     return LocaleHelper::LookupSupportedLocales(availableLocales, requestedLocales);
496 }
497 
ParseConfigs(const std::unordered_map<std::string,std::string> & configs,std::string & errMessage)498 bool IntlDateTimeFormat::ParseConfigs(const std::unordered_map<std::string, std::string>& configs,
499     std::string& errMessage)
500 {
501     auto iter = configs.find(IntlDateTimeFormat::LOCALE_MATCHER_TAG);
502     if (iter != configs.end()) {
503         if (!LocaleHelper::IsValidOptionName(IntlDateTimeFormat::LOCALE_MATCHER_TAG, iter->second)) {
504             errMessage = "getStringOption failed";
505             return false;
506         }
507         localeMatcher = iter->second;
508     }
509 
510     iter = configs.find(IntlDateTimeFormat::CALENDAR_TAG);
511     if (iter != configs.end()) {
512         calendar = iter->second;
513         if (!LocaleHelper::IsNormativeCalendar(calendar)) {
514             errMessage = "invalid calendar";
515             return false;
516         }
517     }
518 
519     iter = configs.find(IntlDateTimeFormat::NUMBERING_SYSTEM_TAG);
520     if (iter != configs.end()) {
521         numberingSystem = iter->second;
522         if (!LocaleHelper::IsNormativeNumberingSystem(numberingSystem)) {
523             errMessage = "invalid numberingSystem";
524             return false;
525         }
526     }
527 
528     iter = configs.find(IntlDateTimeFormat::HOUR12_TAG);
529     if (iter != configs.end()) {
530         hour12 = iter->second;
531     }
532 
533     iter = configs.find(IntlDateTimeFormat::HOUR_CYCLE_TAG);
534     if (iter != configs.end()) {
535         if (!LocaleHelper::IsValidOptionName(IntlDateTimeFormat::HOUR_CYCLE_TAG, iter->second)) {
536             errMessage = "getStringOption failed";
537             return false;
538         }
539         if (hour12.empty()) {
540             hourCycle = iter->second;
541         }
542     }
543 
544     return true;
545 }
546 
InitIntlDateTimeFormat(const std::unordered_map<std::string,std::string> & configs,std::string & errMessage)547 bool IntlDateTimeFormat::InitIntlDateTimeFormat(const std::unordered_map<std::string, std::string>& configs,
548     std::string& errMessage)
549 {
550     if (!IntlDateTimeFormat::InitIcuTimeZone(configs, errMessage)) {
551         HILOG_ERROR_I18N("IntlDateTimeFormat::InitIntlDateTimeFormat: Init icuTimeZone failed.");
552         return false;
553     }
554     if (!IntlDateTimeFormat::InitDateTimePatternGenerator(errMessage)) {
555         HILOG_ERROR_I18N("IntlDateTimeFormat::InitIntlDateTimeFormat: Init icuDateTimePatternGenerator failed.");
556         return false;
557     }
558     std::string effectiveHourCycle;
559     if (!IntlDateTimeFormat::InitHourCycle(effectiveHourCycle)) {
560         HILOG_ERROR_I18N("IntlDateTimeFormat::InitIntlDateTimeFormat: Init hour cycle failed.");
561         return false;
562     }
563     bool isHourDefined = false;
564     bool isExistComponents = false;
565     if (!IntlDateTimeFormat::InitSkeleton(configs, effectiveHourCycle, isHourDefined, isExistComponents, errMessage)) {
566         HILOG_ERROR_I18N("IntlDateTimeFormat::InitIntlDateTimeFormat: Init skeleton failed.");
567         return false;
568     }
569     if (!IntlDateTimeFormat::InitDateTimeStyle(configs, effectiveHourCycle, isHourDefined, errMessage)) {
570         HILOG_ERROR_I18N("IntlDateTimeFormat::InitIntlDateTimeFormat: Init date time style failed.");
571         return false;
572     }
573     if (!IntlDateTimeFormat::InitIcuSimpleDateFormat(isExistComponents, errMessage)) {
574         HILOG_ERROR_I18N("IntlDateTimeFormat::InitIntlDateTimeFormat: Init icuSimpleDateFormat failed.");
575         return false;
576     }
577     return true;
578 }
579 
InitIcuLocale()580 void IntlDateTimeFormat::InitIcuLocale()
581 {
582     if (!calendar.empty()) {
583         if (!uloc_toLegacyType(uloc_toLegacyKey("ca"), calendar.c_str())) {
584             HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuLocale: Invalid calendar %{public}s.", calendar.c_str());
585         } else {
586             UErrorCode icuStatus = U_ZERO_ERROR;
587             icuLocale.setUnicodeKeywordValue("ca", calendar, icuStatus);
588             if (U_FAILURE(icuStatus)) {
589                 HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuLocale: Set icuLocale calendar failed.");
590             }
591         }
592     }
593 
594     if (!numberingSystem.empty()) {
595         if (!uloc_toLegacyType(uloc_toLegacyKey("nu"), numberingSystem.c_str())) {
596             HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuLocale: Invalid numberingSystem %{public}s.",
597                 numberingSystem.c_str());
598         } else {
599             UErrorCode icuStatus = U_ZERO_ERROR;
600             icuLocale.setUnicodeKeywordValue("nu", numberingSystem, icuStatus);
601             if (U_FAILURE(icuStatus)) {
602                 HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuLocale: Set icuLocale calendar failed.");
603             }
604         }
605     }
606 }
607 
InitIcuTimeZone(const std::unordered_map<std::string,std::string> & configs,std::string & errMessage)608 bool IntlDateTimeFormat::InitIcuTimeZone(const std::unordered_map<std::string, std::string>& configs,
609     std::string& errMessage)
610 {
611     auto it = configs.find(IntlDateTimeFormat::TIME_ZONE_TAG);
612     if (it != configs.end()) {
613         timeZone = it->second;
614     }
615 
616     if (timeZone.empty()) {
617         icuTimeZone = icu::TimeZone::createDefault();
618         if (icuTimeZone == nullptr) {
619             HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuTimeZone: Create default time zone.");
620             return false;
621         }
622     } else {
623         icuTimeZone = icu::TimeZone::createTimeZone(timeZone.c_str());
624         if (icuTimeZone == nullptr || !IsValidTimeZoneName(*icuTimeZone)) {
625             errMessage = "invalid timeZone";
626             return false;
627         }
628     }
629     return true;
630 }
631 
InitDateTimePatternGenerator(std::string & errMessage)632 bool IntlDateTimeFormat::InitDateTimePatternGenerator(std::string& errMessage)
633 {
634     UErrorCode icuStatus = U_ZERO_ERROR;
635     icuDateTimePatternGenerator = icu::DateTimePatternGenerator::createInstance(icuLocale, icuStatus);
636     if (U_FAILURE(icuStatus) || icuDateTimePatternGenerator == nullptr) {
637         if (icuStatus == UErrorCode::U_MISSING_RESOURCE_ERROR) {
638             errMessage = "can not find icu data resources";
639         } else {
640             errMessage = "create icu::DateTimePatternGenerator failed";
641         }
642         return false;
643     }
644     return true;
645 }
646 
InitHourCycle(std::string & effectiveHourCycle)647 bool IntlDateTimeFormat::InitHourCycle(std::string& effectiveHourCycle)
648 {
649     if (icuDateTimePatternGenerator == nullptr) {
650         HILOG_ERROR_I18N("IntlDateTimeFormat::InitHourCycle: icuDateTimePatternGenerator is nullptr.");
651         return false;
652     }
653     UErrorCode icuStatus = U_ZERO_ERROR;
654     std::string defaultHourCycle =
655         ConvertUDateFormatHourCycleToString(icuDateTimePatternGenerator->getDefaultHourCycle(icuStatus));
656     if (U_FAILURE(icuStatus)) {
657         HILOG_ERROR_I18N("IntlDateTimeFormat::InitHourCycle: Get default hour cycle failed.");
658         return false;
659     }
660     effectiveHourCycle = hourCycle;
661     if (hourCycle.empty()) {
662         effectiveHourCycle = defaultHourCycle;
663     }
664     if (!hour12.empty()) {
665         if (hour12.compare("true") == 0) {
666             if (defaultHourCycle.compare(HOUR_CYCLE_11) == 0 || defaultHourCycle.compare(HOUR_CYCLE_23) == 0) {
667                 effectiveHourCycle = HOUR_CYCLE_11;
668             } else {
669                 effectiveHourCycle = HOUR_CYCLE_12;
670             }
671         } else {
672             if (defaultHourCycle.compare(HOUR_CYCLE_11) == 0 || defaultHourCycle.compare(HOUR_CYCLE_23) == 0) {
673                 effectiveHourCycle = HOUR_CYCLE_23;
674             } else {
675                 effectiveHourCycle = HOUR_CYCLE_24;
676             }
677         }
678     }
679     return true;
680 }
681 
InitSkeleton(const std::unordered_map<std::string,std::string> & configs,const std::string & effectiveHourCycle,bool & isHourDefined,bool & isExistComponents,std::string & errMessage)682 bool IntlDateTimeFormat::InitSkeleton(const std::unordered_map<std::string, std::string>& configs,
683     const std::string& effectiveHourCycle, bool& isHourDefined, bool& isExistComponents, std::string& errMessage)
684 {
685     std::vector<IcuPatternDesc> data = GetIcuPatternDesc(effectiveHourCycle);
686     skeletonOpts.clear();
687 
688     for (const IcuPatternDesc &item : data) {
689         if (item.property.compare(TIME_ZONE_NAME_TAG) == 0) {
690             int32_t secondDigitsString = GetFractionalSecondDigit(configs);
691             skeleton.append(secondDigitsString, 'S');
692             if (secondDigitsString > 0) {
693                 isExistComponents = true;
694                 skeletonOpts.emplace_back(item.property);
695             }
696         }
697 
698         auto iter = configs.find(item.property);
699         if (iter != configs.end()) {
700             std::string value = iter->second;
701             if (std::find(item.allowedValues.begin(), item.allowedValues.end(), value) == item.allowedValues.end()) {
702                 errMessage = "Value out of range for locale options property";
703                 return false;
704             }
705             skeletonOpts.emplace_back(item.property);
706             isExistComponents = true;
707             auto iterPattern = item.propertyValueToPattern.find(value);
708             if (iterPattern != item.propertyValueToPattern.end()) {
709                 skeleton += iterPattern->second;
710             }
711             isHourDefined = (item.property.compare(HOUR_TAG) == 0) ? true : isHourDefined;
712         }
713     }
714 
715     return true;
716 }
717 
GetIcuPatternDesc(const std::string & effectiveHourCycle)718 std::vector<IcuPatternDesc> IntlDateTimeFormat::GetIcuPatternDesc(const std::string& effectiveHourCycle)
719 {
720     auto iter = HOUR_CYCLE_TO_PATTERN.find(effectiveHourCycle);
721     if (iter == HOUR_CYCLE_TO_PATTERN.end()) {
722         Pattern pattern("jj", "j");
723         return pattern.GetData();
724     }
725     Pattern pattern(iter->second.first, iter->second.second);
726     return pattern.GetData();
727 }
728 
GetFractionalSecondDigit(const std::unordered_map<std::string,std::string> & configs)729 int32_t IntlDateTimeFormat::GetFractionalSecondDigit(const std::unordered_map<std::string, std::string>& configs)
730 {
731     auto iter = configs.find(IntlDateTimeFormat::FRACTIONAL_SECOND_DIGITS_TAG);
732     if (iter != configs.end()) {
733         fractionalSecondDigits = iter->second;
734     }
735     int32_t result = 0;
736     if (!fractionalSecondDigits.empty()) {
737         int32_t status = 0;
738         int32_t temp = ConvertString2Int(fractionalSecondDigits, status);
739         if (status == 0) {
740             result = temp;
741         }
742     }
743     if (result < MIN_VALUE_OF_FRACTIONAL_SECOND_DIGITS || result > MAX_VALUE_OF_FRACTIONAL_SECOND_DIGITS) {
744         return 0;
745     }
746     return result;
747 }
748 
InitDateTimeStyle(const std::unordered_map<std::string,std::string> & configs,const std::string & effectiveHourCycle,bool isHourDefined,std::string & errMessage)749 bool IntlDateTimeFormat::InitDateTimeStyle(const std::unordered_map<std::string, std::string>& configs,
750     const std::string& effectiveHourCycle, bool isHourDefined, std::string& errMessage)
751 {
752     auto iter = configs.find(FORMAT_MATCHER_TAG);
753     if (iter != configs.end()) {
754         if (FORMAT_MATCHER_VALUE.find(iter->second) == FORMAT_MATCHER_VALUE.end()) {
755             errMessage = "Value out of range for locale options property";
756             return false;
757         } else {
758             formatMatcher = iter->second;
759         }
760     }
761 
762     iter = configs.find(DATE_STYLE_TAG);
763     if (iter != configs.end()) {
764         if (DATE_TIME_STYLE_VALUE.find(iter->second) == DATE_TIME_STYLE_VALUE.end()) {
765             errMessage = "Value out of range for locale options property";
766             return false;
767         } else {
768             dateStyle = iter->second;
769         }
770     }
771 
772     iter = configs.find(TIME_STYLE_TAG);
773     if (iter != configs.end()) {
774         if (DATE_TIME_STYLE_VALUE.find(iter->second) == DATE_TIME_STYLE_VALUE.end()) {
775             errMessage = "Value out of range for locale options property";
776             return false;
777         } else {
778             timeStyle = iter->second;
779         }
780     }
781     std::string tempHourCycle;
782     if (!timeStyle.empty()) {
783         tempHourCycle = effectiveHourCycle;
784     }
785     if (dateStyle.empty() && timeStyle.empty()) {
786         IntlDateTimeFormat::InitDateTimeSkeleton(skeletonOpts, skeleton, effectiveHourCycle);
787         if (isHourDefined) {
788             tempHourCycle = effectiveHourCycle;
789         }
790     }
791     hourCycle = tempHourCycle;
792     return true;
793 }
794 
InitIcuSimpleDateFormat(bool isExistComponents,std::string & errMessage)795 bool IntlDateTimeFormat::InitIcuSimpleDateFormat(bool isExistComponents, std::string& errMessage)
796 {
797     if (icuDateTimePatternGenerator == nullptr) {
798         HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuSimpleDateFormat: icuDateTimePatternGenerator is nullptr.");
799         return false;
800     }
801     icu::UnicodeString dtfSkeleton(skeleton.c_str());
802     UErrorCode icuStatus = U_ZERO_ERROR;
803     icu::UnicodeString bestPattern = icuDateTimePatternGenerator->getBestPattern(dtfSkeleton,
804         UDATPG_MATCH_HOUR_FIELD_LENGTH, icuStatus);
805     if (U_FAILURE(icuStatus)) {
806         HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuSimpleDateFormat: Get best pattern failed.");
807         return false;
808     }
809     icu::UnicodeString pattern = IntlDateTimeFormat::ChangeHourCyclePattern(bestPattern, hourCycle);
810     icuSimpleDateFormat = std::make_unique<icu::SimpleDateFormat>(pattern, icuLocale, icuStatus);
811     if (!dateStyle.empty() || !timeStyle.empty()) {
812         if (isExistComponents) {
813             errMessage = "Invalid option : option";
814             return false;
815         }
816         icuSimpleDateFormat = IntlDateTimeFormat::GetSimpleDateFormatFromStyle();
817     }
818     if (U_FAILURE(icuStatus)) {
819         icuSimpleDateFormat = std::unique_ptr<icu::SimpleDateFormat>();
820     }
821     if (IntlDateTimeFormat::InitIcuCalendar() && icuSimpleDateFormat != nullptr) {
822         icuSimpleDateFormat->adoptCalendar(icuCalendar);
823     }
824     std::string icuLocaleName = icuLocale.getName();
825     if (icuLocaleName.find("calendar=iso8601") != std::string::npos) {
826         isIso8601 = true;
827     }
828     localeString = icuLocale.toLanguageTag<std::string>(icuStatus);
829     if (U_FAILURE(icuStatus)) {
830         HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuSimpleDateFormat: Get languageTag from icuLocale failed.");
831         return false;
832     }
833     return true;
834 }
835 
InitDateTimeSkeleton(const std::vector<std::string> & options,std::string & skeleton,const std::string & hc)836 void IntlDateTimeFormat::InitDateTimeSkeleton(const std::vector<std::string> &options,
837     std::string &skeleton, const std::string& hc)
838 {
839     bool needDefaults = true;
840 
841     for (const auto& element : DATE_TIME_ELEMENT) {
842         if (std::find(options.begin(), options.end(), element) != options.end()) {
843             needDefaults = false;
844             break;
845         }
846     }
847 
848     if (needDefaults) {
849         skeleton += "yMd";
850     }
851 }
852 
ChangeHourCyclePattern(const icu::UnicodeString & pattern,const std::string & hc)853 icu::UnicodeString IntlDateTimeFormat::ChangeHourCyclePattern(const icu::UnicodeString &pattern, const std::string& hc)
854 {
855     if (hc.empty()) {
856         return pattern;
857     }
858     icu::UnicodeString result;
859     char16_t key =  u'\0';
860     auto iter = std::find_if(HOUR_CYCLE_MAP.begin(), HOUR_CYCLE_MAP.end(),
861         [hc](const std::map<char16_t, std::string>::value_type item) {
862             return item.second == hc;
863     });
864     if (iter != HOUR_CYCLE_MAP.end()) {
865         key = iter->first;
866     }
867 
868     bool needChange = true;
869     char16_t last = u'\0';
870     for (int32_t i = 0; i < pattern.length(); i++) {
871         char16_t ch = pattern.charAt(i);
872         if (ch == '\'') {
873             needChange = !needChange;
874             result.append(ch);
875         } else if (HOUR_CYCLE_MAP.find(ch) != HOUR_CYCLE_MAP.end()) {
876             result = (needChange && last == u'd') ? result.append(' ') : result;
877             result.append(needChange ? key : ch);
878         } else {
879             result.append(ch);
880         }
881         last = ch;
882     }
883     return result;
884 }
885 
GetSimpleDateFormatFromStyle()886 std::unique_ptr<icu::SimpleDateFormat> IntlDateTimeFormat::GetSimpleDateFormatFromStyle()
887 {
888     std::unique_ptr<icu::SimpleDateFormat> result;
889     icu::DateFormat::EStyle icuDateStyle = GetEStyleFromDateTimeStyle(dateStyle);
890     icu::DateFormat::EStyle icuTimeStyle = GetEStyleFromDateTimeStyle(timeStyle);
891     result.reset(reinterpret_cast<icu::SimpleDateFormat *>(
892         icu::DateFormat::createDateTimeInstance(icuDateStyle, icuTimeStyle, icuLocale)));
893     UErrorCode status = U_ZERO_ERROR;
894     icu::UnicodeString pattern("");
895     if (result == nullptr) {
896         return nullptr;
897     }
898     pattern = result->toPattern(pattern);
899     icu::UnicodeString skeleton = icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
900     if (U_FAILURE(status)) {
901         HILOG_ERROR_I18N("IntlDateTimeFormat::GetSimpleDateFormatFromStyle: Get skeleton failed.");
902         return nullptr;
903     }
904     if (hourCycle == GetHourCycleFromPattern(pattern)) {
905         return result;
906     }
907     skeleton = ReplaceHourCycleOfSkeleton(skeleton, hourCycle);
908     if (icuDateTimePatternGenerator == nullptr) {
909         HILOG_ERROR_I18N("IntlDateTimeFormat::GetSimpleDateFormatFromStyle: icuDateTimePatternGenerator is nullptr.");
910         return nullptr;
911     }
912     pattern = icuDateTimePatternGenerator->getBestPattern(skeleton, UDATPG_MATCH_HOUR_FIELD_LENGTH, status);
913     if (U_FAILURE(status)) {
914         HILOG_ERROR_I18N("IntlDateTimeFormat::GetSimpleDateFormatFromStyle: Get best pattern failed.");
915         return nullptr;
916     }
917     result = std::make_unique<icu::SimpleDateFormat>(pattern, icuLocale, status);
918     if (U_FAILURE(status)) {
919         HILOG_ERROR_I18N("IntlDateTimeFormat::GetSimpleDateFormatFromStyle: Create SimpleDateFormat failed.");
920         return nullptr;
921     }
922     return result;
923 }
924 
GetEStyleFromDateTimeStyle(const std::string & style)925 icu::DateFormat::EStyle IntlDateTimeFormat::GetEStyleFromDateTimeStyle(const std::string& style)
926 {
927     auto iter = DATE_TIME_STYLE_TO_ESTYLE.find(style);
928     if (iter == DATE_TIME_STYLE_TO_ESTYLE.end()) {
929         return icu::DateFormat::kNone;
930     }
931     return iter->second;
932 }
933 
GetHourCycleFromPattern(const icu::UnicodeString & pattern)934 std::string IntlDateTimeFormat::GetHourCycleFromPattern(const icu::UnicodeString& pattern)
935 {
936     bool inQuote = false;
937     for (int32_t i = 0; i < pattern.length(); i++) {
938         char16_t ch = pattern[i];
939         if (ch == '\'') {
940             inQuote = !inQuote;
941             continue;
942         }
943         auto iter = HOUR_CYCLE_MAP.find(ch);
944         if (iter == HOUR_CYCLE_MAP.end() || inQuote) {
945             continue;
946         }
947         return iter->second;
948     }
949     return "";
950 }
951 
ReplaceHourCycleOfSkeleton(const icu::UnicodeString & skeleton,const std::string & hc)952 icu::UnicodeString IntlDateTimeFormat::ReplaceHourCycleOfSkeleton(const icu::UnicodeString& skeleton,
953     const std::string& hc)
954 {
955     icu::UnicodeString result;
956 
957     auto iter = std::find_if(HOUR_CYCLE_MAP.begin(), HOUR_CYCLE_MAP.end(),
958         [hc](const std::map<char16_t, std::string>::value_type item) {
959             return item.second == hc;
960     });
961     if (iter == HOUR_CYCLE_MAP.end()) {
962         return skeleton;
963     }
964     char16_t convertChar = iter->first;
965 
966     int skeletonLength = skeleton.length();
967     for (int32_t i = 0; i < skeletonLength; ++i) {
968         if (skeleton[i] == 'a' || skeleton[i] == 'b' || skeleton[i] == 'B') {
969             continue;
970         }
971         if (HOUR_CYCLE_MAP.find(skeleton[i]) != HOUR_CYCLE_MAP.end()) {
972             result += convertChar;
973             continue;
974         }
975         result += skeleton[i];
976     }
977     return result;
978 }
979 
InitIcuCalendar()980 bool IntlDateTimeFormat::InitIcuCalendar()
981 {
982     if (icuTimeZone == nullptr) {
983         HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuCalendar: icuTimeZone is nullptr.");
984         return false;
985     }
986     UErrorCode icuStatus = U_ZERO_ERROR;
987     icuCalendar = icu::Calendar::createInstance(icuTimeZone, icuLocale, icuStatus);
988     if (U_FAILURE(icuStatus) || icuCalendar == nullptr) {
989         HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuCalendar: Create icuCalendar failed.");
990         return false;
991     }
992 
993     if (icuCalendar->getDynamicClassID() == icu::GregorianCalendar::getStaticClassID()) {
994         auto gregorianCalendar = static_cast<icu::GregorianCalendar *>(icuCalendar);
995         // ECMAScript start time, value = -(2**53)
996         const double beginTime = -9007199254740992;
997         if (gregorianCalendar == nullptr) {
998             HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuCalendar: Static cast failed.");
999             return false;
1000         }
1001         gregorianCalendar->setGregorianChange(beginTime, icuStatus);
1002         if (U_FAILURE(icuStatus)) {
1003             HILOG_ERROR_I18N("IntlDateTimeFormat::InitIcuCalendar: Set gregorian change failed.");
1004             return false;
1005         }
1006     }
1007     return true;
1008 }
1009 
InitDateIntervalFormat()1010 bool IntlDateTimeFormat::InitDateIntervalFormat()
1011 {
1012     if (icuSimpleDateFormat == nullptr) {
1013         HILOG_ERROR_I18N("IntlDateTimeFormat::InitDateIntervalFormat: icuSimpleDateFormat is nullptr.");
1014         return false;
1015     }
1016     icu::UnicodeString pattern;
1017     icuSimpleDateFormat->toPattern(pattern);
1018     UErrorCode status = U_ZERO_ERROR;
1019     icu::UnicodeString skeleton = icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
1020     if (U_FAILURE(status)) {
1021         HILOG_ERROR_I18N("IntlDateTimeFormat::InitDateIntervalFormat: Get skeleton failed.");
1022         return false;
1023     }
1024 
1025     dateIntervalFormat = icu::DateIntervalFormat::createInstance(skeleton, icuLocale, status);
1026     if (U_FAILURE(status) || dateIntervalFormat == nullptr) {
1027         HILOG_ERROR_I18N("IntlDateTimeFormat::InitDateIntervalFormat: Create dateIntervalFormat failed.");
1028         return false;
1029     }
1030     dateIntervalFormat->setTimeZone(*icuTimeZone);
1031     return true;
1032 }
1033 
ConvertUDateFormatHourCycleToString(UDateFormatHourCycle hc)1034 std::string IntlDateTimeFormat::ConvertUDateFormatHourCycleToString(UDateFormatHourCycle hc)
1035 {
1036     auto iter = UDATE_FORMAT_HOUR_CYCLE_TO_STRING.find(hc);
1037     if (iter == UDATE_FORMAT_HOUR_CYCLE_TO_STRING.end()) {
1038         HILOG_ERROR_I18N("IntlDateTimeFormat::ConvertUDateFormatHourCycleToString: Invalid hourCycle.");
1039         return "";
1040     }
1041     return iter->second;
1042 }
1043 
ConvertFieldIdToDateType(int32_t fieldId)1044 std::string IntlDateTimeFormat::ConvertFieldIdToDateType(int32_t fieldId)
1045 {
1046     auto iter = FIELD_ID_TO_DATE_TYPE.find(fieldId);
1047     if (iter == FIELD_ID_TO_DATE_TYPE.end()) {
1048         return "literal";
1049     }
1050     return iter->second;
1051 }
1052 
CreateDateIntervalParts(const icu::FormattedDateInterval & formatted,icu::UnicodeString & formattedValue,std::string & errMessage)1053 std::vector<CommonDateFormatPart> IntlDateTimeFormat::CreateDateIntervalParts(
1054     const icu::FormattedDateInterval& formatted, icu::UnicodeString& formattedValue, std::string& errMessage)
1055 {
1056     UErrorCode status = U_ZERO_ERROR;
1057     formattedValue = formatted.toTempString(status);
1058     if (U_FAILURE(status)) {
1059         HILOG_ERROR_I18N("IntlDateTimeFormat::CreateDateIntervalParts: Get formatted value failed.");
1060         return {};
1061     }
1062     int32_t index = 0;
1063     int32_t preEndPos = 0;
1064     std::vector<int32_t> begin(NUMBER_OF_PARAMETER, 0);
1065     std::vector<int32_t> end(NUMBER_OF_PARAMETER, 0);
1066     std::vector<CommonDateFormatPart> parts;
1067     icu::ConstrainedFieldPosition cfpos;
1068     while (formatted.nextPosition(cfpos, status) && U_SUCCESS(status)) {
1069         int32_t fCategory = cfpos.getCategory();
1070         int32_t fField = cfpos.getField();
1071         int32_t fStart = cfpos.getStart();
1072         int32_t fLimit = cfpos.getLimit();
1073 
1074         if (fCategory == UFIELD_CATEGORY_DATE_INTERVAL_SPAN && (fField == 0 || fField == 1)) {
1075             begin[fField] = fStart;
1076             end[fField] = fLimit;
1077         }
1078         if (fCategory == UFIELD_CATEGORY_DATE) {
1079             if (preEndPos < fStart) {
1080                 parts.emplace_back(CommonDateFormatPart(fField, preEndPos, fStart, index, true));
1081                 index++;
1082             }
1083             parts.emplace_back(CommonDateFormatPart(fField, fStart, fLimit, index, false));
1084             preEndPos = fLimit;
1085             ++index;
1086         }
1087     }
1088     if (U_FAILURE(status)) {
1089         errMessage = "format date interval error";
1090         return {};
1091     }
1092     int32_t length = formattedValue.length();
1093     if (length > preEndPos) {
1094         parts.emplace_back(CommonDateFormatPart(-1, preEndPos, length, index, true));
1095     }
1096     return parts;
1097 }
1098 
ResolvedCalendarAndTimeZone(std::unordered_map<std::string,std::string> & configs)1099 void IntlDateTimeFormat::ResolvedCalendarAndTimeZone(std::unordered_map<std::string, std::string>& configs)
1100 {
1101     if (icuCalendar == nullptr) {
1102         HILOG_ERROR_I18N("IntlDateTimeFormat::ResolvedCalendarAndTimeZone: icuCalendar is nullptr.");
1103         return;
1104     }
1105 
1106     std::string calendarString;
1107     std::string calendarType = icuCalendar->getType();
1108     if (calendarType.compare("gregorian") == 0) {
1109         if (isIso8601) {
1110             calendarString = "iso8601";
1111         } else {
1112             calendarString = "gregory";
1113         }
1114     } else if (calendarType.compare("ethiopic-amete-alem") == 0) {
1115         calendarString = "ethioaa";
1116     } else if (!calendarType.empty()) {
1117         calendarString = calendarType;
1118     }
1119     configs.insert(std::make_pair(CALENDAR_TAG, calendarString));
1120 
1121     const icu::TimeZone &icuTZ = icuCalendar->getTimeZone();
1122     icu::UnicodeString timezone;
1123     icuTZ.getID(timezone);
1124     UErrorCode status = U_ZERO_ERROR;
1125     icu::UnicodeString canonicalTimezone;
1126     icu::TimeZone::getCanonicalID(timezone, canonicalTimezone, status);
1127     if (U_FAILURE(status)) {
1128         HILOG_ERROR_I18N("IntlDateTimeFormat::ResolvedCalendarAndTimeZone: Get canonical Id failed.");
1129         return;
1130     }
1131     std::string timezoneValue;
1132     if (canonicalTimezone == UNICODE_STRING_SIMPLE("Etc/UTC") ||
1133         canonicalTimezone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
1134         timezoneValue = "UTC";
1135     } else {
1136         canonicalTimezone.toUTF8String(timezoneValue);
1137     }
1138     configs.insert(std::make_pair(TIME_ZONE_TAG, timezoneValue));
1139 }
1140 
ResolvedDateTimeStyle(std::unordered_map<std::string,std::string> & configs)1141 void IntlDateTimeFormat::ResolvedDateTimeStyle(std::unordered_map<std::string, std::string>& configs)
1142 {
1143     if (dateStyle.empty() && timeStyle.empty() && icuSimpleDateFormat != nullptr) {
1144         icu::UnicodeString patternUnicode;
1145         icuSimpleDateFormat->toPattern(patternUnicode);
1146         std::string pattern;
1147         patternUnicode.toUTF8String(pattern);
1148         for (const auto &item : Pattern::GetIcuPatternDescs()) {
1149             // fractionalSecondsDigits need to be added before timeZoneName.
1150             if (item.property == TIME_ZONE_NAME_TAG) {
1151                 int tmpResult = count(pattern.begin(), pattern.end(), 'S');
1152                 int fsd = (tmpResult >= MAX_VALUE_OF_FRACTIONAL_SECOND_DIGITS) ?
1153                     MAX_VALUE_OF_FRACTIONAL_SECOND_DIGITS : tmpResult;
1154                 if (fsd > 0) {
1155                     std::string fsdString = std::to_string(fsd);
1156                     configs.insert(std::make_pair(FRACTIONAL_SECOND_DIGITS_TAG, fsdString));
1157                 }
1158             }
1159             for (const auto &pair : item.pairs) {
1160                 if (pattern.find(pair.first) != std::string::npos) {
1161                     configs.insert(std::make_pair(item.property, pair.second));
1162                     break;
1163                 }
1164             }
1165         }
1166     }
1167     if (!dateStyle.empty()) {
1168         configs.insert(std::make_pair(DATE_STYLE_TAG, dateStyle));
1169     }
1170     if (!timeStyle.empty()) {
1171         configs.insert(std::make_pair(TIME_STYLE_TAG, timeStyle));
1172     }
1173 }
1174 
IsValidTimeZoneName(const icu::TimeZone & tz)1175 bool IntlDateTimeFormat::IsValidTimeZoneName(const icu::TimeZone &tz)
1176 {
1177     UErrorCode status = U_ZERO_ERROR;
1178     icu::UnicodeString id;
1179     tz.getID(id);
1180     icu::UnicodeString canonical;
1181     icu::TimeZone::getCanonicalID(id, canonical, status);
1182     bool canonicalFlag = (canonical != icu::UnicodeString("Etc/Unknown", -1, US_INV));
1183     return U_SUCCESS(status) && canonicalFlag;
1184 }
1185 
1186 bool IntlDateTimeFormat::icuInitialized = IntlDateTimeFormat::Init();
1187 
Init()1188 bool IntlDateTimeFormat::Init()
1189 {
1190     SetHwIcuDirectory();
1191     return true;
1192 }
1193 } // namespace I18n
1194 } // namespace Global
1195 } // namespace OHOS
1196