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