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

source code of qtdeclarative/src/qml/jsruntime/qv4dateobject.cpp