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 "builtins_date_time_format.h" 17 18#include "ecmascript/intl/locale_helper.h" 19#include "ecmascript/global_env.h" 20#include "ecmascript/js_date.h" 21#include "ecmascript/js_date_time_format.h" 22#include "ecmascript/js_function.h" 23#include "ecmascript/js_intl.h" 24 25namespace panda::ecmascript::builtins { 26// 13.2.1 Intl.DateTimeFormat ( [ locales [ , options ] ] ) 27JSTaggedValue BuiltinsDateTimeFormat::DateTimeFormatConstructor(EcmaRuntimeCallInfo *argv) 28{ 29 JSThread *thread = argv->GetThread(); 30 BUILTINS_API_TRACE(thread, DateTimeFormat, Constructor); 31 [[maybe_unused]] EcmaHandleScope scope(thread); 32 EcmaVM *ecmaVm = thread->GetEcmaVM(); 33 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv(); 34 ObjectFactory *factory = ecmaVm->GetFactory(); 35 36 // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. 37 JSHandle<JSTaggedValue> constructor = GetConstructor(argv); 38 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 39 if (newTarget->IsUndefined()) { 40 newTarget = constructor; 41 } 42 43 // 2. Let dateTimeFormat be ? OrdinaryCreateFromConstructor(newTarget, "%DateTimeFormatPrototype%", « 44 // [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[Weekday]], 45 // [[Era]], [[Year]], [[Month]], [[Day]], [[Hour]], [[Minute]], [[Second]], [[TimeZoneName]], [[HourCycle]], 46 // [[Pattern]], [[BoundFormat]] »). 47 JSHandle<JSObject> newObject = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget); 48 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 49 JSHandle<JSDateTimeFormat> dateTimeFormat = JSHandle<JSDateTimeFormat>::Cast(newObject); 50 51 // 3. Perform ? InitializeDateTimeFormat(dateTimeFormat, locales, options). 52 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0); 53 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1); 54 JSHandle<JSDateTimeFormat> dtf = 55 JSDateTimeFormat::InitializeDateTimeFormat(thread, dateTimeFormat, locales, options); 56 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 57 58 // 4. Let this be the this value. 59 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 60 61 // 5. If NewTarget is undefined and Type(this) is Object and ? InstanceofOperator(this, %DateTimeFormat%) is true, 62 // then 63 // a. Perform ? DefinePropertyOrThrow(this, %Intl%.[[FallbackSymbol]], PropertyDescriptor{ [[Value]]: 64 // dateTimeFormat, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). 65 // b. Return this. 66 if (GetNewTarget(argv)->IsUndefined() && thisValue->IsJSObject()) { 67 bool isInstanceOf = JSFunction::OrdinaryHasInstance(thread, env->GetDateTimeFormatFunction(), thisValue); 68 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 69 if (isInstanceOf) { 70 PropertyDescriptor descriptor(thread, JSHandle<JSTaggedValue>::Cast(dtf), false, false, false); 71 JSHandle<JSTaggedValue> key(thread, JSHandle<JSIntl>::Cast(env->GetIntlFunction())->GetFallbackSymbol()); 72 JSTaggedValue::DefinePropertyOrThrow(thread, thisValue, key, descriptor); 73 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 74 return thisValue.GetTaggedValue(); 75 } 76 } 77 78 // 6. Return dateTimeFormat. 79 return dtf.GetTaggedValue(); 80} 81 82// 13.3.2 Intl.DateTimeFormat.supportedLocalesOf ( locales [ , options ] ) 83JSTaggedValue BuiltinsDateTimeFormat::SupportedLocalesOf(EcmaRuntimeCallInfo *argv) 84{ 85 JSThread *thread = argv->GetThread(); 86 BUILTINS_API_TRACE(thread, DateTimeFormat, SupportedLocalesOf); 87 [[maybe_unused]] EcmaHandleScope scope(thread); 88 // 1. Let availableLocales be %DateTimeFormat%.[[AvailableLocales]]. 89 JSHandle<TaggedArray> availableLocales = JSDateTimeFormat::GainAvailableLocales(thread); 90 91 // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). 92 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0); 93 JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales); 94 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 95 96 // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). 97 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1); 98 JSHandle<JSArray> result = JSLocale::SupportedLocales(thread, availableLocales, requestedLocales, options); 99 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 100 return result.GetTaggedValue(); 101} 102 103// 13.4.3 get Intl.DateTimeFormat.prototype.format 104JSTaggedValue BuiltinsDateTimeFormat::Format(EcmaRuntimeCallInfo *argv) 105{ 106 JSThread *thread = argv->GetThread(); 107 BUILTINS_API_TRACE(thread, DateTimeFormat, Format); 108 [[maybe_unused]] EcmaHandleScope scope(thread); 109 110 // 1. Let dtf be this value. 111 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 112 113 // 2. If Type(dtf) is not Object, throw a TypeError exception. 114 if (!thisValue->IsJSObject()) { 115 THROW_TYPE_ERROR_AND_RETURN(thread, "dtf is not object", JSTaggedValue::Exception()); 116 } 117 118 // 3. Let dtf be ? UnwrapDateTimeFormat(dtf). 119 JSHandle<JSTaggedValue> dtfValue = JSDateTimeFormat::UnwrapDateTimeFormat(thread, thisValue); 120 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 121 if (dtfValue->IsUndefined()) { 122 THROW_TYPE_ERROR_AND_RETURN(thread, "dtfValue is not object", JSTaggedValue::Exception()); 123 } 124 125 // 4. If dtf.[[BoundFormat]] is undefined, then 126 // a. Let F be a new built-in function object as defined in DateTime Format Functions (13.1.5). 127 // b. Set F.[[DateTimeFormat]] to dtf. 128 // c. Set dtf.[[BoundFormat]] to F. 129 // 5. Return dtf.[[BoundFormat]]. 130 JSHandle<JSDateTimeFormat> dtf = JSHandle<JSDateTimeFormat>::Cast(dtfValue); 131 JSHandle<JSTaggedValue> boundFormat(thread, dtf->GetBoundFormat()); 132 if (boundFormat->IsUndefined()) { 133 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 134 JSHandle<JSIntlBoundFunction> intlBoundFunc = factory->NewJSIntlBoundFunction( 135 MethodIndex::BUILTINS_DATE_TIME_FORMAT_ANONYMOUS_DATE_TIME_FORMAT); 136 intlBoundFunc->SetDateTimeFormat(thread, dtf); 137 dtf->SetBoundFormat(thread, intlBoundFunc); 138 } 139 return dtf->GetBoundFormat(); 140} 141 142// 13.1.5 DateTime Format Functions 143JSTaggedValue BuiltinsDateTimeFormat::AnonymousDateTimeFormat(EcmaRuntimeCallInfo *argv) 144{ 145 // A DateTime format function is an anonymous built-in function that has a [[DateTimeFormat]] internal slot. 146 // When a DateTime format function F is called with optional argument date, the following steps are taken: 147 JSThread *thread = argv->GetThread(); 148 BUILTINS_API_TRACE(thread, DateTimeFormat, AnonymousDateTimeFormat); 149 [[maybe_unused]] EcmaHandleScope scope(thread); 150 JSHandle<JSIntlBoundFunction> intlBoundFunc = JSHandle<JSIntlBoundFunction>::Cast(GetConstructor(argv)); 151 152 // 1. Let dtf be F.[[DateTimeFormat]]. 153 JSHandle<JSTaggedValue> dtf(thread, intlBoundFunc->GetDateTimeFormat()); 154 155 // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]] internal slot. 156 ASSERT_PRINT(dtf->IsJSObject() && dtf->IsJSDateTimeFormat(), "dtf is not object or JSDateTimeFormat"); 157 158 // 3. If date is not provided or is undefined, then 159 // a. Let x be Call(%Date_now%, undefined). 160 // 4. Else, 161 // a. Let x be ? ToNumber(date). 162 JSHandle<JSTaggedValue> date = GetCallArg(argv, 0); 163 double x = 0.0; 164 if (date->IsUndefined()) { 165 x = JSDate::Now().GetNumber(); 166 } else { 167 JSTaggedNumber xNumber = JSTaggedValue::ToNumber(thread, date); 168 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 169 x = xNumber.GetNumber(); 170 } 171 172 // 5. Return ? FormatDateTime(dtf, x). 173 JSHandle<EcmaString> result = JSDateTimeFormat::FormatDateTime(thread, JSHandle<JSDateTimeFormat>::Cast(dtf), x); 174 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 175 return result.GetTaggedValue(); 176} 177 178// 13.4.4 Intl.DateTimeFormat.prototype.formatToParts ( date ) 179JSTaggedValue BuiltinsDateTimeFormat::FormatToParts(EcmaRuntimeCallInfo *argv) 180{ 181 JSThread *thread = argv->GetThread(); 182 BUILTINS_API_TRACE(thread, DateTimeFormat, FormatToParts); 183 [[maybe_unused]] EcmaHandleScope scope(thread); 184 // 1. Let dtf be this value. 185 JSHandle<JSTaggedValue> dtf = GetThis(argv); 186 // 2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]). 187 if (!dtf->IsJSDateTimeFormat()) { 188 THROW_TYPE_ERROR_AND_RETURN(thread, "is not JSDateTimeFormat", JSTaggedValue::Exception()); 189 } 190 191 // 3. If date is undefined, then 192 // a. Let x be Call(%Date_now%, undefined). 193 // 4. Else, 194 // a. Let x be ? ToNumber(date). 195 JSHandle<JSTaggedValue> date = GetCallArg(argv, 0); 196 double x = 0.0; 197 if (date->IsUndefined()) { 198 x = JSDate::Now().GetNumber(); 199 } else { 200 JSTaggedNumber xNumber = JSTaggedValue::ToNumber(thread, date); 201 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 202 x = xNumber.GetNumber(); 203 } 204 205 double xValue = JSDate::TimeClip(x); 206 if (std::isnan(xValue)) { 207 THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid time value", JSTaggedValue::Exception()); 208 } 209 210 // 5. Return ? FormatDateTimeToParts(dtf, x). 211 JSHandle<JSArray> result = 212 JSDateTimeFormat::FormatDateTimeToParts(thread, JSHandle<JSDateTimeFormat>::Cast(dtf), xValue); 213 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 214 return result.GetTaggedValue(); 215} 216 217// 13.4.5 Intl.DateTimeFormat.prototype.resolvedOptions () 218JSTaggedValue BuiltinsDateTimeFormat::ResolvedOptions(EcmaRuntimeCallInfo *argv) 219{ 220 JSThread *thread = argv->GetThread(); 221 BUILTINS_API_TRACE(thread, DateTimeFormat, ResolvedOptions); 222 [[maybe_unused]] EcmaHandleScope scope(thread); 223 // 1. Let dtf be this value. 224 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 225 // 2. If Type(dtf) is not Object, throw a TypeError exception. 226 if (!thisValue->IsJSObject()) { 227 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", JSTaggedValue::Exception()); 228 } 229 // 3. Let dtf be ? UnwrapDateTimeFormat(dtf). 230 thisValue = JSDateTimeFormat::UnwrapDateTimeFormat(thread, thisValue); 231 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 232 233 // Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]). 234 if (!thisValue->IsJSDateTimeFormat()) { 235 THROW_TYPE_ERROR_AND_RETURN(thread, "dtf is not JSDateTimeFormat", JSTaggedValue::Exception()); 236 } 237 238 auto ecmaVm = thread->GetEcmaVM(); 239 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv(); 240 ObjectFactory *factory = ecmaVm->GetFactory(); 241 JSHandle<JSFunction> ctor(env->GetObjectFunction()); 242 JSHandle<JSObject> options(factory->NewJSObjectByConstructor(ctor)); 243 JSDateTimeFormat::ResolvedOptions(thread, JSHandle<JSDateTimeFormat>::Cast(thisValue), options); 244 // 6. Return options. 245 return options.GetTaggedValue(); 246} 247 248// Intl.DateTimeFormat.prototype.formatRange 249JSTaggedValue BuiltinsDateTimeFormat::FormatRange(EcmaRuntimeCallInfo *argv) 250{ 251 JSThread *thread = argv->GetThread(); 252 BUILTINS_API_TRACE(thread, DateTimeFormat, FormatRange); 253 [[maybe_unused]] EcmaHandleScope scope(thread); 254 255 // 1. Let dtf be this value. 256 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 257 // 2. If Type(dtf) is not Object, throw a TypeError exception. 258 if (!thisValue->IsJSObject()) { 259 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", JSTaggedValue::Exception()); 260 } 261 262 // 3. If dtf does not have an [[InitializedDateTimeFormat]] internal slot, throw a TypeError exception. 263 if (!thisValue->IsJSDateTimeFormat()) { 264 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not JSDateTimeFormat", JSTaggedValue::Exception()); 265 } 266 267 // 4. If startDate is undefined or endDate is undefined, throw a TypeError exception. 268 JSHandle<JSTaggedValue> startDate = GetCallArg(argv, 0); 269 JSHandle<JSTaggedValue> endDate = GetCallArg(argv, 1); 270 if (startDate->IsUndefined() || endDate->IsUndefined()) { 271 THROW_TYPE_ERROR_AND_RETURN(thread, "startDate or endDate is undefined", JSTaggedValue::Exception()); 272 } 273 274 // 5. Let x be ? ToNumber(startDate). 275 JSTaggedNumber valueX = JSTaggedValue::ToNumber(thread, startDate); 276 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 277 double x = valueX.GetNumber(); 278 279 // 6. Let y be ? ToNumber(endDate). 280 JSTaggedNumber valueY = JSTaggedValue::ToNumber(thread, endDate); 281 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 282 double y = valueY.GetNumber(); 283 284 // 8. Return ? FormatDateTimeRange(dtf, x, y) 285 JSHandle<JSDateTimeFormat> dtf = JSHandle<JSDateTimeFormat>::Cast(thisValue); 286 JSHandle<EcmaString> result = JSDateTimeFormat::NormDateTimeRange(thread, dtf, x, y); 287 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 288 return result.GetTaggedValue(); 289} 290 291// Intl.DateTimeFormat.prototype.formatRangeToParts 292JSTaggedValue BuiltinsDateTimeFormat::FormatRangeToParts(EcmaRuntimeCallInfo *argv) 293{ 294 JSThread *thread = argv->GetThread(); 295 BUILTINS_API_TRACE(thread, DateTimeFormat, FormatRangeToParts); 296 [[maybe_unused]] EcmaHandleScope scope(thread); 297 // 1. Let dtf be this value. 298 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 299 // 2. If Type(dtf) is not Object, throw a TypeError exception. 300 if (!thisValue->IsJSObject()) { 301 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", JSTaggedValue::Exception()); 302 } 303 304 // 3. If dtf does not have an [[InitializedDateTimeFormat]] internal slot, 305 // throw a TypeError exception. 306 if (!thisValue->IsJSDateTimeFormat()) { 307 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not JSDateTimeFormat", JSTaggedValue::Exception()); 308 } 309 310 // 4. If startDate is undefined or endDate is undefined, throw a TypeError exception. 311 JSHandle<JSTaggedValue> startDate = GetCallArg(argv, 0); 312 JSHandle<JSTaggedValue> endDate = GetCallArg(argv, 1); 313 if (startDate->IsUndefined() || endDate->IsUndefined()) { 314 THROW_TYPE_ERROR_AND_RETURN(thread, "startDate or endDate is undefined", JSTaggedValue::Exception()); 315 } 316 317 // 5. Let x be ? ToNumber(startDate). 318 JSTaggedNumber valueX = JSTaggedValue::ToNumber(thread, startDate); 319 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 320 double x = valueX.GetNumber(); 321 322 // 6. Let y be ? ToNumber(endDate). 323 JSTaggedNumber valueY = JSTaggedValue::ToNumber(thread, endDate); 324 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 325 double y = valueY.GetNumber(); 326 327 // 8. Return ? FormatDateTimeRangeToParts(dtf, x, y) 328 JSHandle<JSDateTimeFormat> dtf = JSHandle<JSDateTimeFormat>::Cast(thisValue); 329 JSHandle<JSArray> result = JSDateTimeFormat::NormDateTimeRangeToParts(thread, dtf, x, y); 330 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 331 return result.GetTaggedValue(); 332} 333} // namespace panda::ecmascript::builtins