• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 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 "ecmascript/js_date_time_format.h"
17 
18 #include "ecmascript/checkpoint/thread_state_transition.h"
19 #include "ecmascript/intl/locale_helper.h"
20 #include "ecmascript/global_env.h"
21 #include "ecmascript/js_date.h"
22 #include "ecmascript/js_function.h"
23 #include "ecmascript/js_intl.h"
24 #include "ecmascript/object_factory-inl.h"
25 
26 namespace panda::ecmascript {
27 struct CommonDateFormatPart {
28     int32_t fField = 0;
29     int32_t fBeginIndex = 0;   // NOLINT(misc-non-private-member-variables-in-classes)
30     int32_t fEndIndex = 0;  // NOLINT(misc-non-private-member-variables-in-classes)
31     int32_t index = 0;    // NOLINT(misc-non-private-member-variables-in-classes)
32     bool isPreExist = false;
33 
34     CommonDateFormatPart() = default;
CommonDateFormatPartpanda::ecmascript::CommonDateFormatPart35     CommonDateFormatPart(int32_t fField, int32_t fBeginIndex, int32_t fEndIndex, int32_t index, bool isPreExist)
36         : fField(fField), fBeginIndex(fBeginIndex), fEndIndex(fEndIndex), index(index), isPreExist(isPreExist)
37     {
38     }
39 
40     ~CommonDateFormatPart() = default;
41 
42     DEFAULT_COPY_SEMANTIC(CommonDateFormatPart);
43     DEFAULT_MOVE_SEMANTIC(CommonDateFormatPart);
44 };
45 
46 namespace {
47 const std::vector<std::string> ICU_LONG_SHORT = {
48     "long", "short",
49     "longOffset", "shortOffset",
50     "longGeneric", "shortGeneric"
51 };
52 const std::vector<std::string> ICU_NARROW_LONG_SHORT = {"narrow", "long", "short"};
53 const std::vector<std::string> ICU2_DIGIT_NUMERIC = {"2-digit", "numeric"};
54 const std::vector<std::string> ICU_NARROW_LONG_SHORT2_DIGIT_NUMERIC = {"narrow", "long", "short", "2-digit", "numeric"};
55 const std::vector<IcuPatternEntry> ICU_WEEKDAY_PE = {
56     {"EEEEE", "narrow"}, {"EEEE", "long"}, {"EEE", "short"},
57     {"ccccc", "narrow"}, {"cccc", "long"}, {"ccc", "short"}
58 };
59 const std::vector<IcuPatternEntry> ICU_ERA_PE = {{"GGGGG", "narrow"}, {"GGGG", "long"}, {"GGG", "short"}};
60 const std::vector<IcuPatternEntry> ICU_YEAR_PE = {{"yy", "2-digit"}, {"y", "numeric"}};
61 const std::vector<IcuPatternEntry> ICU_MONTH_PE = {
62     {"MMMMM", "narrow"}, {"MMMM", "long"}, {"MMM", "short"}, {"MM", "2-digit"}, {"M", "numeric"},
63     {"LLLLL", "narrow"}, {"LLLL", "long"}, {"LLL", "short"}, {"LL", "2-digit"}, {"L", "numeric"}
64 };
65 const std::vector<IcuPatternEntry> ICU_DAY_PE = {{"dd", "2-digit"}, {"d", "numeric"}};
66 const std::vector<IcuPatternEntry> ICU_DAY_PERIOD_PE = {
67     {"BBBBB", "narrow"}, {"bbbbb", "narrow"}, {"BBBB", "long"},
68     {"bbbb", "long"}, {"B", "short"}, {"b", "short"}
69 };
70 const std::vector<IcuPatternEntry> ICU_HOUR_PE = {
71     {"HH", "2-digit"}, {"H", "numeric"}, {"hh", "2-digit"}, {"h", "numeric"},
72     {"kk", "2-digit"}, {"k", "numeric"}, {"KK", "2-digit"}, {"K", "numeric"}
73 };
74 const std::vector<IcuPatternEntry> ICU_MINUTE_PE = {{"mm", "2-digit"}, {"m", "numeric"}};
75 const std::vector<IcuPatternEntry> ICU_SECOND_PE = {{"ss", "2-digit"}, {"s", "numeric"}};
76 const std::vector<IcuPatternEntry> ICU_YIME_ZONE_NAME_PE = {
77     {"zzzz", "long"}, {"z", "short"},
78     {"OOOO", "longOffset"}, {"O", "shortOffset"},
79     {"vvvv", "longGeneric"}, {"v", "shortGeneric"}
80 };
81 
82 const std::map<char16_t, HourCycleOption> HOUR_CYCLE_MAP = {
83     {'K', HourCycleOption::H11},
84     {'h', HourCycleOption::H12},
85     {'H', HourCycleOption::H23},
86     {'k', HourCycleOption::H24}
87 };
88 const std::map<std::string, HourCycleOption> TO_HOUR_CYCLE_MAP = {
89     {"h11", HourCycleOption::H11},
90     {"h12", HourCycleOption::H12},
91     {"h23", HourCycleOption::H23},
92     {"h24", HourCycleOption::H24}
93 };
94 
95 // The value of the [[RelevantExtensionKeys]] internal slot is « "ca", "nu", "hc" ».
96 const std::set<std::string> RELEVANT_EXTENSION_KEYS = {"nu", "ca", "hc"};
97 }
98 
GetIcuLocale(const JSThread * thread) const99 icu::Locale *JSDateTimeFormat::GetIcuLocale(const JSThread *thread) const
100 {
101     ASSERT(GetLocaleIcu(thread).IsJSNativePointer());
102     auto result = JSNativePointer::Cast(GetLocaleIcu(thread).GetTaggedObject())->GetExternalPointer();
103     return reinterpret_cast<icu::Locale *>(result);
104 }
105 
106 /* static */
SetIcuLocale(JSThread * thread,JSHandle<JSDateTimeFormat> obj,const icu::Locale & icuLocale,const NativePointerCallback & callback)107 void JSDateTimeFormat::SetIcuLocale(JSThread *thread, JSHandle<JSDateTimeFormat> obj,
108     const icu::Locale &icuLocale, const NativePointerCallback &callback)
109 {
110     EcmaVM *ecmaVm = thread->GetEcmaVM();
111     ObjectFactory *factory = ecmaVm->GetFactory();
112     icu::Locale *icuPointer = ecmaVm->GetNativeAreaAllocator()->New<icu::Locale>(icuLocale);
113     ASSERT(icuPointer != nullptr);
114     JSTaggedValue data = obj->GetLocaleIcu(thread);
115     if (data.IsHeapObject() && data.IsJSNativePointer()) {
116         JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject());
117         native->ResetExternalPointer(thread, icuPointer);
118         return;
119     }
120     JSHandle<JSNativePointer> pointer = factory->NewJSNativePointer(icuPointer, callback, ecmaVm);
121     obj->SetLocaleIcu(thread, pointer.GetTaggedValue());
122 }
123 
FreeIcuLocale(void * env,void * pointer,void * data)124 void JSDateTimeFormat::FreeIcuLocale([[maybe_unused]] void *env, void *pointer, void *data)
125 {
126     if (pointer == nullptr) {
127         return;
128     }
129     auto icuLocale = reinterpret_cast<icu::Locale *>(pointer);
130     icuLocale->~Locale();
131     if (data != nullptr) {
132         reinterpret_cast<EcmaVM *>(data)->GetNativeAreaAllocator()->FreeBuffer(pointer);
133     }
134 }
135 
GetIcuSimpleDateFormat(const JSThread * thread) const136 icu::SimpleDateFormat *JSDateTimeFormat::GetIcuSimpleDateFormat(const JSThread *thread) const
137 {
138     ASSERT(GetSimpleDateTimeFormatIcu(thread).IsJSNativePointer());
139     auto result = JSNativePointer::Cast(GetSimpleDateTimeFormatIcu(thread).GetTaggedObject())->GetExternalPointer();
140     return reinterpret_cast<icu::SimpleDateFormat *>(result);
141 }
142 
143 /* static */
SetIcuSimpleDateFormat(JSThread * thread,JSHandle<JSDateTimeFormat> obj,const icu::SimpleDateFormat & icuSimpleDateTimeFormat,const NativePointerCallback & callback)144 void JSDateTimeFormat::SetIcuSimpleDateFormat(JSThread *thread, JSHandle<JSDateTimeFormat> obj,
145     const icu::SimpleDateFormat &icuSimpleDateTimeFormat, const NativePointerCallback &callback)
146 {
147     EcmaVM *ecmaVm = thread->GetEcmaVM();
148     ObjectFactory *factory = ecmaVm->GetFactory();
149     icu::SimpleDateFormat *icuPointer =
150         ecmaVm->GetNativeAreaAllocator()->New<icu::SimpleDateFormat>(icuSimpleDateTimeFormat);
151     ASSERT(icuPointer != nullptr);
152     JSTaggedValue data = obj->GetSimpleDateTimeFormatIcu(thread);
153     if (data.IsHeapObject() && data.IsJSNativePointer()) {
154         JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject());
155         native->ResetExternalPointer(thread, icuPointer);
156         return;
157     }
158     // According to the observed native memory, we give an approximate native binding value.
159     constexpr static size_t icuBindingNativeSize = 64 * 1024;
160     JSHandle<JSNativePointer> pointer = factory->NewJSNativePointer(icuPointer, callback, ecmaVm,
161         false, icuBindingNativeSize);
162     obj->SetSimpleDateTimeFormatIcu(thread, pointer.GetTaggedValue());
163 }
164 
FreeSimpleDateFormat(void * env,void * pointer,void * data)165 void JSDateTimeFormat::FreeSimpleDateFormat([[maybe_unused]] void *env, void *pointer, void *data)
166 {
167     if (pointer == nullptr) {
168         return;
169     }
170     auto icuSimpleDateFormat = reinterpret_cast<icu::SimpleDateFormat *>(pointer);
171     icuSimpleDateFormat->~SimpleDateFormat();
172     if (data != nullptr) {
173         reinterpret_cast<EcmaVM *>(data)->GetNativeAreaAllocator()->FreeBuffer(pointer);
174     }
175 }
176 
ToValueString(JSThread * thread,const Value value)177 JSHandle<EcmaString> JSDateTimeFormat::ToValueString(JSThread *thread, const Value value)
178 {
179     auto globalConst = thread->GlobalConstants();
180     JSMutableHandle<EcmaString> result(thread, JSTaggedValue::Undefined());
181     switch (value) {
182         case Value::SHARED:
183             result.Update(globalConst->GetHandledSharedString().GetTaggedValue());
184             break;
185         case Value::START_RANGE:
186             result.Update(globalConst->GetHandledStartRangeString().GetTaggedValue());
187             break;
188         case Value::END_RANGE:
189             result.Update(globalConst->GetHandledEndRangeString().GetTaggedValue());
190             break;
191         default:
192             LOG_ECMA(FATAL) << "this branch is unreachable";
193             UNREACHABLE();
194     }
195     return result;
196 }
197 
DateTimeStyleToEStyle(DateTimeStyleOption style)198 icu::DateFormat::EStyle DateTimeStyleToEStyle(DateTimeStyleOption style)
199 {
200     switch (style) {
201         case DateTimeStyleOption::FULL: {
202             return icu::DateFormat::kFull;
203         }
204         case DateTimeStyleOption::LONG: {
205             return icu::DateFormat::kLong;
206         }
207         case DateTimeStyleOption::MEDIUM: {
208             return icu::DateFormat::kMedium;
209         }
210         case DateTimeStyleOption::SHORT: {
211             return icu::DateFormat::kShort;
212         }
213         case DateTimeStyleOption::UNDEFINED: {
214             return icu::DateFormat::kNone;
215         }
216         default: {
217             return icu::DateFormat::kNone;
218         }
219     }
220 }
221 
HourCycleFromPattern(const icu::UnicodeString pattern)222 HourCycleOption HourCycleFromPattern(const icu::UnicodeString pattern)
223 {
224     bool inQuote = false;
225     for (int32_t i = 0; i < pattern.length(); i++) {
226         char16_t ch = pattern[i];
227         switch (ch) {
228             case '\'':
229                 inQuote = !inQuote;
230                 break;
231             case 'K':
232                 if (!inQuote) {
233                     return HourCycleOption::H11;
234                 }
235                 break;
236             case 'h':
237                 if (!inQuote) {
238                     return HourCycleOption::H12;
239                 }
240                 break;
241             case 'H':
242                 if (!inQuote) {
243                     return HourCycleOption::H23;
244                 }
245                 break;
246             case 'k':
247                 if (!inQuote) {
248                     return HourCycleOption::H24;
249                 }
250                 break;
251             default : {
252                 break;
253             }
254         }
255     }
256     return HourCycleOption::UNDEFINED;
257 }
258 
ReplaceSkeleton(const icu::UnicodeString input,HourCycleOption hc)259 icu::UnicodeString ReplaceSkeleton(const icu::UnicodeString input, HourCycleOption hc)
260 {
261     icu::UnicodeString result;
262     char16_t to;
263     switch (hc) {
264         case HourCycleOption::H11:
265             to = 'K';
266             break;
267         case HourCycleOption::H12:
268             to = 'h';
269             break;
270         case HourCycleOption::H23:
271             to = 'H';
272             break;
273         case HourCycleOption::H24:
274             to = 'k';
275             break;
276         default:
277             UNREACHABLE();
278             break;
279     }
280     int inputLength = input.length();
281     for (int32_t i = 0; i < inputLength; ++i) {
282         switch (input[i]) {
283             case 'a':
284             case 'b':
285             case 'B':
286                 break;
287             case 'h':
288             case 'H':
289             case 'k':
290             case 'K':
291                 result += to;
292                 break;
293             default:
294                 result += input[i];
295                 break;
296         }
297     }
298     return result;
299 }
300 
DateTimeStylePattern(DateTimeStyleOption dateStyle,DateTimeStyleOption timeStyle,icu::Locale & icuLocale,HourCycleOption hc,icu::DateTimePatternGenerator * generator)301 std::unique_ptr<icu::SimpleDateFormat> DateTimeStylePattern(DateTimeStyleOption dateStyle,
302                                                             DateTimeStyleOption timeStyle,
303                                                             icu::Locale &icuLocale,
304                                                             HourCycleOption hc,
305                                                             icu::DateTimePatternGenerator *generator)
306 {
307     std::unique_ptr<icu::SimpleDateFormat> result;
308     icu::DateFormat::EStyle icuDateStyle = DateTimeStyleToEStyle(dateStyle);
309     icu::DateFormat::EStyle icuTimeStyle = DateTimeStyleToEStyle(timeStyle);
310     result.reset(reinterpret_cast<icu::SimpleDateFormat *>(
311         icu::DateFormat::createDateTimeInstance(icuDateStyle, icuTimeStyle, icuLocale)));
312     UErrorCode status = U_ZERO_ERROR;
313     icu::UnicodeString pattern("");
314     pattern = result->toPattern(pattern);
315     icu::UnicodeString skeleton = icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
316     ASSERT_PRINT(U_SUCCESS(status), "staticGetSkeleton failed");
317     if (hc == HourCycleFromPattern(pattern)) {
318         return result;
319     }
320     skeleton = ReplaceSkeleton(skeleton, hc);
321     pattern = generator->getBestPattern(skeleton, UDATPG_MATCH_HOUR_FIELD_LENGTH, status);
322     result = std::make_unique<icu::SimpleDateFormat>(pattern, icuLocale, status);
323     return result;
324 }
325 
326 // 13.1.1 InitializeDateTimeFormat (dateTimeFormat, locales, options)
327 // NOLINTNEXTLINE(readability-function-size)
InitializeDateTimeFormat(JSThread * thread,const JSHandle<JSDateTimeFormat> & dateTimeFormat,const JSHandle<JSTaggedValue> & locales,const JSHandle<JSTaggedValue> & options,IcuCacheType type)328 JSHandle<JSDateTimeFormat> JSDateTimeFormat::InitializeDateTimeFormat(JSThread *thread,
329                                                                       const JSHandle<JSDateTimeFormat> &dateTimeFormat,
330                                                                       const JSHandle<JSTaggedValue> &locales,
331                                                                       const JSHandle<JSTaggedValue> &options,
332                                                                       IcuCacheType type)
333 {
334     EcmaVM *ecmaVm = thread->GetEcmaVM();
335     ObjectFactory *factory = ecmaVm->GetFactory();
336     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
337 
338     // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
339     JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
340     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
341 
342     // 2. Let options be ? ToDateTimeOptions(options, "any", "date").
343     JSHandle<JSObject> dateTimeOptions;
344     if (options->IsUndefined()) {
345         dateTimeOptions = factory->CreateNullJSObject();
346     } else {
347         dateTimeOptions = JSTaggedValue::ToObject(thread, options);
348         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
349     }
350 
351     // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit").
352     auto matcher = JSLocale::GetOptionOfString<LocaleMatcherOption>(
353         thread, dateTimeOptions, globalConst->GetHandledLocaleMatcherString(),
354         {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT}, {"lookup", "best fit"},
355         LocaleMatcherOption::BEST_FIT);
356     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
357 
358     // 6. Let calendar be ? GetOption(options, "calendar", "string", undefined, undefined).
359     JSHandle<JSTaggedValue> calendar =
360         JSLocale::GetOption(thread, dateTimeOptions, globalConst->GetHandledCalendarString(), OptionType::STRING,
361                             globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined());
362     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
363     dateTimeFormat->SetCalendar(thread, calendar);
364 
365     // 7. If calendar is not undefined, then
366     //    a. If calendar does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
367     std::string calendarStr;
368     if (!calendar->IsUndefined()) {
369         JSHandle<EcmaString> calendarEcmaStr = JSHandle<EcmaString>::Cast(calendar);
370         calendarStr = intl::LocaleHelper::ConvertToStdString(thread, calendarEcmaStr);
371         if (!JSLocale::IsNormativeCalendar(calendarStr)) {
372             THROW_RANGE_ERROR_AND_RETURN(thread, "invalid calendar", dateTimeFormat);
373         }
374     }
375 
376     // 9. Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined).
377     JSHandle<JSTaggedValue> numberingSystem =
378         JSLocale::GetOption(thread, dateTimeOptions, globalConst->GetHandledNumberingSystemString(), OptionType::STRING,
379                             globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined());
380     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
381     dateTimeFormat->SetNumberingSystem(thread, numberingSystem);
382 
383     // 10. If numberingSystem is not undefined, then
384     //     a. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError
385     //        exception.
386     std::string nsStr;
387     if (!numberingSystem->IsUndefined()) {
388         JSHandle<EcmaString> nsEcmaStr = JSHandle<EcmaString>::Cast(numberingSystem);
389         nsStr = intl::LocaleHelper::ConvertToStdString(thread, nsEcmaStr);
390         if (!JSLocale::IsNormativeNumberingSystem(nsStr)) {
391             THROW_RANGE_ERROR_AND_RETURN(thread, "invalid numberingSystem", dateTimeFormat);
392         }
393     }
394 
395     // 12. Let hour12 be ? GetOption(options, "hour12", "boolean", undefined, undefined).
396     JSHandle<JSTaggedValue> hour12 =
397         JSLocale::GetOption(thread, dateTimeOptions, globalConst->GetHandledHour12String(), OptionType::BOOLEAN,
398                             globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined());
399     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
400 
401     // 13. Let hourCycle be ? GetOption(options, "hourCycle", "string", « "h11", "h12", "h23", "h24" », undefined).
402     auto hourCycle = JSLocale::GetOptionOfString<HourCycleOption>(
403         thread, dateTimeOptions, globalConst->GetHandledHourCycleString(),
404         {HourCycleOption::H11, HourCycleOption::H12, HourCycleOption::H23, HourCycleOption::H24},
405         {"h11", "h12", "h23", "h24"}, HourCycleOption::UNDEFINED);
406     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
407 
408     // 14. If hour12 is not undefined, then
409     //     a. Let hourCycle be null.
410     if (!hour12->IsUndefined()) {
411         hourCycle = HourCycleOption::UNDEFINED;
412     }
413 
414     // 16. Let localeData be %DateTimeFormat%.[[LocaleData]].
415     JSHandle<TaggedArray> availableLocales = (requestedLocales->GetLength() == 0) ? factory->EmptyArray() :
416                                                                                   GainAvailableLocales(thread);
417 
418     // 17. Let r be ResolveLocale(%DateTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %DateTimeFormat%
419     //     .[[RelevantExtensionKeys]], localeData).
420     ResolvedLocale resolvedLocale =
421         JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, RELEVANT_EXTENSION_KEYS);
422 
423     // 18. Set icuLocale to r.[[locale]].
424     icu::Locale icuLocale = resolvedLocale.localeData;
425     ASSERT_PRINT(!icuLocale.isBogus(), "icuLocale is bogus");
426     UErrorCode status = U_ZERO_ERROR;
427 
428     if (numberingSystem->IsUndefined() || !JSLocale::IsWellNumberingSystem(nsStr)) {
429         std::string numberingSystemStr = JSLocale::GetNumberingSystem(icuLocale);
430         auto result = factory->NewFromStdString(numberingSystemStr);
431         dateTimeFormat->SetNumberingSystem(thread, result);
432     }
433 
434     // Set resolvedIcuLocaleCopy to a copy of icuLocale.
435     // Set icuLocale.[[ca]] to calendar.
436     // Set icuLocale.[[nu]] to numberingSystem.
437     icu::Locale resolvedIcuLocaleCopy(icuLocale);
438     if (!calendar->IsUndefined() && JSLocale::IsWellCalendar(icuLocale, calendarStr)) {
439         icuLocale.setUnicodeKeywordValue("ca", calendarStr, status);
440     }
441     if (!numberingSystem->IsUndefined() && JSLocale::IsWellNumberingSystem(nsStr)) {
442         icuLocale.setUnicodeKeywordValue("nu", nsStr, status);
443     }
444 
445     // 24. Let timeZone be ? Get(options, "timeZone").
446     OperationResult operationResult =
447         JSObject::GetProperty(thread, dateTimeOptions, globalConst->GetHandledTimeZoneString());
448     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
449     dateTimeFormat->SetTimeZone(thread, operationResult.GetValue());
450 
451     // 25. If timeZone is not undefined, then
452     //     a. Let timeZone be ? ToString(timeZone).
453     //     b. If the result of IsValidTimeZoneName(timeZone) is false, then
454     //        i. Throw a RangeError exception.
455     std::unique_ptr<icu::TimeZone> icuTimeZone;
456     if (!operationResult.GetValue()->IsUndefined()) {
457         JSHandle<EcmaString> timezone = JSTaggedValue::ToString(thread, operationResult.GetValue());
458         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
459         icuTimeZone = ConstructTimeZone(intl::LocaleHelper::ConvertToStdString(thread, timezone));
460         if (icuTimeZone == nullptr) {
461             THROW_RANGE_ERROR_AND_RETURN(thread, "invalid timeZone", dateTimeFormat);
462         }
463     } else {
464         // 26. Else,
465         //     a. Let timeZone be DefaultTimeZone().
466         icuTimeZone = std::unique_ptr<icu::TimeZone>(icu::TimeZone::createDefault());
467     }
468 
469     // 36.a. Let hcDefault be dataLocaleData.[[hourCycle]].
470     std::unique_ptr<icu::DateTimePatternGenerator> generator;
471     {
472         ThreadNativeScope nativeScope(thread);
473         generator.reset(icu::DateTimePatternGenerator::createInstance(icuLocale, status));
474     }
475     if (U_FAILURE(status) || generator == nullptr) {
476         if (status == UErrorCode::U_MISSING_RESOURCE_ERROR) {
477             THROW_REFERENCE_ERROR_AND_RETURN(thread, "can not find icu data resources", dateTimeFormat);
478         }
479         THROW_RANGE_ERROR_AND_RETURN(thread, "create icu::DateTimePatternGenerator failed", dateTimeFormat);
480     }
481     HourCycleOption hcDefault = OptionToHourCycle(generator->getDefaultHourCycle(status));
482     // b. Let hc be dateTimeFormat.[[HourCycle]].
483     HourCycleOption hc = hourCycle;
484     if (hourCycle == HourCycleOption::UNDEFINED
485         && resolvedLocale.extensions.find("hc") != resolvedLocale.extensions.end()) {
486         hc = OptionToHourCycle(resolvedLocale.extensions.find("hc")->second);
487     }
488     // c. If hc is null, then
489     //    i. Set hc to hcDefault.
490     if (hc == HourCycleOption::UNDEFINED) {
491         hc = hcDefault;
492     }
493     // d. If hour12 is not undefined, then
494     if (!hour12->IsUndefined()) {
495         // i. If hour12 is true, then
496         if (JSTaggedValue::SameValue(thread, hour12.GetTaggedValue(), JSTaggedValue::True())) {
497             // 1. If hcDefault is "h11" or "h23", then
498             if (hcDefault == HourCycleOption::H11 || hcDefault == HourCycleOption::H23) {
499                 // a. Set hc to "h11".
500                 hc = HourCycleOption::H11;
501             } else {
502                 // 2. Else,
503                 //    a. Set hc to "h12".
504                 hc = HourCycleOption::H12;
505             }
506         } else {
507             // ii. Else,
508             //     2. If hcDefault is "h11" or "h23", then
509             if (hcDefault == HourCycleOption::H11 || hcDefault == HourCycleOption::H23) {
510                 // a. Set hc to "h23".
511                 hc = HourCycleOption::H23;
512             } else {
513                 // 3. Else,
514                 //    a. Set hc to "h24".
515                 hc = HourCycleOption::H24;
516             }
517         }
518     }
519 
520     // Set isHourDefined be false when dateTimeFormat.[[Hour]] is not undefined.
521     bool isHourDefined = false;
522 
523     // 29. For each row of Table 6, except the header row, in table order, do
524     //     a. Let prop be the name given in the Property column of the row.
525     //     b. Let value be ? GetOption(options, prop, "string", « the strings given in the Values column of the
526     //        row », undefined).
527     //     c. Set opt.[[<prop>]] to value.
528     std::string skeleton;
529     std::vector<IcuPatternDesc> data = GetIcuPatternDesc(hc);
530     int32_t explicitFormatComponents = 0;
531     std::vector<std::string> skeletonOpts;
532     for (const IcuPatternDesc &item : data) {
533         // prop be [[TimeZoneName]]
534         if (item.property == "timeZoneName") {
535             // b. If prop is "fractionalSecondDigits", then
536             //     i. Let value be ? GetNumberOption(options, "fractionalSecondDigits", 1, 3, undefined).
537             int secondDigitsString = JSLocale::GetNumberOption(thread, dateTimeOptions,
538                                                                globalConst->GetHandledFractionalSecondDigitsString(),
539                                                                1, 3, 0);
540             skeleton.append(secondDigitsString, 'S');
541             // e. If value is not undefined, then
542             //     i. Set hasExplicitFormatComponents to true.
543             if (secondDigitsString > 0) {
544                 explicitFormatComponents = 1;
545                 skeletonOpts.emplace_back(item.property);
546             }
547         }
548         JSHandle<JSTaggedValue> property(thread, factory->NewFromStdString(item.property).GetTaggedValue());
549         std::string value;
550         bool isFind = JSLocale::GetOptionOfString(thread, dateTimeOptions, property, item.allowedValues, &value);
551         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
552         if (isFind) {
553             skeletonOpts.emplace_back(item.property);
554             explicitFormatComponents = 1;
555             skeleton += item.map.find(value)->second;
556             // [[Hour]] is defined.
557             isHourDefined = (item.property == "hour") ? true : isHourDefined;
558         }
559     }
560 
561     // 13.1.3 BasicFormatMatcher (options, formats)
562     [[maybe_unused]] auto formatMatcher = JSLocale::GetOptionOfString<FormatMatcherOption>(
563         thread, dateTimeOptions, globalConst->GetHandledFormatMatcherString(),
564         {FormatMatcherOption::BASIC, FormatMatcherOption::BEST_FIT}, {"basic", "best fit"},
565         FormatMatcherOption::BEST_FIT);
566     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
567 
568     // Let dateStyle be ? GetOption(options, "string", «"full", "long", "medium", "short"», undefined).
569     // Set dateTimeFormat.[[dateStyle]]
570     auto dateStyle = JSLocale::GetOptionOfString<DateTimeStyleOption>(
571         thread, dateTimeOptions, globalConst->GetHandledDateStyleString(),
572         {DateTimeStyleOption::FULL, DateTimeStyleOption::LONG, DateTimeStyleOption::MEDIUM, DateTimeStyleOption::SHORT},
573         {"full", "long", "medium", "short"}, DateTimeStyleOption::UNDEFINED);
574     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
575     dateTimeFormat->SetDateStyle(dateStyle);
576 
577     // Let timeStyle be ? GetOption(options, "string", «"full", "long", "medium", "short"», undefined).
578     // Set dateTimeFormat.[[timeStyle]]
579     auto timeStyle = JSLocale::GetOptionOfString<DateTimeStyleOption>(
580         thread, dateTimeOptions, globalConst->GetHandledTimeStyleString(),
581         {DateTimeStyleOption::FULL, DateTimeStyleOption::LONG, DateTimeStyleOption::MEDIUM, DateTimeStyleOption::SHORT},
582         {"full", "long", "medium", "short"}, DateTimeStyleOption::UNDEFINED);
583     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
584     dateTimeFormat->SetTimeStyle(timeStyle);
585 
586     HourCycleOption dtfHourCycle = HourCycleOption::UNDEFINED;
587 
588     if (timeStyle != DateTimeStyleOption::UNDEFINED) {
589         // Set dateTimeFormat.[[HourCycle]] to hc.
590         dtfHourCycle = hc;
591     }
592 
593     if (dateStyle == DateTimeStyleOption::UNDEFINED
594         && timeStyle == DateTimeStyleOption::UNDEFINED) {
595         ToDateTimeSkeleton(thread, skeletonOpts, skeleton, hc, RequiredOption::ANY, DefaultsOption::DATE);
596         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
597         // If dateTimeFormat.[[Hour]] is defined, then
598         if (isHourDefined) {
599             // e. Set dateTimeFormat.[[HourCycle]] to hc.
600             dtfHourCycle = hc;
601         } else {
602             // 37. Else,
603             //     a. Set dateTimeFormat.[[HourCycle]] to undefined.
604             dtfHourCycle = HourCycleOption::UNDEFINED;
605         }
606     }
607 
608     // Set dateTimeFormat.[[hourCycle]].
609     dateTimeFormat->SetHourCycle(dtfHourCycle);
610 
611     // Set dateTimeFormat.[[icuLocale]].
612     JSDateTimeFormat::SetIcuLocale(thread, dateTimeFormat, icuLocale, JSDateTimeFormat::FreeIcuLocale);
613 
614     // Creates a Calendar using the given timezone and given locale.
615     // Set dateTimeFormat.[[icuSimpleDateFormat]].
616     icu::UnicodeString dtfSkeleton(skeleton.c_str());
617     status = U_ZERO_ERROR;
618     icu::UnicodeString pattern = ChangeHourCyclePattern(
619         generator.get()->getBestPattern(dtfSkeleton, UDATPG_MATCH_HOUR_FIELD_LENGTH, status), dtfHourCycle);
620     ASSERT_PRINT((U_SUCCESS(status) != 0), "get best pattern failed");
621     auto simpleDateFormatIcu(std::make_unique<icu::SimpleDateFormat>(pattern, icuLocale, status));
622     if (dateStyle != DateTimeStyleOption::UNDEFINED || timeStyle != DateTimeStyleOption::UNDEFINED) {
623         if (explicitFormatComponents != 0) {
624             THROW_TYPE_ERROR_AND_RETURN(thread, "Invalid option : option", dateTimeFormat);
625         }
626         simpleDateFormatIcu = DateTimeStylePattern(dateStyle, timeStyle, icuLocale,
627                                                    hc, generator.get());
628     }
629     if (U_FAILURE(status) != 0) {
630         simpleDateFormatIcu = std::unique_ptr<icu::SimpleDateFormat>();
631     }
632     ASSERT_PRINT(simpleDateFormatIcu != nullptr, "invalid icuSimpleDateFormat");
633     std::unique_ptr<icu::Calendar> calendarPtr = BuildCalendar(icuLocale, *icuTimeZone);
634     ASSERT_PRINT(calendarPtr != nullptr, "invalid calendar");
635     simpleDateFormatIcu->adoptCalendar(calendarPtr.release());
636     if (type != IcuCacheType::NOT_CACHE) {
637         std::string cacheEntry =
638             locales->IsUndefined() ? "" : EcmaStringAccessor(locales.GetTaggedValue()).ToStdString(thread);
639         auto& intlCache = ecmaVm->GetIntlCache();
640         switch (type) {
641             case IcuCacheType::DEFAULT:
642                 intlCache.SetIcuFormatterToCache(IcuFormatterType::SIMPLE_DATE_FORMAT_DEFAULT,
643                     cacheEntry, simpleDateFormatIcu.release(), JSDateTimeFormat::FreeSimpleDateFormat);
644                 break;
645             case IcuCacheType::DATE:
646                 intlCache.SetIcuFormatterToCache(IcuFormatterType::SIMPLE_DATE_FORMAT_DATE,
647                     cacheEntry, simpleDateFormatIcu.release(), JSDateTimeFormat::FreeSimpleDateFormat);
648                 break;
649             case IcuCacheType::TIME:
650                 intlCache.SetIcuFormatterToCache(IcuFormatterType::SIMPLE_DATE_FORMAT_TIME,
651                     cacheEntry, simpleDateFormatIcu.release(), JSDateTimeFormat::FreeSimpleDateFormat);
652                 break;
653             default:
654                 UNREACHABLE();
655         }
656     } else {
657         SetIcuSimpleDateFormat(thread, dateTimeFormat, *simpleDateFormatIcu, JSDateTimeFormat::FreeSimpleDateFormat);
658     }
659 
660     // Set dateTimeFormat.[[iso8601]].
661     bool iso8601 = strstr(icuLocale.getName(), "calendar=iso8601") != nullptr;
662     dateTimeFormat->SetIso8601(thread, JSTaggedValue(iso8601));
663 
664     // Set dateTimeFormat.[[locale]].
665     if (!hour12->IsUndefined() || hourCycle != HourCycleOption::UNDEFINED) {
666         if ((resolvedLocale.extensions.find("hc") != resolvedLocale.extensions.end()) &&
667             (dtfHourCycle != OptionToHourCycle((resolvedLocale.extensions.find("hc")->second)))) {
668             resolvedIcuLocaleCopy.setUnicodeKeywordValue("hc", nullptr, status);
669             ASSERT_PRINT(U_SUCCESS(status), "resolvedIcuLocaleCopy set hc failed");
670         }
671     }
672     JSHandle<EcmaString> localeStr = intl::LocaleHelper::ToLanguageTag(thread, resolvedIcuLocaleCopy);
673     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
674     dateTimeFormat->SetLocale(thread, localeStr.GetTaggedValue());
675 
676     // Set dateTimeFormat.[[boundFormat]].
677     dateTimeFormat->SetBoundFormat(thread, JSTaggedValue::Undefined());
678 
679     // 39. Return dateTimeFormat.
680     return dateTimeFormat;
681 }
682 
GetCachedIcuSimpleDateFormat(JSThread * thread,const JSHandle<JSTaggedValue> & locales,IcuFormatterType type)683 icu::SimpleDateFormat *JSDateTimeFormat::GetCachedIcuSimpleDateFormat(JSThread *thread,
684                                                                       const JSHandle<JSTaggedValue> &locales,
685                                                                       IcuFormatterType type)
686 {
687     std::string cacheEntry =
688         locales->IsUndefined() ? "" : EcmaStringAccessor(locales.GetTaggedValue()).ToStdString(thread);
689     void *cachedSimpleDateFormat = thread->GetEcmaVM()->GetIntlCache().GetIcuFormatterFromCache(type, cacheEntry);
690     if (cachedSimpleDateFormat != nullptr) {
691         return reinterpret_cast<icu::SimpleDateFormat*>(cachedSimpleDateFormat);
692     }
693     return nullptr;
694 }
695 
696 // 13.1.2 ToDateTimeOptions (options, required, defaults)
ToDateTimeOptions(JSThread * thread,const JSHandle<JSTaggedValue> & options,const RequiredOption & required,const DefaultsOption & defaults)697 JSHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions(JSThread *thread, const JSHandle<JSTaggedValue> &options,
698                                                        const RequiredOption &required, const DefaultsOption &defaults)
699 {
700     EcmaVM *ecmaVm = thread->GetEcmaVM();
701     ObjectFactory *factory = ecmaVm->GetFactory();
702 
703     // 1. If options is undefined, let options be null; otherwise let options be ? ToObject(options).
704     JSHandle<JSObject> optionsResult(thread, JSTaggedValue::Null());
705     if (!options->IsUndefined()) {
706         optionsResult = JSTaggedValue::ToObject(thread, options);
707         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
708     }
709 
710     // 2. Let options be ObjectCreate(options).
711     optionsResult = JSObject::ObjectCreate(thread, optionsResult);
712 
713     // 3. Let needDefaults be true.
714     bool needDefaults = true;
715 
716     // 4. If required is "date" or "any", then
717     //    a. For each of the property names "weekday", "year", "month", "day", do
718     //      i. Let prop be the property name.
719     //      ii. Let value be ? Get(options, prop).
720     //      iii. If value is not undefined, let needDefaults be false.
721     auto globalConst = thread->GlobalConstants();
722     if (required == RequiredOption::DATE || required == RequiredOption::ANY) {
723         JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_4);
724         array->Set(thread, 0, globalConst->GetHandledWeekdayString());
725         array->Set(thread, 1, globalConst->GetHandledYearString());
726         array->Set(thread, 2, globalConst->GetHandledMonthString());  // 2 means the third slot
727         array->Set(thread, 3, globalConst->GetHandledDayString());    // 3 means the fourth slot
728         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
729         uint32_t len = array->GetLength();
730         for (uint32_t i = 0; i < len; i++) {
731             key.Update(array->Get(thread, i));
732             OperationResult operationResult = JSObject::GetProperty(thread, optionsResult, key);
733             RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
734             if (!operationResult.GetValue()->IsUndefined()) {
735                 needDefaults = false;
736             }
737         }
738     }
739 
740     // 5. If required is "time" or "any", then
741     //    a. For each of the property names "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits", do
742     //      i. Let prop be the property name.
743     //      ii. Let value be ? Get(options, prop).
744     //      iii. If value is not undefined, let needDefaults be false.
745     if (required == RequiredOption::TIME || required == RequiredOption::ANY) {
746         JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_5);
747         array->Set(thread, 0, globalConst->GetHandledDayPeriodString());
748         array->Set(thread, 1, globalConst->GetHandledHourString());
749         array->Set(thread, 2, globalConst->GetHandledMinuteString());   // 2 means the second slot
750         array->Set(thread, 3, globalConst->GetHandledSecondString());   // 3 means the third slot
751         array->Set(thread, 4, globalConst->GetHandledFractionalSecondDigitsString());   // 4 means the fourth slot
752         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
753         uint32_t len = array->GetLength();
754         for (uint32_t i = 0; i < len; i++) {
755             key.Update(array->Get(thread, i));
756             OperationResult operationResult = JSObject::GetProperty(thread, optionsResult, key);
757             RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
758             if (!operationResult.GetValue()->IsUndefined()) {
759                 needDefaults = false;
760             }
761         }
762     }
763 
764     // Let dateStyle/timeStyle be ? Get(options, "dateStyle"/"timeStyle").
765     OperationResult dateStyleResult =
766         JSObject::GetProperty(thread, optionsResult, globalConst->GetHandledDateStyleString());
767     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
768     JSHandle<JSTaggedValue> dateStyle = dateStyleResult.GetValue();
769     OperationResult timeStyleResult =
770         JSObject::GetProperty(thread, optionsResult, globalConst->GetHandledTimeStyleString());
771     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
772     JSHandle<JSTaggedValue> timeStyle = timeStyleResult.GetValue();
773 
774     // If dateStyle is not undefined or timeStyle is not undefined, let needDefaults be false.
775     if (!dateStyle->IsUndefined() || !timeStyle->IsUndefined()) {
776         needDefaults = false;
777     }
778 
779     // If required is "date"/"time" and timeStyle is not undefined, throw a TypeError exception.
780     if (required == RequiredOption::DATE && !timeStyle->IsUndefined()) {
781         THROW_TYPE_ERROR_AND_RETURN(thread, "timeStyle is not undefined", optionsResult);
782     }
783     if (required == RequiredOption::TIME && !dateStyle->IsUndefined()) {
784         THROW_TYPE_ERROR_AND_RETURN(thread, "dateStyle is not undefined", optionsResult);
785     }
786 
787     // 6. If needDefaults is true and defaults is either "date" or "all", then
788     //    a. For each of the property names "year", "month", "day", do
789     //       i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
790     if (needDefaults && (defaults == DefaultsOption::DATE || defaults == DefaultsOption::ALL)) {
791         JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_3);
792         array->Set(thread, 0, globalConst->GetHandledYearString());
793         array->Set(thread, 1, globalConst->GetHandledMonthString());
794         array->Set(thread, 2, globalConst->GetHandledDayString());  // 2 means the third slot
795         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
796         uint32_t len = array->GetLength();
797         for (uint32_t i = 0; i < len; i++) {
798             key.Update(array->Get(thread, i));
799             JSObject::CreateDataPropertyOrThrow(thread, optionsResult, key, globalConst->GetHandledNumericString());
800             RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
801         }
802     }
803 
804     // 7. If needDefaults is true and defaults is either "time" or "all", then
805     //    a. For each of the property names "hour", "minute", "second", do
806     //       i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
807     if (needDefaults && (defaults == DefaultsOption::TIME || defaults == DefaultsOption::ALL)) {
808         JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_3);
809         array->Set(thread, 0, globalConst->GetHandledHourString());
810         array->Set(thread, 1, globalConst->GetHandledMinuteString());
811         array->Set(thread, 2, globalConst->GetHandledSecondString());  // 2 means the third slot
812         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
813         uint32_t len = array->GetLength();
814         for (uint32_t i = 0; i < len; i++) {
815             key.Update(array->Get(thread, i));
816             JSObject::CreateDataPropertyOrThrow(thread, optionsResult, key, globalConst->GetHandledNumericString());
817             RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
818         }
819     }
820 
821     // 8. Return options.
822     return optionsResult;
823 }
824 
ToDateTimeSkeleton(JSThread * thread,const std::vector<std::string> & options,std::string & skeleton,HourCycleOption hc,const RequiredOption & required,const DefaultsOption & defaults)825 void JSDateTimeFormat::ToDateTimeSkeleton(JSThread *thread, const std::vector<std::string> &options,
826                                           std::string &skeleton, HourCycleOption hc,
827                                           const RequiredOption &required, const DefaultsOption &defaults)
828 {
829     EcmaVM *ecmaVm = thread->GetEcmaVM();
830     ObjectFactory *factory = ecmaVm->GetFactory();
831 
832     // 1. Let needDefaults be true.
833     bool needDefaults = true;
834 
835     // 2. If required is "date" or "any", then
836     //    a. For each of the property names "weekday", "year", "month", "day", do
837     //      i. Let prop be the property name.
838     //      ii. Let value be ? Get(options, prop).
839     //      iii. If value is not undefined, let needDefaults be false.
840     auto globalConst = thread->GlobalConstants();
841     if (required == RequiredOption::DATE || required == RequiredOption::ANY) {
842         JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_4);
843         array->Set(thread, 0, globalConst->GetHandledWeekdayString());
844         array->Set(thread, 1, globalConst->GetHandledYearString());
845         array->Set(thread, 2, globalConst->GetHandledMonthString());  // 2 means the third slot
846         array->Set(thread, 3, globalConst->GetHandledDayString());    // 3 means the fourth slot
847         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
848         uint32_t len = array->GetLength();
849         for (uint32_t i = 0; i < len; i++) {
850             key.Update(array->Get(thread, i));
851             std::string result = EcmaStringAccessor(key.GetTaggedValue()).ToStdString(thread);
852             auto it = std::find(options.begin(), options.end(), result);
853             if (it != options.end()) {
854                 needDefaults = false;
855                 break;
856             }
857         }
858     }
859 
860     // 3. If required is "time" or "any", then
861     //    a. For each of the property names "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits", do
862     //      i. Let prop be the property name.
863     //      ii. Let value be ? Get(options, prop).
864     //      iii. If value is not undefined, let needDefaults be false.
865     if (required == RequiredOption::TIME || required == RequiredOption::ANY) {
866         JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_5);
867         array->Set(thread, 0, globalConst->GetHandledDayPeriodString());
868         array->Set(thread, 1, globalConst->GetHandledHourString());
869         array->Set(thread, 2, globalConst->GetHandledMinuteString());   // 2 means the second slot
870         array->Set(thread, 3, globalConst->GetHandledSecondString());   // 3 means the third slot
871         array->Set(thread, 4, globalConst->GetHandledFractionalSecondDigitsString());   // 4 means the fourth slot
872         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
873         uint32_t len = array->GetLength();
874         for (uint32_t i = 0; i < len; i++) {
875             key.Update(array->Get(thread, i));
876             std::string result = EcmaStringAccessor(key.GetTaggedValue()).ToStdString(thread);
877             auto it = std::find(options.begin(), options.end(), result);
878             if (it != options.end()) {
879                 needDefaults = false;
880                 break;
881             }
882         }
883     }
884 
885     // 4. If needDefaults is true and defaults is either "date" or "all", then
886     //    skeleton += "year", "month", "day"
887     if (needDefaults && (defaults == DefaultsOption::DATE || defaults == DefaultsOption::ALL)) {
888         skeleton += "yMd";
889     }
890 
891     // 5. If needDefaults is true and defaults is either "time" or "all", then
892     //    skeleton += "hour", "minute", "second"
893     if (needDefaults && (defaults == DefaultsOption::TIME || defaults == DefaultsOption::ALL)) {
894         switch (hc) {
895             case HourCycleOption::H12:
896                 skeleton += "hms";
897                 break;
898             case HourCycleOption::H23:
899             case HourCycleOption::UNDEFINED:
900                 skeleton += "Hms";
901                 break;
902             case HourCycleOption::H11:
903                 skeleton += "Kms";
904                 break;
905             case HourCycleOption::H24:
906                 skeleton += "kms";
907                 break;
908             default:
909                 break;
910         }
911     }
912 }
913 
914 // 13.1.7 FormatDateTime(dateTimeFormat, x)
FormatDateTime(JSThread * thread,const JSHandle<JSDateTimeFormat> & dateTimeFormat,double x)915 JSHandle<EcmaString> JSDateTimeFormat::FormatDateTime(JSThread *thread,
916                                                       const JSHandle<JSDateTimeFormat> &dateTimeFormat, double x)
917 {
918     icu::SimpleDateFormat *simpleDateFormat = dateTimeFormat->GetIcuSimpleDateFormat(thread);
919     JSHandle<EcmaString> res = FormatDateTime(thread, simpleDateFormat, x);
920     RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
921     return res;
922 }
923 
FormatDateTime(JSThread * thread,const icu::SimpleDateFormat * simpleDateFormat,double x)924 JSHandle<EcmaString> JSDateTimeFormat::FormatDateTime(JSThread *thread,
925                                                       const icu::SimpleDateFormat *simpleDateFormat, double x)
926 {
927     // 1. Let parts be ? PartitionDateTimePattern(dateTimeFormat, x).
928     double xValue = JSDate::TimeClip(x);
929     if (std::isnan(xValue)) {
930         THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid time value", thread->GetEcmaVM()->GetFactory()->GetEmptyString());
931     }
932 
933     // 2. Let result be the empty String.
934     icu::UnicodeString result;
935 
936     // 3. Set result to the string-concatenation of result and part.[[Value]].
937     {
938         ThreadNativeScope nativeScope(thread);
939         simpleDateFormat->format(xValue, result);
940     }
941 
942     // 4. Return result.
943     return intl::LocaleHelper::UStringToString(thread, result);
944 }
945 
946 // 13.1.8 FormatDateTimeToParts (dateTimeFormat, x)
FormatDateTimeToParts(JSThread * thread,const JSHandle<JSDateTimeFormat> & dateTimeFormat,double x)947 JSHandle<JSArray> JSDateTimeFormat::FormatDateTimeToParts(JSThread *thread,
948                                                           const JSHandle<JSDateTimeFormat> &dateTimeFormat, double x)
949 {
950     icu::SimpleDateFormat *simpleDateFormat = dateTimeFormat->GetIcuSimpleDateFormat(thread);
951     ASSERT(simpleDateFormat != nullptr);
952     UErrorCode status = U_ZERO_ERROR;
953     icu::FieldPositionIterator fieldPositionIter;
954     icu::UnicodeString formattedParts;
955     {
956         ThreadNativeScope nativeScope(thread);
957         simpleDateFormat->format(x, formattedParts, &fieldPositionIter, status);
958     }
959     if (U_FAILURE(status) != 0) {
960         THROW_TYPE_ERROR_AND_RETURN(thread, "format failed", thread->GetEcmaVM()->GetFactory()->NewJSArray());
961     }
962 
963     // 2. Let result be ArrayCreate(0).
964     JSHandle<JSArray> result(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
965     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
966     if (formattedParts.isBogus()) {
967         return result;
968     }
969 
970     // 3. Let n be 0.
971     int32_t index = 0;
972     int32_t preEdgePos = 0;
973     std::vector<CommonDateFormatPart> parts;
974     icu::FieldPosition fieldPosition;
975     while (fieldPositionIter.next(fieldPosition)) {
976         int32_t fField = fieldPosition.getField();
977         int32_t fBeginIndex = fieldPosition.getBeginIndex();
978         int32_t fEndIndex = fieldPosition.getEndIndex();
979         if (preEdgePos < fBeginIndex) {
980             parts.emplace_back(CommonDateFormatPart(fField, preEdgePos, fBeginIndex, index, true));
981             ++index;
982         }
983         parts.emplace_back(CommonDateFormatPart(fField, fBeginIndex, fEndIndex, index, false));
984         preEdgePos = fEndIndex;
985         ++index;
986     }
987     int32_t length = formattedParts.length();
988     if (preEdgePos < length) {
989         parts.emplace_back(CommonDateFormatPart(-1, preEdgePos, length, index, true));
990     }
991     JSMutableHandle<EcmaString> substring(thread, JSTaggedValue::Undefined());
992 
993     // 4. For each part in parts, do
994     for (auto part : parts) {
995         substring.Update(intl::LocaleHelper::UStringToString(thread, formattedParts, part.fBeginIndex,
996             part.fEndIndex).GetTaggedValue());
997         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
998         // Let O be ObjectCreate(%ObjectPrototype%).
999         // Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]).
1000         // Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]).
1001         // Perform ! CreateDataProperty(result, ! ToString(n), O).
1002         if (part.isPreExist) {
1003             JSLocale::PutElement(thread, part.index, result, ConvertFieldIdToDateType(thread, -1),
1004                                  JSHandle<JSTaggedValue>::Cast(substring));
1005         } else {
1006             JSLocale::PutElement(thread, part.index, result, ConvertFieldIdToDateType(thread, part.fField),
1007                                  JSHandle<JSTaggedValue>::Cast(substring));
1008         }
1009     }
1010 
1011     // 5. Return result.
1012     return result;
1013 }
1014 
1015 // 13.1.10 UnwrapDateTimeFormat(dtf)
UnwrapDateTimeFormat(JSThread * thread,const JSHandle<JSTaggedValue> & dateTimeFormat)1016 JSHandle<JSTaggedValue> JSDateTimeFormat::UnwrapDateTimeFormat(JSThread *thread,
1017                                                                const JSHandle<JSTaggedValue> &dateTimeFormat)
1018 {
1019     // 1. Assert: Type(dtf) is Object.
1020     ASSERT_PRINT(dateTimeFormat->IsJSObject(), "dateTimeFormat is not object");
1021 
1022     // 2. If dateTimeFormat does not have an [[InitializedDateTimeFormat]] internal slot
1023     //    and ? InstanceofOperator(dateTimeFormat, %DateTimeFormat%) is true, then
1024     //       a. Let dateTimeFormat be ? Get(dateTimeFormat, %Intl%.[[FallbackSymbol]]).
1025     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1026     bool isInstanceOf = JSFunction::OrdinaryHasInstance(thread, env->GetDateTimeFormatFunction(), dateTimeFormat);
1027     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, dateTimeFormat);
1028     if (!dateTimeFormat->IsJSDateTimeFormat() && isInstanceOf) {
1029         JSHandle<JSTaggedValue> key(thread, JSHandle<JSIntl>::Cast(env->GetIntlFunction())->GetFallbackSymbol(thread));
1030         OperationResult operationResult = JSTaggedValue::GetProperty(thread, dateTimeFormat, key);
1031         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, dateTimeFormat);
1032         return operationResult.GetValue();
1033     }
1034 
1035     // 3. Perform ? RequireInternalSlot(dateTimeFormat, [[InitializedDateTimeFormat]]).
1036     if (!dateTimeFormat->IsJSDateTimeFormat()) {
1037         THROW_TYPE_ERROR_AND_RETURN(thread, "is not JSDateTimeFormat",
1038                                     JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
1039     }
1040 
1041     // 4. Return dateTimeFormat.
1042     return dateTimeFormat;
1043 }
1044 
ToHourCycleEcmaString(JSThread * thread,HourCycleOption hc)1045 JSHandle<JSTaggedValue> ToHourCycleEcmaString(JSThread *thread, HourCycleOption hc)
1046 {
1047     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
1048     auto globalConst = thread->GlobalConstants();
1049     switch (hc) {
1050         case HourCycleOption::H11:
1051             result.Update(globalConst->GetHandledH11String().GetTaggedValue());
1052             break;
1053         case HourCycleOption::H12:
1054             result.Update(globalConst->GetHandledH12String().GetTaggedValue());
1055             break;
1056         case HourCycleOption::H23:
1057             result.Update(globalConst->GetHandledH23String().GetTaggedValue());
1058             break;
1059         case HourCycleOption::H24:
1060             result.Update(globalConst->GetHandledH24String().GetTaggedValue());
1061             break;
1062         default:
1063             LOG_ECMA(FATAL) << "this branch is unreachable";
1064             UNREACHABLE();
1065     }
1066     return result;
1067 }
1068 
ToDateTimeStyleEcmaString(JSThread * thread,DateTimeStyleOption style)1069 JSHandle<JSTaggedValue> ToDateTimeStyleEcmaString(JSThread *thread, DateTimeStyleOption style)
1070 {
1071     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
1072     auto globalConst = thread->GlobalConstants();
1073     switch (style) {
1074         case DateTimeStyleOption::FULL:
1075             result.Update(globalConst->GetHandledFullString().GetTaggedValue());
1076             break;
1077         case DateTimeStyleOption::LONG:
1078             result.Update(globalConst->GetHandledLongString().GetTaggedValue());
1079             break;
1080         case DateTimeStyleOption::MEDIUM:
1081             result.Update(globalConst->GetHandledMediumString().GetTaggedValue());
1082             break;
1083         case DateTimeStyleOption::SHORT:
1084             result.Update(globalConst->GetHandledShortString().GetTaggedValue());
1085             break;
1086         default:
1087             LOG_ECMA(FATAL) << "this branch is unreachable";
1088             UNREACHABLE();
1089     }
1090     return result;
1091 }
1092 
1093 // 13.4.5  Intl.DateTimeFormat.prototype.resolvedOptions ()
ResolvedOptions(JSThread * thread,const JSHandle<JSDateTimeFormat> & dateTimeFormat,const JSHandle<JSObject> & options)1094 void JSDateTimeFormat::ResolvedOptions(JSThread *thread, const JSHandle<JSDateTimeFormat> &dateTimeFormat,
1095                                        const JSHandle<JSObject> &options)
1096 {   //  Table 8: Resolved Options of DateTimeFormat Instances
1097     //    Internal Slot	        Property
1098     //    [[Locale]]	        "locale"
1099     //    [[Calendar]]	        "calendar"
1100     //    [[NumberingSystem]]	"numberingSystem"
1101     //    [[TimeZone]]	        "timeZone"
1102     //    [[HourCycle]]	        "hourCycle"
1103     //                          "hour12"
1104     //    [[Weekday]]	        "weekday"
1105     //    [[Era]]	            "era"
1106     //    [[Year]]	            "year"
1107     //    [[Month]]         	"month"
1108     //    [[Day]]	            "day"
1109     //    [[Hour]]	            "hour"
1110     //    [[Minute]]	        "minute"
1111     //    [[Second]]        	"second"
1112     //    [[TimeZoneName]]	    "timeZoneName"
1113     auto globalConst = thread->GlobalConstants();
1114     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1115 
1116     // 5. For each row of Table 8, except the header row, in table order, do
1117     //    Let p be the Property value of the current row.
1118     // [[Locale]]
1119     JSHandle<JSTaggedValue> locale(thread, dateTimeFormat->GetLocale(thread));
1120     JSHandle<JSTaggedValue> property = globalConst->GetHandledLocaleString();
1121     JSObject::CreateDataPropertyOrThrow(thread, options, property, locale);
1122     RETURN_IF_ABRUPT_COMPLETION(thread);
1123     // [[Calendar]]
1124     JSMutableHandle<JSTaggedValue> calendarValue(thread, dateTimeFormat->GetCalendar(thread));
1125     icu::SimpleDateFormat *icuSimpleDateFormat = dateTimeFormat->GetIcuSimpleDateFormat(thread);
1126     const icu::Calendar *calendar = icuSimpleDateFormat->getCalendar();
1127     std::string icuCalendar = calendar->getType();
1128     if (icuCalendar == "gregorian") {
1129         if (dateTimeFormat->GetIso8601(thread).IsTrue()) {
1130             calendarValue.Update(globalConst->GetHandledIso8601String().GetTaggedValue());
1131         } else {
1132             calendarValue.Update(globalConst->GetHandledGregoryString().GetTaggedValue());
1133         }
1134     } else if (icuCalendar == "ethiopic-amete-alem") {
1135         calendarValue.Update(globalConst->GetHandledEthioaaString().GetTaggedValue());
1136     } else if (icuCalendar.length() != 0) {
1137         calendarValue.Update(factory->NewFromStdString(icuCalendar).GetTaggedValue());
1138     }
1139     property = globalConst->GetHandledCalendarString();
1140     JSObject::CreateDataPropertyOrThrow(thread, options, property, calendarValue);
1141     RETURN_IF_ABRUPT_COMPLETION(thread);
1142     // [[NumberingSystem]]
1143     JSHandle<JSTaggedValue> numberingSystem(thread, dateTimeFormat->GetNumberingSystem(thread));
1144     if (numberingSystem->IsUndefined()) {
1145         numberingSystem = globalConst->GetHandledLatnString();
1146     }
1147     property = globalConst->GetHandledNumberingSystemString();
1148     JSObject::CreateDataPropertyOrThrow(thread, options, property, numberingSystem);
1149     RETURN_IF_ABRUPT_COMPLETION(thread);
1150     // [[TimeZone]]
1151     JSMutableHandle<JSTaggedValue> timezoneValue(thread, dateTimeFormat->GetTimeZone(thread));
1152     const icu::TimeZone &icuTZ = calendar->getTimeZone();
1153     icu::UnicodeString timezone;
1154     icuTZ.getID(timezone);
1155     UErrorCode status = U_ZERO_ERROR;
1156     icu::UnicodeString canonicalTimezone;
1157     icu::TimeZone::getCanonicalID(timezone, canonicalTimezone, status);
1158     if (U_SUCCESS(status) != 0) {
1159         if ((canonicalTimezone == UNICODE_STRING_SIMPLE("Etc/UTC")) != 0 ||
1160             (canonicalTimezone == UNICODE_STRING_SIMPLE("Etc/GMT")) != 0) {
1161             timezoneValue.Update(globalConst->GetUTCString());
1162         } else {
1163             timezoneValue.Update(intl::LocaleHelper::UStringToString(thread, canonicalTimezone).GetTaggedValue());
1164         }
1165     }
1166     property = globalConst->GetHandledTimeZoneString();
1167     JSObject::CreateDataPropertyOrThrow(thread, options, property, timezoneValue);
1168     RETURN_IF_ABRUPT_COMPLETION(thread);
1169     // [[HourCycle]]
1170     // For web compatibility reasons, if the property "hourCycle" is set, the "hour12" property should be set to true
1171     // when "hourCycle" is "h11" or "h12", or to false when "hourCycle" is "h23" or "h24".
1172     // i. Let hc be dtf.[[HourCycle]].
1173     JSHandle<JSTaggedValue> hcValue;
1174     HourCycleOption hc = dateTimeFormat->GetHourCycle();
1175     if (hc != HourCycleOption::UNDEFINED) {
1176         property = globalConst->GetHandledHourCycleString();
1177         hcValue = ToHourCycleEcmaString(thread, dateTimeFormat->GetHourCycle());
1178         JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue);
1179         RETURN_IF_ABRUPT_COMPLETION(thread);
1180         if (hc == HourCycleOption::H11 || hc == HourCycleOption::H12) {
1181             JSHandle<JSTaggedValue> trueValue(thread, JSTaggedValue::True());
1182             hcValue = trueValue;
1183         } else if (hc == HourCycleOption::H23 || hc == HourCycleOption::H24) {
1184             JSHandle<JSTaggedValue> falseValue(thread, JSTaggedValue::False());
1185             hcValue = falseValue;
1186         }
1187         property = globalConst->GetHandledHour12String();
1188         JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue);
1189         RETURN_IF_ABRUPT_COMPLETION(thread);
1190     }
1191     // [[DateStyle]], [[TimeStyle]].
1192     if (dateTimeFormat->GetDateStyle() == DateTimeStyleOption::UNDEFINED &&
1193         dateTimeFormat->GetTimeStyle() == DateTimeStyleOption::UNDEFINED) {
1194         icu::UnicodeString patternUnicode;
1195         icuSimpleDateFormat->toPattern(patternUnicode);
1196         std::string pattern;
1197         patternUnicode.toUTF8String(pattern);
1198         for (const auto &item : BuildIcuPatternDescs()) {
1199             // fractionalSecondsDigits need to be added before timeZoneName.
1200             if (item.property == "timeZoneName") {
1201                 int tmpResult = count(pattern.begin(), pattern.end(), 'S');
1202                 int fsd = (tmpResult >= STRING_LENGTH_3) ? STRING_LENGTH_3 : tmpResult;
1203                 if (fsd > 0) {
1204                     JSHandle<JSTaggedValue> fsdValue(thread, JSTaggedValue(fsd));
1205                     property = globalConst->GetHandledFractionalSecondDigitsString();
1206                     JSObject::CreateDataPropertyOrThrow(thread, options, property, fsdValue);
1207                     RETURN_IF_ABRUPT_COMPLETION(thread);
1208                 }
1209             }
1210             for (const auto &pair : item.pairs) {
1211                 if (pattern.find(pair.first) != std::string::npos) {
1212                     hcValue = JSHandle<JSTaggedValue>::Cast(factory->NewFromStdString(pair.second));
1213                     property = JSHandle<JSTaggedValue>::Cast(factory->NewFromStdString(item.property));
1214                     JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue);
1215                     RETURN_IF_ABRUPT_COMPLETION(thread);
1216                     break;
1217                 }
1218             }
1219         }
1220     }
1221     if (dateTimeFormat->GetDateStyle() != DateTimeStyleOption::UNDEFINED) {
1222         property = globalConst->GetHandledDateStyleString();
1223         hcValue = ToDateTimeStyleEcmaString(thread, dateTimeFormat->GetDateStyle());
1224         JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue);
1225         RETURN_IF_ABRUPT_COMPLETION(thread);
1226     }
1227     if (dateTimeFormat->GetTimeStyle() != DateTimeStyleOption::UNDEFINED) {
1228         property = globalConst->GetHandledTimeStyleString();
1229         hcValue = ToDateTimeStyleEcmaString(thread, dateTimeFormat->GetTimeStyle());
1230         JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue);
1231         RETURN_IF_ABRUPT_COMPLETION(thread);
1232     }
1233 }
1234 
1235 // Use dateInterval(x, y) construct datetimeformatrange
ConstructDTFRange(JSThread * thread,const JSHandle<JSDateTimeFormat> & dtf,double x,double y)1236 icu::FormattedDateInterval JSDateTimeFormat::ConstructDTFRange(JSThread *thread, const JSHandle<JSDateTimeFormat> &dtf,
1237                                                                double x, double y)
1238 {
1239     std::unique_ptr<icu::DateIntervalFormat> dateIntervalFormat(ConstructDateIntervalFormat(thread, dtf));
1240     if (dateIntervalFormat == nullptr) {
1241         icu::FormattedDateInterval emptyValue;
1242         THROW_TYPE_ERROR_AND_RETURN(thread, "create dateIntervalFormat failed", emptyValue);
1243     }
1244     UErrorCode status = U_ZERO_ERROR;
1245     icu::DateInterval dateInterval(x, y);
1246     icu::FormattedDateInterval formatted = dateIntervalFormat->formatToValue(dateInterval, status);
1247     return formatted;
1248 }
1249 
NormDateTimeRange(JSThread * thread,const JSHandle<JSDateTimeFormat> & dtf,double x,double y)1250 JSHandle<EcmaString> JSDateTimeFormat::NormDateTimeRange(JSThread *thread, const JSHandle<JSDateTimeFormat> &dtf,
1251                                                          double x, double y)
1252 {
1253     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1254     JSHandle<EcmaString> result = factory->GetEmptyString();
1255     // 1. Let x be TimeClip(x).
1256     x = JSDate::TimeClip(x);
1257     // 2. If x is NaN, throw a RangeError exception.
1258     if (std::isnan(x)) {
1259         THROW_RANGE_ERROR_AND_RETURN(thread, "x is NaN", result);
1260     }
1261     // 3. Let y be TimeClip(y).
1262     y = JSDate::TimeClip(y);
1263     // 4. If y is NaN, throw a RangeError exception.
1264     if (std::isnan(y)) {
1265         THROW_RANGE_ERROR_AND_RETURN(thread, "y is NaN", result);
1266     }
1267 
1268     icu::FormattedDateInterval formatted = ConstructDTFRange(thread, dtf, x, y);
1269     RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
1270 
1271     // Formatted to string.
1272     bool outputRange = false;
1273     UErrorCode status = U_ZERO_ERROR;
1274     icu::UnicodeString formatResult = formatted.toString(status);
1275     if (U_FAILURE(status) != 0) {
1276         THROW_TYPE_ERROR_AND_RETURN(thread, "format to string failed",
1277                                     thread->GetEcmaVM()->GetFactory()->GetEmptyString());
1278     }
1279     icu::ConstrainedFieldPosition cfpos;
1280     while (formatted.nextPosition(cfpos, status) != 0) {
1281         if (cfpos.getCategory() == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
1282             outputRange = true;
1283             break;
1284         }
1285     }
1286     result = intl::LocaleHelper::UStringToString(thread, formatResult);
1287     if (!outputRange) {
1288         return FormatDateTime(thread, dtf, x);
1289     }
1290     return result;
1291 }
1292 
NormDateTimeRangeToParts(JSThread * thread,const JSHandle<JSDateTimeFormat> & dtf,double x,double y)1293 JSHandle<JSArray> JSDateTimeFormat::NormDateTimeRangeToParts(JSThread *thread, const JSHandle<JSDateTimeFormat> &dtf,
1294                                                              double x, double y)
1295 {
1296     JSHandle<JSArray> result(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1297     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1298     // 1. Let x be TimeClip(x).
1299     x = JSDate::TimeClip(x);
1300     // 2. If x is NaN, throw a RangeError exception.
1301     if (std::isnan(x)) {
1302         THROW_RANGE_ERROR_AND_RETURN(thread, "x is invalid time value", result);
1303     }
1304     // 3. Let y be TimeClip(y).
1305     y = JSDate::TimeClip(y);
1306     // 4. If y is NaN, throw a RangeError exception.
1307     if (std::isnan(y)) {
1308         THROW_RANGE_ERROR_AND_RETURN(thread, "y is invalid time value", result);
1309     }
1310 
1311     icu::FormattedDateInterval formatted = ConstructDTFRange(thread, dtf, x, y);
1312     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1313     return ConstructFDateIntervalToJSArray(thread, formatted);
1314 }
1315 
GainAvailableLocales(JSThread * thread)1316 JSHandle<TaggedArray> JSDateTimeFormat::GainAvailableLocales(JSThread *thread)
1317 {
1318     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1319     JSHandle<JSTaggedValue> dateTimeFormatLocales = env->GetDateTimeFormatLocales();
1320     const char *key = "calendar";
1321     const char *path = nullptr;
1322     if (dateTimeFormatLocales->IsUndefined()) {
1323         std::vector<std::string> availableStringLocales = intl::LocaleHelper::GetAvailableLocales(thread, key, path);
1324         JSHandle<TaggedArray> availableLocales = JSLocale::ConstructLocaleList(thread, availableStringLocales);
1325         env->SetDateTimeFormatLocales(thread, availableLocales);
1326         return availableLocales;
1327     }
1328     return JSHandle<TaggedArray>::Cast(dateTimeFormatLocales);
1329 }
1330 
ConstructFDateIntervalToJSArray(JSThread * thread,const icu::FormattedDateInterval & formatted)1331 JSHandle<JSArray> JSDateTimeFormat::ConstructFDateIntervalToJSArray(JSThread *thread,
1332                                                                     const icu::FormattedDateInterval &formatted)
1333 {
1334     UErrorCode status = U_ZERO_ERROR;
1335     icu::UnicodeString formattedValue = formatted.toTempString(status);
1336     // Let result be ArrayCreate(0).
1337     JSHandle<JSArray> array(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1338     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1339     // Let index be 0.
1340     int index = 0;
1341     int32_t preEndPos = 0;
1342     // 2: number of elements
1343     std::array<int32_t, 2> begin {};
1344     std::array<int32_t, 2> end {}; // 2: number of elements
1345     begin[0] = begin[1] = end[0] = end[1] = 0;
1346     std::vector<CommonDateFormatPart> parts;
1347 
1348     /**
1349      * From ICU header file document @unumberformatter.h
1350      * Sets a constraint on the field category.
1351      *
1352      * When this instance of ConstrainedFieldPosition is passed to FormattedValue#nextPosition,
1353      * positions are skipped unless they have the given category.
1354      *
1355      * Any previously set constraints are cleared.
1356      *
1357      * For example, to loop over only the number-related fields:
1358      *
1359      *     ConstrainedFieldPosition cfpo;
1360      *     cfpo.constrainCategory(UFIELDCATEGORY_NUMBER_FORMAT);
1361      *     while (fmtval.nextPosition(cfpo, status)) {
1362      *         // handle the number-related field position
1363      *     }
1364      */
1365     JSMutableHandle<EcmaString> substring(thread, JSTaggedValue::Undefined());
1366     icu::ConstrainedFieldPosition cfpos;
1367     while (formatted.nextPosition(cfpos, status)) {
1368         int32_t fCategory = cfpos.getCategory();
1369         int32_t fField = cfpos.getField();
1370         int32_t fStart = cfpos.getStart();
1371         int32_t fLimit = cfpos.getLimit();
1372 
1373         // 2 means the number of elements in category
1374         if (fCategory == UFIELD_CATEGORY_DATE_INTERVAL_SPAN && (fField == 0 || fField == 1)) {
1375             begin[fField] = fStart;
1376             end[fField] = fLimit;
1377         }
1378         if (fCategory == UFIELD_CATEGORY_DATE) {
1379             if (preEndPos < fStart) {
1380                 parts.emplace_back(CommonDateFormatPart(fField, preEndPos, fStart, index, true));
1381                 index++;
1382             }
1383             parts.emplace_back(CommonDateFormatPart(fField, fStart, fLimit, index, false));
1384             preEndPos = fLimit;
1385             ++index;
1386         }
1387     }
1388     if (U_FAILURE(status) != 0) {
1389         THROW_TYPE_ERROR_AND_RETURN(thread, "format date interval error", array);
1390     }
1391     int32_t length = formattedValue.length();
1392     if (length > preEndPos) {
1393         parts.emplace_back(CommonDateFormatPart(-1, preEndPos, length, index, true));
1394     }
1395     for (auto part : parts) {
1396         substring.Update(intl::LocaleHelper::UStringToString(thread, formattedValue, part.fBeginIndex,
1397             part.fEndIndex).GetTaggedValue());
1398         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1399         JSHandle<JSObject> element;
1400         if (part.isPreExist) {
1401             element = JSLocale::PutElement(thread, part.index, array, ConvertFieldIdToDateType(thread, -1),
1402                                            JSHandle<JSTaggedValue>::Cast(substring));
1403         } else {
1404             element = JSLocale::PutElement(thread, part.index, array, ConvertFieldIdToDateType(thread, part.fField),
1405                                            JSHandle<JSTaggedValue>::Cast(substring));
1406         }
1407         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1408         JSHandle<JSTaggedValue> value = JSHandle<JSTaggedValue>::Cast(
1409             ToValueString(thread, TrackValue(part.fBeginIndex, part.fEndIndex, begin, end)));
1410         JSObject::SetProperty(thread, element, thread->GlobalConstants()->GetHandledSourceString(), value, true);
1411         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1412     }
1413     return array;
1414 }
1415 
TrackValue(int32_t beginning,int32_t ending,std::array<int32_t,2> begin,std::array<int32_t,2> end)1416 Value JSDateTimeFormat::TrackValue(int32_t beginning, int32_t ending,
1417                                    std::array<int32_t, 2> begin, std::array<int32_t, 2> end)  // 2: number of elements
1418 {
1419     Value value = Value::SHARED;
1420     if ((begin[0] <= beginning) && (beginning <= end[0]) && (begin[0] <= ending) && (ending <= end[0])) {
1421         value = Value::START_RANGE;
1422     } else if ((begin[1] <= beginning) && (beginning <= end[1]) && (begin[1] <= ending) && (ending <= end[1])) {
1423         value = Value::END_RANGE;
1424     }
1425     return value;
1426 }
1427 
BuildIcuPatternDescs()1428 std::vector<IcuPatternDesc> BuildIcuPatternDescs()
1429 {
1430     static const std::vector<IcuPatternDesc> items = {
1431         IcuPatternDesc("weekday", ICU_WEEKDAY_PE, ICU_NARROW_LONG_SHORT),
1432         IcuPatternDesc("era", ICU_ERA_PE, ICU_NARROW_LONG_SHORT),
1433         IcuPatternDesc("year", ICU_YEAR_PE, ICU2_DIGIT_NUMERIC),
1434         IcuPatternDesc("month", ICU_MONTH_PE, ICU_NARROW_LONG_SHORT2_DIGIT_NUMERIC),
1435         IcuPatternDesc("day", ICU_DAY_PE, ICU2_DIGIT_NUMERIC),
1436         IcuPatternDesc("dayPeriod", ICU_DAY_PERIOD_PE, ICU_NARROW_LONG_SHORT),
1437         IcuPatternDesc("hour", ICU_HOUR_PE, ICU2_DIGIT_NUMERIC),
1438         IcuPatternDesc("minute", ICU_MINUTE_PE, ICU2_DIGIT_NUMERIC),
1439         IcuPatternDesc("second", ICU_SECOND_PE, ICU2_DIGIT_NUMERIC),
1440         IcuPatternDesc("timeZoneName", ICU_YIME_ZONE_NAME_PE, ICU_LONG_SHORT)
1441     };
1442     return items;
1443 }
1444 
InitializePattern(const IcuPatternDesc & hourData)1445 std::vector<IcuPatternDesc> InitializePattern(const IcuPatternDesc &hourData)
1446 {
1447     std::vector<IcuPatternDesc> result;
1448     std::vector<IcuPatternDesc> items = BuildIcuPatternDescs();
1449     std::vector<IcuPatternDesc>::iterator item = items.begin();
1450     while (item != items.end()) {
1451         if (item->property != "hour") {
1452             result.emplace_back(IcuPatternDesc(item->property, item->pairs, item->allowedValues));
1453         } else {
1454             result.emplace_back(hourData);
1455         }
1456         ++item;
1457     }
1458     return result;
1459 }
1460 
GetIcuPatternDesc(const HourCycleOption & hourCycle)1461 std::vector<IcuPatternDesc> JSDateTimeFormat::GetIcuPatternDesc(const HourCycleOption &hourCycle)
1462 {
1463     if (hourCycle == HourCycleOption::H11) {
1464         Pattern h11("KK", "K");
1465         return h11.Get();
1466     } else if (hourCycle == HourCycleOption::H12) {
1467         Pattern h12("hh", "h");
1468         return h12.Get();
1469     } else if (hourCycle == HourCycleOption::H23) {
1470         Pattern h23("HH", "H");
1471         return h23.Get();
1472     } else if (hourCycle == HourCycleOption::H24) {
1473         Pattern h24("kk", "k");
1474         return h24.Get();
1475     } else if (hourCycle == HourCycleOption::UNDEFINED) {
1476         Pattern pattern("jj", "j");
1477         return pattern.Get();
1478     }
1479     LOG_ECMA(FATAL) << "this branch is unreachable";
1480     UNREACHABLE();
1481 }
1482 
ChangeHourCyclePattern(const icu::UnicodeString & pattern,HourCycleOption hc)1483 icu::UnicodeString JSDateTimeFormat::ChangeHourCyclePattern(const icu::UnicodeString &pattern, HourCycleOption hc)
1484 {
1485     if (hc == HourCycleOption::UNDEFINED || hc == HourCycleOption::EXCEPTION) {
1486         return pattern;
1487     }
1488     icu::UnicodeString result;
1489     char16_t key = u'\0';
1490     auto mapIter = std::find_if(HOUR_CYCLE_MAP.begin(), HOUR_CYCLE_MAP.end(),
1491         [hc](const std::map<char16_t, HourCycleOption>::value_type item) {
1492                                     return item.second == hc;
1493     });
1494     if (mapIter != HOUR_CYCLE_MAP.end()) {
1495         key = mapIter->first;
1496     }
1497     bool needChange = true;
1498     char16_t last = u'\0';
1499     for (int32_t i = 0; i < pattern.length(); i++) {
1500         char16_t ch = pattern.charAt(i);
1501         if (ch == '\'') {
1502             needChange = !needChange;
1503             result.append(ch);
1504         } else if (HOUR_CYCLE_MAP.find(ch) != HOUR_CYCLE_MAP.end()) {
1505             result = (needChange && last == u'd') ? result.append(' ') : result;
1506             result.append(needChange ? key : ch);
1507         } else {
1508             result.append(ch);
1509         }
1510         last = ch;
1511     }
1512     return result;
1513 }
1514 
CreateICUSimpleDateFormat(const icu::Locale & icuLocale,const icu::UnicodeString & skeleton,icu::DateTimePatternGenerator * gn,HourCycleOption hc)1515 std::unique_ptr<icu::SimpleDateFormat> JSDateTimeFormat::CreateICUSimpleDateFormat(const icu::Locale &icuLocale,
1516                                                                                    const icu::UnicodeString &skeleton,
1517                                                                                    icu::DateTimePatternGenerator *gn,
1518                                                                                    HourCycleOption hc)
1519 {
1520     // See https://github.com/tc39/ecma402/issues/225
1521     UErrorCode status = U_ZERO_ERROR;
1522     icu::UnicodeString pattern = ChangeHourCyclePattern(
1523         gn->getBestPattern(skeleton, UDATPG_MATCH_HOUR_FIELD_LENGTH, status), hc);
1524     ASSERT_PRINT((U_SUCCESS(status) != 0), "get best pattern failed");
1525 
1526     status = U_ZERO_ERROR;
1527     auto dateFormat(std::make_unique<icu::SimpleDateFormat>(pattern, icuLocale, status));
1528     if (U_FAILURE(status) != 0) {
1529         return std::unique_ptr<icu::SimpleDateFormat>();
1530     }
1531     ASSERT_PRINT(dateFormat != nullptr, "dateFormat failed");
1532     return dateFormat;
1533 }
1534 
BuildCalendar(const icu::Locale & locale,const icu::TimeZone & timeZone)1535 std::unique_ptr<icu::Calendar> JSDateTimeFormat::BuildCalendar(const icu::Locale &locale, const icu::TimeZone &timeZone)
1536 {
1537     UErrorCode status = U_ZERO_ERROR;
1538     std::unique_ptr<icu::Calendar> calendar(icu::Calendar::createInstance(timeZone, locale, status));
1539     if (U_FAILURE(status) || calendar == nullptr) {
1540         return nullptr;
1541     }
1542     ASSERT_PRINT(U_SUCCESS(status), "buildCalendar failed");
1543     ASSERT_PRINT(calendar.get() != nullptr, "calendar is nullptr");
1544 
1545     /**
1546      * Return the class ID for this class.
1547      *
1548      * This is useful only for comparing to a return value from getDynamicClassID(). For example:
1549      *
1550      *     Base* polymorphic_pointer = createPolymorphicObject();
1551      *     if (polymorphic_pointer->getDynamicClassID() ==
1552      *     Derived::getStaticClassID()) ...
1553      */
1554     if (calendar->getDynamicClassID() == icu::GregorianCalendar::getStaticClassID()) {
1555         auto gregorianCalendar = static_cast<icu::GregorianCalendar *>(calendar.get());
1556         // ECMAScript start time, value = -(2**53)
1557         const double beginTime = -9007199254740992;
1558         gregorianCalendar->setGregorianChange(beginTime, status);
1559         ASSERT(U_SUCCESS(status));
1560     }
1561     return calendar;
1562 }
1563 
ConstructTimeZone(const std::string & timezone)1564 std::unique_ptr<icu::TimeZone> JSDateTimeFormat::ConstructTimeZone(const std::string &timezone)
1565 {
1566     if (timezone.empty()) {
1567         return std::unique_ptr<icu::TimeZone>();
1568     }
1569     std::string canonicalized = ConstructFormattedTimeZoneID(timezone);
1570 
1571     std::unique_ptr<icu::TimeZone> tz(icu::TimeZone::createTimeZone(canonicalized.c_str()));
1572     if (!JSLocale::IsValidTimeZoneName(*tz)) {
1573         return std::unique_ptr<icu::TimeZone>();
1574     }
1575     return tz;
1576 }
1577 
GetSpecialTimeZoneMap()1578 std::map<std::string, std::string> JSDateTimeFormat::GetSpecialTimeZoneMap()
1579 {
1580     std::vector<std::string> specialTimeZones = {
1581         "America/Argentina/ComodRivadavia",
1582         "America/Knox_IN",
1583         "Antarctica/McMurdo",
1584         "Australia/ACT",
1585         "Australia/LHI",
1586         "Australia/NSW",
1587         "Antarctica/DumontDUrville",
1588         "Brazil/DeNoronha",
1589         "CET",
1590         "CST6CDT",
1591         "Chile/EasterIsland",
1592         "EET",
1593         "EST",
1594         "EST5EDT",
1595         "GB",
1596         "GB-Eire",
1597         "HST",
1598         "MET",
1599         "MST",
1600         "MST7MDT",
1601         "Mexico/BajaNorte",
1602         "Mexico/BajaSur",
1603         "NZ",
1604         "NZ-CHAT",
1605         "PRC",
1606         "PST8PDT",
1607         "ROC",
1608         "ROK",
1609         "UCT",
1610         "W-SU",
1611         "WET"};
1612     std::map<std::string, std::string> map;
1613     for (const auto &item : specialTimeZones) {
1614         std::string upper(item);
1615         transform(upper.begin(), upper.end(), upper.begin(), toupper);
1616         map.emplace(upper, item);
1617     }
1618     return map;
1619 }
1620 
ConstructFormattedTimeZoneID(const std::string & input)1621 std::string JSDateTimeFormat::ConstructFormattedTimeZoneID(const std::string &input)
1622 {
1623     std::string result = input;
1624     transform(result.begin(), result.end(), result.begin(), toupper);
1625     std::map<std::string, std::string> map = JSDateTimeFormat::GetSpecialTimeZoneMap();
1626     auto it = map.find(result);
1627     if (it != map.end()) {
1628         return it->second;
1629     }
1630     static const std::vector<std::string> tzStyleEntry = {
1631         "GMT", "ETC/UTC", "ETC/UCT", "GMT0", "ETC/GMT", "GMT+0", "GMT-0"
1632     };
1633     if (result.find("SYSTEMV/") == 0) {
1634         result.replace(0, STRING_LENGTH_8, "SystemV/");
1635     } else if (result.find("US/") == 0) {
1636         result = (result.length() == STRING_LENGTH_3) ? result : "US/" + ToTitleCaseTimezonePosition(
1637             input.substr(STRING_LENGTH_3));
1638     } else if (result.find("ETC/GMT") == 0 && result.length() > STRING_LENGTH_7) {
1639         result = ConstructGMTTimeZoneID(input);
1640     } else if (count(tzStyleEntry.begin(), tzStyleEntry.end(), result)) {
1641         result = "UTC";
1642     } else if (result.length() == STRING_LENGTH_3) {
1643         return result;
1644     } else {
1645         return ToTitleCaseTimezonePosition(result);
1646     }
1647 
1648     return result;
1649 }
1650 
ToTitleCaseFunction(const std::string & input)1651 std::string JSDateTimeFormat::ToTitleCaseFunction(const std::string &input)
1652 {
1653     std::string result(input);
1654     transform(result.begin(), result.end(), result.begin(), tolower);
1655     result[0] = static_cast<int8_t>(toupper(result[0]));
1656     return result;
1657 }
1658 
ToTitleCaseTimezonePosition(const std::string & input)1659 std::string JSDateTimeFormat::ToTitleCaseTimezonePosition(const std::string &input)
1660 {
1661     std::vector<std::string> titleEntry;
1662     std::vector<std::string> charEntry;
1663     uint32_t leftPosition = 0;
1664     uint32_t titleLength = 0;
1665     for (size_t i = 0; i < input.length(); i++) {
1666         if (input[i] == '_' || input[i] == '-' || input[i] == '/') {
1667             std::string s(1, input[i]);
1668             charEntry.emplace_back(s);
1669             titleLength = i - leftPosition;
1670             titleEntry.emplace_back(input.substr(leftPosition, titleLength));
1671             leftPosition = i + 1;
1672         } else if (JSLocale::IsAsciiAlpha(input[i]) || input[i] == '\\') {
1673             continue;
1674         } else {
1675             return std::string();
1676         }
1677     }
1678     ASSERT(input.length() >= static_cast<size_t>(leftPosition));
1679     titleEntry.emplace_back(input.substr(leftPosition, input.length() - leftPosition));
1680     std::string result;
1681     size_t len = titleEntry.size();
1682     if (len == 0) {
1683         return ToTitleCaseFunction(input);
1684     }
1685     for (size_t i = 0; i < len - 1; i++) {
1686         std::string titleValue = ToTitleCaseFunction(titleEntry[i]);
1687         if (titleValue == "Of" || titleValue == "Es" || titleValue == "Au") {
1688             titleValue[0] = static_cast<int8_t>(tolower(titleValue[0]));
1689         }
1690         result = result + titleValue + charEntry[i];
1691     }
1692     result = result + ToTitleCaseFunction(titleEntry[len - 1]);
1693     return result;
1694 }
1695 
ConstructGMTTimeZoneID(const std::string & input)1696 std::string JSDateTimeFormat::ConstructGMTTimeZoneID(const std::string &input)
1697 {
1698     if (input.length() < STRING_LENGTH_8 || input.length() > STRING_LENGTH_10) {
1699         return "";
1700     }
1701     std::string ret = "Etc/GMT";
1702     int timeZoneOffsetFlag = 7; // The offset of time zone flag, to match RegExp starting with the correct string
1703     if (regex_match(input.substr(timeZoneOffsetFlag), std::regex("[+-][1][0-4]")) ||
1704         (regex_match(input.substr(timeZoneOffsetFlag), std::regex("[+-][0-9]")) ||
1705         input.substr(timeZoneOffsetFlag) == "0")) {
1706         return ret + input.substr(timeZoneOffsetFlag);
1707     }
1708     return "";
1709 }
1710 
ToHourCycleString(HourCycleOption hc)1711 std::string JSDateTimeFormat::ToHourCycleString(HourCycleOption hc)
1712 {
1713     auto mapIter = std::find_if(TO_HOUR_CYCLE_MAP.begin(), TO_HOUR_CYCLE_MAP.end(),
1714         [hc](const std::map<std::string, HourCycleOption>::value_type item) {
1715         return item.second == hc;
1716     });
1717     if (mapIter != TO_HOUR_CYCLE_MAP.end()) {
1718         return mapIter->first;
1719     }
1720     return "";
1721 }
1722 
OptionToHourCycle(const std::string & hc)1723 HourCycleOption JSDateTimeFormat::OptionToHourCycle(const std::string &hc)
1724 {
1725     auto iter = TO_HOUR_CYCLE_MAP.find(hc);
1726     if (iter != TO_HOUR_CYCLE_MAP.end()) {
1727         return iter->second;
1728     }
1729     return HourCycleOption::UNDEFINED;
1730 }
1731 
OptionToHourCycle(UDateFormatHourCycle hc)1732 HourCycleOption JSDateTimeFormat::OptionToHourCycle(UDateFormatHourCycle hc)
1733 {
1734     HourCycleOption hcOption = HourCycleOption::UNDEFINED;
1735     switch (hc) {
1736         case UDAT_HOUR_CYCLE_11:
1737             hcOption = HourCycleOption::H11;
1738             break;
1739         case UDAT_HOUR_CYCLE_12:
1740             hcOption = HourCycleOption::H12;
1741             break;
1742         case UDAT_HOUR_CYCLE_23:
1743             hcOption = HourCycleOption::H23;
1744             break;
1745         case UDAT_HOUR_CYCLE_24:
1746             hcOption = HourCycleOption::H24;
1747             break;
1748         default:
1749             LOG_ECMA(FATAL) << "this branch is unreachable";
1750             UNREACHABLE();
1751     }
1752     return hcOption;
1753 }
1754 
ConvertFieldIdToDateType(JSThread * thread,int32_t fieldId)1755 JSHandle<JSTaggedValue> JSDateTimeFormat::ConvertFieldIdToDateType(JSThread *thread, int32_t fieldId)
1756 {
1757     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
1758     auto globalConst = thread->GlobalConstants();
1759     if (fieldId == -1) {
1760         result.Update(globalConst->GetHandledLiteralString().GetTaggedValue());
1761     } else if (fieldId == UDAT_YEAR_FIELD || fieldId == UDAT_EXTENDED_YEAR_FIELD) {
1762         result.Update(globalConst->GetHandledYearString().GetTaggedValue());
1763     } else if (fieldId == UDAT_YEAR_NAME_FIELD) {
1764         result.Update(globalConst->GetHandledYearNameString().GetTaggedValue());
1765     } else if (fieldId == UDAT_MONTH_FIELD || fieldId == UDAT_STANDALONE_MONTH_FIELD) {
1766         result.Update(globalConst->GetHandledMonthString().GetTaggedValue());
1767     } else if (fieldId == UDAT_DATE_FIELD) {
1768         result.Update(globalConst->GetHandledDayString().GetTaggedValue());
1769     } else if (fieldId == UDAT_HOUR_OF_DAY1_FIELD ||
1770                fieldId == UDAT_HOUR_OF_DAY0_FIELD || fieldId == UDAT_HOUR1_FIELD || fieldId == UDAT_HOUR0_FIELD) {
1771         result.Update(globalConst->GetHandledHourString().GetTaggedValue());
1772     } else if (fieldId == UDAT_MINUTE_FIELD) {
1773         result.Update(globalConst->GetHandledMinuteString().GetTaggedValue());
1774     } else if (fieldId == UDAT_SECOND_FIELD) {
1775         result.Update(globalConst->GetHandledSecondString().GetTaggedValue());
1776     } else if (fieldId == UDAT_DAY_OF_WEEK_FIELD || fieldId == UDAT_DOW_LOCAL_FIELD ||
1777                fieldId == UDAT_STANDALONE_DAY_FIELD) {
1778         result.Update(globalConst->GetHandledWeekdayString().GetTaggedValue());
1779     } else if (fieldId == UDAT_AM_PM_FIELD || fieldId == UDAT_AM_PM_MIDNIGHT_NOON_FIELD ||
1780                fieldId == UDAT_FLEXIBLE_DAY_PERIOD_FIELD) {
1781         result.Update(globalConst->GetHandledDayPeriodString().GetTaggedValue());
1782     } else if (fieldId == UDAT_TIMEZONE_FIELD || fieldId == UDAT_TIMEZONE_RFC_FIELD ||
1783                fieldId == UDAT_TIMEZONE_GENERIC_FIELD || fieldId == UDAT_TIMEZONE_SPECIAL_FIELD ||
1784                fieldId == UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD || fieldId == UDAT_TIMEZONE_ISO_FIELD ||
1785                fieldId == UDAT_TIMEZONE_ISO_LOCAL_FIELD) {
1786         result.Update(globalConst->GetHandledTimeZoneNameString().GetTaggedValue());
1787     } else if (fieldId == UDAT_ERA_FIELD) {
1788         result.Update(globalConst->GetHandledEraString().GetTaggedValue());
1789     } else if (fieldId == UDAT_FRACTIONAL_SECOND_FIELD) {
1790         result.Update(globalConst->GetHandledFractionalSecondString().GetTaggedValue());
1791     } else if (fieldId == UDAT_RELATED_YEAR_FIELD) {
1792         result.Update(globalConst->GetHandledRelatedYearString().GetTaggedValue());
1793     } else if (fieldId == UDAT_QUARTER_FIELD || fieldId == UDAT_STANDALONE_QUARTER_FIELD) {
1794         LOG_ECMA(FATAL) << "this branch is unreachable";
1795         UNREACHABLE();
1796     }
1797     return result;
1798 }
1799 
ConstructDateIntervalFormat(const JSThread * thread,const JSHandle<JSDateTimeFormat> & dtf)1800 std::unique_ptr<icu::DateIntervalFormat> JSDateTimeFormat::ConstructDateIntervalFormat(
1801     const JSThread *thread, const JSHandle<JSDateTimeFormat> &dtf)
1802 {
1803     icu::SimpleDateFormat *icuSimpleDateFormat = dtf->GetIcuSimpleDateFormat(thread);
1804     icu::Locale locale = *(dtf->GetIcuLocale(thread));
1805     std::string hcString = ToHourCycleString(dtf->GetHourCycle());
1806     UErrorCode status = U_ZERO_ERROR;
1807     // Sets the Unicode value for a Unicode keyword.
1808     if (!hcString.empty()) {
1809         locale.setUnicodeKeywordValue("hc", hcString, status);
1810     }
1811     icu::UnicodeString pattern;
1812     // Return a pattern string describing this date format.
1813     pattern = icuSimpleDateFormat->toPattern(pattern);
1814     // Utility to return a unique skeleton from a given pattern.
1815     icu::UnicodeString skeleton = icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
1816     // Construct a DateIntervalFormat from skeleton and a given locale.
1817     std::unique_ptr<icu::DateIntervalFormat> dateIntervalFormat(
1818         icu::DateIntervalFormat::createInstance(skeleton, locale, status));
1819     if (U_FAILURE(status)) {
1820         return nullptr;
1821     }
1822     dateIntervalFormat->setTimeZone(icuSimpleDateFormat->getTimeZone());
1823     return dateIntervalFormat;
1824 }
1825 }  // namespace panda::ecmascript
1826