1 /*
2 * Copyright (c) 2021 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/builtins/builtins_date.h"
17
18 #include "ecmascript/ecma_macros.h"
19 #include "ecmascript/ecma_vm.h"
20 #include "ecmascript/global_env.h"
21 #include "ecmascript/interpreter/interpreter.h"
22 #include "ecmascript/js_function.h"
23 #include "ecmascript/js_object-inl.h"
24 #include "ecmascript/js_tagged_value-inl.h"
25 #include "ecmascript/js_thread.h"
26 #include "ecmascript/tagged_array.h"
27 #ifdef ARK_SUPPORT_INTL
28 #include "ecmascript/js_date.h"
29 #include "ecmascript/js_date_time_format.h"
30 #else
31 #ifndef ARK_NOT_SUPPORT_INTL_GLOBAL
32 #include "ecmascript/intl/global_intl_helper.h"
33 #endif
34 #endif
35
36 namespace panda::ecmascript::builtins {
37 // constructor
DateConstructor(EcmaRuntimeCallInfo * argv)38 JSTaggedValue BuiltinsDate::DateConstructor(EcmaRuntimeCallInfo *argv)
39 {
40 BUILTINS_ENTRY_DEBUG_LOG();
41 BUILTINS_API_TRACE(argv->GetThread(), Date, Constructor);
42 JSThread *thread = argv->GetThread();
43 [[maybe_unused]] EcmaHandleScope handleScope(thread);
44 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
45 if (newTarget->IsUndefined()) {
46 double now = JSDate::Now().GetDouble();
47 CString str = JSDate::ToDateString(now);
48 return GetTaggedString(thread, str.c_str());
49 }
50
51 JSTaggedValue timeValue(0.0);
52 uint32_t length = argv->GetArgsNumber();
53 if (length == 0) { // no value
54 timeValue = JSDate::Now();
55 } else if (length == 1) { // one value
56 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
57 if (value->IsDate()) { // The value is a date object.
58 JSHandle<JSDate> jsDate(thread, JSDate::Cast(value->GetTaggedObject()));
59 timeValue = jsDate->GetTimeValue();
60 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
61 } else {
62 JSHandle<JSTaggedValue> objValue(thread, JSTaggedValue::ToPrimitive(thread, value));
63 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
64 if (objValue->IsString()) { // The value is a string object.
65 timeValue = JSDate::Parse(argv);
66 } else { // The value is a number.
67 JSTaggedNumber val = JSTaggedValue::ToNumber(thread, objValue);
68 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
69 timeValue = JSTaggedValue(val.GetNumber());
70 }
71 timeValue = JSTaggedValue(JSDate::TimeClip(timeValue.GetDouble()));
72 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
73 }
74 } else { // two or more values
75 std::array<int64_t, DATE_LENGTH> fields = {0, 0, 1, 0, 0, 0, 0, 0, 0};
76 if (length > CONSTRUCTOR_MAX_LENGTH) { // The max length is 7.
77 length = CONSTRUCTOR_MAX_LENGTH;
78 }
79 uint32_t i = 0;
80 for (; i < length; ++i) {
81 JSHandle<JSTaggedValue> value = GetCallArg(argv, i);
82 JSTaggedNumber res = JSTaggedValue::ToNumber(thread, value);
83 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
84 double temp = res.GetNumber();
85 if (std::isnan(temp) || !std::isfinite(temp)) { // Check the double value is finite.
86 break;
87 }
88 fields[i] = static_cast<int64_t>(temp);
89 if (i == 0 && fields[0] >= 0 && fields[0] < JSDate::HUNDRED) {
90 fields[0] += JSDate::NINETEEN_HUNDRED_YEAR;
91 }
92 }
93 timeValue = JSTaggedValue((i == length) ? JSDate::SetDateValues(&fields, true) : base::NAN_VALUE);
94 }
95
96 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
97 JSHandle<JSFunction> constructor(GetConstructor(argv));
98 JSHandle<JSDate> dateObject =
99 JSHandle<JSDate>::Cast(factory->NewJSObjectByConstructor(constructor, newTarget));
100 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
101 dateObject->SetTimeValue(thread, timeValue);
102 return dateObject.GetTaggedValue();
103 }
104
105 // 20.4.3.1
Now(EcmaRuntimeCallInfo * argv)106 JSTaggedValue BuiltinsDate::Now([[maybe_unused]] EcmaRuntimeCallInfo *argv)
107 {
108 BUILTINS_API_TRACE(argv->GetThread(), Date, Now);
109 return JSDate::Now();
110 }
111
112 // 20.4.3.2
Parse(EcmaRuntimeCallInfo * argv)113 JSTaggedValue BuiltinsDate::Parse(EcmaRuntimeCallInfo *argv)
114 {
115 BUILTINS_API_TRACE(argv->GetThread(), Date, Parse);
116 [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
117 return JSDate::Parse(argv);
118 }
119
120 // 20.4.3.4
UTC(EcmaRuntimeCallInfo * argv)121 JSTaggedValue BuiltinsDate::UTC(EcmaRuntimeCallInfo *argv)
122 {
123 BUILTINS_API_TRACE(argv->GetThread(), Date, UTC);
124 [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
125 return JSDate::UTC(argv);
126 }
127
128 // 20.4.4.10
GetTime(EcmaRuntimeCallInfo * argv)129 JSTaggedValue BuiltinsDate::GetTime(EcmaRuntimeCallInfo *argv)
130 {
131 ASSERT(argv);
132 BUILTINS_API_TRACE(argv->GetThread(), Date, GetTime);
133 JSThread *thread = argv->GetThread();
134 JSHandle<JSTaggedValue> msg = GetThis(argv);
135 if (!msg->IsDate()) {
136 [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
137 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception());
138 }
139 return JSDate::Cast(msg->GetTaggedObject())->GetTime();
140 }
141
SetTime(EcmaRuntimeCallInfo * argv)142 JSTaggedValue BuiltinsDate::SetTime(EcmaRuntimeCallInfo *argv)
143 {
144 ASSERT(argv);
145 BUILTINS_API_TRACE(argv->GetThread(), Date, SetTime);
146 JSThread *thread = argv->GetThread();
147 [[maybe_unused]] EcmaHandleScope handleScope(thread);
148
149 JSHandle<JSTaggedValue> msg = GetThis(argv);
150 if (!msg->IsDate()) {
151 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception());
152 }
153 JSHandle<JSDate> jsDate(thread, JSDate::Cast(msg->GetTaggedObject()));
154 JSTaggedNumber res = JSTaggedValue::ToNumber(thread, GetCallArg(argv, 0));
155 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread());
156 double number = res.GetNumber();
157 double value = JSDate::TimeClip(number);
158 jsDate->SetTimeValue(thread, JSTaggedValue(value));
159 return GetTaggedDouble(value);
160 }
161
162 // 20.4.4.37
ToJSON(EcmaRuntimeCallInfo * argv)163 JSTaggedValue BuiltinsDate::ToJSON(EcmaRuntimeCallInfo *argv)
164 {
165 ASSERT(argv);
166 BUILTINS_API_TRACE(argv->GetThread(), Date, ToJSON);
167 JSThread *thread = argv->GetThread();
168 [[maybe_unused]] EcmaHandleScope handleScope(thread);
169
170 // 1. Let O be ToObject(this value).
171 JSHandle<JSTaggedValue> msg = GetThis(argv);
172 JSHandle<JSObject> object = JSTaggedValue::ToObject(thread, msg);
173 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
174
175 // 2. Let tv be ToPrimitive(hint Number)
176 JSHandle<JSTaggedValue> objectHandle = JSHandle<JSTaggedValue>::Cast(object);
177 JSHandle<JSTaggedValue> tv(thread,
178 JSTaggedValue::ToPrimitive(thread, objectHandle, PreferredPrimitiveType::PREFER_NUMBER));
179
180 // 3. If Type(tv) is Number and tv is not finite, return null
181 if (tv->IsNumber()) {
182 if (tv->IsDouble() && !std::isfinite(tv->GetDouble())) {
183 return JSTaggedValue::Null();
184 }
185 }
186 JSHandle<JSTaggedValue> calleeKey(thread->GetEcmaVM()->GetFactory()->NewFromASCII("toISOString"));
187 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
188 EcmaRuntimeCallInfo *info =
189 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, objectHandle, undefined, 0);
190 return JSFunction::Invoke(info, calleeKey);
191 }
192
193 // 20.4.4.44
ValueOf(EcmaRuntimeCallInfo * argv)194 JSTaggedValue BuiltinsDate::ValueOf(EcmaRuntimeCallInfo *argv)
195 {
196 ASSERT(argv);
197 BUILTINS_API_TRACE(argv->GetThread(), Date, ValueOf);
198 JSThread *thread = argv->GetThread();
199 JSHandle<JSTaggedValue> msg = GetThis(argv);
200 if (!msg->IsDate()) {
201 [[maybe_unused]] EcmaHandleScope handleScope(thread);
202 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception());
203 }
204 return JSDate::Cast(msg->GetTaggedObject())->ValueOf();
205 }
206
207 // 20.4.4.45
ToPrimitive(EcmaRuntimeCallInfo * argv)208 JSTaggedValue BuiltinsDate::ToPrimitive(EcmaRuntimeCallInfo *argv)
209 {
210 ASSERT(argv);
211 BUILTINS_API_TRACE(argv->GetThread(), Date, ToPrimitive);
212 JSThread *thread = argv->GetThread();
213 auto vm = thread->GetEcmaVM();
214 [[maybe_unused]] EcmaHandleScope handleScope(thread);
215
216 JSHandle<JSTaggedValue> object = GetThis(argv);
217 if (!object->IsECMAObject()) {
218 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a JSObject", JSTaggedValue::Exception());
219 }
220 JSHandle<JSTaggedValue> hint = GetCallArg(argv, 0);
221 PreferredPrimitiveType tryFirst = PREFER_STRING;
222 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
223 if (hint->IsString()) {
224 JSHandle<EcmaString> numberStrHandle = JSHandle<EcmaString>::Cast(globalConst->GetHandledNumberString());
225 if (EcmaStringAccessor::StringsAreEqual(vm, JSHandle<EcmaString>(hint), numberStrHandle)) {
226 tryFirst = PREFER_NUMBER;
227 } else {
228 JSHandle<EcmaString> stringStrHandle = JSHandle<EcmaString>::Cast(globalConst->GetHandledStringString());
229 JSHandle<EcmaString> defaultStrHandle = JSHandle<EcmaString>::Cast(globalConst->GetHandledDefaultString());
230 if (EcmaStringAccessor::StringsAreEqual(vm, JSHandle<EcmaString>(hint), stringStrHandle) ||
231 EcmaStringAccessor::StringsAreEqual(vm, JSHandle<EcmaString>(hint), defaultStrHandle)) {
232 tryFirst = PREFER_STRING;
233 } else {
234 THROW_TYPE_ERROR_AND_RETURN(thread, "This is not a primitiveType.", JSTaggedValue::Exception());
235 }
236 }
237 } else {
238 THROW_TYPE_ERROR_AND_RETURN(thread, "This is not an primitiveType.", JSTaggedValue::Exception());
239 }
240 return JSTaggedValue::OrdinaryToPrimitive(thread, object, tryFirst);
241 }
242
243 // ecma 402 16.4.1 Date.prototype.toLocaleString ( [ locales [ , options ] ] )
ToLocaleString(EcmaRuntimeCallInfo * argv)244 JSTaggedValue BuiltinsDate::ToLocaleString(EcmaRuntimeCallInfo *argv)
245 {
246 ASSERT(argv);
247 JSThread *thread = argv->GetThread();
248 BUILTINS_API_TRACE(thread, Date, ToLocaleString);
249 EcmaVM *ecmaVm = thread->GetEcmaVM();
250 [[maybe_unused]] ObjectFactory *factory = ecmaVm->GetFactory();
251 [[maybe_unused]] EcmaHandleScope handleScope(thread);
252
253 // Let x be ? thisTimeValue(this value).
254 JSHandle<JSTaggedValue> msg = GetThis(argv);
255 if (!msg->IsDate()) {
256 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception());
257 }
258 JSTaggedValue value = JSDate::Cast(msg->GetTaggedObject())->GetTime();
259 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
260
261 // If x is NaN, return "Invalid Date".
262 double x = value.GetNumber();
263 if (std::isnan(x)) {
264 return thread->GlobalConstants()->GetInvalidDateString();
265 }
266
267 // Let options be ? ToDateTimeOptions(options, "any", "all").
268 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
269 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1);
270 [[maybe_unused]] bool cacheable = (locales->IsUndefined() || locales->IsString()) && options->IsUndefined();
271 #ifdef ARK_SUPPORT_INTL
272 if (cacheable) {
273 auto simpleDateFormat = JSDateTimeFormat::GetCachedIcuSimpleDateFormat(thread, locales,
274 IcuFormatterType::SIMPLE_DATE_FORMAT_DEFAULT);
275 if (simpleDateFormat != nullptr) {
276 JSHandle<EcmaString> result = JSDateTimeFormat::FormatDateTime(thread, simpleDateFormat, x);
277 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
278 return result.GetTaggedValue();
279 }
280 }
281 JSHandle<JSObject> dateTimeOptions =
282 JSDateTimeFormat::ToDateTimeOptions(thread, options, RequiredOption::ANY, DefaultsOption::ALL);
283 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
284 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
285 // Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »).
286 JSHandle<JSFunction> ctor(env->GetDateTimeFormatFunction());
287 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(ctor);
288 IcuCacheType type = cacheable ? IcuCacheType::DEFAULT : IcuCacheType::NOT_CACHE;
289 JSHandle<JSDateTimeFormat> dtf = JSDateTimeFormat::InitializeDateTimeFormat(thread,
290 JSHandle<JSDateTimeFormat>::Cast(obj), locales, JSHandle<JSTaggedValue>::Cast(dateTimeOptions), type);
291 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
292 if (cacheable) {
293 auto simpleDateFormat = JSDateTimeFormat::GetCachedIcuSimpleDateFormat(thread, locales,
294 IcuFormatterType::SIMPLE_DATE_FORMAT_DEFAULT);
295 ASSERT(simpleDateFormat != nullptr);
296 JSHandle<EcmaString> result = JSDateTimeFormat::FormatDateTime(thread, simpleDateFormat, x);
297 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
298 return result.GetTaggedValue();
299 }
300
301 // Return ? FormatDateTime(dateFormat, x).
302 JSHandle<EcmaString> result = JSDateTimeFormat::FormatDateTime(thread, dtf, x);
303 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
304 return result.GetTaggedValue();
305 #else
306 #ifdef ARK_NOT_SUPPORT_INTL_GLOBAL
307 ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "ToLocaleString");
308 #else
309 intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::DateFormatter);
310 auto dateFormatter = gh.GetGlobalObject<intl::GlobalDateFormatter>(thread,
311 locales, options, intl::GlobalFormatterType::DateFormatter, cacheable);
312 if (dateFormatter == nullptr) {
313 LOG_ECMA(ERROR) << "BuiltinsDate::ToLocaleString:dateFormatter is nullptr";
314 }
315 ASSERT(dateFormatter != nullptr);
316 std::string result = dateFormatter->Format(intl::GlobalIntlHelper::DoubleToInt64(x));
317 JSHandle returnValue = factory->NewFromStdString(result);
318 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
319 return returnValue.GetTaggedValue();
320 #endif
321 #endif
322 }
323
324 // ecma 402 16.4.1 Date.prototype.toLocaleString ( [ locales [ , options ] ] )
ToLocaleDateString(EcmaRuntimeCallInfo * argv)325 JSTaggedValue BuiltinsDate::ToLocaleDateString(EcmaRuntimeCallInfo *argv)
326 {
327 ASSERT(argv);
328 JSThread *thread = argv->GetThread();
329 BUILTINS_API_TRACE(thread, Date, ToLocaleDateString);
330 EcmaVM *ecmaVm = thread->GetEcmaVM();
331 [[maybe_unused]] ObjectFactory *factory = ecmaVm->GetFactory();
332 [[maybe_unused]] EcmaHandleScope handleScope(thread);
333
334 // Let x be ? thisTimeValue(this value).
335 JSHandle<JSTaggedValue> msg = GetThis(argv);
336 if (!msg->IsDate()) {
337 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception());
338 }
339 JSTaggedValue value = JSDate::Cast(msg->GetTaggedObject())->GetTime();
340 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
341
342 // If x is NaN, return "Invalid Date".
343 double x = value.GetNumber();
344 if (std::isnan(x)) {
345 return thread->GlobalConstants()->GetInvalidDateString();
346 }
347
348 // Let options be ? ToDateTimeOptions(options, "any", "all").
349 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
350 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1);
351 [[maybe_unused]] bool cacheable = (locales->IsUndefined() || locales->IsString()) && options->IsUndefined();
352 #ifdef ARK_SUPPORT_INTL
353 if (cacheable) {
354 auto simpleDateFormat = JSDateTimeFormat::GetCachedIcuSimpleDateFormat(thread, locales,
355 IcuFormatterType::SIMPLE_DATE_FORMAT_DATE);
356 if (simpleDateFormat != nullptr) {
357 JSHandle<EcmaString> result = JSDateTimeFormat::FormatDateTime(thread, simpleDateFormat, x);
358 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
359 return result.GetTaggedValue();
360 }
361 }
362 JSHandle<JSObject> dateTimeOptions =
363 JSDateTimeFormat::ToDateTimeOptions(thread, options, RequiredOption::DATE, DefaultsOption::DATE);
364 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
365 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
366 // Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »).
367 JSHandle<JSFunction> ctor(env->GetDateTimeFormatFunction());
368 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(ctor);
369 IcuCacheType type = cacheable ? IcuCacheType::DATE : IcuCacheType::NOT_CACHE;
370 JSHandle<JSDateTimeFormat> dtf = JSDateTimeFormat::InitializeDateTimeFormat(thread,
371 JSHandle<JSDateTimeFormat>::Cast(obj), locales, JSHandle<JSTaggedValue>::Cast(dateTimeOptions), type);
372 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
373 if (cacheable) {
374 auto simpleDateFormat = JSDateTimeFormat::GetCachedIcuSimpleDateFormat(thread, locales,
375 IcuFormatterType::SIMPLE_DATE_FORMAT_DATE);
376 ASSERT(simpleDateFormat != nullptr);
377 JSHandle<EcmaString> result = JSDateTimeFormat::FormatDateTime(thread, simpleDateFormat, x);
378 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
379 return result.GetTaggedValue();
380 }
381
382 // Return ? FormatDateTime(dateFormat, x).
383 JSHandle<EcmaString> result = JSDateTimeFormat::FormatDateTime(thread, dtf, x);
384 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
385 return result.GetTaggedValue();
386 #else
387 #ifdef ARK_NOT_SUPPORT_INTL_GLOBAL
388 ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "LocaleCompare");
389 #else
390 intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::SimpleDateFormatDate);
391 auto dateFormatter = gh.GetGlobalObject<intl::GlobalDateFormatter>(thread,
392 locales, options, intl::GlobalFormatterType::SimpleDateFormatDate, cacheable);
393 if (dateFormatter == nullptr) {
394 LOG_ECMA(ERROR) << "BuiltinsDate::ToLocaleDateString:dateFormatter is nullptr";
395 }
396 ASSERT(dateFormatter != nullptr);
397 std::string result = dateFormatter->Format(intl::GlobalIntlHelper::DoubleToInt64(x));
398 JSHandle returnValue = factory->NewFromStdString(result);
399 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
400 return returnValue.GetTaggedValue();
401 #endif
402 #endif
403 }
404
405 // ecma 402 16.4.1 Date.prototype.toLocaleString ( [ locales [ , options ] ] )
ToLocaleTimeString(EcmaRuntimeCallInfo * argv)406 JSTaggedValue BuiltinsDate::ToLocaleTimeString(EcmaRuntimeCallInfo *argv)
407 {
408 ASSERT(argv);
409 JSThread *thread = argv->GetThread();
410 BUILTINS_API_TRACE(thread, Date, ToLocaleTimeString);
411 EcmaVM *ecmaVm = thread->GetEcmaVM();
412 [[maybe_unused]] ObjectFactory *factory = ecmaVm->GetFactory();
413 [[maybe_unused]] EcmaHandleScope handleScope(thread);
414
415 // Let x be ? thisTimeValue(this value).
416 JSHandle<JSTaggedValue> msg = GetThis(argv);
417 if (!msg->IsDate()) {
418 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception());
419 }
420 JSTaggedValue value = JSDate::Cast(msg->GetTaggedObject())->GetTime();
421 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
422
423 // If x is NaN, return "Invalid Date".
424 double x = value.GetNumber();
425 if (std::isnan(x)) {
426 return thread->GlobalConstants()->GetInvalidDateString();
427 }
428
429 // Let options be ? ToDateTimeOptions(options, "any", "all").
430 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
431 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1);
432 [[maybe_unused]] bool cacheable = (locales->IsUndefined() || locales->IsString()) && options->IsUndefined();
433 #ifdef ARK_SUPPORT_INTL
434 if (cacheable) {
435 auto simpleDateFormat = JSDateTimeFormat::GetCachedIcuSimpleDateFormat(thread, locales,
436 IcuFormatterType::SIMPLE_DATE_FORMAT_TIME);
437 if (simpleDateFormat != nullptr) {
438 JSHandle<EcmaString> result = JSDateTimeFormat::FormatDateTime(thread, simpleDateFormat, x);
439 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
440 return result.GetTaggedValue();
441 }
442 }
443 JSHandle<JSObject> dateTimeOptions =
444 JSDateTimeFormat::ToDateTimeOptions(thread, options, RequiredOption::TIME, DefaultsOption::TIME);
445 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
446 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
447 // Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »).
448 JSHandle<JSFunction> ctor(env->GetDateTimeFormatFunction());
449 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(ctor);
450 IcuCacheType type = cacheable ? IcuCacheType::TIME : IcuCacheType::NOT_CACHE;
451 JSHandle<JSDateTimeFormat> dtf = JSDateTimeFormat::InitializeDateTimeFormat(thread,
452 JSHandle<JSDateTimeFormat>::Cast(obj), locales, JSHandle<JSTaggedValue>::Cast(dateTimeOptions), type);
453 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
454 if (cacheable) {
455 auto simpleDateFormat = JSDateTimeFormat::GetCachedIcuSimpleDateFormat(thread, locales,
456 IcuFormatterType::SIMPLE_DATE_FORMAT_TIME);
457 ASSERT(simpleDateFormat != nullptr);
458 JSHandle<EcmaString> result = JSDateTimeFormat::FormatDateTime(thread, simpleDateFormat, x);
459 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
460 return result.GetTaggedValue();
461 }
462
463 // Return ? FormatDateTime(dateFormat, x).
464 JSHandle<EcmaString> result = JSDateTimeFormat::FormatDateTime(thread, dtf, x);
465 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
466 return result.GetTaggedValue();
467 #else
468 #ifdef ARK_NOT_SUPPORT_INTL_GLOBAL
469 ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "LocaleCompare");
470 #else
471 intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::SimpleDateFormatTime);
472 auto dateFormatter = gh.GetGlobalObject<intl::GlobalDateFormatter>(thread,
473 locales, options, intl::GlobalFormatterType::SimpleDateFormatTime, cacheable);
474 if (dateFormatter == nullptr) {
475 LOG_ECMA(ERROR) << "BuiltinsDate::ToLocaleTimeString:dateFormatter is nullptr";
476 }
477 ASSERT(dateFormatter != nullptr);
478 std::string result = dateFormatter->Format(intl::GlobalIntlHelper::DoubleToInt64(x));
479 JSHandle returnValue = factory->NewFromStdString(result);
480 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
481 return returnValue.GetTaggedValue();
482 #endif
483 #endif
484 }
485 } // namespace panda::ecmascript::builtins
486