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