1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40
41 #include "qv4dateobject_p.h"
42 #include "qv4objectproto_p.h"
43 #include "qv4scopedvalue_p.h"
44 #include "qv4runtime_p.h"
45 #include "qv4string_p.h"
46 #include "qv4jscall_p.h"
47 #include "qv4symbol_p.h"
48
49 #include <QtCore/QDebug>
50 #include <QtCore/QDateTime>
51 #include <QtCore/QStringList>
52
53 #include <time.h>
54
55 #include <wtf/MathExtras.h>
56
57 #if QT_CONFIG(timezone) && !defined(Q_OS_WIN)
58 /*
59 See QTBUG-56899. Although we don't (yet) have a proper way to reset the
60 system zone, the code below, that uses QTimeZone::systemTimeZone(), works
61 adequately on Linux.
62
63 QTimeZone::systemTimeZone() will automatically produce an updated value on
64 non-android Linux systems when the TZ environment variable or the relevant
65 files in /etc are changed. On other platforms it won't, and the information
66 produced here may be incorrect after changes to the system time zone.
67
68 We accept this defect for now because the localtime_r approach will
69 consistently produce incorrect results for some time zones, not only when
70 the system time zone changes. This is a worse problem, see also QTBUG-84474.
71
72 On windows we have a better implementation of getLocalTZA that hopefully
73 updates on time zone changes. However, we currently use the worse
74 implementation of DaylightSavingTA (returning either an hour or 0).
75
76 QTimeZone::systemTimeZone() on Linux is also slower than other approaches
77 because it has to poll the relevant files (if TZ is not set). See
78 QTBUG-75585 for an explanation and possible workarounds.
79 */
80 #define USE_QTZ_SYSTEM_ZONE
81 #endif
82
83 #ifdef USE_QTZ_SYSTEM_ZONE
84 #include <QtCore/QTimeZone>
85 #else
86 # ifdef Q_OS_WIN
87 # include <windows.h>
88 # else
89 # ifndef Q_OS_VXWORKS
90 # include <sys/time.h>
91 # else
92 # include "qplatformdefs.h"
93 # endif
94 # include <unistd.h> // for _POSIX_THREAD_SAFE_FUNCTIONS
95 # endif
96 #endif // USE_QTZ_SYSTEM_ZONE
97
98 using namespace QV4;
99
100 static const double HoursPerDay = 24.0;
101 static const double MinutesPerHour = 60.0;
102 static const double SecondsPerMinute = 60.0;
103 static const double msPerSecond = 1000.0;
104 static const double msPerMinute = 60000.0;
105 static const double msPerHour = 3600000.0;
106 static const double msPerDay = 86400000.0;
107
TimeWithinDay(double t)108 static inline double TimeWithinDay(double t)
109 {
110 double r = ::fmod(t, msPerDay);
111 return (r >= 0) ? r : r + msPerDay;
112 }
113
HourFromTime(double t)114 static inline int HourFromTime(double t)
115 {
116 int r = int(::fmod(::floor(t / msPerHour), HoursPerDay));
117 return (r >= 0) ? r : r + int(HoursPerDay);
118 }
119
MinFromTime(double t)120 static inline int MinFromTime(double t)
121 {
122 int r = int(::fmod(::floor(t / msPerMinute), MinutesPerHour));
123 return (r >= 0) ? r : r + int(MinutesPerHour);
124 }
125
SecFromTime(double t)126 static inline int SecFromTime(double t)
127 {
128 int r = int(::fmod(::floor(t / msPerSecond), SecondsPerMinute));
129 return (r >= 0) ? r : r + int(SecondsPerMinute);
130 }
131
msFromTime(double t)132 static inline int msFromTime(double t)
133 {
134 int r = int(::fmod(t, msPerSecond));
135 return (r >= 0) ? r : r + int(msPerSecond);
136 }
137
Day(double t)138 static inline double Day(double t)
139 {
140 return ::floor(t / msPerDay);
141 }
142
DaysInYear(double y)143 static inline double DaysInYear(double y)
144 {
145 if (::fmod(y, 4))
146 return 365;
147
148 else if (::fmod(y, 100))
149 return 366;
150
151 else if (::fmod(y, 400))
152 return 365;
153
154 return 366;
155 }
156
DayFromYear(double y)157 static inline double DayFromYear(double y)
158 {
159 return 365 * (y - 1970)
160 + ::floor((y - 1969) / 4)
161 - ::floor((y - 1901) / 100)
162 + ::floor((y - 1601) / 400);
163 }
164
TimeFromYear(double y)165 static inline double TimeFromYear(double y)
166 {
167 return msPerDay * DayFromYear(y);
168 }
169
YearFromTime(double t)170 static inline double YearFromTime(double t)
171 {
172 int y = 1970;
173 y += (int) ::floor(t / (msPerDay * 365.2425));
174
175 double t2 = TimeFromYear(y);
176 return (t2 > t) ? y - 1 : ((t2 + msPerDay * DaysInYear(y)) <= t) ? y + 1 : y;
177 }
178
InLeapYear(double t)179 static inline bool InLeapYear(double t)
180 {
181 double x = DaysInYear(YearFromTime(t));
182 if (x == 365)
183 return 0;
184
185 Q_ASSERT(x == 366);
186 return 1;
187 }
188
DayWithinYear(double t)189 static inline double DayWithinYear(double t)
190 {
191 return Day(t) - DayFromYear(YearFromTime(t));
192 }
193
MonthFromTime(double t)194 static inline double MonthFromTime(double t)
195 {
196 double d = DayWithinYear(t);
197 double l = InLeapYear(t);
198
199 if (d < 31.0)
200 return 0;
201
202 else if (d < 59.0 + l)
203 return 1;
204
205 else if (d < 90.0 + l)
206 return 2;
207
208 else if (d < 120.0 + l)
209 return 3;
210
211 else if (d < 151.0 + l)
212 return 4;
213
214 else if (d < 181.0 + l)
215 return 5;
216
217 else if (d < 212.0 + l)
218 return 6;
219
220 else if (d < 243.0 + l)
221 return 7;
222
223 else if (d < 273.0 + l)
224 return 8;
225
226 else if (d < 304.0 + l)
227 return 9;
228
229 else if (d < 334.0 + l)
230 return 10;
231
232 else if (d < 365.0 + l)
233 return 11;
234
235 return qt_qnan(); // ### assert?
236 }
237
DateFromTime(double t)238 static inline double DateFromTime(double t)
239 {
240 int m = (int) QV4::Value::toInteger(MonthFromTime(t));
241 double d = DayWithinYear(t);
242 double l = InLeapYear(t);
243
244 switch (m) {
245 case 0: return d + 1.0;
246 case 1: return d - 30.0;
247 case 2: return d - 58.0 - l;
248 case 3: return d - 89.0 - l;
249 case 4: return d - 119.0 - l;
250 case 5: return d - 150.0 - l;
251 case 6: return d - 180.0 - l;
252 case 7: return d - 211.0 - l;
253 case 8: return d - 242.0 - l;
254 case 9: return d - 272.0 - l;
255 case 10: return d - 303.0 - l;
256 case 11: return d - 333.0 - l;
257 }
258
259 return qt_qnan(); // ### assert
260 }
261
WeekDay(double t)262 static inline double WeekDay(double t)
263 {
264 double r = ::fmod (Day(t) + 4.0, 7.0);
265 return (r >= 0) ? r : r + 7.0;
266 }
267
268
MakeTime(double hour,double min,double sec,double ms)269 static inline double MakeTime(double hour, double min, double sec, double ms)
270 {
271 if (!qIsFinite(hour) || !qIsFinite(min) || !qIsFinite(sec) || !qIsFinite(ms))
272 return qQNaN();
273 hour = QV4::Value::toInteger(hour);
274 min = QV4::Value::toInteger(min);
275 sec = QV4::Value::toInteger(sec);
276 ms = QV4::Value::toInteger(ms);
277 return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms;
278 }
279
DayFromMonth(double month,double leap)280 static inline double DayFromMonth(double month, double leap)
281 {
282 switch ((int) month) {
283 case 0: return 0;
284 case 1: return 31.0;
285 case 2: return 59.0 + leap;
286 case 3: return 90.0 + leap;
287 case 4: return 120.0 + leap;
288 case 5: return 151.0 + leap;
289 case 6: return 181.0 + leap;
290 case 7: return 212.0 + leap;
291 case 8: return 243.0 + leap;
292 case 9: return 273.0 + leap;
293 case 10: return 304.0 + leap;
294 case 11: return 334.0 + leap;
295 }
296
297 return qt_qnan(); // ### assert?
298 }
299
MakeDay(double year,double month,double day)300 static double MakeDay(double year, double month, double day)
301 {
302 if (!qIsFinite(year) || !qIsFinite(month) || !qIsFinite(day))
303 return qQNaN();
304 year = QV4::Value::toInteger(year);
305 month = QV4::Value::toInteger(month);
306 day = QV4::Value::toInteger(day);
307
308 year += ::floor(month / 12.0);
309
310 month = ::fmod(month, 12.0);
311 if (month < 0)
312 month += 12.0;
313
314 /* Quoting the spec:
315
316 Find a value t such that YearFromTime(t) is ym and MonthFromTime(t) is mn
317 and DateFromTime(t) is 1; but if this is not possible (because some
318 argument is out of range), return NaN.
319 */
320 double first = DayFromYear(year);
321 /* Beware floating-point glitches: don't test the first millisecond of a
322 * year, month or day when we could test a moment firmly in the interior of
323 * the interval. A rounding glitch might give the first millisecond to the
324 * preceding interval.
325 */
326 bool leap = InLeapYear((first + 60) * msPerDay);
327
328 first += DayFromMonth(month, leap);
329 const double t = first * msPerDay + msPerDay / 2; // Noon on the first of the month
330 Q_ASSERT(Day(t) == first);
331 if (YearFromTime(t) != year || MonthFromTime(t) != month || DateFromTime(t) != 1) {
332 qWarning("Apparently out-of-range date %.0f-%02.0f-%02.0f", year, month, day);
333 return qt_qnan();
334 }
335 return first + day - 1;
336 }
337
MakeDate(double day,double time)338 static inline double MakeDate(double day, double time)
339 {
340 return day * msPerDay + time;
341 }
342
343 #ifdef USE_QTZ_SYSTEM_ZONE
344 /*
345 ECMAScript specifies use of a fixed (current, standard) time-zone offset,
346 LocalTZA; and LocalTZA + DaylightSavingTA(t) is taken to be (see LocalTime and
347 UTC, following) local time's offset from UTC at time t. For simple zones,
348 DaylightSavingTA(t) is thus the DST offset applicable at date/time t; however,
349 if a zone has changed its standard offset, the only way to make LocalTime and
350 UTC (if implemented in accord with the spec) perform correct transformations
351 is to have DaylightSavingTA(t) correct for the zone's standard offset change
352 as well as its actual DST offset.
353
354 This means we have to treat any historical changes in the zone's standard
355 offset as DST perturbations, regardless of historical reality. (This shall
356 mean a whole day of DST offset for some zones, that have crossed the
357 international date line. This shall confuse client code.) The bug report
358 against the ECMAScript spec is https://github.com/tc39/ecma262/issues/725
359 */
360
DaylightSavingTA(double t,double localTZA)361 static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time
362 {
363 return QTimeZone::systemTimeZone().offsetFromUtc(
364 QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - localTZA;
365 }
366 #else
367 // This implementation fails to take account of past changes in standard offset.
DaylightSavingTA(double t,double)368 static inline double DaylightSavingTA(double t, double /*localTZA*/)
369 {
370 struct tm tmtm;
371 #if defined(Q_CC_MSVC)
372 __time64_t tt = (__time64_t)(t / msPerSecond);
373 // _localtime_64_s returns non-zero on failure
374 if (_localtime64_s(&tmtm, &tt) != 0)
375 #elif !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
376 long int tt = (long int)(t / msPerSecond);
377 if (!localtime_r((const time_t*) &tt, &tmtm))
378 #else
379 // Returns shared static data which may be overwritten at any time
380 // (for MinGW/Windows localtime is luckily thread safe)
381 long int tt = (long int)(t / msPerSecond);
382 if (struct tm *tmtm_p = localtime((const time_t*) &tt))
383 tmtm = *tmtm_p;
384 else
385 #endif
386 return 0;
387 return (tmtm.tm_isdst > 0) ? msPerHour : 0;
388 }
389 #endif // USE_QTZ_SYSTEM_ZONE
390
LocalTime(double t,double localTZA)391 static inline double LocalTime(double t, double localTZA)
392 {
393 // Flawed, yet verbatim from the spec:
394 return t + localTZA + DaylightSavingTA(t, localTZA);
395 }
396
397 // The spec does note [*] that UTC and LocalTime are not quite mutually inverse.
398 // [*] http://www.ecma-international.org/ecma-262/7.0/index.html#sec-utc-t
399
UTC(double t,double localTZA)400 static inline double UTC(double t, double localTZA)
401 {
402 // Flawed, yet verbatim from the spec:
403 return t - localTZA - DaylightSavingTA(t - localTZA, localTZA);
404 }
405
currentTime()406 static inline double currentTime()
407 {
408 return QDateTime::currentDateTimeUtc().toMSecsSinceEpoch();
409 }
410
TimeClip(double t)411 static inline double TimeClip(double t)
412 {
413 if (! qt_is_finite(t) || fabs(t) > 8.64e15)
414 return qt_qnan();
415
416 // +0 looks weird, but is correct. See ES6 20.3.1.15. We must not return -0.
417 return QV4::Value::toInteger(t) + 0;
418 }
419
ParseString(const QString & s,double localTZA)420 static inline double ParseString(const QString &s, double localTZA)
421 {
422 /*
423 First, try the format defined in ECMA 262's "Date Time String Format";
424 only if that fails, fall back to QDateTime for parsing
425
426 The defined string format is YYYY-MM-DDTHH:mm:ss.sssZ; the time (T and all
427 after it) may be omitted; in each part, the second and later components
428 are optional; and there's an extended syntax for negative and large
429 positive years: +/-YYYYYY; the leading sign, even when +, isn't optional.
430 If month or day is omitted, it is 01; if minute or second is omitted, it's
431 00; if milliseconds are omitted, they're 000.
432
433 When the time zone offset is absent, date-only forms are interpreted as
434 indicating a UTC time and date-time forms are interpreted in local time.
435 */
436
437 enum Format {
438 Year,
439 Month,
440 Day,
441 Hour,
442 Minute,
443 Second,
444 MilliSecond,
445 TimezoneHour,
446 TimezoneMinute,
447 Done
448 };
449
450 const QChar *ch = s.constData();
451 const QChar *end = ch + s.length();
452
453 uint format = Year;
454 int current = 0;
455 int currentSize = 0;
456 bool extendedYear = false;
457
458 int yearSign = 1;
459 int year = 0;
460 int month = 0;
461 int day = 1;
462 int hour = 0;
463 int minute = 0;
464 int second = 0;
465 int msec = 0;
466 int offsetSign = 1;
467 int offset = 0;
468 bool seenT = false;
469 bool seenZ = false; // Have seen zone, i.e. +HH:mm or literal Z.
470
471 bool error = false;
472 if (*ch == '+' || *ch == '-') {
473 extendedYear = true;
474 if (*ch == '-')
475 yearSign = -1;
476 ++ch;
477 }
478 for (; ch <= end && !error && format != Done; ++ch) {
479 if (*ch >= '0' && *ch <= '9') {
480 current *= 10;
481 current += ch->unicode() - '0';
482 ++currentSize;
483 } else { // other char, delimits field
484 switch (format) {
485 case Year:
486 year = current;
487 if (extendedYear)
488 error = (currentSize != 6);
489 else
490 error = (currentSize != 4);
491 break;
492 case Month:
493 month = current - 1;
494 error = (currentSize != 2) || month > 11;
495 break;
496 case Day:
497 day = current;
498 error = (currentSize != 2) || day > 31;
499 break;
500 case Hour:
501 hour = current;
502 error = (currentSize != 2) || hour > 24;
503 break;
504 case Minute:
505 minute = current;
506 error = (currentSize != 2) || minute >= 60;
507 break;
508 case Second:
509 second = current;
510 error = (currentSize != 2) || second > 60;
511 break;
512 case MilliSecond:
513 msec = current;
514 error = (currentSize != 3);
515 break;
516 case TimezoneHour:
517 Q_ASSERT(offset == 0 && !seenZ);
518 offset = current * 60;
519 error = (currentSize != 2) || current > 23;
520 seenZ = true;
521 break;
522 case TimezoneMinute:
523 offset += current;
524 error = (currentSize != 2) || current >= 60;
525 break;
526 }
527 if (*ch == 'T') {
528 if (format >= Hour)
529 error = true;
530 format = Hour;
531 seenT = true;
532 } else if (*ch == '-') {
533 if (format < Day)
534 ++format;
535 else if (format < Minute)
536 error = true;
537 else if (format >= TimezoneHour)
538 error = true;
539 else {
540 Q_ASSERT(offset == 0 && !seenZ);
541 offsetSign = -1;
542 format = TimezoneHour;
543 }
544 } else if (*ch == ':') {
545 if (format != Hour && format != Minute && format != TimezoneHour)
546 error = true;
547 ++format;
548 } else if (*ch == '.') {
549 if (format != Second)
550 error = true;
551 ++format;
552 } else if (*ch == '+') {
553 if (seenZ || format < Minute || format >= TimezoneHour)
554 error = true;
555 format = TimezoneHour;
556 } else if (*ch == 'Z') {
557 if (seenZ || format < Minute || format >= TimezoneHour)
558 error = true;
559 else
560 Q_ASSERT(offset == 0);
561 format = Done;
562 seenZ = true;
563 } else if (ch->unicode() == 0) {
564 format = Done;
565 }
566 current = 0;
567 currentSize = 0;
568 }
569 }
570
571 if (!error) {
572 double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec));
573 if (seenZ)
574 t -= offset * offsetSign * 60 * 1000;
575 else if (seenT) // No zone specified, treat date-time as local time
576 t = UTC(t, localTZA);
577 // else: treat plain date as already in UTC
578 return TimeClip(t);
579 }
580
581 QDateTime dt = QDateTime::fromString(s, Qt::TextDate);
582 if (!dt.isValid())
583 dt = QDateTime::fromString(s, Qt::ISODate);
584 if (!dt.isValid())
585 dt = QDateTime::fromString(s, Qt::RFC2822Date);
586 if (!dt.isValid()) {
587 const QString formats[] = {
588 QStringLiteral("M/d/yyyy"),
589 QStringLiteral("M/d/yyyy hh:mm"),
590 QStringLiteral("M/d/yyyy hh:mm A"),
591
592 QStringLiteral("M/d/yyyy, hh:mm"),
593 QStringLiteral("M/d/yyyy, hh:mm A"),
594
595 QStringLiteral("MMM d yyyy"),
596 QStringLiteral("MMM d yyyy hh:mm"),
597 QStringLiteral("MMM d yyyy hh:mm:ss"),
598 QStringLiteral("MMM d yyyy, hh:mm"),
599 QStringLiteral("MMM d yyyy, hh:mm:ss"),
600
601 QStringLiteral("MMMM d yyyy"),
602 QStringLiteral("MMMM d yyyy hh:mm"),
603 QStringLiteral("MMMM d yyyy hh:mm:ss"),
604 QStringLiteral("MMMM d yyyy, hh:mm"),
605 QStringLiteral("MMMM d yyyy, hh:mm:ss"),
606
607 QStringLiteral("MMM d, yyyy"),
608 QStringLiteral("MMM d, yyyy hh:mm"),
609 QStringLiteral("MMM d, yyyy hh:mm:ss"),
610
611 QStringLiteral("MMMM d, yyyy"),
612 QStringLiteral("MMMM d, yyyy hh:mm"),
613 QStringLiteral("MMMM d, yyyy hh:mm:ss"),
614
615 QStringLiteral("d MMM yyyy"),
616 QStringLiteral("d MMM yyyy hh:mm"),
617 QStringLiteral("d MMM yyyy hh:mm:ss"),
618 QStringLiteral("d MMM yyyy, hh:mm"),
619 QStringLiteral("d MMM yyyy, hh:mm:ss"),
620
621 QStringLiteral("d MMMM yyyy"),
622 QStringLiteral("d MMMM yyyy hh:mm"),
623 QStringLiteral("d MMMM yyyy hh:mm:ss"),
624 QStringLiteral("d MMMM yyyy, hh:mm"),
625 QStringLiteral("d MMMM yyyy, hh:mm:ss"),
626
627 QStringLiteral("d MMM, yyyy"),
628 QStringLiteral("d MMM, yyyy hh:mm"),
629 QStringLiteral("d MMM, yyyy hh:mm:ss"),
630
631 QStringLiteral("d MMMM, yyyy"),
632 QStringLiteral("d MMMM, yyyy hh:mm"),
633 QStringLiteral("d MMMM, yyyy hh:mm:ss"),
634 };
635
636 for (const QString &format : formats) {
637 dt = format.indexOf(QLatin1String("hh:mm")) < 0
638 ? QDateTime(QDate::fromString(s, format),
639 QTime(0, 0, 0), Qt::UTC)
640 : QDateTime::fromString(s, format); // as local time
641 if (dt.isValid())
642 break;
643 }
644 }
645 if (!dt.isValid())
646 return qt_qnan();
647 return TimeClip(dt.toMSecsSinceEpoch());
648 }
649
650 /*!
651 \internal
652
653 Converts the ECMA Date value \tt (in UTC form) to QDateTime
654 according to \a spec.
655 */
ToDateTime(double t,Qt::TimeSpec spec)656 static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec)
657 {
658 if (std::isnan(t))
659 return QDateTime();
660 return QDateTime::fromMSecsSinceEpoch(t, Qt::UTC).toTimeSpec(spec);
661 }
662
ToString(double t,double localTZA)663 static inline QString ToString(double t, double localTZA)
664 {
665 if (std::isnan(t))
666 return QStringLiteral("Invalid Date");
667 QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT");
668 double tzoffset = localTZA + DaylightSavingTA(t, localTZA);
669 if (tzoffset) {
670 int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60);
671 int mins = int(::fabs(tzoffset) / 1000 / 60) % 60;
672 str.append(QLatin1Char((tzoffset > 0) ? '+' : '-'));
673 if (hours < 10)
674 str.append(QLatin1Char('0'));
675 str.append(QString::number(hours));
676 if (mins < 10)
677 str.append(QLatin1Char('0'));
678 str.append(QString::number(mins));
679 }
680 return str;
681 }
682
ToUTCString(double t)683 static inline QString ToUTCString(double t)
684 {
685 if (std::isnan(t))
686 return QStringLiteral("Invalid Date");
687 return ToDateTime(t, Qt::UTC).toString();
688 }
689
ToDateString(double t)690 static inline QString ToDateString(double t)
691 {
692 return ToDateTime(t, Qt::LocalTime).date().toString();
693 }
694
ToTimeString(double t)695 static inline QString ToTimeString(double t)
696 {
697 return ToDateTime(t, Qt::LocalTime).time().toString();
698 }
699
ToLocaleString(double t)700 static inline QString ToLocaleString(double t)
701 {
702 return QLocale().toString(ToDateTime(t, Qt::LocalTime), QLocale::ShortFormat);
703 }
704
ToLocaleDateString(double t)705 static inline QString ToLocaleDateString(double t)
706 {
707 return QLocale().toString(ToDateTime(t, Qt::LocalTime).date(), QLocale::ShortFormat);
708 }
709
ToLocaleTimeString(double t)710 static inline QString ToLocaleTimeString(double t)
711 {
712 return QLocale().toString(ToDateTime(t, Qt::LocalTime).time(), QLocale::ShortFormat);
713 }
714
getLocalTZA()715 static double getLocalTZA()
716 {
717 #ifndef Q_OS_WIN
718 tzset();
719 #endif
720 #ifdef USE_QTZ_SYSTEM_ZONE
721 // TODO: QTimeZone::resetSystemTimeZone(), see QTBUG-56899 and comment above.
722 // Standard offset, with no daylight-savings adjustment, in ms:
723 return QTimeZone::systemTimeZone().standardTimeOffset(QDateTime::currentDateTime()) * 1e3;
724 #else
725 # ifdef Q_OS_WIN
726 TIME_ZONE_INFORMATION tzInfo;
727 GetTimeZoneInformation(&tzInfo);
728 return -tzInfo.Bias * 60.0 * 1000.0;
729 # else
730 struct tm t;
731 time_t curr;
732 time(&curr);
733 localtime_r(&curr, &t); // Wrong: includes DST offset
734 time_t locl = mktime(&t);
735 gmtime_r(&curr, &t);
736 time_t globl = mktime(&t);
737 return (double(locl) - double(globl)) * 1000.0;
738 # endif
739 #endif // USE_QTZ_SYSTEM_ZONE
740 }
741
742 DEFINE_OBJECT_VTABLE(DateObject);
743
init(const QDateTime & date)744 void Heap::DateObject::init(const QDateTime &date)
745 {
746 Object::init();
747 this->date = date.isValid() ? TimeClip(date.toMSecsSinceEpoch()) : qt_qnan();
748 }
749
init(const QTime & time)750 void Heap::DateObject::init(const QTime &time)
751 {
752 Object::init();
753 if (!time.isValid()) {
754 date = qt_qnan();
755 return;
756 }
757
758 /* We have to chose a date on which to instantiate this time. All we really
759 * care about is that it round-trips back to the same time if we extract the
760 * time from it, which shall (via toQDateTime(), below) discard the date
761 * part. We need a date for which time-zone data is likely to be sane (so
762 * MakeDay(0, 0, 0) was a bad choice; 2 BC, December 31st is before
763 * time-zones were standardized), with no transition nearby in date.
764 * QDateTime ignores DST transitions before 1970, but even then zone
765 * transitions did happen; and DaylightSavingTA() will include DST, at odds
766 * with QDateTime. So pick a date since 1970 and prefer one when no zone
767 * was in DST. One such interval (according to the Olson database, at
768 * least) was 1971 March 15th to April 17th. Since converting a time to a
769 * date-time without specifying a date is foolish, let's use April Fools'
770 * day.
771 */
772 static const double d = MakeDay(1971, 3, 1);
773 double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec());
774 date = TimeClip(UTC(MakeDate(d, t), internalClass->engine->localTZA));
775 }
776
toQDateTime() const777 QDateTime DateObject::toQDateTime() const
778 {
779 return ToDateTime(date(), Qt::LocalTime);
780 }
781
782 DEFINE_OBJECT_VTABLE(DateCtor);
783
init(QV4::ExecutionContext * scope)784 void Heap::DateCtor::init(QV4::ExecutionContext *scope)
785 {
786 Heap::FunctionObject::init(scope, QStringLiteral("Date"));
787 }
788
virtualCallAsConstructor(const FunctionObject * that,const Value * argv,int argc,const Value * newTarget)789 ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *newTarget)
790 {
791 ExecutionEngine *v4 = that->engine();
792 double t = 0;
793
794 if (argc == 0)
795 t = currentTime();
796
797 else if (argc == 1) {
798 Scope scope(v4);
799 ScopedValue arg(scope, argv[0]);
800 if (DateObject *d = arg->as<DateObject>()) {
801 t = d->date();
802 } else {
803 arg = RuntimeHelpers::toPrimitive(arg, PREFERREDTYPE_HINT);
804
805 if (String *s = arg->stringValue())
806 t = ParseString(s->toQString(), v4->localTZA);
807 else
808 t = TimeClip(arg->toNumber());
809 }
810 }
811
812 else { // d.argc > 1
813 double year = argv[0].toNumber();
814 double month = argv[1].toNumber();
815 double day = argc >= 3 ? argv[2].toNumber() : 1;
816 double hours = argc >= 4 ? argv[3].toNumber() : 0;
817 double mins = argc >= 5 ? argv[4].toNumber() : 0;
818 double secs = argc >= 6 ? argv[5].toNumber() : 0;
819 double ms = argc >= 7 ? argv[6].toNumber() : 0;
820 if (year >= 0 && year <= 99)
821 year += 1900;
822 t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms));
823 t = TimeClip(UTC(t, v4->localTZA));
824 }
825
826 ReturnedValue o = Encode(v4->newDateObject(Value::fromDouble(t)));
827 if (!newTarget)
828 return o;
829 Scope scope(v4);
830 ScopedObject obj(scope, o);
831 obj->setProtoFromNewTarget(newTarget);
832 return obj->asReturnedValue();
833 }
834
virtualCall(const FunctionObject * m,const Value *,const Value *,int)835 ReturnedValue DateCtor::virtualCall(const FunctionObject *m, const Value *, const Value *, int)
836 {
837 ExecutionEngine *e = m->engine();
838 double t = currentTime();
839 return e->newString(ToString(t, e->localTZA))->asReturnedValue();
840 }
841
init(ExecutionEngine * engine,Object * ctor)842 void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
843 {
844 Scope scope(engine);
845 ScopedObject o(scope);
846 ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
847 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(7));
848 engine->localTZA = getLocalTZA();
849
850 ctor->defineDefaultProperty(QStringLiteral("parse"), method_parse, 1);
851 ctor->defineDefaultProperty(QStringLiteral("UTC"), method_UTC, 7);
852 ctor->defineDefaultProperty(QStringLiteral("now"), method_now, 0);
853
854 defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
855 defineDefaultProperty(engine->id_toString(), method_toString, 0);
856 defineDefaultProperty(QStringLiteral("toDateString"), method_toDateString, 0);
857 defineDefaultProperty(QStringLiteral("toTimeString"), method_toTimeString, 0);
858 defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString, 0);
859 defineDefaultProperty(QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0);
860 defineDefaultProperty(QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0);
861 defineDefaultProperty(engine->id_valueOf(), method_valueOf, 0);
862 defineDefaultProperty(QStringLiteral("getTime"), method_getTime, 0);
863 defineDefaultProperty(QStringLiteral("getYear"), method_getYear, 0);
864 defineDefaultProperty(QStringLiteral("getFullYear"), method_getFullYear, 0);
865 defineDefaultProperty(QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0);
866 defineDefaultProperty(QStringLiteral("getMonth"), method_getMonth, 0);
867 defineDefaultProperty(QStringLiteral("getUTCMonth"), method_getUTCMonth, 0);
868 defineDefaultProperty(QStringLiteral("getDate"), method_getDate, 0);
869 defineDefaultProperty(QStringLiteral("getUTCDate"), method_getUTCDate, 0);
870 defineDefaultProperty(QStringLiteral("getDay"), method_getDay, 0);
871 defineDefaultProperty(QStringLiteral("getUTCDay"), method_getUTCDay, 0);
872 defineDefaultProperty(QStringLiteral("getHours"), method_getHours, 0);
873 defineDefaultProperty(QStringLiteral("getUTCHours"), method_getUTCHours, 0);
874 defineDefaultProperty(QStringLiteral("getMinutes"), method_getMinutes, 0);
875 defineDefaultProperty(QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0);
876 defineDefaultProperty(QStringLiteral("getSeconds"), method_getSeconds, 0);
877 defineDefaultProperty(QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0);
878 defineDefaultProperty(QStringLiteral("getMilliseconds"), method_getMilliseconds, 0);
879 defineDefaultProperty(QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0);
880 defineDefaultProperty(QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0);
881 defineDefaultProperty(QStringLiteral("setTime"), method_setTime, 1);
882 defineDefaultProperty(QStringLiteral("setMilliseconds"), method_setMilliseconds, 1);
883 defineDefaultProperty(QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1);
884 defineDefaultProperty(QStringLiteral("setSeconds"), method_setSeconds, 2);
885 defineDefaultProperty(QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2);
886 defineDefaultProperty(QStringLiteral("setMinutes"), method_setMinutes, 3);
887 defineDefaultProperty(QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3);
888 defineDefaultProperty(QStringLiteral("setHours"), method_setHours, 4);
889 defineDefaultProperty(QStringLiteral("setUTCHours"), method_setUTCHours, 4);
890 defineDefaultProperty(QStringLiteral("setDate"), method_setDate, 1);
891 defineDefaultProperty(QStringLiteral("setUTCDate"), method_setUTCDate, 1);
892 defineDefaultProperty(QStringLiteral("setMonth"), method_setMonth, 2);
893 defineDefaultProperty(QStringLiteral("setUTCMonth"), method_setUTCMonth, 2);
894 defineDefaultProperty(QStringLiteral("setYear"), method_setYear, 1);
895 defineDefaultProperty(QStringLiteral("setFullYear"), method_setFullYear, 3);
896 defineDefaultProperty(QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3);
897
898 // ES6: B.2.4.3 & 20.3.4.43:
899 // We have to use the *same object* for toUTCString and toGMTString
900 {
901 QString toUtcString(QStringLiteral("toUTCString"));
902 QString toGmtString(QStringLiteral("toGMTString"));
903 ScopedString us(scope, engine->newIdentifier(toUtcString));
904 ScopedString gs(scope, engine->newIdentifier(toGmtString));
905 ScopedFunctionObject toUtcGmtStringFn(scope, FunctionObject::createBuiltinFunction(engine, us, method_toUTCString, 0));
906 defineDefaultProperty(us, toUtcGmtStringFn);
907 defineDefaultProperty(gs, toUtcGmtStringFn);
908 }
909
910 defineDefaultProperty(QStringLiteral("toISOString"), method_toISOString, 0);
911 defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1);
912 defineDefaultProperty(engine->symbol_toPrimitive(), method_symbolToPrimitive, 1, Attr_ReadOnly_ButConfigurable);
913 }
914
getThisDate(ExecutionEngine * v4,const Value * thisObject)915 double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject)
916 {
917 if (const DateObject *that = thisObject->as<DateObject>())
918 return that->date();
919 v4->throwTypeError();
920 return 0;
921 }
922
method_parse(const FunctionObject * f,const Value *,const Value * argv,int argc)923 ReturnedValue DatePrototype::method_parse(const FunctionObject *f, const Value *, const Value *argv, int argc)
924 {
925 if (!argc)
926 return Encode(qt_qnan());
927 else
928 return Encode(ParseString(argv[0].toQString(), f->engine()->localTZA));
929 }
930
method_UTC(const FunctionObject * f,const Value *,const Value * argv,int argc)931 ReturnedValue DatePrototype::method_UTC(const FunctionObject *f, const Value *, const Value *argv, int argc)
932 {
933 const int numArgs = argc;
934 if (numArgs < 1)
935 return Encode(qQNaN());
936 ExecutionEngine *e = f->engine();
937 double year = argv[0].toNumber();
938 if (e->hasException)
939 return Encode::undefined();
940 double month = numArgs >= 2 ? argv[1].toNumber() : 0;
941 if (e->hasException)
942 return Encode::undefined();
943 double day = numArgs >= 3 ? argv[2].toNumber() : 1;
944 if (e->hasException)
945 return Encode::undefined();
946 double hours = numArgs >= 4 ? argv[3].toNumber() : 0;
947 if (e->hasException)
948 return Encode::undefined();
949 double mins = numArgs >= 5 ? argv[4].toNumber() : 0;
950 if (e->hasException)
951 return Encode::undefined();
952 double secs = numArgs >= 6 ? argv[5].toNumber() : 0;
953 if (e->hasException)
954 return Encode::undefined();
955 double ms = numArgs >= 7 ? argv[6].toNumber() : 0;
956 if (e->hasException)
957 return Encode::undefined();
958 double iyear = QV4::Value::toInteger(year);
959 if (!qIsNaN(year) && iyear >= 0 && iyear <= 99)
960 year = 1900 + iyear;
961 double t = MakeDate(MakeDay(year, month, day),
962 MakeTime(hours, mins, secs, ms));
963 return Encode(TimeClip(t));
964 }
965
method_now(const FunctionObject *,const Value *,const Value *,int)966 ReturnedValue DatePrototype::method_now(const FunctionObject *, const Value *, const Value *, int)
967 {
968 return Encode(currentTime());
969 }
970
method_toString(const FunctionObject * b,const Value * thisObject,const Value *,int)971 ReturnedValue DatePrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
972 {
973 ExecutionEngine *v4 = b->engine();
974 double t = getThisDate(v4, thisObject);
975 return Encode(v4->newString(ToString(t, v4->localTZA)));
976 }
977
method_toDateString(const FunctionObject * b,const Value * thisObject,const Value *,int)978 ReturnedValue DatePrototype::method_toDateString(const FunctionObject *b, const Value *thisObject, const Value *, int)
979 {
980 ExecutionEngine *v4 = b->engine();
981 double t = getThisDate(v4, thisObject);
982 return Encode(v4->newString(ToDateString(t)));
983 }
984
method_toTimeString(const FunctionObject * b,const Value * thisObject,const Value *,int)985 ReturnedValue DatePrototype::method_toTimeString(const FunctionObject *b, const Value *thisObject, const Value *, int)
986 {
987 ExecutionEngine *v4 = b->engine();
988 double t = getThisDate(v4, thisObject);
989 return Encode(v4->newString(ToTimeString(t)));
990 }
991
method_toLocaleString(const FunctionObject * b,const Value * thisObject,const Value *,int)992 ReturnedValue DatePrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int)
993 {
994 ExecutionEngine *v4 = b->engine();
995 double t = getThisDate(v4, thisObject);
996 return Encode(v4->newString(ToLocaleString(t)));
997 }
998
method_toLocaleDateString(const FunctionObject * b,const Value * thisObject,const Value *,int)999 ReturnedValue DatePrototype::method_toLocaleDateString(const FunctionObject *b, const Value *thisObject, const Value *, int)
1000 {
1001 ExecutionEngine *v4 = b->engine();
1002 double t = getThisDate(v4, thisObject);
1003 return Encode(v4->newString(ToLocaleDateString(t)));
1004 }
1005
method_toLocaleTimeString(const FunctionObject * b,const Value * thisObject,const Value *,int)1006 ReturnedValue DatePrototype::method_toLocaleTimeString(const FunctionObject *b, const Value *thisObject, const Value *, int)
1007 {
1008 ExecutionEngine *v4 = b->engine();
1009 double t = getThisDate(v4, thisObject);
1010 return Encode(v4->newString(ToLocaleTimeString(t)));
1011 }
1012
method_valueOf(const FunctionObject * b,const Value * thisObject,const Value *,int)1013 ReturnedValue DatePrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int)
1014 {
1015 ExecutionEngine *v4 = b->engine();
1016 double t = getThisDate(v4, thisObject);
1017 return Encode(t);
1018 }
1019
method_getTime(const FunctionObject * b,const Value * thisObject,const Value *,int)1020 ReturnedValue DatePrototype::method_getTime(const FunctionObject *b, const Value *thisObject, const Value *, int)
1021 {
1022 ExecutionEngine *v4 = b->engine();
1023 double t = getThisDate(v4, thisObject);
1024 return Encode(t);
1025 }
1026
method_getYear(const FunctionObject * b,const Value * thisObject,const Value *,int)1027 ReturnedValue DatePrototype::method_getYear(const FunctionObject *b, const Value *thisObject, const Value *, int)
1028 {
1029 ExecutionEngine *v4 = b->engine();
1030 double t = getThisDate(v4, thisObject);
1031 if (!std::isnan(t))
1032 t = YearFromTime(LocalTime(t, v4->localTZA)) - 1900;
1033 return Encode(t);
1034 }
1035
method_getFullYear(const FunctionObject * b,const Value * thisObject,const Value *,int)1036 ReturnedValue DatePrototype::method_getFullYear(const FunctionObject *b, const Value *thisObject, const Value *, int)
1037 {
1038 ExecutionEngine *v4 = b->engine();
1039 double t = getThisDate(v4, thisObject);
1040 if (!std::isnan(t))
1041 t = YearFromTime(LocalTime(t, v4->localTZA));
1042 return Encode(t);
1043 }
1044
method_getUTCFullYear(const FunctionObject * b,const Value * thisObject,const Value *,int)1045 ReturnedValue DatePrototype::method_getUTCFullYear(const FunctionObject *b, const Value *thisObject, const Value *, int)
1046 {
1047 ExecutionEngine *v4 = b->engine();
1048 double t = getThisDate(v4, thisObject);
1049 if (!std::isnan(t))
1050 t = YearFromTime(t);
1051 return Encode(t);
1052 }
1053
method_getMonth(const FunctionObject * b,const Value * thisObject,const Value *,int)1054 ReturnedValue DatePrototype::method_getMonth(const FunctionObject *b, const Value *thisObject, const Value *, int)
1055 {
1056 ExecutionEngine *v4 = b->engine();
1057 double t = getThisDate(v4, thisObject);
1058 if (!std::isnan(t))
1059 t = MonthFromTime(LocalTime(t, v4->localTZA));
1060 return Encode(t);
1061 }
1062
method_getUTCMonth(const FunctionObject * b,const Value * thisObject,const Value *,int)1063 ReturnedValue DatePrototype::method_getUTCMonth(const FunctionObject *b, const Value *thisObject, const Value *, int)
1064 {
1065 ExecutionEngine *v4 = b->engine();
1066 double t = getThisDate(v4, thisObject);
1067 if (!std::isnan(t))
1068 t = MonthFromTime(t);
1069 return Encode(t);
1070 }
1071
method_getDate(const FunctionObject * b,const Value * thisObject,const Value *,int)1072 ReturnedValue DatePrototype::method_getDate(const FunctionObject *b, const Value *thisObject, const Value *, int)
1073 {
1074 ExecutionEngine *v4 = b->engine();
1075 double t = getThisDate(v4, thisObject);
1076 if (!std::isnan(t))
1077 t = DateFromTime(LocalTime(t, v4->localTZA));
1078 return Encode(t);
1079 }
1080
method_getUTCDate(const FunctionObject * b,const Value * thisObject,const Value *,int)1081 ReturnedValue DatePrototype::method_getUTCDate(const FunctionObject *b, const Value *thisObject, const Value *, int)
1082 {
1083 ExecutionEngine *v4 = b->engine();
1084 double t = getThisDate(v4, thisObject);
1085 if (!std::isnan(t))
1086 t = DateFromTime(t);
1087 return Encode(t);
1088 }
1089
method_getDay(const FunctionObject * b,const Value * thisObject,const Value *,int)1090 ReturnedValue DatePrototype::method_getDay(const FunctionObject *b, const Value *thisObject, const Value *, int)
1091 {
1092 ExecutionEngine *v4 = b->engine();
1093 double t = getThisDate(v4, thisObject);
1094 if (!std::isnan(t))
1095 t = WeekDay(LocalTime(t, v4->localTZA));
1096 return Encode(t);
1097 }
1098
method_getUTCDay(const FunctionObject * b,const Value * thisObject,const Value *,int)1099 ReturnedValue DatePrototype::method_getUTCDay(const FunctionObject *b, const Value *thisObject, const Value *, int)
1100 {
1101 ExecutionEngine *v4 = b->engine();
1102 double t = getThisDate(v4, thisObject);
1103 if (!std::isnan(t))
1104 t = WeekDay(t);
1105 return Encode(t);
1106 }
1107
method_getHours(const FunctionObject * b,const Value * thisObject,const Value *,int)1108 ReturnedValue DatePrototype::method_getHours(const FunctionObject *b, const Value *thisObject, const Value *, int)
1109 {
1110 ExecutionEngine *v4 = b->engine();
1111 double t = getThisDate(v4, thisObject);
1112 if (!std::isnan(t))
1113 t = HourFromTime(LocalTime(t, v4->localTZA));
1114 return Encode(t);
1115 }
1116
method_getUTCHours(const FunctionObject * b,const Value * thisObject,const Value *,int)1117 ReturnedValue DatePrototype::method_getUTCHours(const FunctionObject *b, const Value *thisObject, const Value *, int)
1118 {
1119 ExecutionEngine *v4 = b->engine();
1120 double t = getThisDate(v4, thisObject);
1121 if (!std::isnan(t))
1122 t = HourFromTime(t);
1123 return Encode(t);
1124 }
1125
method_getMinutes(const FunctionObject * b,const Value * thisObject,const Value *,int)1126 ReturnedValue DatePrototype::method_getMinutes(const FunctionObject *b, const Value *thisObject, const Value *, int)
1127 {
1128 ExecutionEngine *v4 = b->engine();
1129 double t = getThisDate(v4, thisObject);
1130 if (!std::isnan(t))
1131 t = MinFromTime(LocalTime(t, v4->localTZA));
1132 return Encode(t);
1133 }
1134
method_getUTCMinutes(const FunctionObject * b,const Value * thisObject,const Value *,int)1135 ReturnedValue DatePrototype::method_getUTCMinutes(const FunctionObject *b, const Value *thisObject, const Value *, int)
1136 {
1137 ExecutionEngine *v4 = b->engine();
1138 double t = getThisDate(v4, thisObject);
1139 if (!std::isnan(t))
1140 t = MinFromTime(t);
1141 return Encode(t);
1142 }
1143
method_getSeconds(const FunctionObject * b,const Value * thisObject,const Value *,int)1144 ReturnedValue DatePrototype::method_getSeconds(const FunctionObject *b, const Value *thisObject, const Value *, int)
1145 {
1146 ExecutionEngine *v4 = b->engine();
1147 double t = getThisDate(v4, thisObject);
1148 if (!std::isnan(t))
1149 t = SecFromTime(LocalTime(t, v4->localTZA));
1150 return Encode(t);
1151 }
1152
method_getUTCSeconds(const FunctionObject * b,const Value * thisObject,const Value *,int)1153 ReturnedValue DatePrototype::method_getUTCSeconds(const FunctionObject *b, const Value *thisObject, const Value *, int)
1154 {
1155 ExecutionEngine *v4 = b->engine();
1156 double t = getThisDate(v4, thisObject);
1157 if (!std::isnan(t))
1158 t = SecFromTime(t);
1159 return Encode(t);
1160 }
1161
method_getMilliseconds(const FunctionObject * b,const Value * thisObject,const Value *,int)1162 ReturnedValue DatePrototype::method_getMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *, int)
1163 {
1164 ExecutionEngine *v4 = b->engine();
1165 double t = getThisDate(v4, thisObject);
1166 if (!std::isnan(t))
1167 t = msFromTime(LocalTime(t, v4->localTZA));
1168 return Encode(t);
1169 }
1170
method_getUTCMilliseconds(const FunctionObject * b,const Value * thisObject,const Value *,int)1171 ReturnedValue DatePrototype::method_getUTCMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *, int)
1172 {
1173 ExecutionEngine *v4 = b->engine();
1174 double t = getThisDate(v4, thisObject);
1175 if (!std::isnan(t))
1176 t = msFromTime(t);
1177 return Encode(t);
1178 }
1179
method_getTimezoneOffset(const FunctionObject * b,const Value * thisObject,const Value *,int)1180 ReturnedValue DatePrototype::method_getTimezoneOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
1181 {
1182 ExecutionEngine *v4 = b->engine();
1183 double t = getThisDate(v4, thisObject);
1184 if (!std::isnan(t))
1185 t = (t - LocalTime(t, v4->localTZA)) / msPerMinute;
1186 return Encode(t);
1187 }
1188
method_setTime(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1189 ReturnedValue DatePrototype::method_setTime(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1190 {
1191 ExecutionEngine *v4 = b->engine();
1192 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1193 if (!self)
1194 return v4->throwTypeError();
1195
1196 double t = argc ? argv[0].toNumber() : qt_qnan();
1197 if (v4->hasException)
1198 return QV4::Encode::undefined();
1199 self->setDate(TimeClip(t));
1200 return Encode(self->date());
1201 }
1202
method_setMilliseconds(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1203 ReturnedValue DatePrototype::method_setMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1204 {
1205 ExecutionEngine *v4 = b->engine();
1206 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1207 if (!self)
1208 return v4->throwTypeError();
1209
1210 double t = LocalTime(self->date(), v4->localTZA);
1211 if (v4->hasException)
1212 return QV4::Encode::undefined();
1213 double ms = argc ? argv[0].toNumber() : qt_qnan();
1214 if (v4->hasException)
1215 return QV4::Encode::undefined();
1216 self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)), v4->localTZA)));
1217 return Encode(self->date());
1218 }
1219
method_setUTCMilliseconds(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1220 ReturnedValue DatePrototype::method_setUTCMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1221 {
1222 ExecutionEngine *v4 = b->engine();
1223 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1224 if (!self)
1225 return v4->throwTypeError();
1226
1227 double t = self->date();
1228 if (v4->hasException)
1229 return QV4::Encode::undefined();
1230 double ms = argc ? argv[0].toNumber() : qt_qnan();
1231 if (v4->hasException)
1232 return QV4::Encode::undefined();
1233 self->setDate(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))));
1234 return Encode(self->date());
1235 }
1236
method_setSeconds(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1237 ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1238 {
1239 ExecutionEngine *v4 = b->engine();
1240 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1241 if (!self)
1242 return v4->throwTypeError();
1243
1244 double t = LocalTime(self->date(), v4->localTZA);
1245 if (v4->hasException)
1246 return QV4::Encode::undefined();
1247 double sec = argc ? argv[0].toNumber() : qt_qnan();
1248 if (v4->hasException)
1249 return QV4::Encode::undefined();
1250 double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber();
1251 if (v4->hasException)
1252 return QV4::Encode::undefined();
1253 t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)), v4->localTZA));
1254 self->setDate(t);
1255 return Encode(self->date());
1256 }
1257
method_setUTCSeconds(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1258 ReturnedValue DatePrototype::method_setUTCSeconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1259 {
1260 ExecutionEngine *v4 = b->engine();
1261 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1262 if (!self)
1263 return v4->throwTypeError();
1264
1265 double t = self->date();
1266 double sec = argc ? argv[0].toNumber() : qt_qnan();
1267 double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber();
1268 t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)));
1269 self->setDate(t);
1270 return Encode(self->date());
1271 }
1272
method_setMinutes(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1273 ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1274 {
1275 ExecutionEngine *v4 = b->engine();
1276 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1277 if (!self)
1278 return v4->throwTypeError();
1279
1280 double t = LocalTime(self->date(), v4->localTZA);
1281 if (v4->hasException)
1282 return QV4::Encode::undefined();
1283 double min = argc ? argv[0].toNumber() : qt_qnan();
1284 if (v4->hasException)
1285 return QV4::Encode::undefined();
1286 double sec = (argc < 2) ? SecFromTime(t) : argv[1].toNumber();
1287 if (v4->hasException)
1288 return QV4::Encode::undefined();
1289 double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber();
1290 if (v4->hasException)
1291 return QV4::Encode::undefined();
1292 t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)), v4->localTZA));
1293 self->setDate(t);
1294 return Encode(self->date());
1295 }
1296
method_setUTCMinutes(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1297 ReturnedValue DatePrototype::method_setUTCMinutes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1298 {
1299 ExecutionEngine *v4 = b->engine();
1300 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1301 if (!self)
1302 return v4->throwTypeError();
1303
1304 double t = self->date();
1305 double min = argc ? argv[0].toNumber() : qt_qnan();
1306 double sec = (argc < 2) ? SecFromTime(t) : argv[1].toNumber();
1307 double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber();
1308 t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)));
1309 self->setDate(t);
1310 return Encode(self->date());
1311 }
1312
method_setHours(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1313 ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1314 {
1315 ExecutionEngine *v4 = b->engine();
1316 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1317 if (!self)
1318 return v4->throwTypeError();
1319
1320 double t = LocalTime(self->date(), v4->localTZA);
1321 if (v4->hasException)
1322 return QV4::Encode::undefined();
1323 double hour = argc ? argv[0].toNumber() : qt_qnan();
1324 if (v4->hasException)
1325 return QV4::Encode::undefined();
1326 double min = (argc < 2) ? MinFromTime(t) : argv[1].toNumber();
1327 if (v4->hasException)
1328 return QV4::Encode::undefined();
1329 double sec = (argc < 3) ? SecFromTime(t) : argv[2].toNumber();
1330 if (v4->hasException)
1331 return QV4::Encode::undefined();
1332 double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber();
1333 if (v4->hasException)
1334 return QV4::Encode::undefined();
1335 t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)), v4->localTZA));
1336 self->setDate(t);
1337 return Encode(self->date());
1338 }
1339
method_setUTCHours(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1340 ReturnedValue DatePrototype::method_setUTCHours(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1341 {
1342 ExecutionEngine *v4 = b->engine();
1343 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1344 if (!self)
1345 return v4->throwTypeError();
1346
1347 double t = self->date();
1348 double hour = argc ? argv[0].toNumber() : qt_qnan();
1349 double min = (argc < 2) ? MinFromTime(t) : argv[1].toNumber();
1350 double sec = (argc < 3) ? SecFromTime(t) : argv[2].toNumber();
1351 double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber();
1352 t = TimeClip(MakeDate(Day(t), MakeTime(hour, min, sec, ms)));
1353 self->setDate(t);
1354 return Encode(self->date());
1355 }
1356
method_setDate(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1357 ReturnedValue DatePrototype::method_setDate(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1358 {
1359 ExecutionEngine *v4 = b->engine();
1360 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1361 if (!self)
1362 return v4->throwTypeError();
1363
1364 double t = LocalTime(self->date(), v4->localTZA);
1365 if (v4->hasException)
1366 return QV4::Encode::undefined();
1367 double date = argc ? argv[0].toNumber() : qt_qnan();
1368 if (v4->hasException)
1369 return QV4::Encode::undefined();
1370 t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)), v4->localTZA));
1371 self->setDate(t);
1372 return Encode(self->date());
1373 }
1374
method_setUTCDate(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1375 ReturnedValue DatePrototype::method_setUTCDate(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1376 {
1377 ExecutionEngine *v4 = b->engine();
1378 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1379 if (!self)
1380 return v4->throwTypeError();
1381
1382 double t = self->date();
1383 if (v4->hasException)
1384 return QV4::Encode::undefined();
1385 double date = argc ? argv[0].toNumber() : qt_qnan();
1386 if (v4->hasException)
1387 return QV4::Encode::undefined();
1388 t = TimeClip(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)));
1389 self->setDate(t);
1390 return Encode(self->date());
1391 }
1392
method_setMonth(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1393 ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1394 {
1395 ExecutionEngine *v4 = b->engine();
1396 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1397 if (!self)
1398 return v4->throwTypeError();
1399
1400 double t = LocalTime(self->date(), v4->localTZA);
1401 if (v4->hasException)
1402 return QV4::Encode::undefined();
1403 double month = argc ? argv[0].toNumber() : qt_qnan();
1404 if (v4->hasException)
1405 return QV4::Encode::undefined();
1406 double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber();
1407 if (v4->hasException)
1408 return QV4::Encode::undefined();
1409 t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)), v4->localTZA));
1410 self->setDate(t);
1411 return Encode(self->date());
1412 }
1413
method_setUTCMonth(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1414 ReturnedValue DatePrototype::method_setUTCMonth(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1415 {
1416 ExecutionEngine *v4 = b->engine();
1417 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1418 if (!self)
1419 return v4->throwTypeError();
1420
1421 double t = self->date();
1422 double month = argc ? argv[0].toNumber() : qt_qnan();
1423 double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber();
1424 t = TimeClip(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)));
1425 self->setDate(t);
1426 return Encode(self->date());
1427 }
1428
method_setYear(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1429 ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1430 {
1431 ExecutionEngine *v4 = b->engine();
1432 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1433 if (!self)
1434 return v4->throwTypeError();
1435
1436 double t = self->date();
1437 if (std::isnan(t))
1438 t = 0;
1439 else
1440 t = LocalTime(t, v4->localTZA);
1441 double year = argc ? argv[0].toNumber() : qt_qnan();
1442 double r;
1443 if (std::isnan(year)) {
1444 r = qt_qnan();
1445 } else {
1446 if ((QV4::Value::toInteger(year) >= 0) && (QV4::Value::toInteger(year) <= 99))
1447 year += 1900;
1448 r = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1449 r = UTC(MakeDate(r, TimeWithinDay(t)), v4->localTZA);
1450 r = TimeClip(r);
1451 }
1452 self->setDate(r);
1453 return Encode(self->date());
1454 }
1455
method_setUTCFullYear(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1456 ReturnedValue DatePrototype::method_setUTCFullYear(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1457 {
1458 ExecutionEngine *v4 = b->engine();
1459 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1460 if (!self)
1461 return v4->throwTypeError();
1462
1463 double t = self->date();
1464 double year = argc ? argv[0].toNumber() : qt_qnan();
1465 double month = (argc < 2) ? MonthFromTime(t) : argv[1].toNumber();
1466 double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber();
1467 t = TimeClip(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)));
1468 self->setDate(t);
1469 return Encode(self->date());
1470 }
1471
method_setFullYear(const FunctionObject * b,const Value * thisObject,const Value * argv,int argc)1472 ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1473 {
1474 ExecutionEngine *v4 = b->engine();
1475 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1476 if (!self)
1477 return v4->throwTypeError();
1478
1479 double t = LocalTime(self->date(), v4->localTZA);
1480 if (v4->hasException)
1481 return QV4::Encode::undefined();
1482 if (std::isnan(t))
1483 t = 0;
1484 double year = argc ? argv[0].toNumber() : qt_qnan();
1485 if (v4->hasException)
1486 return QV4::Encode::undefined();
1487 double month = (argc < 2) ? MonthFromTime(t) : argv[1].toNumber();
1488 if (v4->hasException)
1489 return QV4::Encode::undefined();
1490 double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber();
1491 if (v4->hasException)
1492 return QV4::Encode::undefined();
1493 t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)), v4->localTZA));
1494 self->setDate(t);
1495 return Encode(self->date());
1496 }
1497
method_toUTCString(const FunctionObject * b,const Value * thisObject,const Value *,int)1498 ReturnedValue DatePrototype::method_toUTCString(const FunctionObject *b, const Value *thisObject, const Value *, int)
1499 {
1500 ExecutionEngine *v4 = b->engine();
1501 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1502 if (!self)
1503 return v4->throwTypeError();
1504
1505 double t = self->date();
1506 return Encode(v4->newString(ToUTCString(t)));
1507 }
1508
addZeroPrefixedInt(QString & str,int num,int nDigits)1509 static void addZeroPrefixedInt(QString &str, int num, int nDigits)
1510 {
1511 str.resize(str.size() + nDigits);
1512
1513 QChar *c = str.data() + str.size() - 1;
1514 while (nDigits) {
1515 *c = QChar(num % 10 + '0');
1516 num /= 10;
1517 --c;
1518 --nDigits;
1519 }
1520 }
1521
method_toISOString(const FunctionObject * b,const Value * thisObject,const Value *,int)1522 ReturnedValue DatePrototype::method_toISOString(const FunctionObject *b, const Value *thisObject, const Value *, int)
1523 {
1524 ExecutionEngine *v4 = b->engine();
1525 DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
1526 if (!self)
1527 return v4->throwTypeError();
1528
1529 double t = self->date();
1530 if (!std::isfinite(t))
1531 RETURN_RESULT(v4->throwRangeError(*thisObject));
1532
1533 QString result;
1534 int year = (int)YearFromTime(t);
1535 if (year < 0 || year > 9999) {
1536 if (qAbs(year) >= 1000000)
1537 RETURN_RESULT(v4->throwRangeError(*thisObject));
1538 result += year < 0 ? QLatin1Char('-') : QLatin1Char('+');
1539 year = qAbs(year);
1540 addZeroPrefixedInt(result, year, 6);
1541 } else {
1542 addZeroPrefixedInt(result, year, 4);
1543 }
1544 result += QLatin1Char('-');
1545 addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2);
1546 result += QLatin1Char('-');
1547 addZeroPrefixedInt(result, (int)DateFromTime(t), 2);
1548 result += QLatin1Char('T');
1549 addZeroPrefixedInt(result, HourFromTime(t), 2);
1550 result += QLatin1Char(':');
1551 addZeroPrefixedInt(result, MinFromTime(t), 2);
1552 result += QLatin1Char(':');
1553 addZeroPrefixedInt(result, SecFromTime(t), 2);
1554 result += QLatin1Char('.');
1555 addZeroPrefixedInt(result, msFromTime(t), 3);
1556 result += QLatin1Char('Z');
1557
1558 return Encode(v4->newString(result));
1559 }
1560
method_toJSON(const FunctionObject * b,const Value * thisObject,const Value *,int)1561 ReturnedValue DatePrototype::method_toJSON(const FunctionObject *b, const Value *thisObject, const Value *, int)
1562 {
1563 ExecutionEngine *v4 = b->engine();
1564 Scope scope(v4);
1565 ScopedObject O(scope, thisObject->toObject(v4));
1566 if (v4->hasException)
1567 return QV4::Encode::undefined();
1568
1569 ScopedValue tv(scope, RuntimeHelpers::toPrimitive(O, NUMBER_HINT));
1570
1571 if (tv->isNumber() && !std::isfinite(tv->toNumber()))
1572 return Encode::null();
1573
1574 ScopedString s(scope, v4->newString(QStringLiteral("toISOString")));
1575 ScopedValue v(scope, O->get(s));
1576 FunctionObject *toIso = v->as<FunctionObject>();
1577
1578 if (!toIso)
1579 return v4->throwTypeError();
1580
1581 return checkedResult(v4, toIso->call(O, nullptr, 0));
1582 }
1583
method_symbolToPrimitive(const FunctionObject * f,const Value * thisObject,const Value * argv,int argc)1584 ReturnedValue DatePrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
1585 {
1586 ExecutionEngine *e = f->engine();
1587 if (!thisObject->isObject() || !argc || !argv->isString())
1588 return e->throwTypeError();
1589
1590 String *hint = argv->stringValue();
1591 PropertyKey id = hint->toPropertyKey();
1592 if (id == e->id_default()->propertyKey())
1593 hint = e->id_string();
1594 else if (id != e->id_string()->propertyKey() && id != e->id_number()->propertyKey())
1595 return e->throwTypeError();
1596
1597 return RuntimeHelpers::ordinaryToPrimitive(e, static_cast<const Object *>(thisObject), hint);
1598 }
1599
timezoneUpdated(ExecutionEngine * e)1600 void DatePrototype::timezoneUpdated(ExecutionEngine *e)
1601 {
1602 e->localTZA = getLocalTZA();
1603 }
1604