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