• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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