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