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