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