• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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