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