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() const99 icu::Locale *JSDateTimeFormat::GetIcuLocale() const
100 {
101 ASSERT(GetLocaleIcu().IsJSNativePointer());
102 auto result = JSNativePointer::Cast(GetLocaleIcu().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();
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([[maybe_unused]] 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() const136 icu::SimpleDateFormat *JSDateTimeFormat::GetIcuSimpleDateFormat() const
137 {
138 ASSERT(GetSimpleDateTimeFormatIcu().IsJSNativePointer());
139 auto result = JSNativePointer::Cast(GetSimpleDateTimeFormatIcu().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();
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([[maybe_unused]] 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(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(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(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(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();
639 switch (type) {
640 case IcuCacheType::DEFAULT:
641 thread->GetCurrentEcmaContext()->SetIcuFormatterToCache(IcuFormatterType::SIMPLE_DATE_FORMAT_DEFAULT,
642 cacheEntry, simpleDateFormatIcu.release(), JSDateTimeFormat::FreeSimpleDateFormat);
643 break;
644 case IcuCacheType::DATE:
645 thread->GetCurrentEcmaContext()->SetIcuFormatterToCache(IcuFormatterType::SIMPLE_DATE_FORMAT_DATE,
646 cacheEntry, simpleDateFormatIcu.release(), JSDateTimeFormat::FreeSimpleDateFormat);
647 break;
648 case IcuCacheType::TIME:
649 thread->GetCurrentEcmaContext()->SetIcuFormatterToCache(IcuFormatterType::SIMPLE_DATE_FORMAT_TIME,
650 cacheEntry, simpleDateFormatIcu.release(), JSDateTimeFormat::FreeSimpleDateFormat);
651 break;
652 default:
653 UNREACHABLE();
654 }
655 } else {
656 SetIcuSimpleDateFormat(thread, dateTimeFormat, *simpleDateFormatIcu, JSDateTimeFormat::FreeSimpleDateFormat);
657 }
658
659 // Set dateTimeFormat.[[iso8601]].
660 bool iso8601 = strstr(icuLocale.getName(), "calendar=iso8601") != nullptr;
661 dateTimeFormat->SetIso8601(thread, JSTaggedValue(iso8601));
662
663 // Set dateTimeFormat.[[locale]].
664 if (!hour12->IsUndefined() || hourCycle != HourCycleOption::UNDEFINED) {
665 if ((resolvedLocale.extensions.find("hc") != resolvedLocale.extensions.end()) &&
666 (dtfHourCycle != OptionToHourCycle((resolvedLocale.extensions.find("hc")->second)))) {
667 resolvedIcuLocaleCopy.setUnicodeKeywordValue("hc", nullptr, status);
668 ASSERT_PRINT(U_SUCCESS(status), "resolvedIcuLocaleCopy set hc failed");
669 }
670 }
671 JSHandle<EcmaString> localeStr = intl::LocaleHelper::ToLanguageTag(thread, resolvedIcuLocaleCopy);
672 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread);
673 dateTimeFormat->SetLocale(thread, localeStr.GetTaggedValue());
674
675 // Set dateTimeFormat.[[boundFormat]].
676 dateTimeFormat->SetBoundFormat(thread, JSTaggedValue::Undefined());
677
678 // 39. Return dateTimeFormat.
679 return dateTimeFormat;
680 }
681
GetCachedIcuSimpleDateFormat(JSThread *thread, const JSHandle<JSTaggedValue> &locales, IcuFormatterType type)682 icu::SimpleDateFormat *JSDateTimeFormat::GetCachedIcuSimpleDateFormat(JSThread *thread,
683 const JSHandle<JSTaggedValue> &locales,
684 IcuFormatterType type)
685 {
686 std::string cacheEntry = locales->IsUndefined() ? "" : EcmaStringAccessor(locales.GetTaggedValue()).ToStdString();
687 void *cachedSimpleDateFormat = thread->GetCurrentEcmaContext()->GetIcuFormatterFromCache(type, cacheEntry);
688 if (cachedSimpleDateFormat != nullptr) {
689 return reinterpret_cast<icu::SimpleDateFormat*>(cachedSimpleDateFormat);
690 }
691 return nullptr;
692 }
693
694 // 13.1.2 ToDateTimeOptions (options, required, defaults)
ToDateTimeOptions(JSThread *thread, const JSHandle<JSTaggedValue> &options, const RequiredOption &required, const DefaultsOption &defaults)695 JSHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions(JSThread *thread, const JSHandle<JSTaggedValue> &options,
696 const RequiredOption &required, const DefaultsOption &defaults)
697 {
698 EcmaVM *ecmaVm = thread->GetEcmaVM();
699 ObjectFactory *factory = ecmaVm->GetFactory();
700
701 // 1. If options is undefined, let options be null; otherwise let options be ? ToObject(options).
702 JSHandle<JSObject> optionsResult(thread, JSTaggedValue::Null());
703 if (!options->IsUndefined()) {
704 optionsResult = JSTaggedValue::ToObject(thread, options);
705 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
706 }
707
708 // 2. Let options be ObjectCreate(options).
709 optionsResult = JSObject::ObjectCreate(thread, optionsResult);
710
711 // 3. Let needDefaults be true.
712 bool needDefaults = true;
713
714 // 4. If required is "date" or "any", then
715 // a. For each of the property names "weekday", "year", "month", "day", do
716 // i. Let prop be the property name.
717 // ii. Let value be ? Get(options, prop).
718 // iii. If value is not undefined, let needDefaults be false.
719 auto globalConst = thread->GlobalConstants();
720 if (required == RequiredOption::DATE || required == RequiredOption::ANY) {
721 JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_4);
722 array->Set(thread, 0, globalConst->GetHandledWeekdayString());
723 array->Set(thread, 1, globalConst->GetHandledYearString());
724 array->Set(thread, 2, globalConst->GetHandledMonthString()); // 2 means the third slot
725 array->Set(thread, 3, globalConst->GetHandledDayString()); // 3 means the fourth slot
726 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
727 uint32_t len = array->GetLength();
728 for (uint32_t i = 0; i < len; i++) {
729 key.Update(array->Get(thread, i));
730 OperationResult operationResult = JSObject::GetProperty(thread, optionsResult, key);
731 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
732 if (!operationResult.GetValue()->IsUndefined()) {
733 needDefaults = false;
734 }
735 }
736 }
737
738 // 5. If required is "time" or "any", then
739 // a. For each of the property names "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits", do
740 // i. Let prop be the property name.
741 // ii. Let value be ? Get(options, prop).
742 // iii. If value is not undefined, let needDefaults be false.
743 if (required == RequiredOption::TIME || required == RequiredOption::ANY) {
744 JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_5);
745 array->Set(thread, 0, globalConst->GetHandledDayPeriodString());
746 array->Set(thread, 1, globalConst->GetHandledHourString());
747 array->Set(thread, 2, globalConst->GetHandledMinuteString()); // 2 means the second slot
748 array->Set(thread, 3, globalConst->GetHandledSecondString()); // 3 means the third slot
749 array->Set(thread, 4, globalConst->GetHandledFractionalSecondDigitsString()); // 4 means the fourth slot
750 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
751 uint32_t len = array->GetLength();
752 for (uint32_t i = 0; i < len; i++) {
753 key.Update(array->Get(thread, i));
754 OperationResult operationResult = JSObject::GetProperty(thread, optionsResult, key);
755 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
756 if (!operationResult.GetValue()->IsUndefined()) {
757 needDefaults = false;
758 }
759 }
760 }
761
762 // Let dateStyle/timeStyle be ? Get(options, "dateStyle"/"timeStyle").
763 OperationResult dateStyleResult =
764 JSObject::GetProperty(thread, optionsResult, globalConst->GetHandledDateStyleString());
765 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
766 JSHandle<JSTaggedValue> dateStyle = dateStyleResult.GetValue();
767 OperationResult timeStyleResult =
768 JSObject::GetProperty(thread, optionsResult, globalConst->GetHandledTimeStyleString());
769 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
770 JSHandle<JSTaggedValue> timeStyle = timeStyleResult.GetValue();
771
772 // If dateStyle is not undefined or timeStyle is not undefined, let needDefaults be false.
773 if (!dateStyle->IsUndefined() || !timeStyle->IsUndefined()) {
774 needDefaults = false;
775 }
776
777 // If required is "date"/"time" and timeStyle is not undefined, throw a TypeError exception.
778 if (required == RequiredOption::DATE && !timeStyle->IsUndefined()) {
779 THROW_TYPE_ERROR_AND_RETURN(thread, "timeStyle is not undefined", optionsResult);
780 }
781 if (required == RequiredOption::TIME && !dateStyle->IsUndefined()) {
782 THROW_TYPE_ERROR_AND_RETURN(thread, "dateStyle is not undefined", optionsResult);
783 }
784
785 // 6. If needDefaults is true and defaults is either "date" or "all", then
786 // a. For each of the property names "year", "month", "day", do
787 // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
788 if (needDefaults && (defaults == DefaultsOption::DATE || defaults == DefaultsOption::ALL)) {
789 JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_3);
790 array->Set(thread, 0, globalConst->GetHandledYearString());
791 array->Set(thread, 1, globalConst->GetHandledMonthString());
792 array->Set(thread, 2, globalConst->GetHandledDayString()); // 2 means the third slot
793 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
794 uint32_t len = array->GetLength();
795 for (uint32_t i = 0; i < len; i++) {
796 key.Update(array->Get(thread, i));
797 JSObject::CreateDataPropertyOrThrow(thread, optionsResult, key, globalConst->GetHandledNumericString());
798 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
799 }
800 }
801
802 // 7. If needDefaults is true and defaults is either "time" or "all", then
803 // a. For each of the property names "hour", "minute", "second", do
804 // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
805 if (needDefaults && (defaults == DefaultsOption::TIME || defaults == DefaultsOption::ALL)) {
806 JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_3);
807 array->Set(thread, 0, globalConst->GetHandledHourString());
808 array->Set(thread, 1, globalConst->GetHandledMinuteString());
809 array->Set(thread, 2, globalConst->GetHandledSecondString()); // 2 means the third slot
810 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
811 uint32_t len = array->GetLength();
812 for (uint32_t i = 0; i < len; i++) {
813 key.Update(array->Get(thread, i));
814 JSObject::CreateDataPropertyOrThrow(thread, optionsResult, key, globalConst->GetHandledNumericString());
815 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
816 }
817 }
818
819 // 8. Return options.
820 return optionsResult;
821 }
822
ToDateTimeSkeleton(JSThread *thread, const std::vector<std::string> &options, std::string &skeleton, HourCycleOption hc, const RequiredOption &required, const DefaultsOption &defaults)823 void JSDateTimeFormat::ToDateTimeSkeleton(JSThread *thread, const std::vector<std::string> &options,
824 std::string &skeleton, HourCycleOption hc,
825 const RequiredOption &required, const DefaultsOption &defaults)
826 {
827 EcmaVM *ecmaVm = thread->GetEcmaVM();
828 ObjectFactory *factory = ecmaVm->GetFactory();
829
830 // 1. Let needDefaults be true.
831 bool needDefaults = true;
832
833 // 2. If required is "date" or "any", then
834 // a. For each of the property names "weekday", "year", "month", "day", do
835 // i. Let prop be the property name.
836 // ii. Let value be ? Get(options, prop).
837 // iii. If value is not undefined, let needDefaults be false.
838 auto globalConst = thread->GlobalConstants();
839 if (required == RequiredOption::DATE || required == RequiredOption::ANY) {
840 JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_4);
841 array->Set(thread, 0, globalConst->GetHandledWeekdayString());
842 array->Set(thread, 1, globalConst->GetHandledYearString());
843 array->Set(thread, 2, globalConst->GetHandledMonthString()); // 2 means the third slot
844 array->Set(thread, 3, globalConst->GetHandledDayString()); // 3 means the fourth slot
845 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
846 uint32_t len = array->GetLength();
847 for (uint32_t i = 0; i < len; i++) {
848 key.Update(array->Get(thread, i));
849 std::string result = EcmaStringAccessor(key.GetTaggedValue()).ToStdString();
850 auto it = std::find(options.begin(), options.end(), result);
851 if (it != options.end()) {
852 needDefaults = false;
853 break;
854 }
855 }
856 }
857
858 // 3. If required is "time" or "any", then
859 // a. For each of the property names "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits", do
860 // i. Let prop be the property name.
861 // ii. Let value be ? Get(options, prop).
862 // iii. If value is not undefined, let needDefaults be false.
863 if (required == RequiredOption::TIME || required == RequiredOption::ANY) {
864 JSHandle<TaggedArray> array = factory->NewTaggedArray(CAPACITY_5);
865 array->Set(thread, 0, globalConst->GetHandledDayPeriodString());
866 array->Set(thread, 1, globalConst->GetHandledHourString());
867 array->Set(thread, 2, globalConst->GetHandledMinuteString()); // 2 means the second slot
868 array->Set(thread, 3, globalConst->GetHandledSecondString()); // 3 means the third slot
869 array->Set(thread, 4, globalConst->GetHandledFractionalSecondDigitsString()); // 4 means the fourth slot
870 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
871 uint32_t len = array->GetLength();
872 for (uint32_t i = 0; i < len; i++) {
873 key.Update(array->Get(thread, i));
874 std::string result = EcmaStringAccessor(key.GetTaggedValue()).ToStdString();
875 auto it = std::find(options.begin(), options.end(), result);
876 if (it != options.end()) {
877 needDefaults = false;
878 break;
879 }
880 }
881 }
882
883 // 4. If needDefaults is true and defaults is either "date" or "all", then
884 // skeleton += "year", "month", "day"
885 if (needDefaults && (defaults == DefaultsOption::DATE || defaults == DefaultsOption::ALL)) {
886 skeleton += "yMd";
887 }
888
889 // 5. If needDefaults is true and defaults is either "time" or "all", then
890 // skeleton += "hour", "minute", "second"
891 if (needDefaults && (defaults == DefaultsOption::TIME || defaults == DefaultsOption::ALL)) {
892 switch (hc) {
893 case HourCycleOption::H12:
894 skeleton += "hms";
895 break;
896 case HourCycleOption::H23:
897 case HourCycleOption::UNDEFINED:
898 skeleton += "Hms";
899 break;
900 case HourCycleOption::H11:
901 skeleton += "Kms";
902 break;
903 case HourCycleOption::H24:
904 skeleton += "kms";
905 break;
906 default:
907 break;
908 }
909 }
910 }
911
912 // 13.1.7 FormatDateTime(dateTimeFormat, x)
FormatDateTime(JSThread *thread, const JSHandle<JSDateTimeFormat> &dateTimeFormat, double x)913 JSHandle<EcmaString> JSDateTimeFormat::FormatDateTime(JSThread *thread,
914 const JSHandle<JSDateTimeFormat> &dateTimeFormat, double x)
915 {
916 icu::SimpleDateFormat *simpleDateFormat = dateTimeFormat->GetIcuSimpleDateFormat();
917 JSHandle<EcmaString> res = FormatDateTime(thread, simpleDateFormat, x);
918 RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
919 return res;
920 }
921
FormatDateTime(JSThread *thread, const icu::SimpleDateFormat *simpleDateFormat, double x)922 JSHandle<EcmaString> JSDateTimeFormat::FormatDateTime(JSThread *thread,
923 const icu::SimpleDateFormat *simpleDateFormat, double x)
924 {
925 // 1. Let parts be ? PartitionDateTimePattern(dateTimeFormat, x).
926 double xValue = JSDate::TimeClip(x);
927 if (std::isnan(xValue)) {
928 THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid time value", thread->GetEcmaVM()->GetFactory()->GetEmptyString());
929 }
930
931 // 2. Let result be the empty String.
932 icu::UnicodeString result;
933
934 // 3. Set result to the string-concatenation of result and part.[[Value]].
935 {
936 ThreadNativeScope nativeScope(thread);
937 simpleDateFormat->format(xValue, result);
938 }
939
940 // 4. Return result.
941 return intl::LocaleHelper::UStringToString(thread, result);
942 }
943
944 // 13.1.8 FormatDateTimeToParts (dateTimeFormat, x)
FormatDateTimeToParts(JSThread *thread, const JSHandle<JSDateTimeFormat> &dateTimeFormat, double x)945 JSHandle<JSArray> JSDateTimeFormat::FormatDateTimeToParts(JSThread *thread,
946 const JSHandle<JSDateTimeFormat> &dateTimeFormat, double x)
947 {
948 icu::SimpleDateFormat *simpleDateFormat = dateTimeFormat->GetIcuSimpleDateFormat();
949 ASSERT(simpleDateFormat != nullptr);
950 UErrorCode status = U_ZERO_ERROR;
951 icu::FieldPositionIterator fieldPositionIter;
952 icu::UnicodeString formattedParts;
953 {
954 ThreadNativeScope nativeScope(thread);
955 simpleDateFormat->format(x, formattedParts, &fieldPositionIter, status);
956 }
957 if (U_FAILURE(status) != 0) {
958 THROW_TYPE_ERROR_AND_RETURN(thread, "format failed", thread->GetEcmaVM()->GetFactory()->NewJSArray());
959 }
960
961 // 2. Let result be ArrayCreate(0).
962 JSHandle<JSArray> result(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
963 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
964 if (formattedParts.isBogus()) {
965 return result;
966 }
967
968 // 3. Let n be 0.
969 int32_t index = 0;
970 int32_t preEdgePos = 0;
971 std::vector<CommonDateFormatPart> parts;
972 icu::FieldPosition fieldPosition;
973 while (fieldPositionIter.next(fieldPosition)) {
974 int32_t fField = fieldPosition.getField();
975 int32_t fBeginIndex = fieldPosition.getBeginIndex();
976 int32_t fEndIndex = fieldPosition.getEndIndex();
977 if (preEdgePos < fBeginIndex) {
978 parts.emplace_back(CommonDateFormatPart(fField, preEdgePos, fBeginIndex, index, true));
979 ++index;
980 }
981 parts.emplace_back(CommonDateFormatPart(fField, fBeginIndex, fEndIndex, index, false));
982 preEdgePos = fEndIndex;
983 ++index;
984 }
985 int32_t length = formattedParts.length();
986 if (preEdgePos < length) {
987 parts.emplace_back(CommonDateFormatPart(-1, preEdgePos, length, index, true));
988 }
989 JSMutableHandle<EcmaString> substring(thread, JSTaggedValue::Undefined());
990
991 // 4. For each part in parts, do
992 for (auto part : parts) {
993 substring.Update(intl::LocaleHelper::UStringToString(thread, formattedParts, part.fBeginIndex,
994 part.fEndIndex).GetTaggedValue());
995 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
996 // Let O be ObjectCreate(%ObjectPrototype%).
997 // Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]).
998 // Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]).
999 // Perform ! CreateDataProperty(result, ! ToString(n), O).
1000 if (part.isPreExist) {
1001 JSLocale::PutElement(thread, part.index, result, ConvertFieldIdToDateType(thread, -1),
1002 JSHandle<JSTaggedValue>::Cast(substring));
1003 } else {
1004 JSLocale::PutElement(thread, part.index, result, ConvertFieldIdToDateType(thread, part.fField),
1005 JSHandle<JSTaggedValue>::Cast(substring));
1006 }
1007 }
1008
1009 // 5. Return result.
1010 return result;
1011 }
1012
1013 // 13.1.10 UnwrapDateTimeFormat(dtf)
UnwrapDateTimeFormat(JSThread *thread, const JSHandle<JSTaggedValue> &dateTimeFormat)1014 JSHandle<JSTaggedValue> JSDateTimeFormat::UnwrapDateTimeFormat(JSThread *thread,
1015 const JSHandle<JSTaggedValue> &dateTimeFormat)
1016 {
1017 // 1. Assert: Type(dtf) is Object.
1018 ASSERT_PRINT(dateTimeFormat->IsJSObject(), "dateTimeFormat is not object");
1019
1020 // 2. If dateTimeFormat does not have an [[InitializedDateTimeFormat]] internal slot
1021 // and ? InstanceofOperator(dateTimeFormat, %DateTimeFormat%) is true, then
1022 // a. Let dateTimeFormat be ? Get(dateTimeFormat, %Intl%.[[FallbackSymbol]]).
1023 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1024 bool isInstanceOf = JSFunction::OrdinaryHasInstance(thread, env->GetDateTimeFormatFunction(), dateTimeFormat);
1025 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, dateTimeFormat);
1026 if (!dateTimeFormat->IsJSDateTimeFormat() && isInstanceOf) {
1027 JSHandle<JSTaggedValue> key(thread, JSHandle<JSIntl>::Cast(env->GetIntlFunction())->GetFallbackSymbol());
1028 OperationResult operationResult = JSTaggedValue::GetProperty(thread, dateTimeFormat, key);
1029 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, dateTimeFormat);
1030 return operationResult.GetValue();
1031 }
1032
1033 // 3. Perform ? RequireInternalSlot(dateTimeFormat, [[InitializedDateTimeFormat]]).
1034 if (!dateTimeFormat->IsJSDateTimeFormat()) {
1035 THROW_TYPE_ERROR_AND_RETURN(thread, "is not JSDateTimeFormat",
1036 JSHandle<JSTaggedValue>(thread, JSTaggedValue::Exception()));
1037 }
1038
1039 // 4. Return dateTimeFormat.
1040 return dateTimeFormat;
1041 }
1042
ToHourCycleEcmaString(JSThread *thread, HourCycleOption hc)1043 JSHandle<JSTaggedValue> ToHourCycleEcmaString(JSThread *thread, HourCycleOption hc)
1044 {
1045 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
1046 auto globalConst = thread->GlobalConstants();
1047 switch (hc) {
1048 case HourCycleOption::H11:
1049 result.Update(globalConst->GetHandledH11String().GetTaggedValue());
1050 break;
1051 case HourCycleOption::H12:
1052 result.Update(globalConst->GetHandledH12String().GetTaggedValue());
1053 break;
1054 case HourCycleOption::H23:
1055 result.Update(globalConst->GetHandledH23String().GetTaggedValue());
1056 break;
1057 case HourCycleOption::H24:
1058 result.Update(globalConst->GetHandledH24String().GetTaggedValue());
1059 break;
1060 default:
1061 LOG_ECMA(FATAL) << "this branch is unreachable";
1062 UNREACHABLE();
1063 }
1064 return result;
1065 }
1066
ToDateTimeStyleEcmaString(JSThread *thread, DateTimeStyleOption style)1067 JSHandle<JSTaggedValue> ToDateTimeStyleEcmaString(JSThread *thread, DateTimeStyleOption style)
1068 {
1069 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
1070 auto globalConst = thread->GlobalConstants();
1071 switch (style) {
1072 case DateTimeStyleOption::FULL:
1073 result.Update(globalConst->GetHandledFullString().GetTaggedValue());
1074 break;
1075 case DateTimeStyleOption::LONG:
1076 result.Update(globalConst->GetHandledLongString().GetTaggedValue());
1077 break;
1078 case DateTimeStyleOption::MEDIUM:
1079 result.Update(globalConst->GetHandledMediumString().GetTaggedValue());
1080 break;
1081 case DateTimeStyleOption::SHORT:
1082 result.Update(globalConst->GetHandledShortString().GetTaggedValue());
1083 break;
1084 default:
1085 LOG_ECMA(FATAL) << "this branch is unreachable";
1086 UNREACHABLE();
1087 }
1088 return result;
1089 }
1090
1091 // 13.4.5 Intl.DateTimeFormat.prototype.resolvedOptions ()
ResolvedOptions(JSThread *thread, const JSHandle<JSDateTimeFormat> &dateTimeFormat, const JSHandle<JSObject> &options)1092 void JSDateTimeFormat::ResolvedOptions(JSThread *thread, const JSHandle<JSDateTimeFormat> &dateTimeFormat,
1093 const JSHandle<JSObject> &options)
1094 { // Table 8: Resolved Options of DateTimeFormat Instances
1095 // Internal Slot Property
1096 // [[Locale]] "locale"
1097 // [[Calendar]] "calendar"
1098 // [[NumberingSystem]] "numberingSystem"
1099 // [[TimeZone]] "timeZone"
1100 // [[HourCycle]] "hourCycle"
1101 // "hour12"
1102 // [[Weekday]] "weekday"
1103 // [[Era]] "era"
1104 // [[Year]] "year"
1105 // [[Month]] "month"
1106 // [[Day]] "day"
1107 // [[Hour]] "hour"
1108 // [[Minute]] "minute"
1109 // [[Second]] "second"
1110 // [[TimeZoneName]] "timeZoneName"
1111 auto globalConst = thread->GlobalConstants();
1112 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1113
1114 // 5. For each row of Table 8, except the header row, in table order, do
1115 // Let p be the Property value of the current row.
1116 // [[Locale]]
1117 JSHandle<JSTaggedValue> locale(thread, dateTimeFormat->GetLocale());
1118 JSHandle<JSTaggedValue> property = globalConst->GetHandledLocaleString();
1119 JSObject::CreateDataPropertyOrThrow(thread, options, property, locale);
1120 RETURN_IF_ABRUPT_COMPLETION(thread);
1121 // [[Calendar]]
1122 JSMutableHandle<JSTaggedValue> calendarValue(thread, dateTimeFormat->GetCalendar());
1123 icu::SimpleDateFormat *icuSimpleDateFormat = dateTimeFormat->GetIcuSimpleDateFormat();
1124 const icu::Calendar *calendar = icuSimpleDateFormat->getCalendar();
1125 std::string icuCalendar = calendar->getType();
1126 if (icuCalendar == "gregorian") {
1127 if (dateTimeFormat->GetIso8601().IsTrue()) {
1128 calendarValue.Update(globalConst->GetHandledIso8601String().GetTaggedValue());
1129 } else {
1130 calendarValue.Update(globalConst->GetHandledGregoryString().GetTaggedValue());
1131 }
1132 } else if (icuCalendar == "ethiopic-amete-alem") {
1133 calendarValue.Update(globalConst->GetHandledEthioaaString().GetTaggedValue());
1134 } else if (icuCalendar.length() != 0) {
1135 calendarValue.Update(factory->NewFromStdString(icuCalendar).GetTaggedValue());
1136 }
1137 property = globalConst->GetHandledCalendarString();
1138 JSObject::CreateDataPropertyOrThrow(thread, options, property, calendarValue);
1139 RETURN_IF_ABRUPT_COMPLETION(thread);
1140 // [[NumberingSystem]]
1141 JSHandle<JSTaggedValue> numberingSystem(thread, dateTimeFormat->GetNumberingSystem());
1142 if (numberingSystem->IsUndefined()) {
1143 numberingSystem = globalConst->GetHandledLatnString();
1144 }
1145 property = globalConst->GetHandledNumberingSystemString();
1146 JSObject::CreateDataPropertyOrThrow(thread, options, property, numberingSystem);
1147 RETURN_IF_ABRUPT_COMPLETION(thread);
1148 // [[TimeZone]]
1149 JSMutableHandle<JSTaggedValue> timezoneValue(thread, dateTimeFormat->GetTimeZone());
1150 const icu::TimeZone &icuTZ = calendar->getTimeZone();
1151 icu::UnicodeString timezone;
1152 icuTZ.getID(timezone);
1153 UErrorCode status = U_ZERO_ERROR;
1154 icu::UnicodeString canonicalTimezone;
1155 icu::TimeZone::getCanonicalID(timezone, canonicalTimezone, status);
1156 if (U_SUCCESS(status) != 0) {
1157 if ((canonicalTimezone == UNICODE_STRING_SIMPLE("Etc/UTC")) != 0 ||
1158 (canonicalTimezone == UNICODE_STRING_SIMPLE("Etc/GMT")) != 0) {
1159 timezoneValue.Update(globalConst->GetUTCString());
1160 } else {
1161 timezoneValue.Update(intl::LocaleHelper::UStringToString(thread, canonicalTimezone).GetTaggedValue());
1162 }
1163 }
1164 property = globalConst->GetHandledTimeZoneString();
1165 JSObject::CreateDataPropertyOrThrow(thread, options, property, timezoneValue);
1166 RETURN_IF_ABRUPT_COMPLETION(thread);
1167 // [[HourCycle]]
1168 // For web compatibility reasons, if the property "hourCycle" is set, the "hour12" property should be set to true
1169 // when "hourCycle" is "h11" or "h12", or to false when "hourCycle" is "h23" or "h24".
1170 // i. Let hc be dtf.[[HourCycle]].
1171 JSHandle<JSTaggedValue> hcValue;
1172 HourCycleOption hc = dateTimeFormat->GetHourCycle();
1173 if (hc != HourCycleOption::UNDEFINED) {
1174 property = globalConst->GetHandledHourCycleString();
1175 hcValue = ToHourCycleEcmaString(thread, dateTimeFormat->GetHourCycle());
1176 JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue);
1177 RETURN_IF_ABRUPT_COMPLETION(thread);
1178 if (hc == HourCycleOption::H11 || hc == HourCycleOption::H12) {
1179 JSHandle<JSTaggedValue> trueValue(thread, JSTaggedValue::True());
1180 hcValue = trueValue;
1181 } else if (hc == HourCycleOption::H23 || hc == HourCycleOption::H24) {
1182 JSHandle<JSTaggedValue> falseValue(thread, JSTaggedValue::False());
1183 hcValue = falseValue;
1184 }
1185 property = globalConst->GetHandledHour12String();
1186 JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue);
1187 RETURN_IF_ABRUPT_COMPLETION(thread);
1188 }
1189 // [[DateStyle]], [[TimeStyle]].
1190 if (dateTimeFormat->GetDateStyle() == DateTimeStyleOption::UNDEFINED &&
1191 dateTimeFormat->GetTimeStyle() == DateTimeStyleOption::UNDEFINED) {
1192 icu::UnicodeString patternUnicode;
1193 icuSimpleDateFormat->toPattern(patternUnicode);
1194 std::string pattern;
1195 patternUnicode.toUTF8String(pattern);
1196 for (const auto &item : BuildIcuPatternDescs()) {
1197 // fractionalSecondsDigits need to be added before timeZoneName.
1198 if (item.property == "timeZoneName") {
1199 int tmpResult = count(pattern.begin(), pattern.end(), 'S');
1200 int fsd = (tmpResult >= STRING_LENGTH_3) ? STRING_LENGTH_3 : tmpResult;
1201 if (fsd > 0) {
1202 JSHandle<JSTaggedValue> fsdValue(thread, JSTaggedValue(fsd));
1203 property = globalConst->GetHandledFractionalSecondDigitsString();
1204 JSObject::CreateDataPropertyOrThrow(thread, options, property, fsdValue);
1205 RETURN_IF_ABRUPT_COMPLETION(thread);
1206 }
1207 }
1208 for (const auto &pair : item.pairs) {
1209 if (pattern.find(pair.first) != std::string::npos) {
1210 hcValue = JSHandle<JSTaggedValue>::Cast(factory->NewFromStdString(pair.second));
1211 property = JSHandle<JSTaggedValue>::Cast(factory->NewFromStdString(item.property));
1212 JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue);
1213 RETURN_IF_ABRUPT_COMPLETION(thread);
1214 break;
1215 }
1216 }
1217 }
1218 }
1219 if (dateTimeFormat->GetDateStyle() != DateTimeStyleOption::UNDEFINED) {
1220 property = globalConst->GetHandledDateStyleString();
1221 hcValue = ToDateTimeStyleEcmaString(thread, dateTimeFormat->GetDateStyle());
1222 JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue);
1223 RETURN_IF_ABRUPT_COMPLETION(thread);
1224 }
1225 if (dateTimeFormat->GetTimeStyle() != DateTimeStyleOption::UNDEFINED) {
1226 property = globalConst->GetHandledTimeStyleString();
1227 hcValue = ToDateTimeStyleEcmaString(thread, dateTimeFormat->GetTimeStyle());
1228 JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue);
1229 RETURN_IF_ABRUPT_COMPLETION(thread);
1230 }
1231 }
1232
1233 // Use dateInterval(x, y) construct datetimeformatrange
ConstructDTFRange(JSThread *thread, const JSHandle<JSDateTimeFormat> &dtf, double x, double y)1234 icu::FormattedDateInterval JSDateTimeFormat::ConstructDTFRange(JSThread *thread, const JSHandle<JSDateTimeFormat> &dtf,
1235 double x, double y)
1236 {
1237 std::unique_ptr<icu::DateIntervalFormat> dateIntervalFormat(ConstructDateIntervalFormat(dtf));
1238 if (dateIntervalFormat == nullptr) {
1239 icu::FormattedDateInterval emptyValue;
1240 THROW_TYPE_ERROR_AND_RETURN(thread, "create dateIntervalFormat failed", emptyValue);
1241 }
1242 UErrorCode status = U_ZERO_ERROR;
1243 icu::DateInterval dateInterval(x, y);
1244 icu::FormattedDateInterval formatted = dateIntervalFormat->formatToValue(dateInterval, status);
1245 return formatted;
1246 }
1247
NormDateTimeRange(JSThread *thread, const JSHandle<JSDateTimeFormat> &dtf, double x, double y)1248 JSHandle<EcmaString> JSDateTimeFormat::NormDateTimeRange(JSThread *thread, const JSHandle<JSDateTimeFormat> &dtf,
1249 double x, double y)
1250 {
1251 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1252 JSHandle<EcmaString> result = factory->GetEmptyString();
1253 // 1. Let x be TimeClip(x).
1254 x = JSDate::TimeClip(x);
1255 // 2. If x is NaN, throw a RangeError exception.
1256 if (std::isnan(x)) {
1257 THROW_RANGE_ERROR_AND_RETURN(thread, "x is NaN", result);
1258 }
1259 // 3. Let y be TimeClip(y).
1260 y = JSDate::TimeClip(y);
1261 // 4. If y is NaN, throw a RangeError exception.
1262 if (std::isnan(y)) {
1263 THROW_RANGE_ERROR_AND_RETURN(thread, "y is NaN", result);
1264 }
1265
1266 icu::FormattedDateInterval formatted = ConstructDTFRange(thread, dtf, x, y);
1267 RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
1268
1269 // Formatted to string.
1270 bool outputRange = false;
1271 UErrorCode status = U_ZERO_ERROR;
1272 icu::UnicodeString formatResult = formatted.toString(status);
1273 if (U_FAILURE(status) != 0) {
1274 THROW_TYPE_ERROR_AND_RETURN(thread, "format to string failed",
1275 thread->GetEcmaVM()->GetFactory()->GetEmptyString());
1276 }
1277 icu::ConstrainedFieldPosition cfpos;
1278 while (formatted.nextPosition(cfpos, status) != 0) {
1279 if (cfpos.getCategory() == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
1280 outputRange = true;
1281 break;
1282 }
1283 }
1284 result = intl::LocaleHelper::UStringToString(thread, formatResult);
1285 if (!outputRange) {
1286 return FormatDateTime(thread, dtf, x);
1287 }
1288 return result;
1289 }
1290
NormDateTimeRangeToParts(JSThread *thread, const JSHandle<JSDateTimeFormat> &dtf, double x, double y)1291 JSHandle<JSArray> JSDateTimeFormat::NormDateTimeRangeToParts(JSThread *thread, const JSHandle<JSDateTimeFormat> &dtf,
1292 double x, double y)
1293 {
1294 JSHandle<JSArray> result(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1295 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1296 // 1. Let x be TimeClip(x).
1297 x = JSDate::TimeClip(x);
1298 // 2. If x is NaN, throw a RangeError exception.
1299 if (std::isnan(x)) {
1300 THROW_RANGE_ERROR_AND_RETURN(thread, "x is invalid time value", result);
1301 }
1302 // 3. Let y be TimeClip(y).
1303 y = JSDate::TimeClip(y);
1304 // 4. If y is NaN, throw a RangeError exception.
1305 if (std::isnan(y)) {
1306 THROW_RANGE_ERROR_AND_RETURN(thread, "y is invalid time value", result);
1307 }
1308
1309 icu::FormattedDateInterval formatted = ConstructDTFRange(thread, dtf, x, y);
1310 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1311 return ConstructFDateIntervalToJSArray(thread, formatted);
1312 }
1313
GainAvailableLocales(JSThread *thread)1314 JSHandle<TaggedArray> JSDateTimeFormat::GainAvailableLocales(JSThread *thread)
1315 {
1316 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1317 JSHandle<JSTaggedValue> dateTimeFormatLocales = env->GetDateTimeFormatLocales();
1318 const char *key = "calendar";
1319 const char *path = nullptr;
1320 if (dateTimeFormatLocales->IsUndefined()) {
1321 std::vector<std::string> availableStringLocales = intl::LocaleHelper::GetAvailableLocales(thread, key, path);
1322 JSHandle<TaggedArray> availableLocales = JSLocale::ConstructLocaleList(thread, availableStringLocales);
1323 env->SetDateTimeFormatLocales(thread, availableLocales);
1324 return availableLocales;
1325 }
1326 return JSHandle<TaggedArray>::Cast(dateTimeFormatLocales);
1327 }
1328
ConstructFDateIntervalToJSArray(JSThread *thread, const icu::FormattedDateInterval &formatted)1329 JSHandle<JSArray> JSDateTimeFormat::ConstructFDateIntervalToJSArray(JSThread *thread,
1330 const icu::FormattedDateInterval &formatted)
1331 {
1332 UErrorCode status = U_ZERO_ERROR;
1333 icu::UnicodeString formattedValue = formatted.toTempString(status);
1334 // Let result be ArrayCreate(0).
1335 JSHandle<JSArray> array(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1336 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1337 // Let index be 0.
1338 int index = 0;
1339 int32_t preEndPos = 0;
1340 // 2: number of elements
1341 std::array<int32_t, 2> begin {};
1342 std::array<int32_t, 2> end {}; // 2: number of elements
1343 begin[0] = begin[1] = end[0] = end[1] = 0;
1344 std::vector<CommonDateFormatPart> parts;
1345
1346 /**
1347 * From ICU header file document @unumberformatter.h
1348 * Sets a constraint on the field category.
1349 *
1350 * When this instance of ConstrainedFieldPosition is passed to FormattedValue#nextPosition,
1351 * positions are skipped unless they have the given category.
1352 *
1353 * Any previously set constraints are cleared.
1354 *
1355 * For example, to loop over only the number-related fields:
1356 *
1357 * ConstrainedFieldPosition cfpo;
1358 * cfpo.constrainCategory(UFIELDCATEGORY_NUMBER_FORMAT);
1359 * while (fmtval.nextPosition(cfpo, status)) {
1360 * // handle the number-related field position
1361 * }
1362 */
1363 JSMutableHandle<EcmaString> substring(thread, JSTaggedValue::Undefined());
1364 icu::ConstrainedFieldPosition cfpos;
1365 while (formatted.nextPosition(cfpos, status)) {
1366 int32_t fCategory = cfpos.getCategory();
1367 int32_t fField = cfpos.getField();
1368 int32_t fStart = cfpos.getStart();
1369 int32_t fLimit = cfpos.getLimit();
1370
1371 // 2 means the number of elements in category
1372 if (fCategory == UFIELD_CATEGORY_DATE_INTERVAL_SPAN && (fField == 0 || fField == 1)) {
1373 begin[fField] = fStart;
1374 end[fField] = fLimit;
1375 }
1376 if (fCategory == UFIELD_CATEGORY_DATE) {
1377 if (preEndPos < fStart) {
1378 parts.emplace_back(CommonDateFormatPart(fField, preEndPos, fStart, index, true));
1379 index++;
1380 }
1381 parts.emplace_back(CommonDateFormatPart(fField, fStart, fLimit, index, false));
1382 preEndPos = fLimit;
1383 ++index;
1384 }
1385 }
1386 if (U_FAILURE(status) != 0) {
1387 THROW_TYPE_ERROR_AND_RETURN(thread, "format date interval error", array);
1388 }
1389 int32_t length = formattedValue.length();
1390 if (length > preEndPos) {
1391 parts.emplace_back(CommonDateFormatPart(-1, preEndPos, length, index, true));
1392 }
1393 for (auto part : parts) {
1394 substring.Update(intl::LocaleHelper::UStringToString(thread, formattedValue, part.fBeginIndex,
1395 part.fEndIndex).GetTaggedValue());
1396 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1397 JSHandle<JSObject> element;
1398 if (part.isPreExist) {
1399 element = JSLocale::PutElement(thread, part.index, array, ConvertFieldIdToDateType(thread, -1),
1400 JSHandle<JSTaggedValue>::Cast(substring));
1401 } else {
1402 element = JSLocale::PutElement(thread, part.index, array, ConvertFieldIdToDateType(thread, part.fField),
1403 JSHandle<JSTaggedValue>::Cast(substring));
1404 }
1405 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1406 JSHandle<JSTaggedValue> value = JSHandle<JSTaggedValue>::Cast(
1407 ToValueString(thread, TrackValue(part.fBeginIndex, part.fEndIndex, begin, end)));
1408 JSObject::SetProperty(thread, element, thread->GlobalConstants()->GetHandledSourceString(), value, true);
1409 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
1410 }
1411 return array;
1412 }
1413
TrackValue(int32_t beginning, int32_t ending, std::array<int32_t, 2> begin, std::array<int32_t, 2> end)1414 Value JSDateTimeFormat::TrackValue(int32_t beginning, int32_t ending,
1415 std::array<int32_t, 2> begin, std::array<int32_t, 2> end) // 2: number of elements
1416 {
1417 Value value = Value::SHARED;
1418 if ((begin[0] <= beginning) && (beginning <= end[0]) && (begin[0] <= ending) && (ending <= end[0])) {
1419 value = Value::START_RANGE;
1420 } else if ((begin[1] <= beginning) && (beginning <= end[1]) && (begin[1] <= ending) && (ending <= end[1])) {
1421 value = Value::END_RANGE;
1422 }
1423 return value;
1424 }
1425
BuildIcuPatternDescs()1426 std::vector<IcuPatternDesc> BuildIcuPatternDescs()
1427 {
1428 static const std::vector<IcuPatternDesc> items = {
1429 IcuPatternDesc("weekday", ICU_WEEKDAY_PE, ICU_NARROW_LONG_SHORT),
1430 IcuPatternDesc("era", ICU_ERA_PE, ICU_NARROW_LONG_SHORT),
1431 IcuPatternDesc("year", ICU_YEAR_PE, ICU2_DIGIT_NUMERIC),
1432 IcuPatternDesc("month", ICU_MONTH_PE, ICU_NARROW_LONG_SHORT2_DIGIT_NUMERIC),
1433 IcuPatternDesc("day", ICU_DAY_PE, ICU2_DIGIT_NUMERIC),
1434 IcuPatternDesc("dayPeriod", ICU_DAY_PERIOD_PE, ICU_NARROW_LONG_SHORT),
1435 IcuPatternDesc("hour", ICU_HOUR_PE, ICU2_DIGIT_NUMERIC),
1436 IcuPatternDesc("minute", ICU_MINUTE_PE, ICU2_DIGIT_NUMERIC),
1437 IcuPatternDesc("second", ICU_SECOND_PE, ICU2_DIGIT_NUMERIC),
1438 IcuPatternDesc("timeZoneName", ICU_YIME_ZONE_NAME_PE, ICU_LONG_SHORT)
1439 };
1440 return items;
1441 }
1442
InitializePattern(const IcuPatternDesc &hourData)1443 std::vector<IcuPatternDesc> InitializePattern(const IcuPatternDesc &hourData)
1444 {
1445 std::vector<IcuPatternDesc> result;
1446 std::vector<IcuPatternDesc> items = BuildIcuPatternDescs();
1447 std::vector<IcuPatternDesc>::iterator item = items.begin();
1448 while (item != items.end()) {
1449 if (item->property != "hour") {
1450 result.emplace_back(IcuPatternDesc(item->property, item->pairs, item->allowedValues));
1451 } else {
1452 result.emplace_back(hourData);
1453 }
1454 ++item;
1455 }
1456 return result;
1457 }
1458
GetIcuPatternDesc(const HourCycleOption &hourCycle)1459 std::vector<IcuPatternDesc> JSDateTimeFormat::GetIcuPatternDesc(const HourCycleOption &hourCycle)
1460 {
1461 if (hourCycle == HourCycleOption::H11) {
1462 Pattern h11("KK", "K");
1463 return h11.Get();
1464 } else if (hourCycle == HourCycleOption::H12) {
1465 Pattern h12("hh", "h");
1466 return h12.Get();
1467 } else if (hourCycle == HourCycleOption::H23) {
1468 Pattern h23("HH", "H");
1469 return h23.Get();
1470 } else if (hourCycle == HourCycleOption::H24) {
1471 Pattern h24("kk", "k");
1472 return h24.Get();
1473 } else if (hourCycle == HourCycleOption::UNDEFINED) {
1474 Pattern pattern("jj", "j");
1475 return pattern.Get();
1476 }
1477 LOG_ECMA(FATAL) << "this branch is unreachable";
1478 UNREACHABLE();
1479 }
1480
ChangeHourCyclePattern(const icu::UnicodeString &pattern, HourCycleOption hc)1481 icu::UnicodeString JSDateTimeFormat::ChangeHourCyclePattern(const icu::UnicodeString &pattern, HourCycleOption hc)
1482 {
1483 if (hc == HourCycleOption::UNDEFINED || hc == HourCycleOption::EXCEPTION) {
1484 return pattern;
1485 }
1486 icu::UnicodeString result;
1487 char16_t key = u'\0';
1488 auto mapIter = std::find_if(HOUR_CYCLE_MAP.begin(), HOUR_CYCLE_MAP.end(),
1489 [hc](const std::map<char16_t, HourCycleOption>::value_type item) {
1490 return item.second == hc;
1491 });
1492 if (mapIter != HOUR_CYCLE_MAP.end()) {
1493 key = mapIter->first;
1494 }
1495 bool needChange = true;
1496 char16_t last = u'\0';
1497 for (int32_t i = 0; i < pattern.length(); i++) {
1498 char16_t ch = pattern.charAt(i);
1499 if (ch == '\'') {
1500 needChange = !needChange;
1501 result.append(ch);
1502 } else if (HOUR_CYCLE_MAP.find(ch) != HOUR_CYCLE_MAP.end()) {
1503 result = (needChange && last == u'd') ? result.append(' ') : result;
1504 result.append(needChange ? key : ch);
1505 } else {
1506 result.append(ch);
1507 }
1508 last = ch;
1509 }
1510 return result;
1511 }
1512
CreateICUSimpleDateFormat(const icu::Locale &icuLocale, const icu::UnicodeString &skeleton, icu::DateTimePatternGenerator *gn, HourCycleOption hc)1513 std::unique_ptr<icu::SimpleDateFormat> JSDateTimeFormat::CreateICUSimpleDateFormat(const icu::Locale &icuLocale,
1514 const icu::UnicodeString &skeleton,
1515 icu::DateTimePatternGenerator *gn,
1516 HourCycleOption hc)
1517 {
1518 // See https://github.com/tc39/ecma402/issues/225
1519 UErrorCode status = U_ZERO_ERROR;
1520 icu::UnicodeString pattern = ChangeHourCyclePattern(
1521 gn->getBestPattern(skeleton, UDATPG_MATCH_HOUR_FIELD_LENGTH, status), hc);
1522 ASSERT_PRINT((U_SUCCESS(status) != 0), "get best pattern failed");
1523
1524 status = U_ZERO_ERROR;
1525 auto dateFormat(std::make_unique<icu::SimpleDateFormat>(pattern, icuLocale, status));
1526 if (U_FAILURE(status) != 0) {
1527 return std::unique_ptr<icu::SimpleDateFormat>();
1528 }
1529 ASSERT_PRINT(dateFormat != nullptr, "dateFormat failed");
1530 return dateFormat;
1531 }
1532
BuildCalendar(const icu::Locale &locale, const icu::TimeZone &timeZone)1533 std::unique_ptr<icu::Calendar> JSDateTimeFormat::BuildCalendar(const icu::Locale &locale, const icu::TimeZone &timeZone)
1534 {
1535 UErrorCode status = U_ZERO_ERROR;
1536 std::unique_ptr<icu::Calendar> calendar(icu::Calendar::createInstance(timeZone, locale, status));
1537 if (U_FAILURE(status) || calendar == nullptr) {
1538 return nullptr;
1539 }
1540 ASSERT_PRINT(U_SUCCESS(status), "buildCalendar failed");
1541 ASSERT_PRINT(calendar.get() != nullptr, "calendar is nullptr");
1542
1543 /**
1544 * Return the class ID for this class.
1545 *
1546 * This is useful only for comparing to a return value from getDynamicClassID(). For example:
1547 *
1548 * Base* polymorphic_pointer = createPolymorphicObject();
1549 * if (polymorphic_pointer->getDynamicClassID() ==
1550 * Derived::getStaticClassID()) ...
1551 */
1552 if (calendar->getDynamicClassID() == icu::GregorianCalendar::getStaticClassID()) {
1553 auto gregorianCalendar = static_cast<icu::GregorianCalendar *>(calendar.get());
1554 // ECMAScript start time, value = -(2**53)
1555 const double beginTime = -9007199254740992;
1556 gregorianCalendar->setGregorianChange(beginTime, status);
1557 ASSERT(U_SUCCESS(status));
1558 }
1559 return calendar;
1560 }
1561
ConstructTimeZone(const std::string &timezone)1562 std::unique_ptr<icu::TimeZone> JSDateTimeFormat::ConstructTimeZone(const std::string &timezone)
1563 {
1564 if (timezone.empty()) {
1565 return std::unique_ptr<icu::TimeZone>();
1566 }
1567 std::string canonicalized = ConstructFormattedTimeZoneID(timezone);
1568
1569 std::unique_ptr<icu::TimeZone> tz(icu::TimeZone::createTimeZone(canonicalized.c_str()));
1570 if (!JSLocale::IsValidTimeZoneName(*tz)) {
1571 return std::unique_ptr<icu::TimeZone>();
1572 }
1573 return tz;
1574 }
1575
GetSpecialTimeZoneMap()1576 std::map<std::string, std::string> JSDateTimeFormat::GetSpecialTimeZoneMap()
1577 {
1578 std::vector<std::string> specialTimeZones = {
1579 "America/Argentina/ComodRivadavia",
1580 "America/Knox_IN",
1581 "Antarctica/McMurdo",
1582 "Australia/ACT",
1583 "Australia/LHI",
1584 "Australia/NSW",
1585 "Antarctica/DumontDUrville",
1586 "Brazil/DeNoronha",
1587 "CET",
1588 "CST6CDT",
1589 "Chile/EasterIsland",
1590 "EET",
1591 "EST",
1592 "EST5EDT",
1593 "GB",
1594 "GB-Eire",
1595 "HST",
1596 "MET",
1597 "MST",
1598 "MST7MDT",
1599 "Mexico/BajaNorte",
1600 "Mexico/BajaSur",
1601 "NZ",
1602 "NZ-CHAT",
1603 "PRC",
1604 "PST8PDT",
1605 "ROC",
1606 "ROK",
1607 "UCT",
1608 "W-SU",
1609 "WET"};
1610 std::map<std::string, std::string> map;
1611 for (const auto &item : specialTimeZones) {
1612 std::string upper(item);
1613 transform(upper.begin(), upper.end(), upper.begin(), toupper);
1614 map.emplace(upper, item);
1615 }
1616 return map;
1617 }
1618
ConstructFormattedTimeZoneID(const std::string &input)1619 std::string JSDateTimeFormat::ConstructFormattedTimeZoneID(const std::string &input)
1620 {
1621 std::string result = input;
1622 transform(result.begin(), result.end(), result.begin(), toupper);
1623 std::map<std::string, std::string> map = JSDateTimeFormat::GetSpecialTimeZoneMap();
1624 auto it = map.find(result);
1625 if (it != map.end()) {
1626 return it->second;
1627 }
1628 static const std::vector<std::string> tzStyleEntry = {
1629 "GMT", "ETC/UTC", "ETC/UCT", "GMT0", "ETC/GMT", "GMT+0", "GMT-0"
1630 };
1631 if (result.find("SYSTEMV/") == 0) {
1632 result.replace(0, STRING_LENGTH_8, "SystemV/");
1633 } else if (result.find("US/") == 0) {
1634 result = (result.length() == STRING_LENGTH_3) ? result : "US/" + ToTitleCaseTimezonePosition(
1635 input.substr(STRING_LENGTH_3));
1636 } else if (result.find("ETC/GMT") == 0 && result.length() > STRING_LENGTH_7) {
1637 result = ConstructGMTTimeZoneID(input);
1638 } else if (count(tzStyleEntry.begin(), tzStyleEntry.end(), result)) {
1639 result = "UTC";
1640 } else if (result.length() == STRING_LENGTH_3) {
1641 return result;
1642 } else {
1643 return ToTitleCaseTimezonePosition(result);
1644 }
1645
1646 return result;
1647 }
1648
ToTitleCaseFunction(const std::string &input)1649 std::string JSDateTimeFormat::ToTitleCaseFunction(const std::string &input)
1650 {
1651 std::string result(input);
1652 transform(result.begin(), result.end(), result.begin(), tolower);
1653 result[0] = static_cast<int8_t>(toupper(result[0]));
1654 return result;
1655 }
1656
IsValidTimeZoneInput(const std::string &input)1657 bool JSDateTimeFormat::IsValidTimeZoneInput(const std::string &input)
1658 {
1659 std::regex r("[a-zA-Z_\\-/]*");
1660 bool isValid = regex_match(input, r);
1661 return isValid;
1662 }
1663
ToTitleCaseTimezonePosition(const std::string &input)1664 std::string JSDateTimeFormat::ToTitleCaseTimezonePosition(const std::string &input)
1665 {
1666 if (!IsValidTimeZoneInput(input)) {
1667 return std::string();
1668 }
1669 std::vector<std::string> titleEntry;
1670 std::vector<std::string> charEntry;
1671 int32_t leftPosition = 0;
1672 int32_t titleLength = 0;
1673 for (int32_t i = 0; i < static_cast<int>(input.length()); i++) {
1674 if (input[i] == '_' || input[i] == '-' || input[i] == '/') {
1675 std::string s(1, input[i]);
1676 charEntry.emplace_back(s);
1677 titleLength = i - leftPosition;
1678 titleEntry.emplace_back(input.substr(leftPosition, titleLength));
1679 leftPosition = i + 1;
1680 } else {
1681 continue;
1682 }
1683 }
1684 ASSERT(input.length() >= static_cast<size_t>(leftPosition));
1685 titleEntry.emplace_back(input.substr(leftPosition, input.length() - leftPosition));
1686 std::string result;
1687 size_t len = titleEntry.size();
1688 if (len == 0) {
1689 return ToTitleCaseFunction(input);
1690 }
1691 for (size_t i = 0; i < len - 1; i++) {
1692 std::string titleValue = ToTitleCaseFunction(titleEntry[i]);
1693 if (titleValue == "Of" || titleValue == "Es" || titleValue == "Au") {
1694 titleValue[0] = static_cast<int8_t>(tolower(titleValue[0]));
1695 }
1696 result = result + titleValue + charEntry[i];
1697 }
1698 result = result + ToTitleCaseFunction(titleEntry[len - 1]);
1699 return result;
1700 }
1701
ConstructGMTTimeZoneID(const std::string &input)1702 std::string JSDateTimeFormat::ConstructGMTTimeZoneID(const std::string &input)
1703 {
1704 if (input.length() < STRING_LENGTH_8 || input.length() > STRING_LENGTH_10) {
1705 return "";
1706 }
1707 std::string ret = "Etc/GMT";
1708 int timeZoneOffsetFlag = 7; // The offset of time zone flag, to match RegExp starting with the correct string
1709 if (regex_match(input.substr(timeZoneOffsetFlag), std::regex("[+-][1][0-4]")) ||
1710 (regex_match(input.substr(timeZoneOffsetFlag), std::regex("[+-][0-9]")) ||
1711 input.substr(timeZoneOffsetFlag) == "0")) {
1712 return ret + input.substr(timeZoneOffsetFlag);
1713 }
1714 return "";
1715 }
1716
ToHourCycleString(HourCycleOption hc)1717 std::string JSDateTimeFormat::ToHourCycleString(HourCycleOption hc)
1718 {
1719 auto mapIter = std::find_if(TO_HOUR_CYCLE_MAP.begin(), TO_HOUR_CYCLE_MAP.end(),
1720 [hc](const std::map<std::string, HourCycleOption>::value_type item) {
1721 return item.second == hc;
1722 });
1723 if (mapIter != TO_HOUR_CYCLE_MAP.end()) {
1724 return mapIter->first;
1725 }
1726 return "";
1727 }
1728
OptionToHourCycle(const std::string &hc)1729 HourCycleOption JSDateTimeFormat::OptionToHourCycle(const std::string &hc)
1730 {
1731 auto iter = TO_HOUR_CYCLE_MAP.find(hc);
1732 if (iter != TO_HOUR_CYCLE_MAP.end()) {
1733 return iter->second;
1734 }
1735 return HourCycleOption::UNDEFINED;
1736 }
1737
OptionToHourCycle(UDateFormatHourCycle hc)1738 HourCycleOption JSDateTimeFormat::OptionToHourCycle(UDateFormatHourCycle hc)
1739 {
1740 HourCycleOption hcOption = HourCycleOption::UNDEFINED;
1741 switch (hc) {
1742 case UDAT_HOUR_CYCLE_11:
1743 hcOption = HourCycleOption::H11;
1744 break;
1745 case UDAT_HOUR_CYCLE_12:
1746 hcOption = HourCycleOption::H12;
1747 break;
1748 case UDAT_HOUR_CYCLE_23:
1749 hcOption = HourCycleOption::H23;
1750 break;
1751 case UDAT_HOUR_CYCLE_24:
1752 hcOption = HourCycleOption::H24;
1753 break;
1754 default:
1755 LOG_ECMA(FATAL) << "this branch is unreachable";
1756 UNREACHABLE();
1757 }
1758 return hcOption;
1759 }
1760
ConvertFieldIdToDateType(JSThread *thread, int32_t fieldId)1761 JSHandle<JSTaggedValue> JSDateTimeFormat::ConvertFieldIdToDateType(JSThread *thread, int32_t fieldId)
1762 {
1763 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
1764 auto globalConst = thread->GlobalConstants();
1765 if (fieldId == -1) {
1766 result.Update(globalConst->GetHandledLiteralString().GetTaggedValue());
1767 } else if (fieldId == UDAT_YEAR_FIELD || fieldId == UDAT_EXTENDED_YEAR_FIELD) {
1768 result.Update(globalConst->GetHandledYearString().GetTaggedValue());
1769 } else if (fieldId == UDAT_YEAR_NAME_FIELD) {
1770 result.Update(globalConst->GetHandledYearNameString().GetTaggedValue());
1771 } else if (fieldId == UDAT_MONTH_FIELD || fieldId == UDAT_STANDALONE_MONTH_FIELD) {
1772 result.Update(globalConst->GetHandledMonthString().GetTaggedValue());
1773 } else if (fieldId == UDAT_DATE_FIELD) {
1774 result.Update(globalConst->GetHandledDayString().GetTaggedValue());
1775 } else if (fieldId == UDAT_HOUR_OF_DAY1_FIELD ||
1776 fieldId == UDAT_HOUR_OF_DAY0_FIELD || fieldId == UDAT_HOUR1_FIELD || fieldId == UDAT_HOUR0_FIELD) {
1777 result.Update(globalConst->GetHandledHourString().GetTaggedValue());
1778 } else if (fieldId == UDAT_MINUTE_FIELD) {
1779 result.Update(globalConst->GetHandledMinuteString().GetTaggedValue());
1780 } else if (fieldId == UDAT_SECOND_FIELD) {
1781 result.Update(globalConst->GetHandledSecondString().GetTaggedValue());
1782 } else if (fieldId == UDAT_DAY_OF_WEEK_FIELD || fieldId == UDAT_DOW_LOCAL_FIELD ||
1783 fieldId == UDAT_STANDALONE_DAY_FIELD) {
1784 result.Update(globalConst->GetHandledWeekdayString().GetTaggedValue());
1785 } else if (fieldId == UDAT_AM_PM_FIELD || fieldId == UDAT_AM_PM_MIDNIGHT_NOON_FIELD ||
1786 fieldId == UDAT_FLEXIBLE_DAY_PERIOD_FIELD) {
1787 result.Update(globalConst->GetHandledDayPeriodString().GetTaggedValue());
1788 } else if (fieldId == UDAT_TIMEZONE_FIELD || fieldId == UDAT_TIMEZONE_RFC_FIELD ||
1789 fieldId == UDAT_TIMEZONE_GENERIC_FIELD || fieldId == UDAT_TIMEZONE_SPECIAL_FIELD ||
1790 fieldId == UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD || fieldId == UDAT_TIMEZONE_ISO_FIELD ||
1791 fieldId == UDAT_TIMEZONE_ISO_LOCAL_FIELD) {
1792 result.Update(globalConst->GetHandledTimeZoneNameString().GetTaggedValue());
1793 } else if (fieldId == UDAT_ERA_FIELD) {
1794 result.Update(globalConst->GetHandledEraString().GetTaggedValue());
1795 } else if (fieldId == UDAT_FRACTIONAL_SECOND_FIELD) {
1796 result.Update(globalConst->GetHandledFractionalSecondString().GetTaggedValue());
1797 } else if (fieldId == UDAT_RELATED_YEAR_FIELD) {
1798 result.Update(globalConst->GetHandledRelatedYearString().GetTaggedValue());
1799 } else if (fieldId == UDAT_QUARTER_FIELD || fieldId == UDAT_STANDALONE_QUARTER_FIELD) {
1800 LOG_ECMA(FATAL) << "this branch is unreachable";
1801 UNREACHABLE();
1802 }
1803 return result;
1804 }
1805
ConstructDateIntervalFormat( const JSHandle<JSDateTimeFormat> &dtf)1806 std::unique_ptr<icu::DateIntervalFormat> JSDateTimeFormat::ConstructDateIntervalFormat(
1807 const JSHandle<JSDateTimeFormat> &dtf)
1808 {
1809 icu::SimpleDateFormat *icuSimpleDateFormat = dtf->GetIcuSimpleDateFormat();
1810 icu::Locale locale = *(dtf->GetIcuLocale());
1811 std::string hcString = ToHourCycleString(dtf->GetHourCycle());
1812 UErrorCode status = U_ZERO_ERROR;
1813 // Sets the Unicode value for a Unicode keyword.
1814 if (!hcString.empty()) {
1815 locale.setUnicodeKeywordValue("hc", hcString, status);
1816 }
1817 icu::UnicodeString pattern;
1818 // Return a pattern string describing this date format.
1819 pattern = icuSimpleDateFormat->toPattern(pattern);
1820 // Utility to return a unique skeleton from a given pattern.
1821 icu::UnicodeString skeleton = icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
1822 // Construct a DateIntervalFormat from skeleton and a given locale.
1823 std::unique_ptr<icu::DateIntervalFormat> dateIntervalFormat(
1824 icu::DateIntervalFormat::createInstance(skeleton, locale, status));
1825 if (U_FAILURE(status)) {
1826 return nullptr;
1827 }
1828 dateIntervalFormat->setTimeZone(icuSimpleDateFormat->getTimeZone());
1829 return dateIntervalFormat;
1830 }
1831 } // namespace panda::ecmascript
1832