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