QtBase  v6.3.1
qdatetime.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2022 The Qt Company Ltd.
4 ** Copyright (C) 2021 Intel Corporation.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qplatformdefs.h"
42 #include "qdatetime.h"
43 
44 #include "qcalendar.h"
45 #include "qdatastream.h"
46 #include "qdebug.h"
47 #include "qset.h"
48 #include "qlocale.h"
49 
50 #include "private/qcalendarmath_p.h"
51 #include "private/qdatetime_p.h"
52 #if QT_CONFIG(datetimeparser)
53 #include "private/qdatetimeparser_p.h"
54 #endif
55 #ifdef Q_OS_DARWIN
56 #include "private/qcore_mac_p.h"
57 #endif
58 #include "private/qgregoriancalendar_p.h"
59 #include "private/qnumeric_p.h"
60 #include "private/qstringiterator_p.h"
61 #if QT_CONFIG(timezone)
62 #include "private/qtimezoneprivate_p.h"
63 #endif
64 
65 #include <cmath>
66 #ifdef Q_OS_WIN
67 # include <qt_windows.h>
68 #endif
69 #include <time.h>
70 
72 
73 /*****************************************************************************
74  Date/Time Constants
75  *****************************************************************************/
76 
77 constexpr qint64 SECS_PER_DAY = 86400;
78 constexpr qint64 MSECS_PER_DAY = 86400000;
79 constexpr qint64 SECS_PER_HOUR = 3600;
80 constexpr qint64 MSECS_PER_HOUR = 3600000;
81 constexpr qint64 SECS_PER_MIN = 60;
82 constexpr qint64 MSECS_PER_MIN = 60000;
83 constexpr qint64 MSECS_PER_SEC = 1000;
84 constexpr qint64 TIME_T_MAX = std::numeric_limits<time_t>::max();
85 constexpr qint64 JULIAN_DAY_FOR_EPOCH = 2440588; // result of julianDayFromDate(1970, 1, 1)
86 
87 /*****************************************************************************
88  QDate static helper functions
89  *****************************************************************************/
90 
91 static inline QDate fixedDate(QCalendar::YearMonthDay &&parts, QCalendar cal)
92 {
93  if ((parts.year < 0 && !cal.isProleptic()) || (parts.year == 0 && !cal.hasYearZero()))
94  return QDate();
95 
96  parts.day = qMin(parts.day, cal.daysInMonth(parts.month, parts.year));
97  return cal.dateFromParts(parts);
98 }
99 
100 static inline QDate fixedDate(QCalendar::YearMonthDay &&parts)
101 {
102  if (parts.year) {
103  parts.day = qMin(parts.day, QGregorianCalendar::monthLength(parts.month, parts.year));
104  qint64 jd;
105  if (QGregorianCalendar::julianFromParts(parts.year, parts.month, parts.day, &jd))
106  return QDate::fromJulianDay(jd);
107  }
108  return QDate();
109 }
110 
111 /*****************************************************************************
112  Date/Time formatting helper functions
113  *****************************************************************************/
114 
115 #if QT_CONFIG(textdate)
116 static const char qt_shortMonthNames[][4] = {
117  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
118  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
119 };
120 
121 static int fromShortMonthName(QStringView monthName)
122 {
123  for (unsigned int i = 0; i < sizeof(qt_shortMonthNames) / sizeof(qt_shortMonthNames[0]); ++i) {
124  if (monthName == QLatin1String(qt_shortMonthNames[i], 3))
125  return i + 1;
126  }
127  return -1;
128 }
129 #endif // textdate
130 
131 #if QT_CONFIG(datestring) // depends on, so implies, textdate
132 struct ParsedRfcDateTime {
133  QDate date;
134  QTime time;
135  int utcOffset;
136 };
137 
138 static int shortDayFromName(QStringView name)
139 {
140  const char16_t shortDayNames[] = u"MonTueWedThuFriSatSun";
141  for (int i = 0; i < 7; i++) {
142  if (name == QStringView(shortDayNames + 3 * i, 3))
143  return i + 1;
144  }
145  return 0;
146 }
147 
148 static ParsedRfcDateTime rfcDateImpl(QStringView s)
149 {
150  // Matches "[ddd,] dd MMM yyyy[ hh:mm[:ss]] [±hhmm]" - correct RFC 822, 2822, 5322 format -
151  // or "ddd MMM dd[ hh:mm:ss] yyyy [±hhmm]" - permissive RFC 850, 1036 (read only)
152  ParsedRfcDateTime result;
153 
155 
156  auto tokens = s.tokenize(u' ', Qt::SkipEmptyParts);
157  auto it = tokens.begin();
158  for (int i = 0; i < 6 && it != tokens.end(); ++i, ++it)
159  words.emplace_back(*it);
160 
161  if (words.size() < 3 || it != tokens.end())
162  return result;
163  const QChar colon(u':');
164  bool ok = true;
165  QDate date;
166 
167  const auto isShortName = [](QStringView name) {
168  return (name.length() == 3 && name[0].isUpper()
169  && name[1].isLower() && name[2].isLower());
170  };
171 
172  /* Reject entirely (return) if the string is malformed; however, if the date
173  * is merely invalid, (break, so as to) go on to parsing of the time.
174  */
175  int yearIndex;
176  do { // "loop" so that we can use break on merely invalid, but "right shape" date.
177  QStringView dayName;
178  bool rfcX22 = true;
179  const QStringView maybeDayName = words.front();
180  if (maybeDayName.endsWith(u',')) {
181  dayName = maybeDayName.chopped(1);
182  words.erase(words.begin());
183  } else if (!maybeDayName.front().isDigit()) {
184  dayName = maybeDayName;
185  words.erase(words.begin());
186  rfcX22 = false;
187  } // else: dayName is not specified (so we can only be RFC *22)
188  if (words.size() < 3 || words.size() > 5)
189  return result;
190 
191  // Don't break before setting yearIndex.
192  int dayIndex, monthIndex;
193  if (rfcX22) {
194  // dd MMM yyyy [hh:mm[:ss]] [±hhmm]
195  dayIndex = 0;
196  monthIndex = 1;
197  yearIndex = 2;
198  } else {
199  // MMM dd[ hh:mm:ss] yyyy [±hhmm]
200  dayIndex = 1;
201  monthIndex = 0;
202  yearIndex = words.size() > 3 && words.at(2).contains(colon) ? 3 : 2;
203  }
204 
205  int dayOfWeek = 0;
206  if (!dayName.isEmpty()) {
207  if (!isShortName(dayName))
208  return result;
209  dayOfWeek = shortDayFromName(dayName);
210  if (!dayOfWeek)
211  break;
212  }
213 
214  const int day = words.at(dayIndex).toInt(&ok);
215  if (!ok)
216  return result;
217  const int year = words.at(yearIndex).toInt(&ok);
218  if (!ok)
219  return result;
220  const QStringView monthName = words.at(monthIndex);
221  if (!isShortName(monthName))
222  return result;
223  int month = fromShortMonthName(monthName);
224  if (month < 0)
225  break;
226 
227  date = QDate(year, month, day);
228  if (dayOfWeek && date.dayOfWeek() != dayOfWeek)
229  date = QDate();
230  } while (false);
231  words.remove(yearIndex);
232  words.remove(0, 2); // month and day-of-month, in some order
233 
234  // Time: [hh:mm[:ss]]
235  QTime time;
236  if (words.size() && words.at(0).contains(colon)) {
237  const QStringView when = words.front();
238  words.erase(words.begin());
239  if (when.size() < 5 || when[2] != colon
240  || (when.size() == 8 ? when[5] != colon : when.size() > 5)) {
241  return result;
242  }
243  const int hour = when.first(2).toInt(&ok);
244  if (!ok)
245  return result;
246  const int minute = when.sliced(3, 2).toInt(&ok);
247  if (!ok)
248  return result;
249  const auto secs = when.size() == 8 ? when.last(2).toInt(&ok) : 0;
250  if (!ok)
251  return result;
252  time = QTime(hour, minute, secs);
253  }
254 
255  // Offset: [±hh[mm]]
256  int offset = 0;
257  if (words.size()) {
258  const QStringView zone = words.front();
259  words.erase(words.begin());
260  if (words.size() || !(zone.size() == 3 || zone.size() == 5))
261  return result;
262  bool negate = false;
263  if (zone[0] == u'-')
264  negate = true;
265  else if (zone[0] != u'+')
266  return result;
267  const int hour = zone.sliced(1, 2).toInt(&ok);
268  if (!ok)
269  return result;
270  const auto minute = zone.size() == 5 ? zone.last(2).toInt(&ok) : 0;
271  if (!ok)
272  return result;
273  offset = (hour * 60 + minute) * 60;
274  if (negate)
275  offset = -offset;
276  }
277 
278  result.date = date;
279  result.time = time;
280  result.utcOffset = offset;
281  return result;
282 }
283 #endif // datestring
284 
285 // Return offset in [+-]HH:mm format
286 static QString toOffsetString(Qt::DateFormat format, int offset)
287 {
288  return QString::asprintf("%c%02d%s%02d",
289  offset >= 0 ? '+' : '-',
290  qAbs(offset) / int(SECS_PER_HOUR),
291  // Qt::ISODate puts : between the hours and minutes, but Qt:TextDate does not:
292  format == Qt::TextDate ? "" : ":",
293  (qAbs(offset) / 60) % 60);
294 }
295 
296 #if QT_CONFIG(datestring)
297 // Parse offset in [+-]HH[[:]mm] format
298 static int fromOffsetString(QStringView offsetString, bool *valid) noexcept
299 {
300  *valid = false;
301 
302  const int size = offsetString.size();
303  if (size < 2 || size > 6)
304  return 0;
305 
306  // sign will be +1 for a positive and -1 for a negative offset
307  int sign;
308 
309  // First char must be + or -
310  const QChar signChar = offsetString[0];
311  if (signChar == u'+')
312  sign = 1;
313  else if (signChar == u'-')
314  sign = -1;
315  else
316  return 0;
317 
318  // Split the hour and minute parts
319  const QStringView time = offsetString.sliced(1);
320  qsizetype hhLen = time.indexOf(u':');
321  qsizetype mmIndex;
322  if (hhLen == -1)
323  mmIndex = hhLen = 2; // [+-]HHmm or [+-]HH format
324  else
325  mmIndex = hhLen + 1;
326 
327  const QStringView hhRef = time.first(qMin(hhLen, time.size()));
328  bool ok = false;
329  const int hour = hhRef.toInt(&ok);
330  if (!ok || hour > 23) // More generous than QTimeZone::MaxUtcOffsetSecs
331  return 0;
332 
333  const QStringView mmRef = time.sliced(qMin(mmIndex, time.size()));
334  const int minute = mmRef.isEmpty() ? 0 : mmRef.toInt(&ok);
335  if (!ok || minute < 0 || minute > 59)
336  return 0;
337 
338  *valid = true;
339  return sign * ((hour * 60) + minute) * 60;
340 }
341 #endif // datestring
342 
343 /*****************************************************************************
344  QDate member functions
345  *****************************************************************************/
346 
438 QDate::QDate(int y, int m, int d)
439 {
441  jd = nullJd();
442 }
443 
444 QDate::QDate(int y, int m, int d, QCalendar cal)
445 {
446  *this = cal.dateFromParts(y, m, d);
447 }
448 
490 int QDate::year(QCalendar cal) const
491 {
492  if (isValid()) {
493  const auto parts = cal.partsFromDate(*this);
494  if (parts.isValid())
495  return parts.year;
496  }
497  return 0;
498 }
499 
504 int QDate::year() const
505 {
506  if (isValid()) {
507  const auto parts = QGregorianCalendar::partsFromJulian(jd);
508  if (parts.isValid())
509  return parts.year;
510  }
511  return 0;
512 }
513 
542 int QDate::month(QCalendar cal) const
543 {
544  if (isValid()) {
545  const auto parts = cal.partsFromDate(*this);
546  if (parts.isValid())
547  return parts.month;
548  }
549  return 0;
550 }
551 
556 int QDate::month() const
557 {
558  if (isValid()) {
559  const auto parts = QGregorianCalendar::partsFromJulian(jd);
560  if (parts.isValid())
561  return parts.month;
562  }
563  return 0;
564 }
565 
575 int QDate::day(QCalendar cal) const
576 {
577  if (isValid()) {
578  const auto parts = cal.partsFromDate(*this);
579  if (parts.isValid())
580  return parts.day;
581  }
582  return 0;
583 }
584 
589 int QDate::day() const
590 {
591  if (isValid()) {
592  const auto parts = QGregorianCalendar::partsFromJulian(jd);
593  if (parts.isValid())
594  return parts.day;
595  }
596  return 0;
597 }
598 
610 {
611  if (isNull())
612  return 0;
613 
614  return cal.dayOfWeek(*this);
615 }
616 
621 int QDate::dayOfWeek() const
622 {
623  return isValid() ? QGregorianCalendar::weekDayOfJulian(jd) : 0;
624 }
625 
636 {
637  if (isValid()) {
638  QDate firstDay = cal.dateFromParts(year(cal), 1, 1);
639  if (firstDay.isValid())
640  return firstDay.daysTo(*this) + 1;
641  }
642  return 0;
643 }
644 
649 int QDate::dayOfYear() const
650 {
651  if (isValid()) {
652  qint64 first;
654  return jd - first + 1;
655  }
656  return 0;
657 }
658 
670 {
671  if (isValid()) {
672  const auto parts = cal.partsFromDate(*this);
673  if (parts.isValid())
674  return cal.daysInMonth(parts.month, parts.year);
675  }
676  return 0;
677 }
678 
684 {
685  if (isValid()) {
686  const auto parts = QGregorianCalendar::partsFromJulian(jd);
687  if (parts.isValid())
688  return QGregorianCalendar::monthLength(parts.month, parts.year);
689  }
690  return 0;
691 }
692 
703 {
704  if (isNull())
705  return 0;
706 
707  return cal.daysInYear(year(cal));
708 }
709 
714 int QDate::daysInYear() const
715 {
716  return isValid() ? QGregorianCalendar::leapTest(year()) ? 366 : 365 : 0;
717 }
718 
738 int QDate::weekNumber(int *yearNumber) const
739 {
740  if (!isValid())
741  return 0;
742 
743  // This could be replaced by use of QIso8601Calendar, once we implement it.
744  // The Thursday of the same week determines our answer:
745  const QDate thursday(addDays(4 - dayOfWeek()));
746  if (yearNumber)
747  *yearNumber = thursday.year();
748 
749  // Week n's Thurs's DOY has 1 <= DOY - 7*(n-1) < 8, so 0 <= DOY + 6 - 7*n < 7:
750  return (thursday.dayOfYear() + 6) / 7;
751 }
752 
753 static bool inDateTimeRange(qint64 jd, bool start)
754 {
755  using Bounds = std::numeric_limits<qint64>;
756  if (jd < Bounds::min() + JULIAN_DAY_FOR_EPOCH)
757  return false;
758  jd -= JULIAN_DAY_FOR_EPOCH;
759  const qint64 maxDay = Bounds::max() / MSECS_PER_DAY;
760  const qint64 minDay = Bounds::min() / MSECS_PER_DAY - 1;
761  // (Divisions rounded towards zero, as MSECS_PER_DAY has factors other than two.)
762  // Range includes start of last day and end of first:
763  if (start)
764  return jd > minDay && jd <= maxDay;
765  return jd >= minDay && jd < maxDay;
766 }
767 
768 static QDateTime toEarliest(QDate day, const QDateTime &form)
769 {
770  const Qt::TimeSpec spec = form.timeSpec();
771  const int offset = (spec == Qt::OffsetFromUTC) ? form.offsetFromUtc() : 0;
772 #if QT_CONFIG(timezone)
773  QTimeZone zone;
774  if (spec == Qt::TimeZone)
775  zone = form.timeZone();
776 #endif
777  auto moment = [=](QTime time) {
778  switch (spec) {
779  case Qt::OffsetFromUTC: return QDateTime(day, time, spec, offset);
780 #if QT_CONFIG(timezone)
781  case Qt::TimeZone: return QDateTime(day, time, zone);
782 #endif
783  default: return QDateTime(day, time, spec);
784  }
785  };
786  // Longest routine time-zone transition is 2 hours:
787  QDateTime when = moment(QTime(2, 0));
788  if (!when.isValid()) {
789  // Noon should be safe ...
790  when = moment(QTime(12, 0));
791  if (!when.isValid()) {
792  // ... unless it's a 24-hour jump (moving the date-line)
793  when = moment(QTime(23, 59, 59, 999));
794  if (!when.isValid())
795  return QDateTime();
796  }
797  }
798  int high = when.time().msecsSinceStartOfDay() / 60000;
799  int low = 0;
800  // Binary chop to the right minute
801  while (high > low + 1) {
802  int mid = (high + low) / 2;
803  QDateTime probe = moment(QTime(mid / 60, mid % 60));
804  if (probe.isValid() && probe.date() == day) {
805  high = mid;
806  when = probe;
807  } else {
808  low = mid;
809  }
810  }
811  return when;
812 }
813 
840 QDateTime QDate::startOfDay(Qt::TimeSpec spec, int offsetSeconds) const
841 {
842  if (!inDateTimeRange(jd, true))
843  return QDateTime();
844 
845  switch (spec) {
846  case Qt::TimeZone: // should pass a QTimeZone instead of Qt::TimeZone
847  qWarning() << "Called QDate::startOfDay(Qt::TimeZone) on" << *this;
848  return QDateTime();
849  case Qt::OffsetFromUTC:
850  case Qt::UTC:
851  return QDateTime(*this, QTime(0, 0), spec, offsetSeconds);
852 
853  case Qt::LocalTime:
854  if (offsetSeconds)
855  qWarning("Ignoring offset (%d seconds) passed with Qt::LocalTime", offsetSeconds);
856  break;
857  }
858  QDateTime when(*this, QTime(0, 0), spec);
859  if (!when.isValid())
860  when = toEarliest(*this, when);
861 
862  return when.isValid() ? when : QDateTime();
863 }
864 
865 #if QT_CONFIG(timezone)
870 QDateTime QDate::startOfDay(const QTimeZone &zone) const
871 {
872  if (!inDateTimeRange(jd, true) || !zone.isValid())
873  return QDateTime();
874 
875  QDateTime when(*this, QTime(0, 0), zone);
876  if (when.isValid())
877  return when;
878 
879  // The start of the day must have fallen in a spring-forward's gap; find the spring-forward:
880  if (zone.hasTransitions()) {
882  // There's unlikely to be another transition before noon tomorrow.
883  // However, the whole of today may have been skipped !
884  = zone.previousTransition(QDateTime(addDays(1), QTime(12, 0), zone));
885  const QDateTime &at = tran.atUtc.toTimeZone(zone);
886  if (at.isValid() && at.date() == *this)
887  return at;
888  }
889 
890  when = toEarliest(*this, when);
891  return when.isValid() ? when : QDateTime();
892 }
893 #endif // timezone
894 
895 static QDateTime toLatest(QDate day, const QDateTime &form)
896 {
897  const Qt::TimeSpec spec = form.timeSpec();
898  const int offset = (spec == Qt::OffsetFromUTC) ? form.offsetFromUtc() : 0;
899 #if QT_CONFIG(timezone)
900  QTimeZone zone;
901  if (spec == Qt::TimeZone)
902  zone = form.timeZone();
903 #endif
904  auto moment = [=](QTime time) {
905  switch (spec) {
906  case Qt::OffsetFromUTC: return QDateTime(day, time, spec, offset);
907 #if QT_CONFIG(timezone)
908  case Qt::TimeZone: return QDateTime(day, time, zone);
909 #endif
910  default: return QDateTime(day, time, spec);
911  }
912  };
913  // Longest routine time-zone transition is 2 hours:
914  QDateTime when = moment(QTime(21, 59, 59, 999));
915  if (!when.isValid()) {
916  // Noon should be safe ...
917  when = moment(QTime(12, 0));
918  if (!when.isValid()) {
919  // ... unless it's a 24-hour jump (moving the date-line)
920  when = moment(QTime(0, 0));
921  if (!when.isValid())
922  return QDateTime();
923  }
924  }
925  int high = 24 * 60;
926  int low = when.time().msecsSinceStartOfDay() / 60000;
927  // Binary chop to the right minute
928  while (high > low + 1) {
929  int mid = (high + low) / 2;
930  QDateTime probe = moment(QTime(mid / 60, mid % 60, 59, 999));
931  if (probe.isValid() && probe.date() == day) {
932  low = mid;
933  when = probe;
934  } else {
935  high = mid;
936  }
937  }
938  return when;
939 }
940 
967 QDateTime QDate::endOfDay(Qt::TimeSpec spec, int offsetSeconds) const
968 {
969  if (!inDateTimeRange(jd, false))
970  return QDateTime();
971 
972  switch (spec) {
973  case Qt::TimeZone: // should pass a QTimeZone instead of Qt::TimeZone
974  qWarning() << "Called QDate::endOfDay(Qt::TimeZone) on" << *this;
975  return QDateTime();
976  case Qt::UTC:
977  case Qt::OffsetFromUTC:
978  return QDateTime(*this, QTime(23, 59, 59, 999), spec, offsetSeconds);
979 
980  case Qt::LocalTime:
981  if (offsetSeconds)
982  qWarning("Ignoring offset (%d seconds) passed with Qt::LocalTime", offsetSeconds);
983  break;
984  }
985  QDateTime when(*this, QTime(23, 59, 59, 999), spec);
986  if (!when.isValid())
987  when = toLatest(*this, when);
988  return when.isValid() ? when : QDateTime();
989 }
990 
991 #if QT_CONFIG(timezone)
996 QDateTime QDate::endOfDay(const QTimeZone &zone) const
997 {
998  if (!inDateTimeRange(jd, false) || !zone.isValid())
999  return QDateTime();
1000 
1001  QDateTime when(*this, QTime(23, 59, 59, 999), zone);
1002  if (when.isValid())
1003  return when;
1004 
1005  // The end of the day must have fallen in a spring-forward's gap; find the spring-forward:
1006  if (zone.hasTransitions()) {
1008  // It's unlikely there's been another transition since yesterday noon.
1009  // However, the whole of today may have been skipped !
1010  = zone.nextTransition(QDateTime(addDays(-1), QTime(12, 0), zone));
1011  const QDateTime &at = tran.atUtc.toTimeZone(zone);
1012  if (at.isValid() && at.date() == *this)
1013  return at;
1014  }
1015 
1016  when = toLatest(*this, when);
1017  return when.isValid() ? when : QDateTime();
1018 }
1019 #endif // timezone
1020 
1021 #if QT_CONFIG(datestring) // depends on, so implies, textdate
1022 
1023 static QString toStringTextDate(QDate date)
1024 {
1025  if (date.isValid()) {
1026  QCalendar cal; // Always Gregorian
1027  const auto parts = cal.partsFromDate(date);
1028  if (parts.isValid()) {
1029  const QLatin1Char sp(' ');
1031  + cal.monthName(QLocale::c(), parts.month, parts.year, QLocale::ShortFormat)
1032  // Documented to use 4-digit year
1033  + sp + QString::asprintf("%d %04d", parts.day, parts.year);
1034  }
1035  }
1036  return QString();
1037 }
1038 
1039 static QString toStringIsoDate(QDate date)
1040 {
1041  const auto parts = QCalendar().partsFromDate(date);
1042  if (parts.isValid() && parts.year >= 0 && parts.year <= 9999)
1043  return QString::asprintf("%04d-%02d-%02d", parts.year, parts.month, parts.day);
1044  return QString();
1045 }
1046 
1075 QString QDate::toString(Qt::DateFormat format) const
1076 {
1077  if (!isValid())
1078  return QString();
1079 
1080  switch (format) {
1081  case Qt::RFC2822Date:
1082  return QLocale::c().toString(*this, u"dd MMM yyyy");
1083  default:
1084  case Qt::TextDate:
1085  return toStringTextDate(*this);
1086  case Qt::ISODate:
1087  case Qt::ISODateWithMs:
1088  // No calendar dependence
1089  return toStringIsoDate(*this);
1090  }
1091 }
1092 
1147 QString QDate::toString(QStringView format, QCalendar cal) const
1148 {
1149  return QLocale::c().toString(*this, format, cal);
1150 }
1151 #endif // datestring
1152 
1163 bool QDate::setDate(int year, int month, int day)
1164 {
1166  return true;
1167 
1168  jd = nullJd();
1169  return false;
1170 }
1171 
1183 bool QDate::setDate(int year, int month, int day, QCalendar cal)
1184 {
1185  *this = QDate(year, month, day, cal);
1186  return isValid();
1187 }
1188 
1201 void QDate::getDate(int *year, int *month, int *day) const
1202 {
1203  QCalendar::YearMonthDay parts; // invalid by default
1204  if (isValid())
1206 
1207  const bool ok = parts.isValid();
1208  if (year)
1209  *year = ok ? parts.year : 0;
1210  if (month)
1211  *month = ok ? parts.month : 0;
1212  if (day)
1213  *day = ok ? parts.day : 0;
1214 }
1215 
1227 {
1228  if (isNull())
1229  return QDate();
1230 
1231  if (qint64 r; Q_UNLIKELY(qAddOverflow(jd, ndays, &r)))
1232  return QDate();
1233  else
1234  return fromJulianDay(r);
1235 }
1236 
1250 QDate QDate::addMonths(int nmonths, QCalendar cal) const
1251 {
1252  if (!isValid())
1253  return QDate();
1254 
1255  if (nmonths == 0)
1256  return *this;
1257 
1258  auto parts = cal.partsFromDate(*this);
1259 
1260  if (!parts.isValid())
1261  return QDate();
1262  Q_ASSERT(parts.year || cal.hasYearZero());
1263 
1264  parts.month += nmonths;
1265  while (parts.month <= 0) {
1266  if (--parts.year || cal.hasYearZero())
1267  parts.month += cal.monthsInYear(parts.year);
1268  }
1269  int count = cal.monthsInYear(parts.year);
1270  while (parts.month > count) {
1271  parts.month -= count;
1272  count = (++parts.year || cal.hasYearZero()) ? cal.monthsInYear(parts.year) : 0;
1273  }
1274 
1275  return fixedDate(std::move(parts), cal);
1276 }
1277 
1282 QDate QDate::addMonths(int nmonths) const
1283 {
1284  if (isNull())
1285  return QDate();
1286 
1287  if (nmonths == 0)
1288  return *this;
1289 
1290  auto parts = QGregorianCalendar::partsFromJulian(jd);
1291 
1292  if (!parts.isValid())
1293  return QDate();
1294  Q_ASSERT(parts.year);
1295 
1296  parts.month += nmonths;
1297  while (parts.month <= 0) {
1298  if (--parts.year) // skip over year 0
1299  parts.month += 12;
1300  }
1301  while (parts.month > 12) {
1302  parts.month -= 12;
1303  if (!++parts.year) // skip over year 0
1304  ++parts.year;
1305  }
1306 
1307  return fixedDate(std::move(parts));
1308 }
1309 
1324 QDate QDate::addYears(int nyears, QCalendar cal) const
1325 {
1326  if (!isValid())
1327  return QDate();
1328 
1329  auto parts = cal.partsFromDate(*this);
1330  if (!parts.isValid())
1331  return QDate();
1332 
1333  int old_y = parts.year;
1334  parts.year += nyears;
1335 
1336  // If we just crossed (or hit) a missing year zero, adjust year by +/- 1:
1337  if (!cal.hasYearZero() && ((old_y > 0) != (parts.year > 0) || !parts.year))
1338  parts.year += nyears > 0 ? +1 : -1;
1339 
1340  return fixedDate(std::move(parts), cal);
1341 }
1342 
1347 QDate QDate::addYears(int nyears) const
1348 {
1349  if (isNull())
1350  return QDate();
1351 
1352  auto parts = QGregorianCalendar::partsFromJulian(jd);
1353  if (!parts.isValid())
1354  return QDate();
1355 
1356  int old_y = parts.year;
1357  parts.year += nyears;
1358 
1359  // If we just crossed (or hit) a missing year zero, adjust year by +/- 1:
1360  if ((old_y > 0) != (parts.year > 0) || !parts.year)
1361  parts.year += nyears > 0 ? +1 : -1;
1362 
1363  return fixedDate(std::move(parts));
1364 }
1365 
1379 {
1380  if (isNull() || d.isNull())
1381  return 0;
1382 
1383  // Due to limits on minJd() and maxJd() we know this will never overflow
1384  return d.jd - jd;
1385 }
1386 
1387 
1437 #if QT_CONFIG(datestring) // depends on, so implies, textdate
1438 namespace {
1439 
1440 struct ParsedInt { qulonglong value = 0; bool ok = false; };
1441 
1442 /*
1443  /internal
1444 
1445  Read a whole number that must be the whole text. QStringView::toULongLong()
1446  will happily ignore spaces and accept signs; but various date formats'
1447  fields (e.g. all in ISO) should not.
1448 */
1449 ParsedInt readInt(QStringView text)
1450 {
1451  ParsedInt result;
1452  for (QStringIterator it(text); it.hasNext();) {
1453  if (!QChar::isDigit(it.next()))
1454  return result;
1455  }
1456  result.value = text.toULongLong(&result.ok);
1457  return result;
1458 }
1459 
1460 }
1461 
1479 QDate QDate::fromString(QStringView string, Qt::DateFormat format)
1480 {
1481  if (string.isEmpty())
1482  return QDate();
1483 
1484  switch (format) {
1485  case Qt::RFC2822Date:
1486  return rfcDateImpl(string).date;
1487  default:
1488  case Qt::TextDate: {
1489  // Documented as "ddd MMM d yyyy"
1491  auto tokens = string.tokenize(u' ', Qt::SkipEmptyParts);
1492  auto it = tokens.begin();
1493  for (int i = 0; i < 4 && it != tokens.end(); ++i, ++it)
1494  parts.emplace_back(*it);
1495 
1496  if (parts.size() != 4 || it != tokens.end())
1497  return QDate();
1498 
1499  bool ok = false;
1500  int year = parts.at(3).toInt(&ok);
1501  int day = ok ? parts.at(2).toInt(&ok) : 0;
1502  if (!ok || !day)
1503  return QDate();
1504 
1505  const int month = fromShortMonthName(parts.at(1));
1506  if (month == -1) // Month name matches no English or localised name.
1507  return QDate();
1508 
1509  return QDate(year, month, day);
1510  }
1511  case Qt::ISODate:
1512  // Semi-strict parsing, must be long enough and have punctuators as separators
1513  if (string.size() >= 10 && string.at(4).isPunct() && string.at(7).isPunct()
1514  && (string.size() == 10 || !string.at(10).isDigit())) {
1515  const ParsedInt year = readInt(string.first(4));
1516  const ParsedInt month = readInt(string.sliced(5, 2));
1517  const ParsedInt day = readInt(string.sliced(8, 2));
1518  if (year.ok && year.value > 0 && year.value <= 9999 && month.ok && day.ok)
1519  return QDate(year.value, month.value, day.value);
1520  }
1521  break;
1522  }
1523  return QDate();
1524 }
1525 
1600 QDate QDate::fromString(const QString &string, QStringView format, QCalendar cal)
1601 {
1602  QDate date;
1603 #if QT_CONFIG(datetimeparser)
1604  QDateTimeParser dt(QMetaType::QDate, QDateTimeParser::FromString, cal);
1605  dt.setDefaultLocale(QLocale::c());
1606  if (dt.parseFormat(format))
1607  dt.fromString(string, &date, nullptr);
1608 #else
1609  Q_UNUSED(string);
1610  Q_UNUSED(format);
1611  Q_UNUSED(cal);
1612 #endif
1613  return date;
1614 }
1615 #endif // datestring
1616 
1629 bool QDate::isValid(int year, int month, int day)
1630 {
1632 }
1633 
1644 {
1646 }
1647 
1662 /*****************************************************************************
1663  QTime member functions
1664  *****************************************************************************/
1665 
1730 QTime::QTime(int h, int m, int s, int ms)
1731 {
1732  setHMS(h, m, s, ms);
1733 }
1734 
1735 
1753 bool QTime::isValid() const
1754 {
1755  return mds > NullTime && mds < MSECS_PER_DAY;
1756 }
1757 
1758 
1767 int QTime::hour() const
1768 {
1769  if (!isValid())
1770  return -1;
1771 
1772  return ds() / MSECS_PER_HOUR;
1773 }
1774 
1783 int QTime::minute() const
1784 {
1785  if (!isValid())
1786  return -1;
1787 
1788  return (ds() % MSECS_PER_HOUR) / MSECS_PER_MIN;
1789 }
1790 
1799 int QTime::second() const
1800 {
1801  if (!isValid())
1802  return -1;
1803 
1804  return (ds() / MSECS_PER_SEC) % SECS_PER_MIN;
1805 }
1806 
1815 int QTime::msec() const
1816 {
1817  if (!isValid())
1818  return -1;
1819 
1820  return ds() % MSECS_PER_SEC;
1821 }
1822 
1823 #if QT_CONFIG(datestring) // depends on, so implies, textdate
1848 QString QTime::toString(Qt::DateFormat format) const
1849 {
1850  if (!isValid())
1851  return QString();
1852 
1853  switch (format) {
1854  case Qt::ISODateWithMs:
1855  return QString::asprintf("%02d:%02d:%02d.%03d", hour(), minute(), second(), msec());
1856  case Qt::RFC2822Date:
1857  case Qt::ISODate:
1858  case Qt::TextDate:
1859  default:
1860  return QString::asprintf("%02d:%02d:%02d", hour(), minute(), second());
1861  }
1862 }
1863 
1937 QString QTime::toString(QStringView format) const
1938 {
1939  return QLocale::c().toString(*this, format);
1940 }
1941 #endif // datestring
1942 
1954 bool QTime::setHMS(int h, int m, int s, int ms)
1955 {
1956  if (!isValid(h,m,s,ms)) {
1957  mds = NullTime; // make this invalid
1958  return false;
1959  }
1960  mds = (h * SECS_PER_HOUR + m * SECS_PER_MIN + s) * MSECS_PER_SEC + ms;
1961  Q_ASSERT(mds >= 0 && mds < MSECS_PER_DAY);
1962  return true;
1963 }
1964 
1981 {
1982  s %= SECS_PER_DAY;
1983  return addMSecs(s * MSECS_PER_SEC);
1984 }
1985 
2002 {
2003  if (!isValid() || !t.isValid())
2004  return 0;
2005 
2006  // Truncate milliseconds as we do not want to consider them.
2007  int ourSeconds = ds() / MSECS_PER_SEC;
2008  int theirSeconds = t.ds() / MSECS_PER_SEC;
2009  return theirSeconds - ourSeconds;
2010 }
2011 
2025 {
2026  QTime t;
2027  if (isValid())
2028  t.mds = QRoundingDown::qMod(ds() + ms, MSECS_PER_DAY);
2029  return t;
2030 }
2031 
2047 {
2048  if (!isValid() || !t.isValid())
2049  return 0;
2050  return t.ds() - ds();
2051 }
2052 
2053 
2126 #if QT_CONFIG(datestring) // depends on, so implies, textdate
2127 
2128 static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *isMidnight24)
2129 {
2131  if (isMidnight24)
2132  *isMidnight24 = false;
2133  // Match /\d\d(:\d\d(:\d\d)?)?([,.]\d+)?/ as "HH[:mm[:ss]][.zzz]"
2134  // The fractional part, if present, is in the same units as the field it follows.
2135  // TextDate restricts fractional parts to the seconds field.
2136 
2137  QStringView tail;
2138  const int dot = string.indexOf(u'.'), comma = string.indexOf(u',');
2139  if (dot != -1) {
2140  tail = string.sliced(dot + 1);
2141  if (tail.indexOf(u'.') != -1) // Forbid second dot:
2142  return QTime();
2143  string = string.first(dot);
2144  } else if (comma != -1) {
2145  tail = string.sliced(comma + 1);
2146  string = string.first(comma);
2147  }
2148  if (tail.indexOf(u',') != -1) // Forbid comma after first dot-or-comma:
2149  return QTime();
2150 
2151  const ParsedInt frac = readInt(tail);
2152  // There must be *some* digits in a fractional part; and it must be all digits:
2153  if (tail.isEmpty() ? dot != -1 || comma != -1 : !frac.ok)
2154  return QTime();
2155  Q_ASSERT(frac.ok ^ tail.isEmpty());
2156  double fraction = frac.ok ? frac.value * std::pow(0.1, tail.size()) : 0.0;
2157 
2158  const int size = string.size();
2159  if (size < 2 || size > 8)
2160  return QTime();
2161 
2162  ParsedInt hour = readInt(string.first(2));
2163  if (!hour.ok || hour.value > (format == Qt::TextDate ? 23 : 24))
2164  return QTime();
2165 
2166  ParsedInt minute;
2167  if (string.size() > 2) {
2168  if (string[2] == u':' && string.size() > 4)
2169  minute = readInt(string.sliced(3, 2));
2170  if (!minute.ok || minute.value >= 60)
2171  return QTime();
2172  } else if (format == Qt::TextDate) { // Requires minutes
2173  return QTime();
2174  } else if (frac.ok) {
2175  Q_ASSERT(!(fraction < 0.0) && fraction < 1.0);
2176  fraction *= 60;
2177  minute.value = qulonglong(fraction);
2178  fraction -= minute.value;
2179  }
2180 
2181  ParsedInt second;
2182  if (string.size() > 5) {
2183  if (string[5] == u':' && string.size() == 8)
2184  second = readInt(string.sliced(6, 2));
2185  if (!second.ok || second.value >= 60)
2186  return QTime();
2187  } else if (frac.ok) {
2188  if (format == Qt::TextDate) // Doesn't allow fraction of minutes
2189  return QTime();
2190  Q_ASSERT(!(fraction < 0.0) && fraction < 1.0);
2191  fraction *= 60;
2192  second.value = qulonglong(fraction);
2193  fraction -= second.value;
2194  }
2195 
2196  Q_ASSERT(!(fraction < 0.0) && fraction < 1.0);
2197  // Round millis to nearest (unlike minutes and seconds, rounded down):
2198  int msec = frac.ok ? qRound(MSECS_PER_SEC * fraction) : 0;
2199  // But handle overflow gracefully:
2200  if (msec == MSECS_PER_SEC) {
2201  // If we can (when data were otherwise valid) validly propagate overflow
2202  // into other fields, do so:
2203  if (isMidnight24 || hour.value < 23 || minute.value < 59 || second.value < 59) {
2204  msec = 0;
2205  if (++second.value == 60) {
2206  second.value = 0;
2207  if (++minute.value == 60) {
2208  minute.value = 0;
2209  ++hour.value;
2210  // May need to propagate further via isMidnight24, see below
2211  }
2212  }
2213  } else {
2214  // QTime::fromString() or Qt::TextDate: rounding up would cause
2215  // 23:59:59.999... to become invalid; clip to 999 ms instead:
2216  msec = MSECS_PER_SEC - 1;
2217  }
2218  }
2219 
2220  // For ISO date format, 24:0:0 means 0:0:0 on the next day:
2221  if (hour.value == 24 && minute.value == 0 && second.value == 0 && msec == 0) {
2222  Q_ASSERT(format != Qt::TextDate); // It clipped hour at 23, above.
2223  if (isMidnight24)
2224  *isMidnight24 = true;
2225  hour.value = 0;
2226  }
2227 
2228  return QTime(hour.value, minute.value, second.value, msec);
2229 }
2230 
2244 QTime QTime::fromString(QStringView string, Qt::DateFormat format)
2245 {
2246  if (string.isEmpty())
2247  return QTime();
2248 
2249  switch (format) {
2250  case Qt::RFC2822Date:
2251  return rfcDateImpl(string).time;
2252  case Qt::ISODate:
2253  case Qt::ISODateWithMs:
2254  case Qt::TextDate:
2255  default:
2256  return fromIsoTimeString(string, format, nullptr);
2257  }
2258 }
2259 
2331 QTime QTime::fromString(const QString &string, QStringView format)
2332 {
2333  QTime time;
2334 #if QT_CONFIG(datetimeparser)
2335  QDateTimeParser dt(QMetaType::QTime, QDateTimeParser::FromString, QCalendar());
2336  dt.setDefaultLocale(QLocale::c());
2337  if (dt.parseFormat(format))
2338  dt.fromString(string, nullptr, &time);
2339 #else
2340  Q_UNUSED(string);
2341  Q_UNUSED(format);
2342 #endif
2343  return time;
2344 }
2345 #endif // datestring
2346 
2347 
2362 bool QTime::isValid(int h, int m, int s, int ms)
2363 {
2364  return uint(h) < 24 && uint(m) < 60 && uint(s) < SECS_PER_MIN && uint(ms) < MSECS_PER_SEC;
2365 }
2366 
2367 /*****************************************************************************
2368  QDateTime static helper functions
2369  *****************************************************************************/
2370 
2371 // get the types from QDateTime (through QDateTimePrivate)
2374 
2375 // Returns the tzname, assume tzset has been called already
2376 static QString qt_tzname(QDateTimePrivate::DaylightStatus daylightStatus)
2377 {
2378  int isDst = (daylightStatus == QDateTimePrivate::DaylightTime) ? 1 : 0;
2379 #if defined(Q_CC_MSVC)
2380  size_t s = 0;
2381  char name[512];
2382  if (_get_tzname(&s, name, 512, isDst))
2383  return QString();
2384  return QString::fromLocal8Bit(name);
2385 #else
2386  return QString::fromLocal8Bit(tzname[isDst]);
2387 #endif // Q_OS_WIN
2388 }
2389 
2390 #if QT_CONFIG(datetimeparser)
2391 /*
2392  \internal
2393  Implemented here to share qt_tzname()
2394 */
2395 int QDateTimeParser::startsWithLocalTimeZone(QStringView name)
2396 {
2400  };
2401  for (const auto z : zones) {
2402  QString zone(qt_tzname(z));
2403  if (name.startsWith(zone))
2404  return zone.size();
2405  }
2406  return 0;
2407 }
2408 #endif // datetimeparser
2409 
2410 /*
2411  Qt represents n BCE as -n, whereas struct tm's tm_year field represents a
2412  year by the number of years after (negative for before) 1900, so that 1+m
2413  BCE is -1900 -m; so treating 1 BCE as 0 CE. We thus shift by different
2414  offsets depending on whether the year is BCE or CE.
2415 */
2416 static constexpr int tmYearFromQYear(int year) { return year - (year < 0 ? 1899 : 1900); }
2417 static constexpr int qYearFromTmYear(int year) { return year + (year < -1899 ? 1899 : 1900); }
2418 
2419 /* If mktime() returns -1, is it really an error ?
2420 
2421  It might return -1 because we're looking at the last second of 1969 and
2422  mktime does support times before 1970 (POSIX says "If the year is <1970 or
2423  the value is negative, the relationship is undefined" and MS rejects the
2424  value, consistent with that; so we don't call qt_mktime() on MS in this case
2425  and can't get -1 unless it's a real error). However, on UNIX, that's -1 UTC
2426  time and all we know, aside from mktime's return, is the local time. (We
2427  could check errno, but we call mktime from within a
2428  qt_scoped_lock(QBasicMutex), whose unlocking and destruction of the locker
2429  might frob errno.)
2430 
2431  We can assume the zone offset is a multiple of five minutes and less than a
2432  day, so this can only arise for the last second of a minute that differs from
2433  59 by a multiple of 5 on the last day of 1969 or the first day of 1970. That
2434  makes for a cheap pre-test; if it holds, we can ask mktime about the first
2435  second of the same minute; if it gives us -60, then the -1 we originally saw
2436  is not an error (or was an error, but needn't have been).
2437 */
2438 static inline bool meansEnd1969(tm *local)
2439 {
2440 #ifdef Q_OS_WIN
2441  Q_UNUSED(local);
2442  return false;
2443 #else
2444  if (local->tm_sec < 59 || local->tm_year < 69 || local->tm_year > 70
2445  || local->tm_min % 5 != 4 // Assume zone offset is a multiple of 5 mins
2446  || (local->tm_year == 69
2447  ? local->tm_mon < 11 || local->tm_mday < 31
2448  : local->tm_mon > 0 || local->tm_mday > 1)) {
2449  return false;
2450  }
2451  tm copy = *local;
2452  copy.tm_sec--; // Preceding second should get -2, not -1
2453  if (qMkTime(&copy) != -2)
2454  return false;
2455  // The original call to qMkTime() may have returned -1 as failure, not
2456  // updating local, even though it could have; so fake it here. Assumes there
2457  // was no transition in the last minute of the day !
2458  *local = copy;
2459  local->tm_sec++; // Advance back to the intended second
2460  return true;
2461 #endif
2462 }
2463 
2464 /*
2465  Call mktime but bypass its fixing of denormal times.
2466 
2467  The POSIX spec says mktime() accepts a struct tm whose fields lie outside
2468  the usual ranges; the parameter is not const-qualified and will be updated
2469  to have values in those ranges. However, MS's implementation doesn't do that
2470  (or hasn't always done it); and the only member we actually want updated is
2471  the tm_isdst flag. (Aside: MS's implementation also only works for tm_year
2472  >= 70; this is, in fact, in accordance with the POSIX spec; but all known
2473  UNIX libc implementations in fact have a signed time_t and Do The Sensible
2474  Thing, to the best of their ability, at least for 0 <= tm_year < 70; see
2475  meansEnd1969 for the handling of the last second of UTC's 1969.)
2476 
2477  If we thought we knew tm_isdst and mktime() disagrees, it'll let us know
2478  either by correcting it - in which case it adjusts the struct tm to reflect
2479  the same time, but represented using the right tm_isdst, so typically an
2480  hour earlier or later - or by returning -1. When this happens, the way we
2481  actually use qt_mktime(), we don't want a revised time with corrected DST,
2482  we want the original time with its corrected DST; so we retry the call, this
2483  time not claiming to know the DST-ness.
2484 
2485  POSIX doesn't actually say what to do if the specified struct tm describes a
2486  time in a spring-forward gap: read literally, this is an unrepresentable
2487  time and it could return -1, setting errno to EOVERFLOW. However, actual
2488  implementations chose a time one side or the other of the gap. For example,
2489  if we claim to know DST, glibc pushes to the other side of the gap (changing
2490  tm_isdst), but stays on the indicated branch of a repetition (no change to
2491  tm_isdst); this matches how QTimeZonePrivate::dataForLocalTime() uses its
2492  hint; in either case, if we don't claim to know DST, glibc picks the DST
2493  candidate. (Experiments conducted with glibc 2.31-9.)
2494 */
2495 static inline bool callMkTime(tm *local, time_t *secs)
2496 {
2497  constexpr time_t maybeError = -1; // mktime()'s return on error; or last second of 1969 UTC.
2498  const tm copy = *local;
2499  *secs = qMkTime(local);
2500  bool good = *secs != maybeError || meansEnd1969(local);
2501  if (copy.tm_isdst >= 0 && (!good || local->tm_isdst != copy.tm_isdst)) {
2502  // We thought we knew DST-ness, but were wrong:
2503  *local = copy;
2504  local->tm_isdst = -1;
2505  *secs = qMkTime(local);
2506  good = *secs != maybeError || meansEnd1969(local);
2507  }
2508 #if defined(Q_OS_WIN)
2509  // Windows mktime for the missing hour backs up 1 hour instead of advancing
2510  // 1 hour. If time differs and is standard time then this has happened, so
2511  // add 2 hours to the time and 1 hour to the secs
2512  if (local->tm_isdst == 0 && local->tm_hour != copy.tm_hour) {
2513  local->tm_hour += 2;
2514  if (local->tm_hour > 23) {
2515  local->tm_hour -= 24;
2516  if (++local->tm_mday > QGregorianCalendar::monthLength(
2517  local->tm_mon + 1, qYearFromTmYear(local->tm_year))) {
2518  local->tm_mday = 1;
2519  if (++local->tm_mon > 11) {
2520  local->tm_mon = 0;
2521  ++local->tm_year;
2522  }
2523  }
2524  }
2525  *secs += SECS_PER_HOUR;
2526  local->tm_isdst = 1;
2527  }
2528 #endif // Q_OS_WIN
2529  return good;
2530 }
2531 
2532 // Calls the platform variant of mktime for the given date, time and
2533 // daylightStatus, and updates the date, time, daylightStatus and abbreviation
2534 // with the returned values. If the date falls outside the time_t range
2535 // supported by mktime, then date/time will not be updated and *ok is set false.
2536 static qint64 qt_mktime(QDate *date, QTime *time, QDateTimePrivate::DaylightStatus *daylightStatus,
2537  QString *abbreviation, bool *ok)
2538 {
2539  Q_ASSERT(ok && date && time);
2540  qint64 msec = time->msec();
2541  Q_ASSERT(msec < MSECS_PER_SEC);
2542  int yy, mm, dd;
2543  date->getDate(&yy, &mm, &dd);
2544 
2545  tm local = {};
2546  local.tm_sec = time->second();
2547  local.tm_min = time->minute();
2548  local.tm_hour = time->hour();
2549  local.tm_mday = dd;
2550  local.tm_mon = mm - 1;
2551  local.tm_year = tmYearFromQYear(yy);
2552  local.tm_isdst = daylightStatus ? int(*daylightStatus) : -1;
2553 
2554  time_t secsSinceEpoch;
2555  if (!callMkTime(&local, &secsSinceEpoch)) {
2556  *date = QDate();
2557  *time = QTime();
2558  if (daylightStatus)
2559  *daylightStatus = QDateTimePrivate::UnknownDaylightTime;
2560  if (abbreviation)
2561  *abbreviation = QString();
2562  *ok = false;
2563  return 0;
2564  }
2565 
2566  // Store date and time:
2567  *date = QDate(qYearFromTmYear(local.tm_year), local.tm_mon + 1, local.tm_mday);
2568  *time = QTime(local.tm_hour, local.tm_min, local.tm_sec, msec);
2569 
2570  // Store zone details:
2571  if (local.tm_isdst > 0) {
2572  if (daylightStatus)
2573  *daylightStatus = QDateTimePrivate::DaylightTime;
2574  if (abbreviation)
2575  *abbreviation = qt_tzname(QDateTimePrivate::DaylightTime);
2576  } else {
2577  if (daylightStatus) {
2578  *daylightStatus = (local.tm_isdst == 0
2581  }
2582  if (abbreviation)
2583  *abbreviation = qt_tzname(QDateTimePrivate::StandardTime);
2584  }
2585 
2586  // Compute final UTC milliseconds since epoch:
2587  if (secsSinceEpoch < 0 && msec > 0) {
2588  secsSinceEpoch++;
2589  msec -= MSECS_PER_SEC;
2590  }
2591  qint64 millis;
2592  const bool overflow =
2593  mul_overflow(qint64(secsSinceEpoch),
2594  std::integral_constant<qint64, MSECS_PER_SEC>(), &millis)
2595  || add_overflow(millis, msec, &msec);
2596  *ok = !overflow;
2597 
2598  return msec;
2599 }
2600 
2601 // Calls the platform variant of localtime for the given msecs, and updates
2602 // the date, time, and DST status with the returned values.
2603 static bool qt_localtime(qint64 msecsSinceEpoch, QDate *localDate, QTime *localTime,
2604  QDateTimePrivate::DaylightStatus *daylightStatus)
2605 {
2606  const int signFix = msecsSinceEpoch % MSECS_PER_SEC && msecsSinceEpoch < 0 ? 1 : 0;
2607  const time_t secsSinceEpoch = msecsSinceEpoch / MSECS_PER_SEC - signFix;
2608  const int msec = msecsSinceEpoch % MSECS_PER_SEC + signFix * MSECS_PER_SEC;
2609  Q_ASSERT(msec >= 0 && msec < MSECS_PER_SEC);
2610 
2611  tm local;
2612  bool valid = false;
2613 
2614  // localtime() is specified to work as if it called tzset().
2615  // localtime_r() does not have this constraint, so make an explicit call.
2616  // The explicit call should also request the timezone info be re-parsed.
2617  qTzSet();
2618  if (qint64(secsSinceEpoch) * MSECS_PER_SEC + msec == msecsSinceEpoch) {
2619 #if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
2620  // Use the reentrant version of localtime() where available
2621  // as is thread-safe and doesn't use a shared static data area
2622  if (tm *res = localtime_r(&secsSinceEpoch, &local)) {
2623  Q_ASSERT(res == &local);
2624  valid = true;
2625  }
2626 #elif defined(Q_OS_WIN)
2627  if (!localtime_s(&local, &secsSinceEpoch))
2628  valid = true;
2629 #else
2630  // Returns shared static data which may be overwritten at any time
2631  // So copy the result asap
2632  if (tm *res = localtime(&secsSinceEpoch)) {
2633  local = *res;
2634  valid = true;
2635  }
2636 #endif
2637  }
2638  if (valid) {
2639  *localDate = QDate(qYearFromTmYear(local.tm_year), local.tm_mon + 1, local.tm_mday);
2640  *localTime = QTime(local.tm_hour, local.tm_min, local.tm_sec, msec);
2641  if (daylightStatus) {
2642  if (local.tm_isdst > 0)
2643  *daylightStatus = QDateTimePrivate::DaylightTime;
2644  else if (local.tm_isdst < 0)
2645  *daylightStatus = QDateTimePrivate::UnknownDaylightTime;
2646  else
2647  *daylightStatus = QDateTimePrivate::StandardTime;
2648  }
2649  return true;
2650  } else {
2651  *localDate = QDate();
2652  *localTime = QTime();
2653  if (daylightStatus)
2654  *daylightStatus = QDateTimePrivate::UnknownDaylightTime;
2655  return false;
2656  }
2657 }
2658 
2659 // Converts an msecs value into a date and time
2660 static void msecsToTime(qint64 msecs, QDate *date, QTime *time)
2661 {
2663  qint64 ds = 0;
2664 
2665  if (msecs >= MSECS_PER_DAY || msecs <= -MSECS_PER_DAY) {
2666  jd += msecs / MSECS_PER_DAY;
2667  msecs %= MSECS_PER_DAY;
2668  }
2669 
2670  if (msecs < 0) {
2671  ds = MSECS_PER_DAY - msecs - 1;
2672  jd -= ds / MSECS_PER_DAY;
2673  ds = ds % MSECS_PER_DAY;
2674  ds = MSECS_PER_DAY - ds - 1;
2675  } else {
2676  ds = msecs;
2677  }
2678 
2679  if (date)
2680  *date = QDate::fromJulianDay(jd);
2681  if (time)
2683 }
2684 
2685 // Converts a date/time value into msecs
2686 static qint64 timeToMSecs(QDate date, QTime time)
2687 {
2689  qint64 msecs, dayms = time.msecsSinceStartOfDay();
2690  if (days < 0 && dayms > 0) {
2691  ++days;
2692  dayms -= MSECS_PER_DAY;
2693  }
2694  if (mul_overflow(days, std::integral_constant<qint64, MSECS_PER_DAY>(), &msecs)
2695  || add_overflow(msecs, dayms, &msecs)) {
2696  using Bound = std::numeric_limits<qint64>;
2697  return days < 0 ? Bound::min() : Bound::max();
2698  }
2699  return msecs;
2700 }
2701 
2730 static auto computeSystemMillisRange()
2731 {
2732  struct R { qint64 min, max; bool minClip, maxClip; };
2733  using Bounds = std::numeric_limits<qint64>;
2734  constexpr bool isNarrow = Bounds::max() / MSECS_PER_SEC > TIME_T_MAX;
2735  if constexpr (isNarrow) {
2736  const qint64 msecsMax = quint64(TIME_T_MAX) * MSECS_PER_SEC - 1 + MSECS_PER_SEC;
2737  const qint64 msecsMin = -1 - msecsMax; // TIME_T_MIN is -1 - TIME_T_MAX
2738  // If we reach back to msecsMin, use it; otherwise, assume 1970 cut-off (MS).
2739  struct tm local = {};
2740  local.tm_year = tmYearFromQYear(1901);
2741  local.tm_mon = 11;
2742  local.tm_mday = 15; // A day and a bit after the start of 32-bit time_t:
2743  local.tm_isdst = -1;
2744  return R{qMkTime(&local) == -1 ? 0 : msecsMin, msecsMax, false, false};
2745  } else {
2746  const struct { int year; qint64 millis; } starts[] = {
2747  { int(QDateTime::YearRange::First) + 1, Bounds::min() },
2748  // Beginning of the Common Era:
2749  { 1, -Q_INT64_C(62135596800000) },
2750  // Invention of the Gregorian calendar:
2751  { 1582, -Q_INT64_C(12244089600000) },
2752  // Its adoption by the anglophone world:
2753  { 1752, -Q_INT64_C(6879427200000) },
2754  // Before this, struct tm's tm_year is negative (Darwin):
2755  { 1900, -Q_INT64_C(2208988800000) },
2756  }, ends[] = {
2757  { int(QDateTime::YearRange::Last) - 1, Bounds::max() },
2758  // MS's end-of-range, end of year 3000:
2759  { 3000, Q_INT64_C(32535215999999) },
2760  };
2761  // Assume we do at least reach the end of a signed 32-bit time_t (since
2762  // our actual time_t is bigger than that):
2763  qint64 stop =
2764  quint64(std::numeric_limits<qint32>::max()) * MSECS_PER_SEC - 1 + MSECS_PER_SEC;
2765  // Cleared if first pass round loop fails:
2766  bool stopMax = true;
2767  for (const auto c : ends) {
2768  struct tm local = {};
2769  local.tm_year = tmYearFromQYear(c.year);
2770  local.tm_mon = 11;
2771  local.tm_mday = 31;
2772  local.tm_hour = 23;
2773  local.tm_min = local.tm_sec = 59;
2774  local.tm_isdst = -1;
2775  if (qMkTime(&local) != -1) {
2776  stop = c.millis;
2777  break;
2778  }
2779  stopMax = false;
2780  }
2781  bool startMin = true;
2782  for (const auto c : starts) {
2783  struct tm local {};
2784  local.tm_year = tmYearFromQYear(c.year);
2785  local.tm_mon = 1;
2786  local.tm_mday = 1;
2787  local.tm_isdst = -1;
2788  if (qMkTime(&local) != -1)
2789  return R{c.millis, stop, startMin, stopMax};
2790  startMin = false;
2791  }
2792  return R{0, stop, false, stopMax};
2793  }
2794 }
2795 
2813 static inline bool millisInSystemRange(qint64 millis, qint64 slack = 0)
2814 {
2815  static const auto bounds = computeSystemMillisRange();
2816  return (bounds.minClip || millis >= bounds.min - slack)
2817  && (bounds.maxClip || millis <= bounds.max + slack);
2818 }
2819 
2836 static int systemTimeYearMatching(int year)
2837 {
2838 #if defined(Q_OS_WIN) || defined(Q_OS_WASM)// They don't support times before the epoch
2839  static constexpr int forLeapEarly[] = { 1984, 1996, 1980, 1992, 1976, 1988, 1972 };
2840  static constexpr int regularEarly[] = { 1978, 1973, 1974, 1975, 1970, 1971, 1977 };
2841 #else // First year fully in 32-bit time_t range is 1902
2842  static constexpr int forLeapEarly[] = { 1928, 1912, 1924, 1908, 1920, 1904, 1916 };
2843  static constexpr int regularEarly[] = { 1905, 1906, 1907, 1902, 1903, 1909, 1910 };
2844 #endif
2845  static constexpr int forLeapLate[] = { 2012, 2024, 2036, 2020, 2032, 2016, 2028 };
2846  static constexpr int regularLate[] = { 2034, 2035, 2030, 2031, 2037, 2027, 2033 };
2847  const int dow = QGregorianCalendar::yearStartWeekDay(year);
2848  Q_ASSERT(dow == QDate(year, 1, 1).dayOfWeek());
2849  const int res = (QGregorianCalendar::leapTest(year)
2850  ? (year < 1970 ? forLeapEarly : forLeapLate)
2851  : (year < 1970 ? regularEarly : regularLate))[dow == 7 ? 0 : dow];
2852  Q_ASSERT(QDate(res, 1, 1).dayOfWeek() == dow);
2853  Q_ASSERT(QDate(res, 12, 31).dayOfWeek() == QDate(year, 12, 31).dayOfWeek());
2854  return res;
2855 }
2856 
2857 // Convert an MSecs Since Epoch into Local Time
2858 bool QDateTimePrivate::epochMSecsToLocalTime(qint64 msecs, QDate *localDate, QTime *localTime,
2859  DaylightStatus *daylightStatus)
2860 {
2861  if (!millisInSystemRange(msecs)) {
2862  // Docs state any LocalTime after 2038-01-18 *will* have any DST applied.
2863  // When this falls outside the supported range, we need to fake it.
2864 #if QT_CONFIG(timezone)
2865  // Use the system time-zone.
2866  const auto sys = QTimeZone::systemTimeZone();
2867  if (sys.isValid()) {
2868  if (daylightStatus) {
2869  *daylightStatus = sys.d->isDaylightTime(msecs)
2872  }
2873  if (add_overflow(msecs, sys.d->offsetFromUtc(msecs) * MSECS_PER_SEC, &msecs))
2874  return false;
2875  msecsToTime(msecs, localDate, localTime);
2876  return true;
2877  }
2878 #endif // timezone
2879  // Kludge
2880  // Use existing method to fake the conversion.
2881  QDate utcDate;
2882  QTime utcTime;
2883  msecsToTime(msecs, &utcDate, &utcTime);
2884  int year, month, day;
2885  utcDate.getDate(&year, &month, &day);
2886 
2887  QDate fakeDate(systemTimeYearMatching(year), month, day);
2888  qint64 fakeMsecs = QDateTime(fakeDate, utcTime, Qt::UTC).toMSecsSinceEpoch();
2889  bool res = qt_localtime(fakeMsecs, localDate, localTime, daylightStatus);
2890  *localDate = localDate->addDays(fakeDate.daysTo(utcDate));
2891  return res;
2892  }
2893 
2894  // Falls inside time_t supported range so localtime can handle it:
2895  return qt_localtime(msecs, localDate, localTime, daylightStatus);
2896 }
2897 
2898 // Convert a LocalTime expressed in local msecs encoding and the corresponding
2899 // DST status into a UTC epoch msecs. Optionally populate the returned
2900 // values from mktime for the adjusted local date and time.
2902  DaylightStatus *daylightStatus,
2903  QDate *localDate, QTime *localTime,
2904  QString *abbreviation)
2905 {
2906  // First, if localMsecs is within +/- 1 day of viable range, try mktime() in
2907  // case it does fall in the range and gets proper DST conversion:
2908  if (millisInSystemRange(localMsecs, MSECS_PER_DAY)) {
2909  bool valid;
2910  QDate dt;
2911  QTime tm;
2912  msecsToTime(localMsecs, &dt, &tm);
2913  const qint64 utcMsecs = qt_mktime(&dt, &tm, daylightStatus, abbreviation, &valid);
2914  if (valid && millisInSystemRange(utcMsecs)) {
2915  // mktime worked and falls in valid range, so use it
2916  if (localDate)
2917  *localDate = dt;
2918  if (localTime)
2919  *localTime = tm;
2920  return utcMsecs;
2921  }
2922  }
2923 
2924  // Otherwise, outside the system range.
2925 #if QT_CONFIG(timezone)
2926  // Use the system zone:
2927  const auto sys = QTimeZone::systemTimeZone();
2928  if (sys.isValid()) {
2929  return QDateTimePrivate::zoneMSecsToEpochMSecs(localMsecs, sys, daylightStatus,
2930  localDate, localTime, abbreviation);
2931  }
2932 #endif // timezone
2933  // Kludge
2934  // Use existing method to fake the conversion.
2935  QDate dt;
2936  QTime tm;
2937  msecsToTime(localMsecs, &dt, &tm);
2938  int year, month, day;
2939  dt.getDate(&year, &month, &day);
2940  bool ok;
2941  QDate fakeDate(systemTimeYearMatching(year), month, day);
2942  const qint64 fakeDiff = fakeDate.daysTo(dt);
2943  const qint64 utcMsecs = qt_mktime(&fakeDate, &tm, daylightStatus, abbreviation, &ok);
2944  Q_ASSERT(ok);
2945  if (localDate)
2946  *localDate = fakeDate.addDays(fakeDiff);
2947  if (localTime)
2948  *localTime = tm;
2949  QDate utcDate;
2950  QTime utcTime;
2951  msecsToTime(utcMsecs, &utcDate, &utcTime);
2952  return timeToMSecs(utcDate.addDays(fakeDiff), utcTime);
2953 }
2954 
2955 static inline bool specCanBeSmall(Qt::TimeSpec spec)
2956 {
2957  return spec == Qt::LocalTime || spec == Qt::UTC;
2958 }
2959 
2960 static inline bool msecsCanBeSmall(qint64 msecs)
2961 {
2962  if (!QDateTimeData::CanBeSmall)
2963  return false;
2964 
2965  ShortData sd;
2966  sd.msecs = qintptr(msecs);
2967  return sd.msecs == msecs;
2968 }
2969 
2970 static constexpr inline
2971 QDateTimePrivate::StatusFlags mergeSpec(QDateTimePrivate::StatusFlags status, Qt::TimeSpec spec)
2972 {
2973  status &= ~QDateTimePrivate::TimeSpecMask;
2974  status |= QDateTimePrivate::StatusFlags::fromInt(int(spec) << QDateTimePrivate::TimeSpecShift);
2975  return status;
2976 }
2977 
2978 static constexpr inline Qt::TimeSpec extractSpec(QDateTimePrivate::StatusFlags status)
2979 {
2981 }
2982 
2983 // Set the Daylight Status if LocalTime set via msecs
2984 static constexpr inline QDateTimePrivate::StatusFlags
2985 mergeDaylightStatus(QDateTimePrivate::StatusFlags sf, QDateTimePrivate::DaylightStatus status)
2986 {
2987  sf &= ~QDateTimePrivate::DaylightMask;
2988  if (status == QDateTimePrivate::DaylightTime) {
2990  } else if (status == QDateTimePrivate::StandardTime) {
2992  }
2993  return sf;
2994 }
2995 
2996 // Get the DST Status if LocalTime set via msecs
2997 static constexpr inline
2998 QDateTimePrivate::DaylightStatus extractDaylightStatus(QDateTimePrivate::StatusFlags status)
2999 {
3005 }
3006 
3007 static inline qint64 getMSecs(const QDateTimeData &d)
3008 {
3009  if (d.isShort()) {
3010  // same as, but producing better code
3011  //return d.data.msecs;
3012  return qintptr(d.d) >> 8;
3013  }
3014  return d->m_msecs;
3015 }
3016 
3017 static inline QDateTimePrivate::StatusFlags getStatus(const QDateTimeData &d)
3018 {
3019  if (d.isShort()) {
3020  // same as, but producing better code
3021  //return StatusFlag(d.data.status);
3022  return QDateTimePrivate::StatusFlag(qintptr(d.d) & 0xFF);
3023  }
3024  return d->m_status;
3025 }
3026 
3027 static inline Qt::TimeSpec getSpec(const QDateTimeData &d)
3028 {
3029  return extractSpec(getStatus(d));
3030 }
3031 
3032 /* True if we *can cheaply determine* that a and b use the same offset.
3033  If they use different offsets or it would be expensive to find out, false.
3034  Calls to toMSecsSinceEpoch() are expensive, for these purposes.
3035  See QDateTime's comparison operators.
3036 */
3037 static inline bool usesSameOffset(const QDateTimeData &a, const QDateTimeData &b)
3038 {
3039  const auto status = getStatus(a);
3040  if (status != getStatus(b))
3041  return false;
3042  // Status includes DST-ness, so we now know they match in it.
3043 
3044  switch (extractSpec(status)) {
3045  case Qt::LocalTime:
3046  case Qt::UTC:
3047  return true;
3048 
3049  case Qt::TimeZone:
3050  /* TimeZone always determines its offset during construction of the
3051  private data. Even if we're in different zones, what matters is the
3052  offset actually in effect at the specific time. (DST can cause things
3053  with the same time-zone to use different offsets, but we already
3054  checked their DSTs match.) */
3055  case Qt::OffsetFromUTC: // always knows its offset, which is all that matters.
3056  Q_ASSERT(!a.isShort() && !b.isShort());
3057  return a->m_offsetFromUtc == b->m_offsetFromUtc;
3058  }
3059  Q_UNREACHABLE();
3060  return false;
3061 }
3062 
3063 // Refresh the LocalTime or TimeZone validity and offset
3064 static void refreshZonedDateTime(QDateTimeData &d, Qt::TimeSpec spec)
3065 {
3066  Q_ASSERT(spec == Qt::TimeZone || spec == Qt::LocalTime);
3067  auto status = getStatus(d);
3068  Q_ASSERT(extractSpec(status) == spec);
3069  int offsetFromUtc = 0;
3070 
3071  // If not valid date and time then is invalid
3072  if (!(status & QDateTimePrivate::ValidDate) || !(status & QDateTimePrivate::ValidTime)) {
3073  status &= ~QDateTimePrivate::ValidDateTime;
3074  } else {
3075  // We have a valid date and time and a Qt::LocalTime or Qt::TimeZone that needs calculating
3076  // LocalTime and TimeZone might fall into a "missing" DST transition hour
3077  // Calling toEpochMSecs will adjust the returned date/time if it does
3078  const qint64 msecs = getMSecs(d);
3079  qint64 epochMSecs = 0;
3080  QDate testDate;
3081  QTime testTime;
3082  auto dstStatus = extractDaylightStatus(status);
3083  if (spec == Qt::LocalTime) {
3084  epochMSecs =
3085  QDateTimePrivate::localMSecsToEpochMSecs(msecs, &dstStatus, &testDate, &testTime);
3086 #if QT_CONFIG(timezone)
3087  // else spec == Qt::TimeZone, so check zone is valid:
3088  } else if (d->m_timeZone.isValid()) {
3089  epochMSecs = QDateTimePrivate::zoneMSecsToEpochMSecs(
3090  msecs, d->m_timeZone, &dstStatus, &testDate, &testTime);
3091 #endif // timezone
3092  } // else: testDate, testTime haven't been set, so are invalid.
3093  const bool ok = testDate.isValid() && testTime.isValid();
3094  // Cache the offset to use in offsetFromUtc() &c., even if the next
3095  // check marks invalid; this lets fromMSecsSinceEpoch() give a useful
3096  // fallback for times in spring-forward gaps.
3097  if (ok)
3098  offsetFromUtc = (msecs - epochMSecs) / MSECS_PER_SEC;
3099  Q_ASSERT(offsetFromUtc >= -SECS_PER_DAY && offsetFromUtc <= SECS_PER_DAY);
3100  if (ok && timeToMSecs(testDate, testTime) == msecs) {
3101  status = mergeDaylightStatus(status, dstStatus);
3103  } else {
3104  status &= ~QDateTimePrivate::ValidDateTime;
3105  }
3106  }
3107 
3108  if (status & QDateTimePrivate::ShortData) {
3109  d.data.status = status.toInt();
3110  } else {
3111  d->m_status = status;
3112  d->m_offsetFromUtc = offsetFromUtc;
3113  }
3114 }
3115 
3116 // Check the UTC / offsetFromUTC validity
3117 static void refreshSimpleDateTime(QDateTimeData &d)
3118 {
3119  auto status = getStatus(d);
3120  Q_ASSERT(extractSpec(status) == Qt::UTC || extractSpec(status) == Qt::OffsetFromUTC);
3121  if ((status & QDateTimePrivate::ValidDate) && (status & QDateTimePrivate::ValidTime))
3123  else
3124  status &= ~QDateTimePrivate::ValidDateTime;
3125 
3126  if (status & QDateTimePrivate::ShortData)
3127  d.data.status = status.toInt();
3128  else
3129  d->m_status = status;
3130 }
3131 
3132 // Clean up and set status after assorted set-up or reworking:
3133 static void checkValidDateTime(QDateTimeData &d)
3134 {
3135  auto status = getStatus(d);
3136  auto spec = extractSpec(status);
3137  switch (spec) {
3138  case Qt::OffsetFromUTC:
3139  case Qt::UTC:
3140  // for these, a valid date and a valid time imply a valid QDateTime
3141  refreshSimpleDateTime(d);
3142  break;
3143  case Qt::TimeZone:
3144  case Qt::LocalTime:
3145  // for these, we need to check whether the timezone is valid and whether
3146  // the time is valid in that timezone. Expensive, but no other option.
3147  refreshZonedDateTime(d, spec);
3148  break;
3149  }
3150 }
3151 
3152 // Caller needs to refresh after calling this
3153 static void setTimeSpec(QDateTimeData &d, Qt::TimeSpec spec, int offsetSeconds)
3154 {
3155  auto status = getStatus(d);
3158 
3159  switch (spec) {
3160  case Qt::OffsetFromUTC:
3161  if (offsetSeconds == 0)
3162  spec = Qt::UTC;
3163  break;
3164  case Qt::TimeZone:
3165  qWarning("Using TimeZone in setTimeSpec() is unsupported"); // Use system time zone instead
3166  spec = Qt::LocalTime;
3167  Q_FALLTHROUGH();
3168  case Qt::UTC:
3169  case Qt::LocalTime:
3170  offsetSeconds = 0;
3171  break;
3172  }
3173 
3174  status = mergeSpec(status, spec);
3175  if (d.isShort() && offsetSeconds == 0) {
3176  d.data.status = status.toInt();
3177  } else {
3178  d.detach();
3179  d->m_status = status & ~QDateTimePrivate::ShortData;
3180  d->m_offsetFromUtc = offsetSeconds;
3181 #if QT_CONFIG(timezone)
3182  d->m_timeZone = QTimeZone();
3183 #endif // timezone
3184  }
3185 }
3186 
3187 static void setDateTime(QDateTimeData &d, QDate date, QTime time)
3188 {
3189  // If the date is valid and the time is not we set time to 00:00:00
3190  QTime useTime = time;
3191  if (!useTime.isValid() && date.isValid())
3192  useTime = QTime::fromMSecsSinceStartOfDay(0);
3193 
3194  QDateTimePrivate::StatusFlags newStatus = { };
3195 
3196  // Set date value and status
3197  qint64 days = 0;
3198  if (date.isValid()) {
3200  newStatus = QDateTimePrivate::ValidDate;
3201  }
3202 
3203  // Set time value and status
3204  int ds = 0;
3205  if (useTime.isValid()) {
3206  ds = useTime.msecsSinceStartOfDay();
3207  newStatus |= QDateTimePrivate::ValidTime;
3208  }
3209  Q_ASSERT(ds < MSECS_PER_DAY);
3210  // Only the later parts of the very first day are representable - its start
3211  // would overflow - so get ds the same side of 0 as days:
3212  if (days < 0 && ds > 0) {
3213  days++;
3214  ds -= MSECS_PER_DAY;
3215  }
3216 
3217  // Check in representable range:
3218  qint64 msecs = 0;
3219  if (mul_overflow(days, std::integral_constant<qint64, MSECS_PER_DAY>(), &msecs)
3220  || add_overflow(msecs, qint64(ds), &msecs)) {
3221  newStatus = QDateTimePrivate::StatusFlags{};
3222  msecs = 0;
3223  }
3224  if (d.isShort()) {
3225  // let's see if we can keep this short
3226  if (msecsCanBeSmall(msecs)) {
3227  // yes, we can
3228  d.data.msecs = qintptr(msecs);
3230  d.data.status |= newStatus.toInt();
3231  } else {
3232  // nope...
3233  d.detach();
3234  }
3235  }
3236  if (!d.isShort()) {
3237  d.detach();
3238  d->m_msecs = msecs;
3240  d->m_status |= newStatus;
3241  }
3242 }
3243 
3244 static QPair<QDate, QTime> getDateTime(const QDateTimeData &d)
3245 {
3247  qint64 msecs = getMSecs(d);
3248  auto status = getStatus(d);
3249  msecsToTime(msecs, &result.first, &result.second);
3250 
3251  if (!status.testFlag(QDateTimePrivate::ValidDate))
3252  result.first = QDate();
3253 
3254  if (!status.testFlag(QDateTimePrivate::ValidTime))
3255  result.second = QTime();
3256 
3257  return result;
3258 }
3259 
3260 /*****************************************************************************
3261  QDateTime::Data member functions
3262  *****************************************************************************/
3263 
3264 inline QDateTime::Data::Data() noexcept
3265 {
3266  // default-constructed data has a special exception:
3267  // it can be small even if CanBeSmall == false
3268  // (optimization so we don't allocate memory in the default constructor)
3270  d = reinterpret_cast<QDateTimePrivate *>(value);
3271 }
3272 
3274 {
3275  if (CanBeSmall && Q_LIKELY(specCanBeSmall(spec))) {
3276  d = reinterpret_cast<QDateTimePrivate *>(quintptr(mergeSpec(QDateTimePrivate::ShortData, spec).toInt()));
3277  } else {
3278  // the structure is too small, we need to detach
3279  d = new QDateTimePrivate;
3280  d->ref.ref();
3281  d->m_status = mergeSpec({}, spec);
3282  }
3283 }
3284 
3285 inline QDateTime::Data::Data(const Data &other)
3286  : d(other.d)
3287 {
3288  if (!isShort()) {
3289  // check if we could shrink
3290  if (specCanBeSmall(extractSpec(d->m_status)) && msecsCanBeSmall(d->m_msecs)) {
3291  ShortData sd;
3292  sd.msecs = qintptr(d->m_msecs);
3293  sd.status = (d->m_status | QDateTimePrivate::ShortData).toInt();
3294  data = sd;
3295  } else {
3296  // no, have to keep it big
3297  d->ref.ref();
3298  }
3299  }
3300 }
3301 
3303  : d(other.d)
3304 {
3305  // reset the other to a short state
3306  Data dummy;
3307  Q_ASSERT(dummy.isShort());
3308  other.d = dummy.d;
3309 }
3310 
3311 inline QDateTime::Data &QDateTime::Data::operator=(const Data &other)
3312 {
3313  if (d == other.d)
3314  return *this;
3315 
3316  auto x = d;
3317  d = other.d;
3318  if (!other.isShort()) {
3319  // check if we could shrink
3320  if (specCanBeSmall(extractSpec(other.d->m_status)) && msecsCanBeSmall(other.d->m_msecs)) {
3321  ShortData sd;
3322  sd.msecs = qintptr(other.d->m_msecs);
3323  sd.status = (other.d->m_status | QDateTimePrivate::ShortData).toInt();
3324  data = sd;
3325  } else {
3326  // no, have to keep it big
3327  other.d->ref.ref();
3328  }
3329  }
3330 
3331  if (!(quintptr(x) & QDateTimePrivate::ShortData) && !x->ref.deref())
3332  delete x;
3333  return *this;
3334 }
3335 
3336 inline QDateTime::Data::~Data()
3337 {
3338  if (!isShort() && !d->ref.deref())
3339  delete d;
3340 }
3341 
3342 inline bool QDateTime::Data::isShort() const
3343 {
3345 
3346  // sanity check:
3347  Q_ASSERT(b || (d->m_status & QDateTimePrivate::ShortData) == 0);
3348 
3349  // even if CanBeSmall = false, we have short data for a default-constructed
3350  // QDateTime object. But it's unlikely.
3351  if (CanBeSmall)
3352  return Q_LIKELY(b);
3353  return Q_UNLIKELY(b);
3354 }
3355 
3356 inline void QDateTime::Data::detach()
3357 {
3359  bool wasShort = isShort();
3360  if (wasShort) {
3361  // force enlarging
3362  x = new QDateTimePrivate;
3363  x->m_status = QDateTimePrivate::StatusFlags::fromInt(data.status) & ~QDateTimePrivate::ShortData;
3364  x->m_msecs = data.msecs;
3365  } else {
3366  if (d->ref.loadRelaxed() == 1)
3367  return;
3368 
3369  x = new QDateTimePrivate(*d);
3370  }
3371 
3372  x->ref.storeRelaxed(1);
3373  if (!wasShort && !d->ref.deref())
3374  delete d;
3375  d = x;
3376 }
3377 
3378 inline const QDateTimePrivate *QDateTime::Data::operator->() const
3379 {
3380  Q_ASSERT(!isShort());
3381  return d;
3382 }
3383 
3384 inline QDateTimePrivate *QDateTime::Data::operator->()
3385 {
3386  // should we attempt to detach here?
3387  Q_ASSERT(!isShort());
3388  Q_ASSERT(d->ref.loadRelaxed() == 1);
3389  return d;
3390 }
3391 
3392 /*****************************************************************************
3393  QDateTimePrivate member functions
3394  *****************************************************************************/
3395 
3396 Q_NEVER_INLINE
3398  int offsetSeconds)
3399 {
3400  QDateTime::Data result(toSpec);
3401  setTimeSpec(result, toSpec, offsetSeconds);
3402  setDateTime(result, toDate, toTime);
3403  if (toSpec == Qt::OffsetFromUTC || toSpec == Qt::UTC)
3404  refreshSimpleDateTime(result);
3405  else
3406  refreshZonedDateTime(result, Qt::LocalTime);
3407  return result;
3408 }
3409 
3410 #if QT_CONFIG(timezone)
3411 inline QDateTime::Data QDateTimePrivate::create(QDate toDate, QTime toTime,
3412  const QTimeZone &toTimeZone)
3413 {
3415  Q_ASSERT(!result.isShort());
3416 
3417  result.d->m_status = mergeSpec(result.d->m_status, Qt::TimeZone);
3418  result.d->m_timeZone = toTimeZone;
3419  setDateTime(result, toDate, toTime);
3420  refreshZonedDateTime(result, Qt::TimeZone);
3421  return result;
3422 }
3423 
3424 // Convert a TimeZone time expressed in zone msecs encoding into a UTC epoch msecs
3425 // DST transitions are disambiguated by hint.
3426 inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QTimeZone &zone,
3427  DaylightStatus *hint,
3428  QDate *zoneDate, QTime *zoneTime,
3429  QString *abbreviation)
3430 {
3431  Q_ASSERT(zone.isValid());
3432  // Get the effective data from QTimeZone
3433  DaylightStatus dst = hint ? *hint : UnknownDaylightTime;
3434  QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs, int(dst));
3435  const bool badDateTime = data.offsetFromUtc == QTimeZonePrivate::invalidSeconds();
3436  Q_ASSERT(badDateTime
3437  || zone.d->offsetFromUtc(data.atMSecsSinceEpoch) == data.offsetFromUtc);
3438  if (hint) {
3439  *hint = badDateTime
3441  : (data.daylightTimeOffset
3444  }
3445  if (abbreviation)
3446  *abbreviation = badDateTime ? QString() : data.abbreviation;
3447  qint64 msecs;
3448  if (badDateTime ||
3449  add_overflow(data.offsetFromUtc * MSECS_PER_SEC, data.atMSecsSinceEpoch, &msecs)) {
3450  if (zoneDate)
3451  *zoneDate = QDate();
3452  if (zoneTime)
3453  *zoneTime = QTime();
3454  } else {
3455  msecsToTime(msecs, zoneDate, zoneTime);
3456  }
3457  return data.atMSecsSinceEpoch;
3458 }
3459 #endif // timezone
3460 
3461 /*****************************************************************************
3462  QDateTime member functions
3463  *****************************************************************************/
3464 
3624 {
3625 #if QT_VERSION >= QT_VERSION_CHECK(7,0,0) || QT_POINTER_SIZE == 8
3626  static_assert(sizeof(ShortData) == sizeof(qint64));
3627  static_assert(sizeof(Data) == sizeof(qint64));
3628 #endif
3629  static_assert(sizeof(ShortData) >= sizeof(void*), "oops, Data::swap() is broken!");
3630 }
3631 
3649  : d(QDateTimePrivate::create(date, time, spec, offsetSeconds))
3650 {
3651 }
3652 
3653 #if QT_CONFIG(timezone)
3666  : d(QDateTimePrivate::create(date, time, timeZone))
3667 {
3668 }
3669 #endif // timezone
3670 
3675  : d(other.d)
3676 {
3677 }
3678 
3685  : d(std::move(other.d))
3686 {
3687 }
3688 
3693 {
3694 }
3695 
3702 {
3703  d = other.d;
3704  return *this;
3705 }
3721 bool QDateTime::isNull() const
3722 {
3723  auto status = getStatus(d);
3724  return !status.testFlag(QDateTimePrivate::ValidDate) &&
3725  !status.testFlag(QDateTimePrivate::ValidTime);
3726 }
3727 
3742 {
3743  auto status = getStatus(d);
3744  return status.testAnyFlag(QDateTimePrivate::ValidDateTime);
3745 }
3746 
3754 {
3755  auto status = getStatus(d);
3756  if (!status.testFlag(QDateTimePrivate::ValidDate))
3757  return QDate();
3758  QDate dt;
3759  msecsToTime(getMSecs(d), &dt, nullptr);
3760  return dt;
3761 }
3762 
3770 {
3771  auto status = getStatus(d);
3772  if (!status.testFlag(QDateTimePrivate::ValidTime))
3773  return QTime();
3774  QTime tm;
3775  msecsToTime(getMSecs(d), nullptr, &tm);
3776  return tm;
3777 }
3778 
3786 {
3787  return getSpec(d);
3788 }
3789 
3790 #if QT_CONFIG(timezone)
3803 QTimeZone QDateTime::timeZone() const
3804 {
3805  switch (getSpec(d)) {
3806  case Qt::UTC:
3807  return QTimeZone::utc();
3808  case Qt::OffsetFromUTC:
3809  return QTimeZone(d->m_offsetFromUtc);
3810  case Qt::TimeZone:
3811  if (d->m_timeZone.isValid())
3812  return d->m_timeZone;
3813  break;
3814  case Qt::LocalTime:
3815  return QTimeZone::systemTimeZone();
3816  }
3817  return QTimeZone();
3818 }
3819 #endif // timezone
3820 
3844 {
3845  if (!d.isShort())
3846  return d->m_offsetFromUtc;
3847  if (!isValid())
3848  return 0;
3849 
3850  auto spec = getSpec(d);
3851  if (spec == Qt::LocalTime) {
3852  // we didn't cache the value, so we need to calculate it now...
3853  qint64 msecs = getMSecs(d);
3854  return (msecs - toMSecsSinceEpoch()) / MSECS_PER_SEC;
3855  }
3856 
3857  Q_ASSERT(spec == Qt::UTC);
3858  return 0;
3859 }
3860 
3883 {
3884  if (!isValid())
3885  return QString();
3886 
3887  switch (getSpec(d)) {
3888  case Qt::UTC:
3889  return QLatin1String("UTC");
3890  case Qt::OffsetFromUTC:
3891  return QLatin1String("UTC") + toOffsetString(Qt::ISODate, d->m_offsetFromUtc);
3892  case Qt::TimeZone:
3893 #if !QT_CONFIG(timezone)
3894  break;
3895 #else
3896  Q_ASSERT(d->m_timeZone.isValid());
3897  return d->m_timeZone.abbreviation(*this);
3898 #endif // timezone
3899  case Qt::LocalTime: {
3900  QString abbrev;
3901  auto status = extractDaylightStatus(getStatus(d));
3902  QDateTimePrivate::localMSecsToEpochMSecs(getMSecs(d), &status, nullptr, nullptr, &abbrev);
3903  return abbrev;
3904  }
3905  }
3906  return QString();
3907 }
3908 
3921 {
3922  if (!isValid())
3923  return false;
3924 
3925  switch (getSpec(d)) {
3926  case Qt::UTC:
3927  case Qt::OffsetFromUTC:
3928  return false;
3929  case Qt::TimeZone:
3930 #if !QT_CONFIG(timezone)
3931  break;
3932 #else
3933  Q_ASSERT(d->m_timeZone.isValid());
3934  return d->m_timeZone.d->isDaylightTime(toMSecsSinceEpoch());
3935 #endif // timezone
3936  case Qt::LocalTime: {
3937  auto status = extractDaylightStatus(getStatus(d));
3939  QDateTimePrivate::localMSecsToEpochMSecs(getMSecs(d), &status);
3940  return (status == QDateTimePrivate::DaylightTime);
3941  }
3942  }
3943  return false;
3944 }
3945 
3954 {
3955  setDateTime(d, date, time());
3956  checkValidDateTime(d);
3957 }
3958 
3973 {
3974  setDateTime(d, date(), time);
3975  checkValidDateTime(d);
3976 }
3977 
3995 {
3996  QT_PREPEND_NAMESPACE(setTimeSpec(d, spec, 0));
3997  if (spec == Qt::OffsetFromUTC || spec == Qt::UTC)
3998  refreshSimpleDateTime(d);
3999  else
4000  refreshZonedDateTime(d, Qt::LocalTime);
4001 }
4002 
4018 void QDateTime::setOffsetFromUtc(int offsetSeconds)
4019 {
4021  refreshSimpleDateTime(d);
4022 }
4023 
4024 #if QT_CONFIG(timezone)
4036 void QDateTime::setTimeZone(const QTimeZone &toZone)
4037 {
4038  d.detach(); // always detach
4039  d->m_status = mergeSpec(d->m_status, Qt::TimeZone);
4040  d->m_offsetFromUtc = 0;
4041  d->m_timeZone = toZone;
4042  refreshZonedDateTime(d, Qt::TimeZone);
4043 }
4044 #endif // timezone
4045 
4062 {
4063  // Note: QDateTimeParser relies on this producing a useful result, even when
4064  // !isValid(), at least when the invalidity is a time in a fall-back (that
4065  // we'll have adjusted to lie outside it, but marked invalid because it's
4066  // not what was asked for). Other things may be doing similar.
4067  switch (getSpec(d)) {
4068  case Qt::UTC:
4069  return getMSecs(d);
4070 
4071  case Qt::OffsetFromUTC:
4072  Q_ASSERT(!d.isShort());
4073  return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC;
4074 
4075  case Qt::LocalTime: {
4076  // recalculate the local timezone
4077  auto status = extractDaylightStatus(getStatus(d));
4078  // If short, use offset saved by refreshZonedDateTime() on creation:
4079  if (!d.isShort())
4080  return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC;
4081  // Offset from UTC not recorded: need to recompute.
4082  return QDateTimePrivate::localMSecsToEpochMSecs(getMSecs(d), &status);
4083  }
4084 
4085  case Qt::TimeZone:
4086  Q_ASSERT(!d.isShort());
4087 #if QT_CONFIG(timezone)
4088  // Use offset refreshZonedDateTime() saved on creation:
4089  if (d->m_timeZone.isValid())
4090  return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC;
4091 #endif
4092  return 0;
4093  }
4094  Q_UNREACHABLE();
4095  return 0;
4096 }
4097 
4114 {
4115  return toMSecsSinceEpoch() / MSECS_PER_SEC;
4116 }
4117 
4133 {
4134  auto status = getStatus(d);
4135  const auto spec = extractSpec(status);
4136  Q_ASSERT(spec == Qt::UTC || spec == Qt::LocalTime || !d.isShort());
4137  qint64 local = msecs;
4138  int offsetFromUtc = 0;
4139 
4140  status &= ~QDateTimePrivate::ValidityMask;
4141  if (spec == Qt::UTC || spec == Qt::OffsetFromUTC) {
4142  if (spec == Qt::OffsetFromUTC)
4143  offsetFromUtc = d->m_offsetFromUtc;
4144  if (!offsetFromUtc || !add_overflow(msecs, offsetFromUtc * MSECS_PER_SEC, &local))
4146  } else {
4147  auto dst = extractDaylightStatus(status);
4148  if (spec == Qt::LocalTime) {
4149  QDate dt;
4150  QTime tm;
4151  if (QDateTimePrivate::epochMSecsToLocalTime(msecs, &dt, &tm, &dst)) {
4152  setDateTime(d, dt, tm);
4153  status = getStatus(d);
4154  } // else leave status marked invalid.
4155  if ((status & QDateTimePrivate::ValidDate) && (status & QDateTimePrivate::ValidTime)) {
4156  local = getMSecs(d);
4157  offsetFromUtc = (local - msecs) / MSECS_PER_SEC;
4159  }
4160 #if QT_CONFIG(timezone)
4161  } else if (spec == Qt::TimeZone && (d.detach(), d->m_timeZone.isValid())) {
4162  const auto data = d->m_timeZone.d->data(msecs);
4163  if (data.offsetFromUtc != QTimeZonePrivate::invalidSeconds()) {
4164  dst = data.daylightTimeOffset
4167  offsetFromUtc = data.offsetFromUtc;
4168  if (!offsetFromUtc
4169  || !add_overflow(msecs, offsetFromUtc * MSECS_PER_SEC, &local)) {
4171  }
4172  }
4173 #endif // timezone
4174  }
4175  status = mergeDaylightStatus(status, dst);
4176  }
4179 
4180  if (msecsCanBeSmall(local) && d.isShort()) {
4181  // we can keep short
4182  d.data.msecs = qintptr(local);
4183  d.data.status = status.toInt();
4184  } else {
4185  d.detach();
4186  d->m_status = status & ~QDateTimePrivate::ShortData;
4187  d->m_msecs = local;
4188  d->m_offsetFromUtc = offsetFromUtc;
4189  }
4190 }
4191 
4203 {
4204  qint64 msecs;
4205  if (!mul_overflow(secs, std::integral_constant<qint64, MSECS_PER_SEC>(), &msecs)) {
4206  setMSecsSinceEpoch(msecs);
4207  } else if (d.isShort()) {
4208  d.data.status &= ~int(QDateTimePrivate::ValidWhenMask);
4209  } else {
4210  d.detach();
4211  d->m_status &= ~QDateTimePrivate::ValidWhenMask;
4212  }
4213 }
4214 
4215 #if QT_CONFIG(datestring) // depends on, so implies, textdate
4247 QString QDateTime::toString(Qt::DateFormat format) const
4248 {
4249  QString buf;
4250  if (!isValid())
4251  return buf;
4252 
4253  switch (format) {
4254  case Qt::RFC2822Date:
4255  buf = QLocale::c().toString(*this, u"dd MMM yyyy hh:mm:ss ");
4256  buf += toOffsetString(Qt::TextDate, offsetFromUtc());
4257  return buf;
4258  default:
4259  case Qt::TextDate: {
4260  const QPair<QDate, QTime> p = getDateTime(d);
4261  buf = toStringTextDate(p.first);
4262  // Insert time between date's day and year:
4263  buf.insert(buf.lastIndexOf(u' '),
4264  u' ' + p.second.toString(Qt::TextDate));
4265  // Append zone/offset indicator, as appropriate:
4266  switch (timeSpec()) {
4267  case Qt::LocalTime:
4268  break;
4269 #if QT_CONFIG(timezone)
4270  case Qt::TimeZone:
4271  buf += u' ' + d->m_timeZone.displayName(
4272  *this, QTimeZone::OffsetName, QLocale::c());
4273  break;
4274 #endif
4275  default:
4276 #if 0 // ### Qt 7 GMT: use UTC instead, see qnamespace.qdoc documentation
4277  buf += QLatin1String(" UTC");
4278 #else
4279  buf += QLatin1String(" GMT");
4280 #endif
4281  if (getSpec(d) == Qt::OffsetFromUTC)
4282  buf += toOffsetString(Qt::TextDate, offsetFromUtc());
4283  }
4284  return buf;
4285  }
4286  case Qt::ISODate:
4287  case Qt::ISODateWithMs: {
4288  const QPair<QDate, QTime> p = getDateTime(d);
4289  buf = toStringIsoDate(p.first);
4290  if (buf.isEmpty())
4291  return QString(); // failed to convert
4292  buf += u'T' + p.second.toString(format);
4293  switch (getSpec(d)) {
4294  case Qt::UTC:
4295  buf += u'Z';
4296  break;
4297  case Qt::OffsetFromUTC:
4298 #if QT_CONFIG(timezone)
4299  case Qt::TimeZone:
4300 #endif
4301  buf += toOffsetString(Qt::ISODate, offsetFromUtc());
4302  break;
4303  default:
4304  break;
4305  }
4306  return buf;
4307  }
4308  }
4309 }
4310 
4352 QString QDateTime::toString(QStringView format, QCalendar cal) const
4353 {
4354  return QLocale::c().toString(*this, format, cal);
4355 }
4356 #endif // datestring
4357 
4358 static inline void massageAdjustedDateTime(QDateTimeData &d, QDate date, QTime time)
4359 {
4360  /*
4361  If we have just adjusted to a day with a DST transition, our given time
4362  may lie in the transition hour (either missing or duplicated). For any
4363  other time, telling mktime (deep in the bowels of localMSecsToEpochMSecs)
4364  or QTimeZone (via zoneMSecsToEpochMSecs) what we know about DST-ness, of
4365  the time we adjusted from, will make no difference; it'll just tell us the
4366  actual DST-ness of the given time. When landing in a transition that
4367  repeats an hour, passing the prior DST-ness - when known - will get us the
4368  indicated side of the duplicate (either local or zone). When landing in a
4369  gap, the zone gives us the other side of the gap but (for now) local time
4370  gets us a platform-dependent side of the gap (e.g. DST-side for glibc).
4371  */
4372  auto status = getStatus(d);
4374  && (status & QDateTimePrivate::ValidDateTime));
4375  auto spec = extractSpec(status);
4376  if (spec == Qt::OffsetFromUTC || spec == Qt::UTC) {
4377  setDateTime(d, date, time);
4378  refreshSimpleDateTime(d);
4379  return;
4380  }
4381  auto dst = extractDaylightStatus(status);
4382  qint64 utc = 0, local = timeToMSecs(date, time);
4383  if (spec == Qt::LocalTime)
4385 #if QT_CONFIG(timezone)
4386  else if (spec == Qt::TimeZone && d.d->m_timeZone.isValid())
4387  utc = QDateTimePrivate::zoneMSecsToEpochMSecs(local, d.d->m_timeZone, &dst, &date, &time);
4388 #endif // timezone
4389  else
4391 
4392  setDateTime(d, date, time); // Detaches d, if needed.
4393  status = getStatus(d); // Updated by setDateTime()
4395  && (status & QDateTimePrivate::ValidDate)
4396  && (status & QDateTimePrivate::ValidTime));
4397  if (ok)
4398  status = mergeDaylightStatus(status | QDateTimePrivate::ValidDateTime, dst);
4399  else
4400  status &= ~QDateTimePrivate::ValidDateTime;
4401 
4402  if (status & QDateTimePrivate::ShortData) {
4403  d.data.status = status.toInt();
4404  } else {
4405  d->m_status = status;
4406  if (ok)
4407  d->m_offsetFromUtc = (local - utc) / MSECS_PER_SEC;
4408  }
4409 }
4410 
4426 {
4427  if (isNull())
4428  return QDateTime();
4429 
4430  QDateTime dt(*this);
4431  QPair<QDate, QTime> p = getDateTime(d);
4432  massageAdjustedDateTime(dt.d, p.first.addDays(ndays), p.second);
4433  return dt;
4434 }
4435 
4451 {
4452  if (isNull())
4453  return QDateTime();
4454 
4455  QDateTime dt(*this);
4456  QPair<QDate, QTime> p = getDateTime(d);
4457  massageAdjustedDateTime(dt.d, p.first.addMonths(nmonths), p.second);
4458  return dt;
4459 }
4460 
4476 {
4477  if (isNull())
4478  return QDateTime();
4479 
4480  QDateTime dt(*this);
4481  QPair<QDate, QTime> p = getDateTime(d);
4482  massageAdjustedDateTime(dt.d, p.first.addYears(nyears), p.second);
4483  return dt;
4484 }
4485 
4497 {
4498  qint64 msecs;
4499  if (mul_overflow(s, std::integral_constant<qint64, MSECS_PER_SEC>(), &msecs))
4500  return QDateTime();
4501  return addMSecs(msecs);
4502 }
4503 
4514 {
4515  if (!isValid())
4516  return QDateTime();
4517 
4518  QDateTime dt(*this);
4519  switch (getSpec(d)) {
4520  case Qt::LocalTime:
4521  case Qt::TimeZone:
4522  // Convert to real UTC first in case this crosses a DST transition:
4523  if (!add_overflow(toMSecsSinceEpoch(), msecs, &msecs)) {
4524  dt.setMSecsSinceEpoch(msecs);
4525  } else if (dt.d.isShort()) {
4526  dt.d.data.status &= ~int(QDateTimePrivate::ValidWhenMask);
4527  } else {
4528  dt.d.detach();
4529  dt.d->m_status &= ~QDateTimePrivate::ValidWhenMask;
4530  }
4531  break;
4532  case Qt::UTC:
4533  case Qt::OffsetFromUTC:
4534  // No need to convert, just add on
4535  if (add_overflow(getMSecs(d), msecs, &msecs)) {
4536  if (dt.d.isShort()) {
4537  dt.d.data.status &= ~int(QDateTimePrivate::ValidWhenMask);
4538  } else {
4539  dt.d.detach();
4540  dt.d->m_status &= ~QDateTimePrivate::ValidWhenMask;
4541  }
4542  } else if (d.isShort()) {
4543  // need to check if we need to enlarge first
4544  if (msecsCanBeSmall(msecs)) {
4545  dt.d.data.msecs = qintptr(msecs);
4546  } else {
4547  dt.d.detach();
4548  dt.d->m_msecs = msecs;
4549  }
4550  } else {
4551  dt.d.detach();
4552  dt.d->m_msecs = msecs;
4553  }
4554  break;
4555  }
4556  return dt;
4557 }
4558 
4576 {
4577  return date().daysTo(other.date());
4578 }
4579 
4598 {
4599  return msecsTo(other) / MSECS_PER_SEC;
4600 }
4601 
4617 {
4618  if (!isValid() || !other.isValid())
4619  return 0;
4620 
4621  return other.toMSecsSinceEpoch() - toMSecsSinceEpoch();
4622 }
4623 
4641 {
4642  if (getSpec(d) == spec && (spec == Qt::UTC || spec == Qt::LocalTime))
4643  return *this;
4644 
4645  if (!isValid()) {
4646  QDateTime ret = *this;
4647  ret.setTimeSpec(spec);
4648  return ret;
4649  }
4650 
4651  return fromMSecsSinceEpoch(toMSecsSinceEpoch(), spec, 0);
4652 }
4653 
4667 QDateTime QDateTime::toOffsetFromUtc(int offsetSeconds) const
4668 {
4669  if (getSpec(d) == Qt::OffsetFromUTC
4670  && d->m_offsetFromUtc == offsetSeconds)
4671  return *this;
4672 
4673  if (!isValid()) {
4674  QDateTime ret = *this;
4675  ret.setOffsetFromUtc(offsetSeconds);
4676  return ret;
4677  }
4678 
4679  return fromMSecsSinceEpoch(toMSecsSinceEpoch(), Qt::OffsetFromUTC, offsetSeconds);
4680 }
4681 
4682 #if QT_CONFIG(timezone)
4691 QDateTime QDateTime::toTimeZone(const QTimeZone &timeZone) const
4692 {
4693  if (getSpec(d) == Qt::TimeZone && d->m_timeZone == timeZone)
4694  return *this;
4695 
4696  if (!isValid()) {
4697  QDateTime ret = *this;
4698  ret.setTimeZone(timeZone);
4699  return ret;
4700  }
4701 
4702  return fromMSecsSinceEpoch(toMSecsSinceEpoch(), timeZone);
4703 }
4704 #endif // timezone
4705 
4714 bool QDateTime::equals(const QDateTime &other) const
4715 {
4716  if (!isValid())
4717  return !other.isValid();
4718  if (!other.isValid())
4719  return false;
4720 
4721  if (usesSameOffset(d, other.d))
4722  return getMSecs(d) == getMSecs(other.d);
4723 
4724  // Convert to UTC and compare
4725  return toMSecsSinceEpoch() == other.toMSecsSinceEpoch();
4726 }
4727 
4761 bool QDateTime::precedes(const QDateTime &other) const
4762 {
4763  if (!isValid())
4764  return other.isValid();
4765  if (!other.isValid())
4766  return false;
4767 
4768  if (usesSameOffset(d, other.d))
4769  return getMSecs(d) < getMSecs(other.d);
4770 
4771  // Convert to UTC and compare
4772  return toMSecsSinceEpoch() < other.toMSecsSinceEpoch();
4773 }
4774 
4848 #if defined(Q_OS_WIN)
4849 static inline uint msecsFromDecomposed(int hour, int minute, int sec, int msec = 0)
4850 {
4851  return MSECS_PER_HOUR * hour + MSECS_PER_MIN * minute + MSECS_PER_SEC * sec + msec;
4852 }
4853 
4855 {
4856  SYSTEMTIME st = {};
4857  GetLocalTime(&st);
4858  return QDate(st.wYear, st.wMonth, st.wDay);
4859 }
4860 
4862 {
4863  QTime ct;
4864  SYSTEMTIME st = {};
4865  GetLocalTime(&st);
4866  ct.setHMS(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
4867  return ct;
4868 }
4869 
4871 {
4872  QTime t;
4873  SYSTEMTIME st = {};
4874  GetLocalTime(&st);
4875  QDate d(st.wYear, st.wMonth, st.wDay);
4876  t.mds = msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
4877  return QDateTime(d, t);
4878 }
4879 
4881 {
4882  QTime t;
4883  SYSTEMTIME st = {};
4884  GetSystemTime(&st);
4885  QDate d(st.wYear, st.wMonth, st.wDay);
4886  t.mds = msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
4887  return QDateTime(d, t, Qt::UTC);
4888 }
4889 
4891 {
4892  SYSTEMTIME st = {};
4893  GetSystemTime(&st);
4894  const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay));
4895 
4896  return msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds) +
4897  daysAfterEpoch * MSECS_PER_DAY;
4898 }
4899 
4901 {
4902  SYSTEMTIME st = {};
4903  GetSystemTime(&st);
4904  const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay));
4905 
4906  return st.wHour * SECS_PER_HOUR + st.wMinute * SECS_PER_MIN + st.wSecond +
4907  daysAfterEpoch * SECS_PER_DAY;
4908 }
4909 
4910 #elif defined(Q_OS_UNIX)
4912 {
4913  return QDateTime::currentDateTime().date();
4914 }
4915 
4917 {
4918  return QDateTime::currentDateTime().time();
4919 }
4920 
4922 {
4924 }
4925 
4927 {
4929 }
4930 
4932 {
4933  // posix compliant system
4934  // we have milliseconds
4935  struct timeval tv;
4936  gettimeofday(&tv, nullptr);
4937  return tv.tv_sec * MSECS_PER_SEC + tv.tv_usec / 1000;
4938 }
4939 
4941 {
4942  struct timeval tv;
4943  gettimeofday(&tv, nullptr);
4944  return tv.tv_sec;
4945 }
4946 #else
4947 #error "What system is this?"
4948 #endif
4949 
4969 {
4970  QDateTime dt;
4971  QT_PREPEND_NAMESPACE(setTimeSpec(dt.d, spec, offsetSeconds));
4972  dt.setMSecsSinceEpoch(msecs);
4973  return dt;
4974 }
4975 
4997 {
4998  constexpr qint64 maxSeconds = std::numeric_limits<qint64>::max() / MSECS_PER_SEC;
4999  constexpr qint64 minSeconds = std::numeric_limits<qint64>::min() / MSECS_PER_SEC;
5000  if (secs > maxSeconds || secs < minSeconds)
5001  return QDateTime(); // Would {und,ov}erflow
5002  return fromMSecsSinceEpoch(secs * MSECS_PER_SEC, spec, offsetSeconds);
5003 }
5004 
5005 #if QT_CONFIG(timezone)
5016 {
5017  QDateTime dt;
5018  dt.setTimeZone(timeZone);
5019  if (timeZone.isValid())
5020  dt.setMSecsSinceEpoch(msecs);
5021  return dt;
5022 }
5023 
5034 {
5035  constexpr qint64 maxSeconds = std::numeric_limits<qint64>::max() / MSECS_PER_SEC;
5036  constexpr qint64 minSeconds = std::numeric_limits<qint64>::min() / MSECS_PER_SEC;
5037  if (secs > maxSeconds || secs < minSeconds)
5038  return QDateTime(); // Would {und,ov}erflow
5039  return fromMSecsSinceEpoch(secs * MSECS_PER_SEC, timeZone);
5040 }
5041 #endif
5042 
5043 #if QT_CONFIG(datestring) // depends on, so implies, textdate
5044 
5061 QDateTime QDateTime::fromString(QStringView string, Qt::DateFormat format)
5062 {
5063  if (string.isEmpty())
5064  return QDateTime();
5065 
5066  switch (format) {
5067  case Qt::RFC2822Date: {
5068  const ParsedRfcDateTime rfc = rfcDateImpl(string);
5069 
5070  if (!rfc.date.isValid() || !rfc.time.isValid())
5071  return QDateTime();
5072 
5073  QDateTime dateTime(rfc.date, rfc.time, Qt::UTC);
5074  dateTime.setOffsetFromUtc(rfc.utcOffset);
5075  return dateTime;
5076  }
5077  case Qt::ISODate:
5078  case Qt::ISODateWithMs: {
5079  const int size = string.size();
5080  if (size < 10)
5081  return QDateTime();
5082 
5083  QDate date = QDate::fromString(string.first(10), Qt::ISODate);
5084  if (!date.isValid())
5085  return QDateTime();
5086  if (size == 10)
5087  return date.startOfDay();
5088 
5089  Qt::TimeSpec spec = Qt::LocalTime;
5090  QStringView isoString = string.sliced(10); // trim "yyyy-MM-dd"
5091 
5092  // Must be left with T (or space) and at least one digit for the hour:
5093  if (isoString.size() < 2
5094  || !(isoString.startsWith(u'T', Qt::CaseInsensitive)
5095  // RFC 3339 (section 5.6) allows a space here. (It actually
5096  // allows any separator one considers more readable, merely
5097  // giving space as an example - but let's not go wild !)
5098  || isoString.startsWith(u' '))) {
5099  return QDateTime();
5100  }
5101  isoString = isoString.sliced(1); // trim 'T' (or space)
5102 
5103  int offset = 0;
5104  // Check end of string for Time Zone definition, either Z for UTC or [+-]HH:mm for Offset
5105  if (isoString.endsWith(u'Z', Qt::CaseInsensitive)) {
5106  spec = Qt::UTC;
5107  isoString.chop(1); // trim 'Z'
5108  } else {
5109  // the loop below is faster but functionally equal to:
5110  // const int signIndex = isoString.indexOf(QRegulargExpression(QStringLiteral("[+-]")));
5111  int signIndex = isoString.size() - 1;
5112  Q_ASSERT(signIndex >= 0);
5113  bool found = false;
5114  do {
5115  QChar character(isoString[signIndex]);
5116  found = character == u'+' || character == u'-';
5117  } while (!found && --signIndex >= 0);
5118 
5119  if (found) {
5120  bool ok;
5121  offset = fromOffsetString(isoString.sliced(signIndex), &ok);
5122  if (!ok)
5123  return QDateTime();
5124  isoString = isoString.first(signIndex);
5125  spec = Qt::OffsetFromUTC;
5126  }
5127  }
5128 
5129  // Might be end of day (24:00, including variants), which QTime considers invalid.
5130  // ISO 8601 (section 4.2.3) says that 24:00 is equivalent to 00:00 the next day.
5131  bool isMidnight24 = false;
5132  QTime time = fromIsoTimeString(isoString, format, &isMidnight24);
5133  if (!time.isValid())
5134  return QDateTime();
5135  if (isMidnight24) // time is 0:0, but we want the start of next day:
5136  return date.addDays(1).startOfDay(spec, offset);
5137  return QDateTime(date, time, spec, offset);
5138  }
5139  case Qt::TextDate: {
5141 
5142  auto tokens = string.tokenize(u' ', Qt::SkipEmptyParts);
5143  auto it = tokens.begin();
5144  for (int i = 0; i < 6 && it != tokens.end(); ++i, ++it)
5145  parts.emplace_back(*it);
5146 
5147  // Documented as "ddd MMM d HH:mm:ss yyyy" with optional offset-suffix;
5148  // and allow time either before or after year.
5149  if (parts.count() < 5 || it != tokens.end())
5150  return QDateTime();
5151 
5152  // Year and time can be in either order.
5153  // Guess which by looking for ':' in the time
5154  int yearPart = 3;
5155  int timePart = 3;
5156  if (parts.at(3).contains(u':'))
5157  yearPart = 4;
5158  else if (parts.at(4).contains(u':'))
5159  timePart = 4;
5160  else
5161  return QDateTime();
5162 
5163  bool ok = false;
5164  int day = parts.at(2).toInt(&ok);
5165  int year = ok ? parts.at(yearPart).toInt(&ok) : 0;
5166  int month = fromShortMonthName(parts.at(1));
5167  if (!ok || year == 0 || day == 0 || month < 1)
5168  return QDateTime();
5169 
5170  const QDate date(year, month, day);
5171  if (!date.isValid())
5172  return QDateTime();
5173 
5174  const QTime time = fromIsoTimeString(parts.at(timePart), format, nullptr);
5175  if (!time.isValid())
5176  return QDateTime();
5177 
5178  if (parts.count() == 5)
5179  return QDateTime(date, time, Qt::LocalTime);
5180 
5181  QStringView tz = parts.at(5);
5182  if (tz.startsWith(QLatin1String("UTC"))
5183  // GMT has long been deprecated as an alias for UTC.
5184  || tz.startsWith(QLatin1String("GMT"), Qt::CaseInsensitive)) {
5185  tz = tz.sliced(3);
5186  if (tz.isEmpty())
5187  return QDateTime(date, time, Qt::UTC);
5188 
5189  int offset = fromOffsetString(tz, &ok);
5191  }
5192  return QDateTime();
5193  }
5194  }
5195 
5196  return QDateTime();
5197 }
5198 
5276 QDateTime QDateTime::fromString(const QString &string, QStringView format, QCalendar cal)
5277 {
5278 #if QT_CONFIG(datetimeparser)
5279  QDateTime datetime;
5280 
5281  QDateTimeParser dt(QMetaType::QDateTime, QDateTimeParser::FromString, cal);
5282  dt.setDefaultLocale(QLocale::c());
5283  if (dt.parseFormat(format) && (dt.fromString(string, &datetime) || !datetime.isValid()))
5284  return datetime;
5285 #else
5286  Q_UNUSED(string);
5287  Q_UNUSED(format);
5288  Q_UNUSED(cal);
5289 #endif
5290  return QDateTime();
5291 }
5292 
5293 #endif // datestring
5320 /*****************************************************************************
5321  Date/time stream functions
5322  *****************************************************************************/
5323 
5324 #ifndef QT_NO_DATASTREAM
5334 {
5335  if (out.version() < QDataStream::Qt_5_0)
5336  return out << quint32(date.jd);
5337  else
5338  return out << date.jd;
5339 }
5340 
5350 {
5351  if (in.version() < QDataStream::Qt_5_0) {
5352  quint32 jd;
5353  in >> jd;
5354  // Older versions consider 0 an invalid jd.
5355  date.jd = (jd != 0 ? jd : QDate::nullJd());
5356  } else {
5357  qint64 jd;
5358  in >> jd;
5359  date.jd = jd;
5360  }
5361 
5362  return in;
5363 }
5364 
5374 {
5375  if (out.version() >= QDataStream::Qt_4_0) {
5376  return out << quint32(time.mds);
5377  } else {
5378  // Qt3 had no support for reading -1, QTime() was valid and serialized as 0
5379  return out << quint32(time.isNull() ? 0 : time.mds);
5380  }
5381 }
5382 
5392 {
5393  quint32 ds;
5394  in >> ds;
5395  if (in.version() >= QDataStream::Qt_4_0) {
5396  time.mds = int(ds);
5397  } else {
5398  // Qt3 would write 0 for a null time
5399  time.mds = (ds == 0) ? QTime::NullTime : int(ds);
5400  }
5401  return in;
5402 }
5403 
5412 {
5413  QPair<QDate, QTime> dateAndTime;
5414 
5415  if (out.version() >= QDataStream::Qt_5_2) {
5416 
5417  // In 5.2 we switched to using Qt::TimeSpec and added offset support
5418  dateAndTime = getDateTime(dateTime.d);
5419  out << dateAndTime << qint8(dateTime.timeSpec());
5422 #if QT_CONFIG(timezone)
5423  else if (dateTime.timeSpec() == Qt::TimeZone)
5424  out << dateTime.timeZone();
5425 #endif // timezone
5426 
5427  } else if (out.version() == QDataStream::Qt_5_0) {
5428 
5429  // In Qt 5.0 we incorrectly serialised all datetimes as UTC.
5430  // This approach is wrong and should not be used again; it breaks
5431  // the guarantee that a deserialised local datetime is the same time
5432  // of day, regardless of which timezone it was serialised in.
5433  dateAndTime = getDateTime((dateTime.isValid() ? dateTime.toUTC() : dateTime).d);
5434  out << dateAndTime << qint8(dateTime.timeSpec());
5435 
5436  } else if (out.version() >= QDataStream::Qt_4_0) {
5437 
5438  // From 4.0 to 5.1 (except 5.0) we used QDateTimePrivate::Spec
5439  dateAndTime = getDateTime(dateTime.d);
5440  out << dateAndTime;
5441  switch (dateTime.timeSpec()) {
5442  case Qt::UTC:
5444  break;
5445  case Qt::OffsetFromUTC:
5447  break;
5448  case Qt::TimeZone:
5450  break;
5451  case Qt::LocalTime:
5453  break;
5454  }
5455 
5456  } else { // version < QDataStream::Qt_4_0
5457 
5458  // Before 4.0 there was no TimeSpec, only Qt::LocalTime was supported
5459  dateAndTime = getDateTime(dateTime.d);
5460  out << dateAndTime;
5461 
5462  }
5463 
5464  return out;
5465 }
5466 
5476 {
5477  QDate dt;
5478  QTime tm;
5479  qint8 ts = 0;
5480  Qt::TimeSpec spec = Qt::LocalTime;
5481  qint32 offset = 0;
5482 #if QT_CONFIG(timezone)
5483  QTimeZone tz;
5484 #endif // timezone
5485 
5486  if (in.version() >= QDataStream::Qt_5_2) {
5487 
5488  // In 5.2 we switched to using Qt::TimeSpec and added offset support
5489  in >> dt >> tm >> ts;
5490  spec = static_cast<Qt::TimeSpec>(ts);
5491  if (spec == Qt::OffsetFromUTC) {
5492  in >> offset;
5493  dateTime = QDateTime(dt, tm, spec, offset);
5494 #if QT_CONFIG(timezone)
5495  } else if (spec == Qt::TimeZone) {
5496  in >> tz;
5497  dateTime = QDateTime(dt, tm, tz);
5498 #endif // timezone
5499  } else {
5500  dateTime = QDateTime(dt, tm, spec);
5501  }
5502 
5503  } else if (in.version() == QDataStream::Qt_5_0) {
5504 
5505  // In Qt 5.0 we incorrectly serialised all datetimes as UTC
5506  in >> dt >> tm >> ts;
5507  spec = static_cast<Qt::TimeSpec>(ts);
5508  dateTime = QDateTime(dt, tm, Qt::UTC);
5509  dateTime = dateTime.toTimeSpec(spec);
5510 
5511  } else if (in.version() >= QDataStream::Qt_4_0) {
5512 
5513  // From 4.0 to 5.1 (except 5.0) we used QDateTimePrivate::Spec
5514  in >> dt >> tm >> ts;
5515  switch ((QDateTimePrivate::Spec)ts) {
5516  case QDateTimePrivate::UTC:
5517  spec = Qt::UTC;
5518  break;
5520  spec = Qt::OffsetFromUTC;
5521  break;
5523  spec = Qt::TimeZone;
5524 #if QT_CONFIG(timezone)
5525  // FIXME: need to use a different constructor !
5526 #endif
5527  break;
5531  spec = Qt::LocalTime;
5532  break;
5533  }
5534  dateTime = QDateTime(dt, tm, spec, offset);
5535 
5536  } else { // version < QDataStream::Qt_4_0
5537 
5538  // Before 4.0 there was no TimeSpec, only Qt::LocalTime was supported
5539  in >> dt >> tm;
5540  dateTime = QDateTime(dt, tm, spec, offset);
5541 
5542  }
5543 
5544  return in;
5545 }
5546 #endif // QT_NO_DATASTREAM
5547 
5548 /*****************************************************************************
5549  Date / Time Debug Streams
5550 *****************************************************************************/
5551 
5552 #if !defined(QT_NO_DEBUG_STREAM) && QT_CONFIG(datestring)
5554 {
5555  QDebugStateSaver saver(dbg);
5556  dbg.nospace() << "QDate(";
5557  if (date.isValid())
5558  dbg.nospace() << date.toString(Qt::ISODate);
5559  else
5560  dbg.nospace() << "Invalid";
5561  dbg.nospace() << ')';
5562  return dbg;
5563 }
5564 
5566 {
5567  QDebugStateSaver saver(dbg);
5568  dbg.nospace() << "QTime(";
5569  if (time.isValid())
5570  dbg.nospace() << time.toString(u"HH:mm:ss.zzz");
5571  else
5572  dbg.nospace() << "Invalid";
5573  dbg.nospace() << ')';
5574  return dbg;
5575 }
5576 
5577 QDebug operator<<(QDebug dbg, const QDateTime &date)
5578 {
5579  QDebugStateSaver saver(dbg);
5580  dbg.nospace() << "QDateTime(";
5581  if (date.isValid()) {
5582  const Qt::TimeSpec ts = date.timeSpec();
5583  dbg.noquote() << date.toString(u"yyyy-MM-dd HH:mm:ss.zzz t")
5584  << ' ' << ts;
5585  switch (ts) {
5586  case Qt::UTC:
5587  break;
5588  case Qt::OffsetFromUTC:
5589  dbg.space() << date.offsetFromUtc() << 's';
5590  break;
5591  case Qt::TimeZone:
5592 #if QT_CONFIG(timezone)
5593  dbg.space() << date.timeZone().id();
5594 #endif // timezone
5595  break;
5596  case Qt::LocalTime:
5597  break;
5598  }
5599  } else {
5600  dbg.nospace() << "Invalid";
5601  }
5602  return dbg.nospace() << ')';
5603 }
5604 #endif // debug_stream && datestring
5605 
5612 size_t qHash(const QDateTime &key, size_t seed)
5613 {
5614  // Use to toMSecsSinceEpoch instead of individual qHash functions for
5615  // QDate/QTime/spec/offset because QDateTime::operator== converts both arguments
5616  // to the same timezone. If we don't, qHash would return different hashes for
5617  // two QDateTimes that are equivalent once converted to the same timezone.
5618  return key.isValid() ? qHash(key.toMSecsSinceEpoch(), seed) : seed;
5619 }
5620 
5627 size_t qHash(QDate key, size_t seed) noexcept
5628 {
5629  return qHash(key.toJulianDay(), seed);
5630 }
5631 
5638 size_t qHash(QTime key, size_t seed) noexcept
5639 {
5640  return qHash(key.msecsSinceStartOfDay(), seed);
5641 }
5642 
small capitals from c petite p scientific f u
Definition: afcover.h:88
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
#define value
[5]
The QCalendar class describes calendar systems.
Definition: qcalendar.h:89
QDate dateFromParts(int year, int month, int day) const
Definition: qcalendar.cpp:1451
QString monthName(const QLocale &locale, int month, int year=Unspecified, QLocale::FormatType format=QLocale::LongFormat) const
Definition: qcalendar.cpp:1515
YearMonthDay partsFromDate(QDate date) const
Definition: qcalendar.cpp:1473
bool hasYearZero() const
Definition: qcalendar.cpp:1396
int monthsInYear(int year) const
Definition: qcalendar.cpp:1273
int dayOfWeek(QDate date) const
Definition: qcalendar.cpp:1488
bool isProleptic() const
Definition: qcalendar.cpp:1365
int daysInMonth(int month, int year=Unspecified) const
Definition: qcalendar.cpp:1248
int daysInYear(int year) const
Definition: qcalendar.cpp:1259
The QChar class provides a 16-bit Unicode character.
Definition: qchar.h:84
constexpr bool isDigit() const noexcept
Definition: qchar.h:504
The QDataStream class provides serialization of binary data to a QIODevice.
Definition: qdatastream.h:66
operator>>(QDataStream &ds, qfloat16 &f)
Definition: qfloat16.cpp:344
operator<<(QDataStream &ds, qfloat16 f)
Definition: qfloat16.cpp:327
The QDate class provides date functions.
Definition: qdatetime.h:64
int weekNumber(int *yearNum=nullptr) const
Definition: qdatetime.cpp:738
qint64 daysTo(QDate d) const
Definition: qdatetime.cpp:1378
constexpr bool isValid() const
Definition: qdatetime.h:72
constexpr qint64 toJulianDay() const
Definition: qdatetime.h:139
int month() const
Definition: qdatetime.cpp:556
static constexpr QDate fromJulianDay(qint64 jd_)
Definition: qdatetime.h:137
int day() const
Definition: qdatetime.cpp:589
QDate addDays(qint64 days) const
Definition: qdatetime.cpp:1226
QDate addYears(int years) const
Definition: qdatetime.cpp:1347
QDateTime endOfDay(Qt::TimeSpec spec=Qt::LocalTime, int offsetSeconds=0) const
Definition: qdatetime.cpp:967
constexpr bool isNull() const
Definition: qdatetime.h:71
int year() const
Definition: qdatetime.cpp:504
QDate addMonths(int months) const
Definition: qdatetime.cpp:1282
QDateTime startOfDay(Qt::TimeSpec spec=Qt::LocalTime, int offsetSeconds=0) const
Definition: qdatetime.cpp:840
void getDate(int *year, int *month, int *day) const
Definition: qdatetime.cpp:1201
friend class QDateTime
Definition: qdatetime.h:149
static QDate currentDate()
int daysInMonth() const
Definition: qdatetime.cpp:683
int daysInYear() const
Definition: qdatetime.cpp:714
constexpr QDate()
Definition: qdatetime.h:67
int dayOfYear() const
Definition: qdatetime.cpp:649
int dayOfWeek() const
Definition: qdatetime.cpp:621
bool setDate(int year, int month, int day)
Definition: qdatetime.cpp:1163
static bool isLeapYear(int year)
Definition: qdatetime.cpp:1643
The QDateTime class provides date and time functions.
Definition: qdatetime.h:238
void setOffsetFromUtc(int offsetSeconds)
Definition: qdatetime.cpp:4018
int offsetFromUtc() const
Definition: qdatetime.cpp:3843
static QDateTime currentDateTime()
QDateTime toTimeSpec(Qt::TimeSpec spec) const
Definition: qdatetime.cpp:4640
void setMSecsSinceEpoch(qint64 msecs)
Definition: qdatetime.cpp:4132
qint64 toMSecsSinceEpoch() const
Definition: qdatetime.cpp:4061
qint64 secsTo(const QDateTime &) const
Definition: qdatetime.cpp:4597
QDateTime addMSecs(qint64 msecs) const
Definition: qdatetime.cpp:4513
QDateTime addMonths(int months) const
Definition: qdatetime.cpp:4450
bool isNull() const
Definition: qdatetime.cpp:3721
QDateTime & operator=(const QDateTime &other) noexcept
Definition: qdatetime.cpp:3701
static QDateTime fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec=Qt::LocalTime, int offsetFromUtc=0)
Definition: qdatetime.cpp:4968
qint64 msecsTo(const QDateTime &) const
Definition: qdatetime.cpp:4616
QDateTime toUTC() const
Definition: qdatetime.h:340
void setTime(QTime time)
Definition: qdatetime.cpp:3972
QTime time() const
Definition: qdatetime.cpp:3769
QDateTime() noexcept
Definition: qdatetime.cpp:3623
QString timeZoneAbbreviation() const
Definition: qdatetime.cpp:3882
QDateTime addSecs(qint64 secs) const
Definition: qdatetime.cpp:4496
static qint64 currentSecsSinceEpoch() noexcept
void setTimeSpec(Qt::TimeSpec spec)
Definition: qdatetime.cpp:3994
Qt::TimeSpec timeSpec() const
Definition: qdatetime.cpp:3785
bool isDaylightTime() const
Definition: qdatetime.cpp:3920
bool isValid() const
Definition: qdatetime.cpp:3741
static QDateTime currentDateTimeUtc()
QDateTime toOffsetFromUtc(int offsetSeconds) const
Definition: qdatetime.cpp:4667
static QDateTime fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec=Qt::LocalTime, int offsetFromUtc=0)
Definition: qdatetime.cpp:4996
qint64 daysTo(const QDateTime &) const
Definition: qdatetime.cpp:4575
static qint64 currentMSecsSinceEpoch() noexcept
qint64 toSecsSinceEpoch() const
Definition: qdatetime.cpp:4113
void setDate(QDate date)
Definition: qdatetime.cpp:3953
QDateTime addYears(int years) const
Definition: qdatetime.cpp:4475
QDate date() const
Definition: qdatetime.cpp:3753
QDateTime addDays(qint64 days) const
Definition: qdatetime.cpp:4425
void setSecsSinceEpoch(qint64 secs)
Definition: qdatetime.cpp:4202
QDateTime::ShortData QDateTimeShortData
Definition: qdatetime_p.h:71
static qint64 localMSecsToEpochMSecs(qint64 localMsecs, DaylightStatus *daylightStatus, QDate *localDate=nullptr, QTime *localTime=nullptr, QString *abbreviation=nullptr)
Definition: qdatetime.cpp:2901
static bool epochMSecsToLocalTime(qint64 msecs, QDate *localDate, QTime *localTime, DaylightStatus *daylightStatus=nullptr)
Definition: qdatetime.cpp:2858
static QDateTime::Data create(QDate toDate, QTime toTime, Qt::TimeSpec toSpec, int offsetSeconds)
Definition: qdatetime.cpp:3397
StatusFlags m_status
Definition: qdatetime_p.h:133
QDateTime::Data QDateTimeData
Definition: qdatetime_p.h:72
The QDebug class provides an output stream for debugging information.
Definition: qdebug.h:65
QDebug & noquote()
Definition: qdebug.h:124
QDebug & nospace()
Definition: qdebug.h:113
QDebug & space()
Definition: qdebug.h:112
Convenience class for custom QDebug operators.
Definition: qdebug.h:176
template< typename Enum > size_t qHash(QFlags< Enum > flags, size_t seed=0) noexcept
static int yearStartWeekDay(int year)
static int monthLength(int month, int year)
static bool leapTest(int year)
static bool julianFromParts(int year, int month, int day, qint64 *jd)
static QCalendar::YearMonthDay partsFromJulian(qint64 jd)
static bool validParts(int year, int month, int day)
static int weekDayOfJulian(qint64 jd)
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
Definition: qstring.h:84
QString dayName(int, FormatType format=LongFormat) const
Definition: qlocale.cpp:2853
@ ShortFormat
Definition: qlocale.h:894
static QLocale c()
Definition: qlocale.h:1134
QString toString(qlonglong i) const
Definition: qlocale.cpp:1981
The QString class provides a Unicode character string.
Definition: qstring.h:388
qulonglong toULongLong(bool *ok=nullptr, int base=10) const
Definition: qstring.cpp:7169
static QString fromLocal8Bit(QByteArrayView ba)
Definition: qstring.cpp:5563
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition: qstring.cpp:6759
The QStringView class provides a unified view on UTF-16 strings with a read-only subset of the QStrin...
Definition: qstringview.h:122
bool startsWith(QStringView s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition: qstringview.h:312
constexpr QChar front() const
Definition: qstringview.h:454
constexpr void chop(qsizetype n) noexcept
Definition: qstringview.h:293
constexpr bool isEmpty() const noexcept
Definition: qstringview.h:463
constexpr qsizetype size() const noexcept
Definition: qstringview.h:239
constexpr QStringView first(qsizetype n) const noexcept
Definition: qstringview.h:280
constexpr QStringView chopped(qsizetype n) const noexcept
Definition: qstringview.h:288
bool endsWith(QStringView s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition: qstringview.h:320
int toInt(bool *ok=nullptr, int base=10) const
Definition: qstring.h:1176
constexpr QStringView last(qsizetype n) const noexcept
Definition: qstringview.h:282
constexpr QStringView sliced(qsizetype pos) const noexcept
Definition: qstringview.h:284
qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition: qstringview.h:328
The QTime class provides clock time functions.
Definition: qdatetime.h:166
int secsTo(QTime t) const
Definition: qdatetime.cpp:2001
constexpr bool isNull() const
Definition: qdatetime.h:174
static QTime currentTime()
QTime addMSecs(int ms) const
Definition: qdatetime.cpp:2024
int hour() const
Definition: qdatetime.cpp:1767
int minute() const
Definition: qdatetime.cpp:1783
bool isValid() const
Definition: qdatetime.cpp:1753
static constexpr QTime fromMSecsSinceStartOfDay(int msecs)
Definition: qdatetime.h:196
int msecsTo(QTime t) const
Definition: qdatetime.cpp:2046
int msec() const
Definition: qdatetime.cpp:1815
bool setHMS(int h, int m, int s, int ms=0)
Definition: qdatetime.cpp:1954
constexpr int msecsSinceStartOfDay() const
Definition: qdatetime.h:197
int second() const
Definition: qdatetime.cpp:1799
constexpr QTime()
Definition: qdatetime.h:170
QTime addSecs(int secs) const
Definition: qdatetime.cpp:1980
The QTimeZone class converts between UTC and local time in a specific time zone.
Definition: qtimezone.h:60
@ OffsetName
Definition: qtimezone.h:80
OffsetData previousTransition(const QDateTime &beforeDateTime) const
Definition: qtimezone.cpp:756
static QTimeZone systemTimeZone()
Definition: qtimezone.cpp:817
bool isValid() const
Definition: qtimezone.cpp:484
static QTimeZone utc()
Definition: qtimezone.cpp:828
bool hasTransitions() const
Definition: qtimezone.cpp:717
OffsetData nextTransition(const QDateTime &afterDateTime) const
Definition: qtimezone.cpp:735
Data dataForLocalTime(qint64 forLocalMSecs, int hint) const
virtual int offsetFromUtc(qint64 atMSecsSinceEpoch) const
static constexpr qint64 invalidSeconds()
constexpr size_type size() const noexcept
void remove(qsizetype i, qsizetype n=1)
qsizetype count() const
reference front()
iterator erase(const_iterator begin, const_iterator end)
const T & at(qsizetype idx) const
T & emplace_back(Args &&...args)
iterator begin() noexcept
QString text
[meta data]
QDate date
[1]
#define local
Definition: zutil.h:30
constexpr Int qMod(Int a, unsigned b)
std::chrono::milliseconds ms
TimeSpec
Definition: qnamespace.h:1259
@ UTC
Definition: qnamespace.h:1261
@ OffsetFromUTC
Definition: qnamespace.h:1262
@ LocalTime
Definition: qnamespace.h:1260
@ TimeZone
Definition: qnamespace.h:1263
DateFormat
Definition: qnamespace.h:1252
@ RFC2822Date
Definition: qnamespace.h:1255
@ ISODate
Definition: qnamespace.h:1254
@ ISODateWithMs
Definition: qnamespace.h:1256
@ TextDate
Definition: qnamespace.h:1253
@ CaseInsensitive
Definition: qnamespace.h:1283
@ SkipEmptyParts
Definition: qnamespace.h:153
string comma
Definition: cordic.py:8
#define QString()
Definition: parse-defines.h:51
#define Q_FALLTHROUGH()
#define Q_UNLIKELY(x)
#define Q_LIKELY(x)
#define Q_UNREACHABLE()
std::pair< T1, T2 > QPair
Definition: qcontainerfwd.h:56
constexpr qint64 MSECS_PER_SEC
Definition: qdatetime.cpp:83
constexpr qint64 TIME_T_MAX
Definition: qdatetime.cpp:84
constexpr qint64 MSECS_PER_HOUR
Definition: qdatetime.cpp:80
constexpr qint64 JULIAN_DAY_FOR_EPOCH
Definition: qdatetime.cpp:85
QDateTimePrivate::QDateTimeShortData ShortData
Definition: qdatetime.cpp:2372
constexpr qint64 SECS_PER_MIN
Definition: qdatetime.cpp:81
QDateTimePrivate::QDateTimeData QDateTimeData
Definition: qdatetime.cpp:2373
constexpr QT_BEGIN_NAMESPACE qint64 SECS_PER_DAY
Definition: qdatetime.cpp:77
constexpr qint64 MSECS_PER_MIN
Definition: qdatetime.cpp:82
constexpr qint64 SECS_PER_HOUR
Definition: qdatetime.cpp:79
constexpr qint64 MSECS_PER_DAY
Definition: qdatetime.cpp:78
EGLOutputLayerEXT EGLint EGLAttrib value
int qRound(qfloat16 d) noexcept
Definition: qfloat16.h:227
time_t qMkTime(struct tm *when)
Definition: qglobal.cpp:3366
void qTzSet()
Definition: qglobal.cpp:3352
unsigned int quint32
Definition: qglobal.h:288
size_t quintptr
Definition: qglobal.h:310
int qint32
Definition: qglobal.h:287
quint64 qulonglong
Definition: qglobal.h:302
unsigned long long quint64
Definition: qglobal.h:299
ptrdiff_t qsizetype
Definition: qglobal.h:308
unsigned int uint
Definition: qglobal.h:334
long long qint64
Definition: qglobal.h:298
QT_BEGIN_NAMESPACE typedef signed char qint8
Definition: qglobal.h:283
#define Q_INT64_C(c)
Definition: qglobal.h:295
ptrdiff_t qintptr
Definition: qglobal.h:309
#define qWarning
Definition: qlogging.h:179
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
Definition: qnumeric.h:144
GLboolean GLboolean GLboolean b
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLuint64 key
GLboolean r
[2]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLenum GLsizei count
GLenum GLenum dst
GLenum GLuint GLenum GLsizei const GLchar * buf
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLenum GLuint GLintptr offset
GLuint name
GLint first
GLint GLsizei GLsizei GLenum format
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLbyte GLbyte tz
Definition: qopenglext.h:6700
GLuint res
Definition: qopenglext.h:8867
const GLubyte * c
Definition: qopenglext.h:12701
GLdouble GLdouble t
[9]
Definition: qopenglext.h:243
GLuint in
Definition: qopenglext.h:8870
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
GLdouble s
[6]
Definition: qopenglext.h:235
GLfloat GLfloat p
[1]
Definition: qopenglext.h:12698
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
QPointF qAbs(const QPointF &p)
Definition: qscroller.cpp:119
int QT_PREPEND_NAMESPACE(QSharedMemoryPrivate)
@ Data
#define sp
Q_UNUSED(salary)
[21]
QTextStream out(stdout)
[7]
QDateTime dateTime
[12]
QTime time
[5]
QSharedPointer< T > other(t)
[5]
QGraphicsWidget * form
QAction * at
view create()
QStringList::Iterator it
bool isValid() const
Definition: qcalendar.h:99
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:53
QThreadStorage< int * > dummy[8]