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