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/js_date.h" 17 18#include <ctime> 19#include <regex> 20#include <sys/time.h> 21 22#include "ecmascript/date_parse.h" 23#include "ecmascript/object_fast_operator-inl.h" 24#include "ecmascript/platform/time.h" 25 26namespace panda::ecmascript { 27using NumberHelper = base::NumberHelper; 28bool DateUtils::isCached_ = false; 29int DateUtils::preSumDays_ = 0; 30int DateUtils::preDays_ = 0; 31int DateUtils::preMonth_ = 0; 32int DateUtils::preYear_ = 0; 33static const std::array<CString, WEEKDAY> WEEK_DAY_NAME = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 34static const std::array<CString, MOUTH_PER_YEAR> MONTH_NAME = { 35 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 36 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 37}; 38void DateUtils::TransferTimeToDate(int64_t timeMs, std::array<int64_t, DATE_LENGTH> *date) 39{ 40 (*date)[HOUR] = Mod(timeMs, MS_PER_DAY); // ms from hour, minutes, second, ms 41 (*date)[DAYS] = (timeMs - (*date)[HOUR]) / MS_PER_DAY; // days from year, month, day 42 (*date)[MS] = (*date)[HOUR] % MS_PER_SECOND; // ms 43 (*date)[HOUR] = ((*date)[HOUR] - (*date)[MS]) / MS_PER_SECOND; // s from hour, minutes, second 44 (*date)[SEC] = (*date)[HOUR] % SEC_PER_MINUTE; // second 45 (*date)[HOUR] = ((*date)[HOUR] - (*date)[SEC]) / SEC_PER_MINUTE; // min from hour, minutes 46 (*date)[MIN] = (*date)[HOUR] % SEC_PER_MINUTE; // min 47 (*date)[HOUR] = ((*date)[HOUR] - (*date)[MIN]) / SEC_PER_MINUTE; // hour 48 (*date)[WEEKDAY] = Mod(((*date)[DAYS] + LEAP_NUMBER[0]), DAY_PER_WEEK); // weekday 49 GetYearFromDays(date); 50} 51// static 52bool DateUtils::IsLeap(int64_t year) 53{ 54 return year % LEAP_NUMBER[0] == 0 && (year % LEAP_NUMBER[1] != 0 || year % LEAP_NUMBER[2] == 0); // 2: means index 55} 56 57// static 58int64_t DateUtils::GetDaysInYear(int64_t year) 59{ 60 int64_t number; 61 number = IsLeap(year) ? (DAYS_IN_YEAR + 1) : DAYS_IN_YEAR; 62 return number; 63} 64 65// static 66int64_t DateUtils::GetDaysFromYear(int64_t year) 67{ 68 return DAYS_IN_YEAR * (year - YEAR_NUMBER[0]) + FloorDiv(year - YEAR_NUMBER[1], LEAP_NUMBER[0]) - 69 FloorDiv(year - YEAR_NUMBER[2], LEAP_NUMBER[1]) + // 2: year index 70 FloorDiv(year - YEAR_NUMBER[3], LEAP_NUMBER[2]); // 3, 2: year index 71} 72 73// static 74int64_t DateUtils::FloorDiv(int64_t a, int64_t b) 75{ 76 ASSERT(b != 0); 77 int64_t m = a % b; 78 int64_t res = m < 0 ? ((a - m - b) / b) : ((a - m) / b); 79 return res; 80} 81 82// static 83void DateUtils::GetYearFromDays(std::array<int64_t, DATE_LENGTH> *date) 84{ 85 if (date == nullptr) { 86 return; 87 } 88 if (isCached_) { 89 int64_t t = (*date)[DAYS]; 90 int64_t newDays = preDays_ + (t - preSumDays_); 91 if (newDays >= 1 && newDays < DAYS_FEBRUARY) { 92 preSumDays_ = t; 93 preDays_ = newDays; 94 (*date)[DAYS] = newDays; 95 (*date)[MONTH] = preMonth_; 96 (*date)[YEAR] = preYear_; 97 return; 98 } 99 } 100 int64_t realDay; 101 int64_t d = (*date)[DAYS]; 102 preSumDays_ = d; 103 d += DAYS_1970_TO_0000; // shift from 1970-01-01 to 0000-03-01 104 int64_t era = (d >= 0 ? d : d - DAYS_IN_400_YEARS + 1) / DAYS_IN_400_YEARS; // an era is a 400 year period 105 int64_t doe = static_cast<int64_t>(d - era * DAYS_IN_400_YEARS); // days of era 106 int64_t yoe = (doe - doe / DAYS_IN_4_YEARS + doe / DAYS_IN_100_YEARS - 107 doe / (DAYS_IN_400_YEARS - 1)) / DAYS_IN_YEAR; // year of era 108 int64_t y = static_cast<int64_t>(yoe) + era * LEAP_NUMBER[2]; 109 int64_t doy = doe - (DAYS_IN_YEAR * yoe + yoe / LEAP_NUMBER[0] - 110 yoe / LEAP_NUMBER[1]); // days of year 111 int64_t mp = (COEFFICIENT_TO_CIVIL[0] * doy + MONTH_COEFFICIENT) / 112 COEFFICIENT_TO_CIVIL[1]; // [0, 11] / [Mar,Feb] system 113 int64_t month = mp + (mp < MONTH_TRANSFORM[1] ? 114 MONTH_TRANSFORM[0] : MONTH_TRANSFORM[2]); // transform month to civil system 115 int64_t year = y + (month <= MONTH_COEFFICIENT); 116 month -= 1; 117 realDay = doy - (COEFFICIENT_TO_CIVIL[1] * mp + 2) / COEFFICIENT_TO_CIVIL[0] + 1; // 2: shift from 03-01 to 01-01 118 (*date)[YEAR] = year; 119 (*date)[MONTH] = month; 120 (*date)[DAYS] = realDay; 121 preDays_ = realDay; 122 preMonth_ = month; 123 preYear_ = year; 124 isCached_ = true; 125} 126 127// static 128int64_t DateUtils::Mod(int64_t a, int b) 129{ 130 ASSERT(b != 0); 131 int64_t m = a % b; 132 int64_t res = m < 0 ? (m + b) : m; 133 return res; 134} 135 136// static 137// 20.4.1.11 138double JSDate::MakeTime(double hour, double min, double sec, double ms) 139{ 140 if (std::isfinite(hour) && std::isfinite(min) && std::isfinite(sec) && std::isfinite(ms)) { 141 double hourInteger = NumberHelper::TruncateDouble(hour); 142 double minInteger = NumberHelper::TruncateDouble(min); 143 double secInteger = NumberHelper::TruncateDouble(sec); 144 double msInteger = NumberHelper::TruncateDouble(ms); 145 return hourInteger * MS_PER_HOUR + minInteger * MS_PER_MINUTE + secInteger * MS_PER_SECOND + msInteger; 146 } 147 return base::NAN_VALUE; 148} 149 150// static 151// 20.4.1.12 152double JSDate::MakeDay(double year, double month, double date) 153{ 154 if (std::isfinite(year) && std::isfinite(month) && std::isfinite(date)) { 155 double yearInteger = NumberHelper::TruncateDouble(year); 156 double monthInteger = NumberHelper::TruncateDouble(month); 157 int64_t y = static_cast<int64_t>(yearInteger) + static_cast<int64_t>(monthInteger / MOUTH_PER_YEAR); 158 int64_t m = static_cast<int64_t>(monthInteger) % MOUTH_PER_YEAR; 159 if (m < 0) { 160 m += MOUTH_PER_YEAR; 161 y -= 1; 162 } 163 164 int64_t days = DateUtils::GetDaysFromYear(y); 165 int index = DateUtils::IsLeap(year) ? 1 : 0; 166 days += DAYS_FROM_MONTH[index][m]; 167 return static_cast<double>(days - 1) + NumberHelper::TruncateDouble(date); 168 } 169 return base::NAN_VALUE; 170} 171 172// static 173// 20.4.1.13 174double JSDate::MakeDate(double day, double time) 175{ 176 if (std::isfinite(day) && std::isfinite(time)) { 177 return time + day * MS_PER_DAY; 178 } 179 return base::NAN_VALUE; 180} 181 182// static 183// 20.4.1.14 184double JSDate::TimeClip(double time) 185{ 186 if (-MAX_TIME_IN_MS <= time && time <= MAX_TIME_IN_MS) { 187 return NumberHelper::TruncateDouble(time); 188 } 189 return base::NAN_VALUE; 190} 191 192// 20.4.1.8 193double JSDate::LocalTime(double timeMs) const 194{ 195 return timeMs + GetLocalOffsetFromOS(timeMs, true); 196} 197 198// 20.4.1.9 199double JSDate::UTCTime(double timeMs) const 200{ 201 return timeMs - GetLocalOffsetFromOS(timeMs, false); 202} 203 204// static 205int JSDate::GetSignedNumFromString(const CString &str, int len, int *index) 206{ 207 int res = 0; 208 GetNumFromString(str, len, index, &res); 209 if (str.at(0) == NEG) { 210 return -res; 211 } 212 return res; 213} 214 215// static 216bool JSDate::GetNumFromString(const CString &str, int len, int *index, int *num) 217{ 218 int indexStr = *index; 219 char oneByte = 0; 220 while (indexStr < len) { 221 oneByte = str.at(indexStr); 222 if (oneByte >= '0' && oneByte <= '9') { 223 break; 224 } 225 indexStr++; 226 } 227 if (indexStr >= len) { 228 return false; 229 } 230 int value = 0; 231 while (indexStr < len) { 232 oneByte = str.at(indexStr); 233 int val = static_cast<int>(oneByte - '0'); 234 if (val >= 0 && val <= NUM_NINE) { 235 value = value * TEN + val; 236 indexStr++; 237 } else { 238 break; 239 } 240 } 241 *num = value; 242 *index = indexStr; 243 return true; 244} 245 246// 20.4.1.7 247int64_t JSDate::GetLocalOffsetInMin(const JSThread *thread, int64_t timeMs, bool isLocal) 248{ 249 if (!isLocal) { 250 return 0; 251 } 252 double localOffset = this->GetLocalOffset().GetDouble(); 253 if (localOffset == MAX_DOUBLE) { 254 localOffset = static_cast<double>(GetLocalOffsetFromOS(timeMs, isLocal)); 255 SetLocalOffset(thread, JSTaggedValue(localOffset)); 256 } 257 return localOffset; 258} 259 260// static 261JSTaggedValue JSDate::LocalParseStringToMs(const CString &str) 262{ 263 int year = 0; 264 int month = 0; 265 int date = 1; 266 int hours = 0; 267 int minutes = 0; 268 int seconds = 0; 269 int ms = 0; 270 int index = 0; 271 int len = static_cast<int>(str.length()); 272 bool isLocal = false; 273 CString::size_type indexGmt; 274 CString::size_type indexPlus = CString::npos; 275 int localTime = 0; 276 int localHours = 0; 277 int localMinutes = 0; 278 int64_t localMs = 0; 279 CString::size_type localSpace; 280 localSpace = str.find(' ', index); 281 CString strMonth = str.substr(localSpace + 1, LENGTH_MONTH_NAME); 282 for (int i = 0; i < MOUTH_PER_YEAR; i++) { 283 if (strMonth == MONTH_NAME[i]) { 284 month = i; 285 break; 286 } 287 } 288 index += (LENGTH_MONTH_NAME + 1); 289 GetNumFromString(str, len, &index, &date); 290 GetNumFromString(str, len, &index, &year); 291 indexGmt = str.find("GMT", index); 292 if (indexGmt == CString::npos) { 293 GetNumFromString(str, len, &index, &hours); 294 GetNumFromString(str, len, &index, &minutes); 295 GetNumFromString(str, len, &index, &seconds); 296 isLocal = true; 297 localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE); 298 } else { 299 indexPlus = str.find(PLUS, indexGmt); 300 int indexLocal = static_cast<int>(indexGmt); 301 GetNumFromString(str, indexGmt, &index, &hours); 302 GetNumFromString(str, indexGmt, &index, &minutes); 303 GetNumFromString(str, indexGmt, &index, &seconds); 304 GetNumFromString(str, len, &indexLocal, &localTime); 305 localHours = localTime / HUNDRED; 306 localMinutes = localTime % HUNDRED; 307 localMs = static_cast<int64_t>(MakeTime(localHours, localMinutes, 0, 0)); 308 if (indexPlus != CString::npos) { 309 localMs = -localMs; 310 } 311 } 312 double day = MakeDay(year, month, date); 313 double time = MakeTime(hours, minutes, seconds, ms); 314 double timeValue = TimeClip(MakeDate(day, time)); 315 if (std::isnan(timeValue)) { 316 return JSTaggedValue(timeValue); 317 } 318 if (isLocal && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) { 319 timeValue += static_cast<double>(localMs - CHINA_BEFORE_1901_MS); 320 } else { 321 timeValue += localMs; 322 } 323 return JSTaggedValue(timeValue); 324} 325 326// static 327JSTaggedValue JSDate::UtcParseStringToMs(const CString &str) 328{ 329 int year = 0; 330 int month = 0; 331 int date = 1; 332 int hours = 0; 333 int minutes = 0; 334 int seconds = 0; 335 int ms = 0; 336 int index = 0; 337 int len = static_cast<int>(str.length()); 338 CString::size_type indexGmt; 339 CString::size_type indexPlus = CString::npos; 340 int localTime = 0; 341 int localHours = 0; 342 int localMinutes = 0; 343 int64_t localMs = 0; 344 bool isLocal = false; 345 GetNumFromString(str, len, &index, &date); 346 CString strMonth = str.substr(index + 1, LENGTH_MONTH_NAME); 347 for (int i = 0; i < MOUTH_PER_YEAR; i++) { 348 if (strMonth == MONTH_NAME[i]) { 349 month = i; 350 break; 351 } 352 } 353 index += (LENGTH_MONTH_NAME + 1); 354 GetNumFromString(str, len, &index, &year); 355 indexGmt = str.find("GMT", index); 356 if (indexGmt == CString::npos) { 357 GetNumFromString(str, len, &index, &hours); 358 GetNumFromString(str, len, &index, &minutes); 359 GetNumFromString(str, len, &index, &seconds); 360 isLocal = true; 361 localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE); 362 } else { 363 indexPlus = str.find(PLUS, indexGmt); 364 int indexLocal = static_cast<int>(indexGmt); 365 GetNumFromString(str, indexGmt, &index, &hours); 366 GetNumFromString(str, indexGmt, &index, &minutes); 367 GetNumFromString(str, indexGmt, &index, &seconds); 368 GetNumFromString(str, len, &indexLocal, &localTime); 369 localHours = localTime / HUNDRED; 370 localMinutes = localTime % HUNDRED; 371 localMs = static_cast<int64_t>(MakeTime(localHours, localMinutes, 0, 0)); 372 if (indexPlus != CString::npos) { 373 localMs = -localMs; 374 } 375 } 376 double day = MakeDay(year, month, date); 377 double time = MakeTime(hours, minutes, seconds, ms); 378 double timeValue = TimeClip(MakeDate(day, time)); 379 if (std::isnan(timeValue)) { 380 return JSTaggedValue(timeValue); 381 } 382 if (isLocal && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) { 383 timeValue += static_cast<double>(localMs - CHINA_BEFORE_1901_MS); 384 } else { 385 timeValue += localMs; 386 } 387 return JSTaggedValue(timeValue); 388} 389// static 390JSTaggedValue JSDate::IsoParseStringToMs(const CString &str) 391{ 392 char flag = 0; 393 int year; 394 int month = 1; 395 int date = 1; 396 int hours = 0; 397 int minutes = 0; 398 int seconds = 0; 399 int ms = 0; 400 int index = 0; 401 int len = static_cast<int>(str.length()); 402 year = GetSignedNumFromString(str, len, &index); 403 CString::size_type indexT = str.find(FLAG_TIME, index); 404 CString::size_type indexZ = str.find(FLAG_UTC, index); 405 CString::size_type indexEndFlag = 0; 406 int64_t localMs = 0; 407 if (indexZ != CString::npos) { 408 indexEndFlag = indexZ; 409 } else if (len >= MIN_LENGTH && str.at(len - INDEX_PLUS_NEG) == NEG) { 410 indexEndFlag = static_cast<CString::size_type>(len - INDEX_PLUS_NEG); 411 flag = NEG; 412 } else if (len >= MIN_LENGTH && str.at(len - INDEX_PLUS_NEG) == PLUS) { 413 indexEndFlag = static_cast<CString::size_type>(len - INDEX_PLUS_NEG); 414 flag = PLUS; 415 } 416 if (indexT != CString::npos) { 417 if (static_cast<int>(indexT) - index == LENGTH_PER_TIME) { 418 GetNumFromString(str, len, &index, &month); 419 } else if (static_cast<int>(indexT) - index == (LENGTH_PER_TIME + LENGTH_PER_TIME)) { 420 GetNumFromString(str, len, &index, &month); 421 GetNumFromString(str, len, &index, &date); 422 } 423 GetNumFromString(str, len, &index, &hours); 424 GetNumFromString(str, len, &index, &minutes); 425 if (indexEndFlag > 0) { 426 if (static_cast<int>(indexEndFlag) - index == LENGTH_PER_TIME) { 427 GetNumFromString(str, len, &index, &seconds); 428 } else if (static_cast<int>(indexEndFlag) - index == (LENGTH_PER_TIME + LENGTH_PER_TIME + 1)) { 429 GetNumFromString(str, len, &index, &seconds); 430 GetNumFromString(str, len, &index, &ms); 431 } 432 } else { 433 if (len - index == LENGTH_PER_TIME) { 434 GetNumFromString(str, len, &index, &seconds); 435 } else if (len - index == (LENGTH_PER_TIME + LENGTH_PER_TIME + 1)) { 436 GetNumFromString(str, len, &index, &seconds); 437 GetNumFromString(str, len, &index, &ms); 438 } 439 } 440 } else { 441 GetNumFromString(str, len, &index, &month); 442 GetNumFromString(str, len, &index, &date); 443 } 444 if (indexEndFlag > 0) { 445 int localHours = 0; 446 int localMinutes = 0; 447 if (indexZ == CString::npos) { 448 GetNumFromString(str, len, &index, &localHours); 449 GetNumFromString(str, len, &index, &localMinutes); 450 if (flag == PLUS) { 451 localMs = static_cast<int64_t>(-MakeTime(localHours, localMinutes, 0, 0)); 452 } else { 453 localMs = static_cast<int64_t>(MakeTime(localHours, localMinutes, 0, 0)); 454 } 455 } 456 } 457 if (indexEndFlag == 0 && indexT != CString::npos) { 458 localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE); 459 } 460 461 double day = MakeDay(year, month - 1, date); 462 double time = MakeTime(hours, minutes, seconds, ms); 463 double timeValue = TimeClip(MakeDate(day, time)); 464 if (std::isnan(timeValue)) { 465 return JSTaggedValue(timeValue); 466 } 467 if (flag == 0 && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) { 468 timeValue += static_cast<double>(localMs - CHINA_BEFORE_1901_MS); 469 } else { 470 timeValue += localMs; 471 } 472 return JSTaggedValue(timeValue); 473} 474 475JSTaggedValue JSDate::GetTimeFromString(const char *str, int len) 476{ 477 int time[TIMEZONE + 1]; 478 bool res = DateParse::ParseDateString(str, len, time); 479 if (res) { 480 double day = MakeDay(time[YEAR], time[MONTH], time[DAYS]); 481 double dateTime = MakeTime(time[HOUR], time[MIN], time[SEC], time[MS]); 482 double timeValue = TimeClip(MakeDate(day, dateTime)); 483 if (std::isnan(timeValue)) { 484 return JSTaggedValue(timeValue); 485 } 486 int64_t localMs; 487 if (time[TIMEZONE] == INT_MAX) { 488 localMs = GetLocalOffsetFromOS(static_cast<int64_t>(timeValue), true) * MS_PER_MINUTE; 489 } else { 490 localMs = time[TIMEZONE] * MS_PER_SECOND; 491 } 492 timeValue -= localMs; 493 return JSTaggedValue(timeValue); 494 } 495 return JSTaggedValue(base::NAN_VALUE); 496} 497 498// 20.4.3.2 static 499JSTaggedValue JSDate::Parse(EcmaRuntimeCallInfo *argv) 500{ 501 ASSERT(argv); 502 JSThread *thread = argv->GetThread(); 503 JSHandle<JSTaggedValue> msg = base::BuiltinsBase::GetCallArg(argv, 0); 504 JSHandle<EcmaString> ecmaStr = JSTaggedValue::ToString(thread, msg); 505 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 506 CVector<uint8_t> tmpBuf; 507 EcmaStringAccessor strAccessor(const_cast<EcmaString *>(*ecmaStr)); 508 if (strAccessor.IsUtf16()) { 509 return JSTaggedValue(base::NAN_VALUE); 510 } 511 int len = static_cast<int>(strAccessor.GetLength()); 512 auto data = reinterpret_cast<const char *>(strAccessor.GetUtf8DataFlat(*ecmaStr, tmpBuf)); 513 return GetTimeFromString(data, len); 514} 515 516// 20.4.3.1 517JSTaggedValue JSDate::Now() 518{ 519 // time from now is in ms. 520 int64_t ans; 521 struct timeval tv { 522 }; 523 gettimeofday(&tv, nullptr); 524 ans = static_cast<int64_t>(tv.tv_sec) * MS_PER_SECOND + (tv.tv_usec / MS_PER_SECOND); 525 return JSTaggedValue(static_cast<double>(ans)); 526} 527 528// 20.4.4.2 static 529JSTaggedValue JSDate::UTC(EcmaRuntimeCallInfo *argv) 530{ 531 double year = 0.0; 532 double month = 0.0; 533 double date = 1.0; 534 double hours = 0.0; 535 double minutes = 0.0; 536 double seconds = 0.0; 537 double ms = 0.0; 538 JSThread *thread = argv->GetThread(); 539 JSHandle<JSTaggedValue> yearArg = base::BuiltinsBase::GetCallArg(argv, 0); 540 JSTaggedNumber yearValue = JSTaggedValue::ToNumber(thread, yearArg); 541 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 542 if (yearValue.IsNumber()) { 543 year = yearValue.GetNumber(); 544 if (std::isfinite(year) && !yearValue.IsInt()) { 545 year = NumberHelper::TruncateDouble(year); 546 } 547 if (year >= 0 && year <= (HUNDRED - 1)) { 548 year = year + NINETEEN_HUNDRED_YEAR; 549 } 550 } else { 551 year = base::NAN_VALUE; 552 } 553 uint32_t index = 1; 554 uint32_t numArgs = argv->GetArgsNumber(); 555 JSTaggedValue res; 556 if (numArgs > index) { 557 JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index); 558 res = JSTaggedValue::ToNumber(thread, value); 559 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 560 month = res.GetNumber(); 561 index++; 562 } 563 if (numArgs > index) { 564 JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index); 565 res = JSTaggedValue::ToNumber(thread, value); 566 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 567 date = res.GetNumber(); 568 index++; 569 } 570 if (numArgs > index) { 571 JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index); 572 res = JSTaggedValue::ToNumber(thread, value); 573 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 574 hours = res.GetNumber(); 575 index++; 576 } 577 if (numArgs > index) { 578 JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index); 579 res = JSTaggedValue::ToNumber(thread, value); 580 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 581 minutes = res.GetNumber(); 582 index++; 583 } 584 if (numArgs > index) { 585 JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index); 586 res = JSTaggedValue::ToNumber(thread, value); 587 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 588 seconds = res.GetNumber(); 589 index++; 590 } 591 if (numArgs > index) { 592 JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index); 593 res = JSTaggedValue::ToNumber(thread, value); 594 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 595 ms = res.GetNumber(); 596 } 597 double day = MakeDay(year, month, date); 598 double time = MakeTime(hours, minutes, seconds, ms); 599 return JSTaggedValue(TimeClip(MakeDate(day, time))); 600} 601 602// 20.4.4.10 603JSTaggedValue JSDate::GetTime() const 604{ 605 return GetTimeValue(); 606} 607 608// static 609CString JSDate::StrToTargetLength(const CString &str, int length) 610{ 611 int len = 0; 612 CString sub; 613 if (str[0] == NEG) { 614 sub.reserve(length + 1); 615 ASSERT(str.length() > 0); 616 len = static_cast<int>(str.length() - 1); 617 sub += NEG; 618 } else { 619 sub.reserve(length); 620 len = static_cast<int>(str.length()); 621 } 622 int dif = length - len; 623 for (int i = 0; i < dif; i++) { 624 sub += '0'; 625 } 626 if (str[0] == NEG) { 627 sub += str.substr(1, len); 628 } else { 629 sub += str; 630 } 631 return sub; 632} 633 634// static 635void JSDate::AppendStrToTargetLength(const CString &str, int length, CString &target) 636{ 637 int len = 0; 638 if (str[0] == NEG) { 639 len = static_cast<int>(str.length() - 1); 640 target += NEG; 641 } else { 642 len = static_cast<int>(str.length()); 643 } 644 int dif = length - len; 645 for (int i = 0; i < dif; i++) { 646 target += '0'; 647 } 648 if (str[0] == NEG) { 649 target += str.substr(1, len); 650 } else { 651 target += str; 652 } 653} 654 655bool JSDate::GetThisDateValues(std::array<int64_t, DATE_LENGTH> *date, bool isLocal) const 656{ 657 double timeMs = this->GetTimeValue().GetDouble(); 658 if (std::isnan(timeMs)) { 659 return false; 660 } 661 GetDateValues(timeMs, date, isLocal); 662 return true; 663} 664 665// 20.4.4.35 666JSTaggedValue JSDate::ToDateString(JSThread *thread) const 667{ 668 std::array<int64_t, DATE_LENGTH> fields = {0}; 669 if (!GetThisDateValues(&fields, true)) { 670 return JSTaggedValue(base::NAN_VALUE); 671 } 672 CString str; 673 str.reserve(DATE_STRING_LENGTH); 674 str.append(WEEK_DAY_NAME[fields[WEEKDAY]]) // Append weekdy name 675 .append(SPACE_STR) // Append SPACE 676 .append(MONTH_NAME[fields[MONTH]]) // Append mouth name 677 .append(SPACE_STR); // Append SPACE 678 ConvertAndAppend(fields[DAYS], STR_LENGTH_OTHERS, str); 679 str += SPACE; 680 ConvertAndAppend(fields[YEAR], STR_LENGTH_YEAR, str); 681 JSHandle<EcmaString> result = thread->GetEcmaVM()->GetFactory()->NewFromASCII(str); 682 return result.GetTaggedValue(); 683} 684 685// static 686CString JSDate::ToDateString(double timeMs) 687{ 688 if (std::isnan(timeMs)) { 689 return "Invalid Date"; 690 } 691 std::array<int64_t, DATE_LENGTH> fields = {0}; 692 GetDateValues(timeMs, &fields, true); 693 CString localTime; 694 int localMin = 0; 695 localMin = GetLocalOffsetFromOS(timeMs, true); 696 if (localMin >= 0) { 697 localTime += PLUS; 698 } else { 699 localTime += NEG; 700 localMin = -localMin; 701 } 702 localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); 703 localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); 704 CString str; 705 str.reserve(DATE_CSTRING_LENGTH); 706 str.append(WEEK_DAY_NAME[fields[WEEKDAY]]) // Append weekday name 707 .append(SPACE_STR) // Append SPACE 708 .append(MONTH_NAME[fields[MONTH]]) // Append mouth name 709 .append(SPACE_STR); // Append SPACE 710 ConvertAndAppend(fields[DAYS], STR_LENGTH_OTHERS, str); 711 str += SPACE; 712 ConvertAndAppend(fields[YEAR], STR_LENGTH_YEAR, str); 713 str += SPACE; 714 ConvertAndAppend(fields[HOUR], STR_LENGTH_OTHERS, str); 715 str += COLON; 716 ConvertAndAppend(fields[MIN], STR_LENGTH_OTHERS, str); 717 str += COLON; 718 ConvertAndAppend(fields[SEC], STR_LENGTH_OTHERS, str); 719 str.append(SPACE_STR) // Append SPACE 720 .append("GMT") // Append GMT 721 .append(localTime); // Append localTime 722 return str; 723} 724// 20.4.4.36 725JSTaggedValue JSDate::ToISOString(JSThread *thread) const 726{ 727 std::array<int64_t, DATE_LENGTH> fields = {0}; 728 if (!GetThisDateValues(&fields, false)) { 729 return JSTaggedValue(base::NAN_VALUE); 730 } 731 CString year = ToCString(fields[YEAR]); 732 if (year[0] == NEG) { 733 year = StrToTargetLength(year, STR_LENGTH_YEAR + STR_LENGTH_OTHERS); 734 } else if (year.length() > STR_LENGTH_YEAR) { 735 year = PLUS + StrToTargetLength(year, STR_LENGTH_YEAR + STR_LENGTH_OTHERS); 736 } else { 737 year = StrToTargetLength(year, STR_LENGTH_YEAR); 738 } 739 CString str; 740 str.reserve(ISO_STRING_LENGTH); 741 str.append(year) // Append year 742 .append(NEG_STR); // Append NEG 743 ConvertAndAppend(fields[MONTH] + 1, STR_LENGTH_OTHERS, str); 744 str += NEG; 745 ConvertAndAppend(fields[DAYS], STR_LENGTH_OTHERS, str); 746 str += FLAG_TIME; 747 ConvertAndAppend(fields[HOUR], STR_LENGTH_OTHERS, str); 748 str += COLON; 749 ConvertAndAppend(fields[MIN], STR_LENGTH_OTHERS, str); 750 str += COLON; 751 ConvertAndAppend(fields[SEC], STR_LENGTH_OTHERS, str); 752 str += POINT; 753 ConvertAndAppend(fields[MS], STR_LENGTH_OTHERS + 1, str); 754 str += FLAG_UTC; 755 return thread->GetEcmaVM()->GetFactory()->NewFromASCII(str).GetTaggedValue(); 756} 757 758// 20.4.4.41 759JSTaggedValue JSDate::ToString(JSThread *thread) const 760{ 761 int localMin = 0; 762 std::array<int64_t, DATE_LENGTH> fields = {0}; 763 if (!GetThisDateValues(&fields, true)) { 764 return JSTaggedValue(base::NAN_VALUE); 765 } 766 CString localTime; 767 localMin = GetLocalOffsetFromOS(static_cast<int64_t>(this->GetTimeValue().GetDouble()), true); 768 if (localMin >= 0) { 769 localTime += PLUS; 770 } else { 771 localTime += NEG; 772 localMin = -localMin; 773 } 774 localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); 775 localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); 776 CString str; 777 str.reserve(TO_STRING_LENGTH); 778 str.append(WEEK_DAY_NAME[fields[WEEKDAY]]) // Append weekday name 779 .append(SPACE_STR) // Append SPACE 780 .append(MONTH_NAME[fields[MONTH]]) // Append mouth name 781 .append(SPACE_STR); // Append SPACE 782 ConvertAndAppend(fields[DAYS], STR_LENGTH_OTHERS, str); 783 str += SPACE; 784 ConvertAndAppend(fields[YEAR], STR_LENGTH_YEAR, str); 785 str += SPACE; 786 ConvertAndAppend(fields[HOUR], STR_LENGTH_OTHERS, str); 787 str += COLON; 788 ConvertAndAppend(fields[MIN], STR_LENGTH_OTHERS, str); 789 str += COLON; 790 ConvertAndAppend(fields[SEC], STR_LENGTH_OTHERS, str); 791 str.append(SPACE_STR) // Append SPACE 792 .append("GMT") // Append GMT 793 .append(localTime); // Append localTime 794 return thread->GetEcmaVM()->GetFactory()->NewFromASCII(str).GetTaggedValue(); 795} 796 797// 20.4.4.42 798JSTaggedValue JSDate::ToTimeString(JSThread *thread) const 799{ 800 int localMin = 0; 801 std::array<int64_t, DATE_LENGTH> fields = {0}; 802 if (!GetThisDateValues(&fields, true)) { 803 return JSTaggedValue(base::NAN_VALUE); 804 } 805 CString localTime; 806 localMin = GetLocalOffsetFromOS(static_cast<int64_t>(this->GetTimeValue().GetDouble()), true); 807 if (localMin >= 0) { 808 localTime += PLUS; 809 } else { 810 localTime += NEG; 811 localMin = -localMin; 812 } 813 localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); 814 localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); 815 CString str; 816 str.reserve(TIME_STRING_LENGTH); 817 ConvertAndAppend(fields[HOUR], STR_LENGTH_OTHERS, str); 818 str += COLON; 819 ConvertAndAppend(fields[MIN], STR_LENGTH_OTHERS, str); 820 str += COLON; 821 ConvertAndAppend(fields[SEC], STR_LENGTH_OTHERS, str); 822 str.append(SPACE_STR) // Append SPACE 823 .append("GMT") // Append GMT 824 .append(localTime); // Append localTime 825 return thread->GetEcmaVM()->GetFactory()->NewFromASCII(str).GetTaggedValue(); 826} 827 828// 20.4.4.43 829JSTaggedValue JSDate::ToUTCString(JSThread *thread) const 830{ 831 std::array<int64_t, DATE_LENGTH> fields = {0}; 832 if (!GetThisDateValues(&fields, false)) { 833 return JSTaggedValue(base::NAN_VALUE); 834 } 835 CString str; 836 str.reserve(UTC_STRING_LENGTH); 837 str.append(WEEK_DAY_NAME[fields[WEEKDAY]]) // Append weekday name 838 .append(COMMA_STR) // Append COMMA 839 .append(SPACE_STR); // Append SPACE 840 ConvertAndAppend(fields[DAYS], STR_LENGTH_OTHERS, str); 841 str.append(SPACE_STR) // Append SPACE 842 .append(MONTH_NAME[fields[MONTH]]) // Append mouth name 843 .append(SPACE_STR); // Append SPACE 844 ConvertAndAppend(fields[YEAR], STR_LENGTH_YEAR, str); 845 str += SPACE; 846 ConvertAndAppend(fields[HOUR], STR_LENGTH_OTHERS, str); 847 str += COLON; 848 ConvertAndAppend(fields[MIN], STR_LENGTH_OTHERS, str); 849 str += COLON; 850 ConvertAndAppend(fields[SEC], STR_LENGTH_OTHERS, str); 851 str.append(SPACE_STR) // Append SPACE 852 .append("GMT"); // Append GMT 853 return thread->GetEcmaVM()->GetFactory()->NewFromASCII(str).GetTaggedValue(); 854} 855 856// 20.4.4.44 857JSTaggedValue JSDate::ValueOf() const 858{ 859 return this->GetTimeValue(); 860} 861 862// static 863void JSDate::GetDateValues(double timeMs, std::array<int64_t, DATE_LENGTH> *date, bool isLocal) 864{ 865 int64_t tz = 0; 866 int64_t timeMsInt = static_cast<int64_t>(timeMs); 867 if (isLocal) { // timezone offset 868 tz = GetLocalOffsetFromOS(timeMsInt, isLocal); 869 timeMsInt += tz * MS_PER_SECOND * SEC_PER_MINUTE; 870 } 871 872 DateUtils::TransferTimeToDate(timeMsInt, date); 873 (*date)[TIMEZONE] = -tz; 874} 875 876double JSDate::GetDateValue(double timeMs, uint8_t code, bool isLocal) const 877{ 878 if (std::isnan(timeMs)) { 879 return base::NAN_VALUE; 880 } 881 std::array<int64_t, DATE_LENGTH> date = {0}; 882 GetDateValues(timeMs, &date, isLocal); 883 return static_cast<double>(date[code]); 884} 885 886JSTaggedValue JSDate::SetDateValue(EcmaRuntimeCallInfo *argv, uint32_t code, bool isLocal) const 887{ 888 // get date values. 889 std::array<int64_t, DATE_LENGTH> date = {0}; 890 double timeMs = this->GetTimeValue().GetDouble(); 891 892 // get values from argv. 893 uint32_t argc = argv->GetArgsNumber(); 894 if (argc == 0) { 895 return JSTaggedValue(base::NAN_VALUE); 896 } 897 898 uint32_t firstValue = code & CODE_FLAG; 899 uint32_t endValue = (code >> CODE_4_BIT) & CODE_FLAG; 900 uint32_t count = endValue - firstValue; 901 902 if (argc < count) { 903 count = argc; 904 } 905 if (std::isnan(timeMs) && firstValue == 0) { 906 timeMs = 0.0; 907 GetDateValues(timeMs, &date, false); 908 } else { 909 GetDateValues(timeMs, &date, isLocal); 910 } 911 912 for (uint32_t i = 0; i < count; i++) { 913 JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, i); 914 JSThread *thread = argv->GetThread(); 915 JSTaggedNumber res = JSTaggedValue::ToNumber(thread, value); 916 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 917 double temp = res.GetNumber(); 918 if (std::isnan(temp)) { 919 return JSTaggedValue(base::NAN_VALUE); 920 } 921 date[firstValue + i] = NumberHelper::TruncateDouble(temp); 922 } 923 // set date values. 924 return JSTaggedValue(SetDateValues(&date, isLocal)); 925} 926 927// static 928double JSDate::SetDateValues(const std::array<int64_t, DATE_LENGTH> *date, bool isLocal) 929{ 930 int64_t month = DateUtils::Mod((*date)[MONTH], MONTH_PER_YEAR); 931 int64_t year = (*date)[YEAR] + ((*date)[MONTH] - month) / MONTH_PER_YEAR; 932 int64_t days = DateUtils::GetDaysFromYear(year); 933 int index = DateUtils::IsLeap(year) ? 1 : 0; 934 days += DAYS_FROM_MONTH[index][month]; 935 936 days += (*date)[DAYS] - 1; 937 int64_t millisecond = 938 (((*date)[HOUR] * MIN_PER_HOUR + (*date)[MIN]) * SEC_PER_MINUTE + (*date)[SEC]) * MS_PER_SECOND + (*date)[MS]; 939 int64_t result = days * MS_PER_DAY + millisecond; 940 if (isLocal) { 941 int64_t offset = GetLocalOffsetFromOS(result, isLocal) * SEC_PER_MINUTE * MS_PER_SECOND; 942 result -= offset; 943 } 944 return TimeClip(result); 945} 946 947double JSDate::SetDateValues(int64_t year, int64_t month, int64_t day) 948{ 949 if (year >= 0 && year < HUNDRED) { 950 year += NINETEEN_HUNDRED_YEAR; 951 } 952 int64_t m = DateUtils::Mod(month, MONTH_PER_YEAR); 953 int64_t y = year + (month - m) / MONTH_PER_YEAR; 954 int64_t d = DateUtils::GetDaysFromYear(y); 955 int index = DateUtils::IsLeap(y) ? 1 : 0; 956 d += DAYS_FROM_MONTH[index][m] + day - 1; 957 int64_t result = d * MS_PER_DAY; 958 959 int64_t offset = GetLocalOffsetFromOS(result, true) * SEC_PER_MINUTE * MS_PER_SECOND; 960 result -= offset; 961 return TimeClip(result); 962} 963} // namespace panda::ecmascript 964