Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qdatetime.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2021 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qdatetime.h"
6
7#include "qcalendar.h"
8#include "qdatastream.h"
9#include "qdebug.h"
10#include "qlocale.h"
11#include "qset.h"
12
13#include "private/qcalendarmath_p.h"
14#include "private/qdatetime_p.h"
15#if QT_CONFIG(datetimeparser)
16#include "private/qdatetimeparser_p.h"
17#endif
18#ifdef Q_OS_DARWIN
19#include "private/qcore_mac_p.h"
20#endif
21#include "private/qgregoriancalendar_p.h"
22#include "private/qlocale_tools_p.h"
23#include "private/qlocaltime_p.h"
24#include "private/qnumeric_p.h"
25#include "private/qstringconverter_p.h"
26#include "private/qstringiterator_p.h"
27#if QT_CONFIG(timezone)
28#include "private/qtimezoneprivate_p.h"
29#endif
30
31#include <cmath>
32#ifdef Q_OS_WIN
33# include <qt_windows.h>
34#endif
35
36#include <private/qtools_p.h>
37
39
40using namespace Qt::StringLiterals;
41using namespace QtPrivate::DateTimeConstants;
42using namespace QtMiscUtils;
43
44/*****************************************************************************
45 Date/Time Constants
46 *****************************************************************************/
47
48/*****************************************************************************
49 QDate static helper functions
50 *****************************************************************************/
51static_assert(std::is_trivially_copyable_v<QCalendar::YearMonthDay>);
52
54{
55 if ((parts.year < 0 && !cal.isProleptic()) || (parts.year == 0 && !cal.hasYearZero()))
56 return QDate();
57
58 parts.day = qMin(parts.day, cal.daysInMonth(parts.month, parts.year));
59 return cal.dateFromParts(parts);
60}
61
63{
64 if (parts.year) {
65 parts.day = qMin(parts.day, QGregorianCalendar::monthLength(parts.month, parts.year));
66 const auto jd = QGregorianCalendar::julianFromParts(parts.year, parts.month, parts.day);
67 if (jd)
68 return QDate::fromJulianDay(*jd);
69 }
70 return QDate();
71}
72
73/*****************************************************************************
74 Date/Time formatting helper functions
75 *****************************************************************************/
76
77#if QT_CONFIG(textdate)
78static const char qt_shortMonthNames[][4] = {
79 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
80 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
81};
82
83static int fromShortMonthName(QStringView monthName)
84{
85 for (unsigned int i = 0; i < sizeof(qt_shortMonthNames) / sizeof(qt_shortMonthNames[0]); ++i) {
86 if (monthName == QLatin1StringView(qt_shortMonthNames[i], 3))
87 return i + 1;
88 }
89 return -1;
90}
91#endif // textdate
92
93#if QT_CONFIG(datestring) // depends on, so implies, textdate
94namespace {
95using ParsedInt = QSimpleParsedNumber<qulonglong>;
96
97/*
98 Reads a whole number that must be the whole text.
99*/
100ParsedInt readInt(QLatin1StringView text)
101{
102 // Various date formats' fields (e.g. all in ISO) should not accept spaces
103 // or signs, so check that the string starts with a digit and that qstrntoull()
104 // converted the whole string.
105
106 if (text.isEmpty() || !isAsciiDigit(text.front().toLatin1()))
107 return {};
108
110 return res.used == text.size() ? res : ParsedInt{};
111}
112
113ParsedInt readInt(QStringView text)
114{
115 if (text.isEmpty())
116 return {};
117
118 // Converting to Latin-1 because QStringView::toULongLong() works with
119 // US-ASCII only by design anyway.
120 // Also QStringView::toULongLong() can't be used here as it will happily ignore
121 // spaces and accept signs; but various date formats' fields (e.g. all in ISO)
122 // should not.
123 QVarLengthArray<char> latin1(text.size());
124 QLatin1::convertFromUnicode(latin1.data(), text);
125 return readInt(QLatin1StringView{latin1.data(), latin1.size()});
126}
127
128} // namespace
129
130struct ParsedRfcDateTime {
131 QDate date;
132 QTime time;
133 int utcOffset = 0;
134};
135
136static int shortDayFromName(QStringView name)
137{
138 const char16_t shortDayNames[] = u"MonTueWedThuFriSatSun";
139 for (int i = 0; i < 7; i++) {
140 if (name == QStringView(shortDayNames + 3 * i, 3))
141 return i + 1;
142 }
143 return 0;
144}
145
146static ParsedRfcDateTime rfcDateImpl(QStringView s)
147{
148 // Matches "[ddd,] dd MMM yyyy[ hh:mm[:ss]] [±hhmm]" - correct RFC 822, 2822, 5322 format -
149 // or "ddd MMM dd[ hh:mm:ss] yyyy [±hhmm]" - permissive RFC 850, 1036 (read only)
150 ParsedRfcDateTime result;
151
152 QVarLengthArray<QStringView, 6> words;
153
154 auto tokens = s.tokenize(u' ', Qt::SkipEmptyParts);
155 auto it = tokens.begin();
156 for (int i = 0; i < 6 && it != tokens.end(); ++i, ++it)
157 words.emplace_back(*it);
158
159 if (words.size() < 3 || it != tokens.end())
160 return result;
161 const QChar colon(u':');
162 bool ok = true;
163 QDate date;
164
165 const auto isShortName = [](QStringView name) {
166 return (name.size() == 3 && name[0].isUpper()
167 && name[1].isLower() && name[2].isLower());
168 };
169
170 /* Reject entirely (return) if the string is malformed; however, if the date
171 * is merely invalid, (break, so as to) go on to parsing of the time.
172 */
173 int yearIndex;
174 do { // "loop" so that we can use break on merely invalid, but "right shape" date.
175 QStringView dayName;
176 bool rfcX22 = true;
177 const QStringView maybeDayName = words.front();
178 if (maybeDayName.endsWith(u',')) {
179 dayName = maybeDayName.chopped(1);
180 words.erase(words.begin());
181 } else if (!maybeDayName.front().isDigit()) {
182 dayName = maybeDayName;
183 words.erase(words.begin());
184 rfcX22 = false;
185 } // else: dayName is not specified (so we can only be RFC *22)
186 if (words.size() < 3 || words.size() > 5)
187 return result;
188
189 // Don't break before setting yearIndex.
190 int dayIndex, monthIndex;
191 if (rfcX22) {
192 // dd MMM yyyy [hh:mm[:ss]] [±hhmm]
193 dayIndex = 0;
194 monthIndex = 1;
195 yearIndex = 2;
196 } else {
197 // MMM dd[ hh:mm:ss] yyyy [±hhmm]
198 dayIndex = 1;
199 monthIndex = 0;
200 yearIndex = words.size() > 3 && words.at(2).contains(colon) ? 3 : 2;
201 }
202
203 int dayOfWeek = 0;
204 if (!dayName.isEmpty()) {
205 if (!isShortName(dayName))
206 return result;
207 dayOfWeek = shortDayFromName(dayName);
208 if (!dayOfWeek)
209 break;
210 }
211
212 const int day = words.at(dayIndex).toInt(&ok);
213 if (!ok)
214 return result;
215 const int year = words.at(yearIndex).toInt(&ok);
216 if (!ok)
217 return result;
218 const QStringView monthName = words.at(monthIndex);
219 if (!isShortName(monthName))
220 return result;
221 int month = fromShortMonthName(monthName);
222 if (month < 0)
223 break;
224
225 date = QDate(year, month, day);
226 if (dayOfWeek && date.dayOfWeek() != dayOfWeek)
227 date = QDate();
228 } while (false);
229 words.remove(yearIndex);
230 words.remove(0, 2); // month and day-of-month, in some order
231
232 // Time: [hh:mm[:ss]]
233 QTime time;
234 if (words.size() && words.at(0).contains(colon)) {
235 const QStringView when = words.front();
236 words.erase(words.begin());
237 if (when.size() < 5 || when[2] != colon
238 || (when.size() == 8 ? when[5] != colon : when.size() > 5)) {
239 return result;
240 }
241 const int hour = when.first(2).toInt(&ok);
242 if (!ok)
243 return result;
244 const int minute = when.sliced(3, 2).toInt(&ok);
245 if (!ok)
246 return result;
247 const auto secs = when.size() == 8 ? when.last(2).toInt(&ok) : 0;
248 if (!ok)
249 return result;
250 time = QTime(hour, minute, secs);
251 }
252
253 // Offset: [±hh[mm]]
254 int offset = 0;
255 if (words.size()) {
256 const QStringView zone = words.front();
257 words.erase(words.begin());
258 if (words.size() || !(zone.size() == 3 || zone.size() == 5))
259 return result;
260 bool negate = false;
261 if (zone[0] == u'-')
262 negate = true;
263 else if (zone[0] != u'+')
264 return result;
265 const int hour = zone.sliced(1, 2).toInt(&ok);
266 if (!ok)
267 return result;
268 const auto minute = zone.size() == 5 ? zone.last(2).toInt(&ok) : 0;
269 if (!ok)
270 return result;
271 offset = (hour * 60 + minute) * 60;
272 if (negate)
273 offset = -offset;
274 }
275
276 result.date = date;
277 result.time = time;
278 result.utcOffset = offset;
279 return result;
280}
281#endif // datestring
282
283// Return offset in ±HH:mm format
285{
286 return QString::asprintf("%c%02d%s%02d",
287 offset >= 0 ? '+' : '-',
288 qAbs(offset) / int(SECS_PER_HOUR),
289 // Qt::ISODate puts : between the hours and minutes, but Qt:TextDate does not:
290 format == Qt::TextDate ? "" : ":",
291 (qAbs(offset) / 60) % 60);
292}
293
294#if QT_CONFIG(datestring)
295// Parse offset in ±HH[[:]mm] format
296static int fromOffsetString(QStringView offsetString, bool *valid) noexcept
297{
298 *valid = false;
299
300 const qsizetype size = offsetString.size();
301 if (size < 2 || size > 6)
302 return 0;
303
304 // sign will be +1 for a positive and -1 for a negative offset
305 int sign;
306
307 // First char must be + or -
308 const QChar signChar = offsetString[0];
309 if (signChar == u'+')
310 sign = 1;
311 else if (signChar == u'-')
312 sign = -1;
313 else
314 return 0;
315
316 // Split the hour and minute parts
317 const QStringView time = offsetString.sliced(1);
318 qsizetype hhLen = time.indexOf(u':');
319 qsizetype mmIndex;
320 if (hhLen == -1)
321 mmIndex = hhLen = 2; // ±HHmm or ±HH format
322 else
323 mmIndex = hhLen + 1;
324
325 const QStringView hhRef = time.first(qMin(hhLen, time.size()));
326 bool ok = false;
327 const int hour = hhRef.toInt(&ok);
328 if (!ok || hour > 23) // More generous than QTimeZone::MaxUtcOffsetSecs
329 return 0;
330
331 const QStringView mmRef = time.sliced(qMin(mmIndex, time.size()));
332 const int minute = mmRef.isEmpty() ? 0 : mmRef.toInt(&ok);
333 if (!ok || minute < 0 || minute > 59)
334 return 0;
335
336 *valid = true;
337 return sign * ((hour * 60) + minute) * 60;
338}
339#endif // datestring
340
341/*****************************************************************************
342 QDate member functions
343 *****************************************************************************/
344
442QDate::QDate(int y, int m, int d)
443{
444 static_assert(maxJd() == JulianDayMax);
445 static_assert(minJd() == JulianDayMin);
446 jd = QGregorianCalendar::julianFromParts(y, m, d).value_or(nullJd());
447}
448
449QDate::QDate(int y, int m, int d, QCalendar cal)
450{
451 *this = cal.dateFromParts(y, m, d);
452}
453
543int QDate::year(QCalendar cal) const
544{
545 if (isValid()) {
546 const auto parts = cal.partsFromDate(*this);
547 if (parts.isValid())
548 return parts.year;
549 }
550 return 0;
551}
552
557int QDate::year() const
558{
559 if (isValid()) {
560 const auto parts = QGregorianCalendar::partsFromJulian(jd);
561 if (parts.isValid())
562 return parts.year;
563 }
564 return 0;
565}
566
596{
597 if (isValid()) {
598 const auto parts = cal.partsFromDate(*this);
599 if (parts.isValid())
600 return parts.month;
601 }
602 return 0;
603}
604
609int QDate::month() const
610{
611 if (isValid()) {
612 const auto parts = QGregorianCalendar::partsFromJulian(jd);
613 if (parts.isValid())
614 return parts.month;
615 }
616 return 0;
617}
618
628int QDate::day(QCalendar cal) const
629{
630 if (isValid()) {
631 const auto parts = cal.partsFromDate(*this);
632 if (parts.isValid())
633 return parts.day;
634 }
635 return 0;
636}
637
642int QDate::day() const
643{
644 if (isValid()) {
645 const auto parts = QGregorianCalendar::partsFromJulian(jd);
646 if (parts.isValid())
647 return parts.day;
648 }
649 return 0;
650}
651
663{
664 if (isNull())
665 return 0;
666
667 return cal.dayOfWeek(*this);
668}
669
675{
677}
678
689{
690 if (isValid()) {
691 QDate firstDay = cal.dateFromParts(year(cal), 1, 1);
692 if (firstDay.isValid())
693 return firstDay.daysTo(*this) + 1;
694 }
695 return 0;
696}
697
703{
704 if (isValid()) {
705 if (const auto first = QGregorianCalendar::julianFromParts(year(), 1, 1))
706 return jd - *first + 1;
707 }
708 return 0;
709}
710
722{
723 if (isValid()) {
724 const auto parts = cal.partsFromDate(*this);
725 if (parts.isValid())
726 return cal.daysInMonth(parts.month, parts.year);
727 }
728 return 0;
729}
730
736{
737 if (isValid()) {
738 const auto parts = QGregorianCalendar::partsFromJulian(jd);
739 if (parts.isValid())
740 return QGregorianCalendar::monthLength(parts.month, parts.year);
741 }
742 return 0;
743}
744
755{
756 if (isNull())
757 return 0;
758
759 return cal.daysInYear(year(cal));
760}
761
767{
768 return isValid() ? QGregorianCalendar::leapTest(year()) ? 366 : 365 : 0;
769}
770
790int QDate::weekNumber(int *yearNumber) const
791{
792 if (!isValid())
793 return 0;
794
795 // This could be replaced by use of QIso8601Calendar, once we implement it.
796 // The Thursday of the same week determines our answer:
797 const QDate thursday(addDays(4 - dayOfWeek()));
798 if (yearNumber)
799 *yearNumber = thursday.year();
800
801 // Week n's Thurs's DOY has 1 <= DOY - 7*(n-1) < 8, so 0 <= DOY + 6 - 7*n < 7:
802 return (thursday.dayOfYear() + 6) / 7;
803}
804
805#if QT_DEPRECATED_SINCE(6, 9)
806// Only called by deprecated methods (so bootstrap builds warn unused without this #if).
807static QTimeZone asTimeZone(Qt::TimeSpec spec, int offset, const char *warner)
808{
809 if (warner) {
810 switch (spec) {
811 case Qt::TimeZone:
812 qWarning("%s: Pass a QTimeZone instead of Qt::TimeZone.", warner);
813 break;
814 case Qt::LocalTime:
815 if (offset) {
816 qWarning("%s: Ignoring offset (%d seconds) passed with Qt::LocalTime",
817 warner, offset);
818 }
819 break;
820 case Qt::UTC:
821 if (offset) {
822 qWarning("%s: Ignoring offset (%d seconds) passed with Qt::UTC",
823 warner, offset);
824 offset = 0;
825 }
826 break;
828 break;
829 }
830 }
834}
835#endif // Helper for 6.9 deprecation
836
837enum class DaySide { Start, End };
838
839static bool inDateTimeRange(qint64 jd, DaySide side)
840{
841 using Bounds = std::numeric_limits<qint64>;
842 if (jd < Bounds::min() + JULIAN_DAY_FOR_EPOCH)
843 return false;
845 const qint64 maxDay = Bounds::max() / MSECS_PER_DAY;
846 const qint64 minDay = Bounds::min() / MSECS_PER_DAY - 1;
847 // (Divisions rounded towards zero, as MSECS_PER_DAY is even - so doesn't
848 // divide max() - and has factors other than two, so doesn't divide min().)
849 // Range includes start of last day and end of first:
850 switch (side) {
851 case DaySide::Start:
852 return jd > minDay && jd <= maxDay;
853 case DaySide::End:
854 return jd >= minDay && jd < maxDay;
855 }
856 Q_UNREACHABLE_RETURN(false);
857}
858
859static QDateTime toEarliest(QDate day, const QTimeZone &zone)
860{
861 Q_ASSERT(!zone.isUtcOrFixedOffset());
862 // And the day starts in a gap. First find a moment not in that gap.
863 const auto moment = [=](QTime time) {
865 };
866 // Longest routine time-zone transition is 2 hours:
867 QDateTime when = moment(QTime(2, 0));
868 if (!when.isValid()) {
869 // Noon should be safe ...
870 when = moment(QTime(12, 0));
871 if (!when.isValid()) {
872 // ... unless it's a 24-hour jump (moving the date-line)
873 when = moment(QTime(23, 59, 59, 999));
874 if (!when.isValid())
875 return QDateTime();
876 }
877 }
878 int high = when.time().msecsSinceStartOfDay() / 60000;
879 int low = 0;
880 // Binary chop to the right minute
881 while (high > low + 1) {
882 const int mid = (high + low) / 2;
883 const QDateTime probe = QDateTime(day, QTime(mid / 60, mid % 60), zone,
885 if (probe.isValid() && probe.date() == day) {
886 high = mid;
887 when = probe;
888 } else {
889 low = mid;
890 }
891 }
892 // Transitions out of local solar mean time, and the few international
893 // date-line crossings before that (Alaska, Philippines), may have happened
894 // between minute boundaries. Don't try to fix milliseconds.
895 if (QDateTime p = moment(when.time().addSecs(-1)); Q_UNLIKELY(p.isValid() && p.date() == day)) {
896 high *= 60;
897 low *= 60;
898 while (high > low + 1) {
899 const int mid = (high + low) / 2;
900 const int min = mid / 60;
901 const QDateTime probe = moment(QTime(min / 60, min % 60, mid % 60));
902 if (probe.isValid() && probe.date() == day) {
903 high = mid;
904 when = probe;
905 } else {
906 low = mid;
907 }
908 }
909 }
910 return when.isValid() ? when : QDateTime();
911}
912
942{
943 if (!inDateTimeRange(jd, DaySide::Start) || !zone.isValid())
944 return QDateTime();
945
946 QDateTime when(*this, QTime(0, 0), zone,
948 if (Q_UNLIKELY(!when.isValid() || when.date() != *this)) {
949#if QT_CONFIG(timezone)
950 // The start of the day must have fallen in a spring-forward's gap; find the spring-forward:
951 if (zone.timeSpec() == Qt::TimeZone && zone.hasTransitions()) {
952 QTimeZone::OffsetData tran
953 // There's unlikely to be another transition before noon tomorrow.
954 // However, the whole of today may have been skipped !
955 = zone.previousTransition(QDateTime(addDays(1), QTime(12, 0), zone));
956 const QDateTime &at = tran.atUtc.toTimeZone(zone);
957 if (at.isValid() && at.date() == *this)
958 return at;
959 }
960#endif
961
962 when = toEarliest(*this, zone);
963 }
964
965 return when;
966}
967
976
977#if QT_DEPRECATED_SINCE(6, 9)
1006QDateTime QDate::startOfDay(Qt::TimeSpec spec, int offsetSeconds) const
1007{
1008 QTimeZone zone = asTimeZone(spec, offsetSeconds, "QDate::startOfDay");
1009 // If spec was Qt::TimeZone, zone's is Qt::LocalTime.
1010 return zone.timeSpec() == spec ? startOfDay(zone) : QDateTime();
1011}
1012#endif // 6.9 deprecation
1013
1014static QDateTime toLatest(QDate day, const QTimeZone &zone)
1015{
1016 Q_ASSERT(!zone.isUtcOrFixedOffset());
1017 // And the day ends in a gap. First find a moment not in that gap:
1018 const auto moment = [=](QTime time) {
1020 };
1021 // Longest routine time-zone transition is 2 hours:
1022 QDateTime when = moment(QTime(21, 59, 59, 999));
1023 if (!when.isValid()) {
1024 // Noon should be safe ...
1025 when = moment(QTime(12, 0));
1026 if (!when.isValid()) {
1027 // ... unless it's a 24-hour jump (moving the date-line)
1028 when = moment(QTime(0, 0));
1029 if (!when.isValid())
1030 return QDateTime();
1031 }
1032 }
1033 int high = 24 * 60;
1034 int low = when.time().msecsSinceStartOfDay() / 60000;
1035 // Binary chop to the right minute
1036 while (high > low + 1) {
1037 const int mid = (high + low) / 2;
1038 const QDateTime probe = QDateTime(day, QTime(mid / 60, mid % 60, 59, 999), zone,
1040 if (probe.isValid() && probe.date() == day) {
1041 low = mid;
1042 when = probe;
1043 } else {
1044 high = mid;
1045 }
1046 }
1047 // Transitions out of local solar mean time, and the few international
1048 // date-line crossings before that (Alaska, Philippines), may have happened
1049 // between minute boundaries. Don't try to fix milliseconds.
1050 if (QDateTime p = moment(when.time().addSecs(1)); Q_UNLIKELY(p.isValid() && p.date() == day)) {
1051 high *= 60;
1052 low *= 60;
1053 while (high > low + 1) {
1054 const int mid = (high + low) / 2;
1055 const int min = mid / 60;
1056 const QDateTime probe = moment(QTime(min / 60, min % 60, mid % 60, 999));
1057 if (probe.isValid() && probe.date() == day) {
1058 low = mid;
1059 when = probe;
1060 } else {
1061 high = mid;
1062 }
1063 }
1064 }
1065 return when.isValid() ? when : QDateTime();
1066}
1067
1098{
1099 if (!inDateTimeRange(jd, DaySide::End) || !zone.isValid())
1100 return QDateTime();
1101
1102 QDateTime when(*this, QTime(23, 59, 59, 999), zone,
1104 if (Q_UNLIKELY(!when.isValid() || when.date() != *this)) {
1105#if QT_CONFIG(timezone)
1106 // The end of the day must have fallen in a spring-forward's gap; find the spring-forward:
1107 if (zone.timeSpec() == Qt::TimeZone && zone.hasTransitions()) {
1108 QTimeZone::OffsetData tran
1109 // It's unlikely there's been another transition since yesterday noon.
1110 // However, the whole of today may have been skipped !
1111 = zone.nextTransition(QDateTime(addDays(-1), QTime(12, 0), zone));
1112 const QDateTime &at = tran.atUtc.toTimeZone(zone);
1113 if (at.isValid() && at.date() == *this)
1114 return at;
1115 }
1116#endif
1117
1118 when = toLatest(*this, zone);
1119 }
1120 return when;
1121}
1122
1128{
1130}
1131
1132#if QT_DEPRECATED_SINCE(6, 9)
1161QDateTime QDate::endOfDay(Qt::TimeSpec spec, int offsetSeconds) const
1162{
1163 QTimeZone zone = asTimeZone(spec, offsetSeconds, "QDate::endOfDay");
1164 // If spec was Qt::TimeZone, zone's is Qt::LocalTime.
1165 return endOfDay(zone);
1166}
1167#endif // 6.9 deprecation
1168
1169#if QT_CONFIG(datestring) // depends on, so implies, textdate
1170
1171static QString toStringTextDate(QDate date)
1172{
1173 if (date.isValid()) {
1174 QCalendar cal; // Always Gregorian
1175 const auto parts = cal.partsFromDate(date);
1176 if (parts.isValid()) {
1177 const QLatin1Char sp(' ');
1178 return QLocale::c().dayName(cal.dayOfWeek(date), QLocale::ShortFormat) + sp
1179 + cal.monthName(QLocale::c(), parts.month, parts.year, QLocale::ShortFormat)
1180 // Documented to use 4-digit year
1181 + sp + QString::asprintf("%d %04d", parts.day, parts.year);
1182 }
1183 }
1184 return QString();
1185}
1186
1187static QString toStringIsoDate(QDate date)
1188{
1189 const auto parts = QCalendar().partsFromDate(date);
1190 if (parts.isValid() && parts.year >= 0 && parts.year <= 9999)
1191 return QString::asprintf("%04d-%02d-%02d", parts.year, parts.month, parts.day);
1192 return QString();
1193}
1194
1223QString QDate::toString(Qt::DateFormat format) const
1224{
1225 if (!isValid())
1226 return QString();
1227
1228 switch (format) {
1229 case Qt::RFC2822Date:
1230 return QLocale::c().toString(*this, u"dd MMM yyyy");
1231 default:
1232 case Qt::TextDate:
1233 return toStringTextDate(*this);
1234 case Qt::ISODate:
1235 case Qt::ISODateWithMs:
1236 // No calendar dependence
1237 return toStringIsoDate(*this);
1238 }
1239}
1240
1303QString QDate::toString(QStringView format, QCalendar cal) const
1304{
1305 return QLocale::c().toString(*this, format, cal);
1306}
1307
1308// Out-of-line no-calendar overloads, since QCalendar is a non-trivial type
1313QString QDate::toString(QStringView format) const
1314{
1315 return QLocale::c().toString(*this, format, QCalendar());
1316}
1317
1322QString QDate::toString(const QString &format) const
1323{
1324 return QLocale::c().toString(*this, qToStringViewIgnoringNull(format), QCalendar());
1325}
1326#endif // datestring
1327
1338bool QDate::setDate(int year, int month, int day)
1339{
1340 const auto maybe = QGregorianCalendar::julianFromParts(year, month, day);
1341 jd = maybe.value_or(nullJd());
1342 return bool(maybe);
1343}
1344
1356bool QDate::setDate(int year, int month, int day, QCalendar cal)
1357{
1358 *this = QDate(year, month, day, cal);
1359 return isValid();
1360}
1361
1374void QDate::getDate(int *year, int *month, int *day) const
1375{
1376 QCalendar::YearMonthDay parts; // invalid by default
1377 if (isValid())
1379
1380 const bool ok = parts.isValid();
1381 if (year)
1382 *year = ok ? parts.year : 0;
1383 if (month)
1384 *month = ok ? parts.month : 0;
1385 if (day)
1386 *day = ok ? parts.day : 0;
1387}
1388
1400{
1401 if (isNull())
1402 return QDate();
1403
1404 if (qint64 r; Q_UNLIKELY(qAddOverflow(jd, ndays, &r)))
1405 return QDate();
1406 else
1407 return fromJulianDay(r);
1408}
1409
1445QDate QDate::addMonths(int nmonths, QCalendar cal) const
1446{
1447 if (!isValid())
1448 return QDate();
1449
1450 if (nmonths == 0)
1451 return *this;
1452
1453 auto parts = cal.partsFromDate(*this);
1454
1455 if (!parts.isValid())
1456 return QDate();
1457 Q_ASSERT(parts.year || cal.hasYearZero());
1458
1459 parts.month += nmonths;
1460 while (parts.month <= 0) {
1461 if (--parts.year || cal.hasYearZero())
1462 parts.month += cal.monthsInYear(parts.year);
1463 }
1464 int count = cal.monthsInYear(parts.year);
1465 while (parts.month > count) {
1466 parts.month -= count;
1467 count = (++parts.year || cal.hasYearZero()) ? cal.monthsInYear(parts.year) : 0;
1468 }
1469
1470 return fixedDate(parts, cal);
1471}
1472
1477QDate QDate::addMonths(int nmonths) const
1478{
1479 if (isNull())
1480 return QDate();
1481
1482 if (nmonths == 0)
1483 return *this;
1484
1485 auto parts = QGregorianCalendar::partsFromJulian(jd);
1486
1487 if (!parts.isValid())
1488 return QDate();
1489 Q_ASSERT(parts.year);
1490
1491 parts.month += nmonths;
1492 while (parts.month <= 0) {
1493 if (--parts.year) // skip over year 0
1494 parts.month += 12;
1495 }
1496 while (parts.month > 12) {
1497 parts.month -= 12;
1498 if (!++parts.year) // skip over year 0
1499 ++parts.year;
1500 }
1501
1502 return fixedDate(parts);
1503}
1504
1519QDate QDate::addYears(int nyears, QCalendar cal) const
1520{
1521 if (!isValid())
1522 return QDate();
1523
1524 auto parts = cal.partsFromDate(*this);
1525 if (!parts.isValid())
1526 return QDate();
1527
1528 int old_y = parts.year;
1529 parts.year += nyears;
1530
1531 // If we just crossed (or hit) a missing year zero, adjust year by ±1:
1532 if (!cal.hasYearZero() && ((old_y > 0) != (parts.year > 0) || !parts.year))
1533 parts.year += nyears > 0 ? +1 : -1;
1534
1535 return fixedDate(parts, cal);
1536}
1537
1542QDate QDate::addYears(int nyears) const
1543{
1544 if (isNull())
1545 return QDate();
1546
1547 auto parts = QGregorianCalendar::partsFromJulian(jd);
1548 if (!parts.isValid())
1549 return QDate();
1550
1551 int old_y = parts.year;
1552 parts.year += nyears;
1553
1554 // If we just crossed (or hit) a missing year zero, adjust year by ±1:
1555 if ((old_y > 0) != (parts.year > 0) || !parts.year)
1556 parts.year += nyears > 0 ? +1 : -1;
1557
1558 return fixedDate(parts);
1559}
1560
1574{
1575 if (isNull() || d.isNull())
1576 return 0;
1577
1578 // Due to limits on minJd() and maxJd() we know this will never overflow
1579 return d.jd - jd;
1580}
1581
1582
1632#if QT_CONFIG(datestring) // depends on, so implies, textdate
1633
1651QDate QDate::fromString(QStringView string, Qt::DateFormat format)
1652{
1653 if (string.isEmpty())
1654 return QDate();
1655
1656 switch (format) {
1657 case Qt::RFC2822Date:
1658 return rfcDateImpl(string).date;
1659 default:
1660 case Qt::TextDate: {
1661 // Documented as "ddd MMM d yyyy"
1662 QVarLengthArray<QStringView, 4> parts;
1663 auto tokens = string.tokenize(u' ', Qt::SkipEmptyParts);
1664 auto it = tokens.begin();
1665 for (int i = 0; i < 4 && it != tokens.end(); ++i, ++it)
1666 parts.emplace_back(*it);
1667
1668 if (parts.size() != 4 || it != tokens.end())
1669 return QDate();
1670
1671 bool ok = false;
1672 int year = parts.at(3).toInt(&ok);
1673 int day = ok ? parts.at(2).toInt(&ok) : 0;
1674 if (!ok || !day)
1675 return QDate();
1676
1677 const int month = fromShortMonthName(parts.at(1));
1678 if (month == -1) // Month name matches no English or localised name.
1679 return QDate();
1680
1681 return QDate(year, month, day);
1682 }
1683 case Qt::ISODate:
1684 // Semi-strict parsing, must be long enough and have punctuators as separators
1685 if (string.size() >= 10 && string[4].isPunct() && string[7].isPunct()
1686 && (string.size() == 10 || !string[10].isDigit())) {
1687 const ParsedInt year = readInt(string.first(4));
1688 const ParsedInt month = readInt(string.sliced(5, 2));
1689 const ParsedInt day = readInt(string.sliced(8, 2));
1690 if (year.ok() && year.result > 0 && year.result <= 9999 && month.ok() && day.ok())
1691 return QDate(year.result, month.result, day.result);
1692 }
1693 break;
1694 }
1695 return QDate();
1696}
1697
1839QDate QDate::fromString(const QString &string, QStringView format, int baseYear, QCalendar cal)
1840{
1841 QDate date;
1842#if QT_CONFIG(datetimeparser)
1843 QDateTimeParser dt(QMetaType::QDate, QDateTimeParser::FromString, cal);
1844 dt.setDefaultLocale(QLocale::c());
1845 if (dt.parseFormat(format))
1846 dt.fromString(string, &date, nullptr, baseYear);
1847#else
1848 Q_UNUSED(string);
1850 Q_UNUSED(baseYear);
1851 Q_UNUSED(cal);
1852#endif
1853 return date;
1854}
1855
1888QDate QDate::fromString(const QString &string, QStringView format, int baseYear)
1889{
1890 return fromString(string, format, baseYear, QCalendar());
1891}
1892
1900#endif // datestring
1901
1914bool QDate::isValid(int year, int month, int day)
1915{
1917}
1918
1929{
1931}
1932
1947/*****************************************************************************
1948 QTime member functions
1949 *****************************************************************************/
1950
2017QTime::QTime(int h, int m, int s, int ms)
2018{
2019 setHMS(h, m, s, ms);
2020}
2021
2022
2040bool QTime::isValid() const
2041{
2042 return mds > NullTime && mds < MSECS_PER_DAY;
2043}
2044
2045
2054int QTime::hour() const
2055{
2056 if (!isValid())
2057 return -1;
2058
2059 return ds() / MSECS_PER_HOUR;
2060}
2061
2070int QTime::minute() const
2071{
2072 if (!isValid())
2073 return -1;
2074
2075 return (ds() % MSECS_PER_HOUR) / MSECS_PER_MIN;
2076}
2077
2086int QTime::second() const
2087{
2088 if (!isValid())
2089 return -1;
2090
2091 return (ds() / MSECS_PER_SEC) % SECS_PER_MIN;
2092}
2093
2102int QTime::msec() const
2103{
2104 if (!isValid())
2105 return -1;
2106
2107 return ds() % MSECS_PER_SEC;
2108}
2109
2110#if QT_CONFIG(datestring) // depends on, so implies, textdate
2135QString QTime::toString(Qt::DateFormat format) const
2136{
2137 if (!isValid())
2138 return QString();
2139
2140 switch (format) {
2141 case Qt::ISODateWithMs:
2142 return QString::asprintf("%02d:%02d:%02d.%03d", hour(), minute(), second(), msec());
2143 case Qt::RFC2822Date:
2144 case Qt::ISODate:
2145 case Qt::TextDate:
2146 default:
2147 return QString::asprintf("%02d:%02d:%02d", hour(), minute(), second());
2148 }
2149}
2150
2253// ### Qt 7 The 't' format specifiers should be specific to QDateTime (compare fromString).
2254QString QTime::toString(QStringView format) const
2255{
2256 return QLocale::c().toString(*this, format);
2257}
2258#endif // datestring
2259
2271bool QTime::setHMS(int h, int m, int s, int ms)
2272{
2273 if (!isValid(h,m,s,ms)) {
2274 mds = NullTime; // make this invalid
2275 return false;
2276 }
2277 mds = ((h * MINS_PER_HOUR + m) * SECS_PER_MIN + s) * MSECS_PER_SEC + ms;
2278 Q_ASSERT(mds >= 0 && mds < MSECS_PER_DAY);
2279 return true;
2280}
2281
2298{
2299 s %= SECS_PER_DAY;
2300 return addMSecs(s * MSECS_PER_SEC);
2301}
2302
2319{
2320 if (!isValid() || !t.isValid())
2321 return 0;
2322
2323 // Truncate milliseconds as we do not want to consider them.
2324 int ourSeconds = ds() / MSECS_PER_SEC;
2325 int theirSeconds = t.ds() / MSECS_PER_SEC;
2326 return theirSeconds - ourSeconds;
2327}
2328
2342{
2343 QTime t;
2344 if (isValid())
2345 t.mds = QRoundingDown::qMod<MSECS_PER_DAY>(ds() + ms);
2346 return t;
2347}
2348
2364{
2365 if (!isValid() || !t.isValid())
2366 return 0;
2367 return t.ds() - ds();
2368}
2369
2370
2443#if QT_CONFIG(datestring) // depends on, so implies, textdate
2444
2445static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *isMidnight24)
2446{
2448 if (isMidnight24)
2449 *isMidnight24 = false;
2450 // Match /\d\d(:\d\d(:\d\d)?)?([,.]\d+)?/ as "HH[:mm[:ss]][.zzz]"
2451 // The fractional part, if present, is in the same units as the field it follows.
2452 // TextDate restricts fractional parts to the seconds field.
2453
2454 QStringView tail;
2455 const qsizetype dot = string.indexOf(u'.'), comma = string.indexOf(u',');
2456 if (dot != -1) {
2457 tail = string.sliced(dot + 1);
2458 if (tail.indexOf(u'.') != -1) // Forbid second dot:
2459 return QTime();
2460 string = string.first(dot);
2461 } else if (comma != -1) {
2462 tail = string.sliced(comma + 1);
2463 string = string.first(comma);
2464 }
2465 if (tail.indexOf(u',') != -1) // Forbid comma after first dot-or-comma:
2466 return QTime();
2467
2468 const ParsedInt frac = readInt(tail);
2469 // There must be *some* digits in a fractional part; and it must be all digits:
2470 if (tail.isEmpty() ? dot != -1 || comma != -1 : !frac.ok())
2471 return QTime();
2472 Q_ASSERT(frac.ok() ^ tail.isEmpty());
2473 double fraction = frac.ok() ? frac.result * std::pow(0.1, tail.size()) : 0.0;
2474
2475 const qsizetype size = string.size();
2476 if (size < 2 || size > 8)
2477 return QTime();
2478
2479 ParsedInt hour = readInt(string.first(2));
2480 if (!hour.ok() || hour.result > (format == Qt::TextDate ? 23 : 24))
2481 return QTime();
2482
2483 ParsedInt minute{};
2484 if (string.size() > 2) {
2485 if (string[2] == u':' && string.size() > 4)
2486 minute = readInt(string.sliced(3, 2));
2487 if (!minute.ok() || minute.result >= MINS_PER_HOUR)
2488 return QTime();
2489 } else if (format == Qt::TextDate) { // Requires minutes
2490 return QTime();
2491 } else if (frac.ok()) {
2492 Q_ASSERT(!(fraction < 0.0) && fraction < 1.0);
2493 fraction *= MINS_PER_HOUR;
2494 minute.result = qulonglong(fraction);
2495 fraction -= minute.result;
2496 }
2497
2498 ParsedInt second{};
2499 if (string.size() > 5) {
2500 if (string[5] == u':' && string.size() == 8)
2501 second = readInt(string.sliced(6, 2));
2502 if (!second.ok() || second.result >= SECS_PER_MIN)
2503 return QTime();
2504 } else if (frac.ok()) {
2505 if (format == Qt::TextDate) // Doesn't allow fraction of minutes
2506 return QTime();
2507 Q_ASSERT(!(fraction < 0.0) && fraction < 1.0);
2508 fraction *= SECS_PER_MIN;
2509 second.result = qulonglong(fraction);
2510 fraction -= second.result;
2511 }
2512
2513 Q_ASSERT(!(fraction < 0.0) && fraction < 1.0);
2514 // Round millis to nearest (unlike minutes and seconds, rounded down):
2515 int msec = frac.ok() ? qRound(MSECS_PER_SEC * fraction) : 0;
2516 // But handle overflow gracefully:
2517 if (msec == MSECS_PER_SEC) {
2518 // If we can (when data were otherwise valid) validly propagate overflow
2519 // into other fields, do so:
2520 if (isMidnight24 || hour.result < 23 || minute.result < 59 || second.result < 59) {
2521 msec = 0;
2522 if (++second.result == SECS_PER_MIN) {
2523 second.result = 0;
2524 if (++minute.result == MINS_PER_HOUR) {
2525 minute.result = 0;
2526 ++hour.result;
2527 // May need to propagate further via isMidnight24, see below
2528 }
2529 }
2530 } else {
2531 // QTime::fromString() or Qt::TextDate: rounding up would cause
2532 // 23:59:59.999... to become invalid; clip to 999 ms instead:
2533 msec = MSECS_PER_SEC - 1;
2534 }
2535 }
2536
2537 // For ISO date format, 24:0:0 means 0:0:0 on the next day:
2538 if (hour.result == 24 && minute.result == 0 && second.result == 0 && msec == 0) {
2539 Q_ASSERT(format != Qt::TextDate); // It clipped hour at 23, above.
2540 if (isMidnight24)
2541 *isMidnight24 = true;
2542 hour.result = 0;
2543 }
2544
2545 return QTime(hour.result, minute.result, second.result, msec);
2546}
2547
2561QTime QTime::fromString(QStringView string, Qt::DateFormat format)
2562{
2563 if (string.isEmpty())
2564 return QTime();
2565
2566 switch (format) {
2567 case Qt::RFC2822Date:
2568 return rfcDateImpl(string).time;
2569 case Qt::ISODate:
2570 case Qt::ISODateWithMs:
2571 case Qt::TextDate:
2572 default:
2573 return fromIsoTimeString(string, format, nullptr);
2574 }
2575}
2576
2665QTime QTime::fromString(const QString &string, QStringView format)
2666{
2667 QTime time;
2668#if QT_CONFIG(datetimeparser)
2669 QDateTimeParser dt(QMetaType::QTime, QDateTimeParser::FromString, QCalendar());
2670 dt.setDefaultLocale(QLocale::c());
2671 if (dt.parseFormat(format))
2672 dt.fromString(string, nullptr, &time);
2673#else
2674 Q_UNUSED(string);
2676#endif
2677 return time;
2678}
2679#endif // datestring
2680
2681
2696bool QTime::isValid(int h, int m, int s, int ms)
2697{
2698 return (uint(h) < 24 && uint(m) < MINS_PER_HOUR && uint(s) < SECS_PER_MIN
2699 && uint(ms) < MSECS_PER_SEC);
2700}
2701
2702/*****************************************************************************
2703 QDateTime static helper functions
2704 *****************************************************************************/
2705
2706// get the types from QDateTime (through QDateTimePrivate)
2709
2710// Converts milliseconds since the start of 1970 into a date and/or time:
2712{
2713 return JULIAN_DAY_FOR_EPOCH + QRoundingDown::qDiv<MSECS_PER_DAY>(msecs);
2714}
2715
2717{
2719}
2720
2722{
2723 return QTime::fromMSecsSinceStartOfDay(QRoundingDown::qMod<MSECS_PER_DAY>(msecs));
2724}
2725
2726// True if combining days with millis overflows; otherwise, stores result in *sumMillis
2727// The inputs should not have opposite signs.
2728static inline bool daysAndMillisOverflow(qint64 days, qint64 millisInDay, qint64 *sumMillis)
2729{
2730 return qMulOverflow(days, std::integral_constant<qint64, MSECS_PER_DAY>(), sumMillis)
2731 || qAddOverflow(*sumMillis, millisInDay, sumMillis);
2732}
2733
2734// Converts a date/time value into msecs
2736{
2738 qint64 msecs, dayms = time.msecsSinceStartOfDay();
2739 if (days < 0 && dayms > 0) {
2740 ++days;
2741 dayms -= MSECS_PER_DAY;
2742 }
2743 if (daysAndMillisOverflow(days, dayms, &msecs)) {
2744 using Bound = std::numeric_limits<qint64>;
2745 return days < 0 ? Bound::min() : Bound::max();
2746 }
2747 return msecs;
2748}
2749
2767static inline bool millisInSystemRange(qint64 millis, qint64 slack = 0)
2768{
2769 static const auto bounds = QLocalTime::computeSystemMillisRange();
2770 return (bounds.minClip || millis >= bounds.min - slack)
2771 && (bounds.maxClip || millis <= bounds.max + slack);
2772}
2773
2790static int systemTimeYearMatching(int year)
2791{
2792#if defined(Q_OS_WIN) || defined(Q_OS_WASM)// They don't support times before the epoch
2793 static constexpr int forLeapEarly[] = { 1984, 1996, 1980, 1992, 1976, 1988, 1972 };
2794 static constexpr int regularEarly[] = { 1978, 1973, 1974, 1975, 1970, 1971, 1977 };
2795#else // First year fully in 32-bit time_t range is 1902
2796 static constexpr int forLeapEarly[] = { 1928, 1912, 1924, 1908, 1920, 1904, 1916 };
2797 static constexpr int regularEarly[] = { 1905, 1906, 1907, 1902, 1903, 1909, 1910 };
2798#endif
2799 static constexpr int forLeapLate[] = { 2012, 2024, 2036, 2020, 2032, 2016, 2028 };
2800 static constexpr int regularLate[] = { 2034, 2035, 2030, 2031, 2037, 2027, 2033 };
2801 const int dow = QGregorianCalendar::yearStartWeekDay(year);
2802 Q_ASSERT(dow == QDate(year, 1, 1).dayOfWeek());
2803 const int res = (QGregorianCalendar::leapTest(year)
2804 ? (year < 1970 ? forLeapEarly : forLeapLate)
2805 : (year < 1970 ? regularEarly : regularLate))[dow == 7 ? 0 : dow];
2806 Q_ASSERT(QDate(res, 1, 1).dayOfWeek() == dow);
2807 Q_ASSERT(QDate(res, 12, 31).dayOfWeek() == QDate(year, 12, 31).dayOfWeek());
2808 return res;
2809}
2810
2811// Sets up d and status to represent local time at the given UTC msecs since epoch:
2813{
2814 ZoneState result{utcMSecs};
2815 // Within the time_t supported range, localtime() can handle it:
2816 if (millisInSystemRange(utcMSecs)) {
2817 result = QLocalTime::utcToLocal(utcMSecs);
2818 if (result.valid)
2819 return result;
2820 }
2821
2822 // Docs state any LocalTime after 2038-01-18 *will* have any DST applied.
2823 // When this falls outside the supported range, we need to fake it.
2824#if QT_CONFIG(timezone) // Use the system time-zone.
2825 if (const auto sys = QTimeZone::systemTimeZone(); sys.isValid()) {
2826 result.offset = sys.d->offsetFromUtc(utcMSecs);
2827 if (qAddOverflow(utcMSecs, result.offset * MSECS_PER_SEC, &result.when))
2828 return result;
2829 result.dst = sys.d->isDaylightTime(utcMSecs) ? DaylightTime : StandardTime;
2830 result.valid = true;
2831 return result;
2832 }
2833#endif // timezone
2834
2835 // Kludge
2836 // Do the conversion in a year with the same days of the week, so DST
2837 // dates might be right, and adjust by the number of days that was off:
2838 const qint64 jd = msecsToJulianDay(utcMSecs);
2839 const auto ymd = QGregorianCalendar::partsFromJulian(jd);
2840 qint64 diffMillis, fakeUtc;
2841 const auto fakeJd = QGregorianCalendar::julianFromParts(systemTimeYearMatching(ymd.year),
2842 ymd.month, ymd.day);
2843 if (Q_UNLIKELY(!fakeJd
2844 || qMulOverflow(jd - *fakeJd, std::integral_constant<qint64, MSECS_PER_DAY>(),
2845 &diffMillis)
2846 || qSubOverflow(utcMSecs, diffMillis, &fakeUtc))) {
2847 return result;
2848 }
2849
2850 result = QLocalTime::utcToLocal(fakeUtc);
2851 // Now correct result.when for the use of the fake date:
2852 if (!result.valid || qAddOverflow(result.when, diffMillis, &result.when)) {
2853 // If utcToLocal() failed, its return has the fake when; restore utcMSecs.
2854 // Fail on overflow, but preserve offset and DST-ness.
2855 result.when = utcMSecs;
2856 result.valid = false;
2857 }
2858 return result;
2859}
2860
2861static auto millisToWithinRange(qint64 millis)
2862{
2863 struct R {
2864 qint64 shifted = 0;
2865 bool good = false;
2866 } result;
2867 qint64 jd = msecsToJulianDay(millis);
2869 const auto fakeJd = QGregorianCalendar::julianFromParts(systemTimeYearMatching(ymd.year),
2870 ymd.month, ymd.day);
2871 result.good = fakeJd && !daysAndMillisOverflow(*fakeJd - jd, millis, &result.shifted);
2872 return result;
2873}
2874
2929constexpr static QDateTimePrivate::TransitionOptions
2953
2954constexpr static QDateTimePrivate::TransitionOptions
2961
2963{
2964 const QDateTimePrivate::TransitionOptions resolve = toTransitionOptions(dst);
2965 QString abbreviation;
2966 if (millisInSystemRange(millis, MSECS_PER_DAY)) {
2967 abbreviation = QLocalTime::localTimeAbbbreviationAt(millis, resolve);
2968 if (!abbreviation.isEmpty())
2969 return abbreviation;
2970 }
2971
2972 // Otherwise, outside the system range.
2973#if QT_CONFIG(timezone)
2974 // Use the system zone:
2975 const auto sys = QTimeZone::systemTimeZone();
2976 if (sys.isValid()) {
2977 ZoneState state = zoneStateAtMillis(sys, millis, resolve);
2978 if (state.valid)
2979 return sys.d->abbreviation(state.when - state.offset * MSECS_PER_SEC);
2980 }
2981#endif // timezone
2982
2983 // Kludge
2984 // Use a time in the system range with the same day-of-week pattern to its year:
2985 auto fake = millisToWithinRange(millis);
2986 if (Q_LIKELY(fake.good))
2987 return QLocalTime::localTimeAbbbreviationAt(fake.shifted, resolve);
2988
2989 // Overflow, apparently.
2990 return {};
2991}
2992
2993// Determine the offset from UTC at the given local time as millis.
2995 qint64 millis, QDateTimePrivate::TransitionOptions resolve)
2996{
2997 // First, if millis is within a day of the viable range, try mktime() in
2998 // case it does fall in the range and gets useful information:
2999 if (millisInSystemRange(millis, MSECS_PER_DAY)) {
3000 auto result = QLocalTime::mapLocalTime(millis, resolve);
3001 if (result.valid)
3002 return result;
3003 }
3004
3005 // Otherwise, outside the system range.
3006#if QT_CONFIG(timezone)
3007 // Use the system zone:
3008 const auto sys = QTimeZone::systemTimeZone();
3009 if (sys.isValid())
3010 return zoneStateAtMillis(sys, millis, resolve);
3011#endif // timezone
3012
3013 // Kludge
3014 // Use a time in the system range with the same day-of-week pattern to its year:
3015 auto fake = millisToWithinRange(millis);
3016 if (Q_LIKELY(fake.good)) {
3017 auto result = QLocalTime::mapLocalTime(fake.shifted, resolve);
3018 if (result.valid) {
3019 qint64 adjusted;
3020 if (Q_UNLIKELY(qAddOverflow(result.when, millis - fake.shifted, &adjusted))) {
3021 using Bound = std::numeric_limits<qint64>;
3022 adjusted = millis < fake.shifted ? Bound::min() : Bound::max();
3023 }
3024 result.when = adjusted;
3025 } else {
3026 result.when = millis;
3027 }
3028 return result;
3029 }
3030 // Overflow, apparently.
3031 return {millis};
3032}
3033
3034#if QT_CONFIG(timezone)
3035// For a TimeZone and a time expressed in zone msecs encoding, compute the
3036// actual DST-ness and offset, adjusting the time if needed to escape a
3037// spring-forward.
3038QDateTimePrivate::ZoneState QDateTimePrivate::zoneStateAtMillis(
3039 const QTimeZone &zone, qint64 millis, QDateTimePrivate::TransitionOptions resolve)
3040{
3041 Q_ASSERT(zone.isValid());
3042 Q_ASSERT(zone.timeSpec() == Qt::TimeZone);
3043 return zone.d->stateAtZoneTime(millis, resolve);
3044}
3045#endif // timezone
3046
3048 QDateTimePrivate::TransitionOptions resolve)
3049{
3050 if (zone.timeSpec() == Qt::LocalTime)
3052#if QT_CONFIG(timezone)
3053 if (zone.timeSpec() == Qt::TimeZone && zone.isValid())
3054 return QDateTimePrivate::zoneStateAtMillis(zone, millis, resolve);
3055#endif
3056 return {millis};
3057}
3058
3059static inline bool specCanBeSmall(Qt::TimeSpec spec)
3060{
3061 return spec == Qt::LocalTime || spec == Qt::UTC;
3062}
3063
3064static inline bool msecsCanBeSmall(qint64 msecs)
3065{
3066 if constexpr (!QDateTimeData::CanBeSmall)
3067 return false;
3068
3069 ShortData sd;
3070 sd.msecs = qintptr(msecs);
3071 return sd.msecs == msecs;
3072}
3073
3074static constexpr inline
3075QDateTimePrivate::StatusFlags mergeSpec(QDateTimePrivate::StatusFlags status, Qt::TimeSpec spec)
3076{
3077 status &= ~QDateTimePrivate::TimeSpecMask;
3078 status |= QDateTimePrivate::StatusFlags::fromInt(int(spec) << QDateTimePrivate::TimeSpecShift);
3079 return status;
3080}
3081
3082static constexpr inline Qt::TimeSpec extractSpec(QDateTimePrivate::StatusFlags status)
3083{
3085}
3086
3087// Set the Daylight Status if LocalTime set via msecs
3088static constexpr inline QDateTimePrivate::StatusFlags
3089mergeDaylightStatus(QDateTimePrivate::StatusFlags sf, QDateTimePrivate::DaylightStatus status)
3090{
3091 sf &= ~QDateTimePrivate::DaylightMask;
3092 if (status == QDateTimePrivate::DaylightTime) {
3094 } else if (status == QDateTimePrivate::StandardTime) {
3096 }
3097 return sf;
3098}
3099
3100// Get the DST Status if LocalTime set via msecs
3101static constexpr inline
3102QDateTimePrivate::DaylightStatus extractDaylightStatus(QDateTimePrivate::StatusFlags status)
3103{
3104 if (status.testFlag(QDateTimePrivate::SetToDaylightTime))
3106 if (status.testFlag(QDateTimePrivate::SetToStandardTime))
3109}
3110
3111static inline qint64 getMSecs(const QDateTimeData &d)
3112{
3113 if (d.isShort()) {
3114 // same as, but producing better code
3115 //return d.data.msecs;
3116 return qintptr(d.d) >> 8;
3117 }
3118 return d->m_msecs;
3119}
3120
3121static inline QDateTimePrivate::StatusFlags getStatus(const QDateTimeData &d)
3122{
3123 if (d.isShort()) {
3124 // same as, but producing better code
3125 //return StatusFlag(d.data.status);
3126 return QDateTimePrivate::StatusFlag(qintptr(d.d) & 0xFF);
3127 }
3128 return d->m_status;
3129}
3130
3132{
3133 return extractSpec(getStatus(d));
3134}
3135
3136/* True if we *can cheaply determine* that a and b use the same offset.
3137 If they use different offsets or it would be expensive to find out, false.
3138 Calls to toMSecsSinceEpoch() are expensive, for these purposes.
3139 See QDateTime's comparison operators.
3140*/
3141static inline bool usesSameOffset(const QDateTimeData &a, const QDateTimeData &b)
3142{
3143 const auto status = getStatus(a);
3144 if (status != getStatus(b))
3145 return false;
3146 // Status includes DST-ness, so we now know they match in it.
3147
3148 switch (extractSpec(status)) {
3149 case Qt::LocalTime:
3150 case Qt::UTC:
3151 return true;
3152
3153 case Qt::TimeZone:
3154 /* TimeZone always determines its offset during construction of the
3155 private data. Even if we're in different zones, what matters is the
3156 offset actually in effect at the specific time. (DST can cause things
3157 with the same time-zone to use different offsets, but we already
3158 checked their DSTs match.) */
3159 case Qt::OffsetFromUTC: // always knows its offset, which is all that matters.
3160 Q_ASSERT(!a.isShort() && !b.isShort());
3161 return a->m_offsetFromUtc == b->m_offsetFromUtc;
3162 }
3163 Q_UNREACHABLE_RETURN(false);
3164}
3165
3166// Refresh the LocalTime or TimeZone validity and offset
3168 QDateTimePrivate::TransitionOptions resolve)
3169{
3170 Q_ASSERT(zone.timeSpec() == Qt::TimeZone || zone.timeSpec() == Qt::LocalTime);
3171 auto status = getStatus(d);
3172 Q_ASSERT(extractSpec(status) == zone.timeSpec());
3173 int offsetFromUtc = 0;
3174 /* Callers are:
3175 * QDTP::create(), where d is too new to be shared yet
3176 * reviseTimeZone(), which detach()es if not short before calling this
3177 * checkValidDateTime(), always follows a setDateTime() that detach()ed if not short
3178
3179 So we can assume d is not shared. We only need to detach() if we convert
3180 from short to pimpled to accommodate an oversize msecs, which can only be
3181 needed in the unlikely event we revise it.
3182 */
3183
3184 // If not valid date and time then is invalid
3186 status.setFlag(QDateTimePrivate::ValidDateTime, false);
3187 } else {
3188 // We have a valid date and time and a Qt::LocalTime or Qt::TimeZone
3189 // that might fall into a "missing" DST transition hour.
3190 qint64 msecs = getMSecs(d);
3192 Q_ASSERT(!state.valid || (state.offset >= -SECS_PER_DAY && state.offset <= SECS_PER_DAY));
3193 if (state.dst == QDateTimePrivate::UnknownDaylightTime) { // Overflow
3194 status.setFlag(QDateTimePrivate::ValidDateTime, false);
3195 } else if (state.valid) {
3196 status = mergeDaylightStatus(status, state.dst);
3197 offsetFromUtc = state.offset;
3198 status.setFlag(QDateTimePrivate::ValidDateTime, true);
3199 if (Q_UNLIKELY(msecs != state.when)) {
3200 // Update msecs to the resolution:
3201 if (status.testFlag(QDateTimePrivate::ShortData)) {
3202 if (msecsCanBeSmall(state.when)) {
3203 d.data.msecs = qintptr(state.when);
3204 } else {
3205 // Convert to long-form so we can hold the revised msecs:
3206 status.setFlag(QDateTimePrivate::ShortData, false);
3207 d.detach();
3208 }
3209 }
3210 if (!status.testFlag(QDateTimePrivate::ShortData))
3211 d->m_msecs = state.when;
3212 }
3213 } else {
3214 status.setFlag(QDateTimePrivate::ValidDateTime, false);
3215 }
3216 }
3217
3218 if (status.testFlag(QDateTimePrivate::ShortData)) {
3219 d.data.status = status.toInt();
3220 } else {
3221 d->m_status = status;
3222 d->m_offsetFromUtc = offsetFromUtc;
3223 }
3224}
3225
3226// Check the UTC / offsetFromUTC validity
3228{
3229 auto status = getStatus(d);
3231 status.setFlag(QDateTimePrivate::ValidDateTime,
3233
3234 if (status.testFlag(QDateTimePrivate::ShortData))
3235 d.data.status = status.toInt();
3236 else
3237 d->m_status = status;
3238}
3239
3240// Clean up and set status after assorted set-up or reworking:
3242{
3243 auto spec = extractSpec(getStatus(d));
3244 switch (spec) {
3245 case Qt::OffsetFromUTC:
3246 case Qt::UTC:
3247 // for these, a valid date and a valid time imply a valid QDateTime
3249 break;
3250 case Qt::TimeZone:
3251 case Qt::LocalTime:
3252 // For these, we need to check whether (the zone is valid and) the time
3253 // is valid for the zone. Expensive, but we have no other option.
3255 break;
3256 }
3257}
3258
3259static void reviseTimeZone(QDateTimeData &d, const QTimeZone &zone,
3261{
3262 Qt::TimeSpec spec = zone.timeSpec();
3263 auto status = mergeSpec(getStatus(d), spec);
3264 bool reuse = d.isShort();
3265 int offset = 0;
3266
3267 switch (spec) {
3268 case Qt::UTC:
3269 Q_ASSERT(zone.fixedSecondsAheadOfUtc() == 0);
3270 break;
3271 case Qt::OffsetFromUTC:
3272 reuse = false;
3273 offset = zone.fixedSecondsAheadOfUtc();
3275 break;
3276 case Qt::TimeZone:
3277 reuse = false;
3278 break;
3279 case Qt::LocalTime:
3280 break;
3281 }
3282
3284 if (reuse) {
3285 d.data.status = status.toInt();
3286 } else {
3287 d.detach();
3288 d->m_status = status & ~QDateTimePrivate::ShortData;
3289 d->m_offsetFromUtc = offset;
3290#if QT_CONFIG(timezone)
3291 if (spec == Qt::TimeZone)
3292 d->m_timeZone = zone;
3293#endif // timezone
3294 }
3295
3298 else
3300}
3301
3303{
3304 // If the date is valid and the time is not we set time to 00:00:00
3305 if (!time.isValid() && date.isValid())
3307
3308 QDateTimePrivate::StatusFlags newStatus = { };
3309
3310 // Set date value and status
3311 qint64 days = 0;
3312 if (date.isValid()) {
3314 newStatus = QDateTimePrivate::ValidDate;
3315 }
3316
3317 // Set time value and status
3318 int ds = 0;
3319 if (time.isValid()) {
3321 newStatus |= QDateTimePrivate::ValidTime;
3322 }
3323 Q_ASSERT(ds < MSECS_PER_DAY);
3324 // Only the later parts of the very first day are representable - its start
3325 // would overflow - so get ds the same side of 0 as days:
3326 if (days < 0 && ds > 0) {
3327 days++;
3328 ds -= MSECS_PER_DAY;
3329 }
3330
3331 // Check in representable range:
3332 qint64 msecs = 0;
3333 if (daysAndMillisOverflow(days, qint64(ds), &msecs)) {
3334 newStatus = QDateTimePrivate::StatusFlags{};
3335 msecs = 0;
3336 }
3337 if (d.isShort()) {
3338 // let's see if we can keep this short
3339 if (msecsCanBeSmall(msecs)) {
3340 // yes, we can
3341 d.data.msecs = qintptr(msecs);
3343 d.data.status |= newStatus.toInt();
3344 } else {
3345 // nope...
3346 d.detach();
3347 }
3348 }
3349 if (!d.isShort()) {
3350 d.detach();
3351 d->m_msecs = msecs;
3353 d->m_status |= newStatus;
3354 }
3355}
3356
3357static std::pair<QDate, QTime> getDateTime(const QDateTimeData &d)
3358{
3359 auto status = getStatus(d);
3360 const qint64 msecs = getMSecs(d);
3361 const auto dayMilli = QRoundingDown::qDivMod<MSECS_PER_DAY>(msecs);
3362 return { status.testFlag(QDateTimePrivate::ValidDate)
3363 ? QDate::fromJulianDay(JULIAN_DAY_FOR_EPOCH + dayMilli.quotient)
3364 : QDate(),
3365 status.testFlag(QDateTimePrivate::ValidTime)
3366 ? QTime::fromMSecsSinceStartOfDay(dayMilli.remainder)
3367 : QTime() };
3368}
3369
3370/*****************************************************************************
3371 QDateTime::Data member functions
3372 *****************************************************************************/
3373
3374inline QDateTime::Data::Data() noexcept
3375{
3376 // default-constructed data has a special exception:
3377 // it can be small even if CanBeSmall == false
3378 // (optimization so we don't allocate memory in the default constructor)
3380 d = reinterpret_cast<QDateTimePrivate *>(value);
3381}
3382
3383inline QDateTime::Data::Data(const QTimeZone &zone)
3384{
3385 Qt::TimeSpec spec = zone.timeSpec();
3386 if (CanBeSmall && Q_LIKELY(specCanBeSmall(spec))) {
3388 d = reinterpret_cast<QDateTimePrivate *>(value);
3389 Q_ASSERT(isShort());
3390 } else {
3391 // the structure is too small, we need to detach
3392 d = new QDateTimePrivate;
3393 d->ref.ref();
3394 d->m_status = mergeSpec({}, spec);
3395 if (spec == Qt::OffsetFromUTC)
3396 d->m_offsetFromUtc = zone.fixedSecondsAheadOfUtc();
3397 else if (spec == Qt::TimeZone)
3398 d->m_timeZone = zone;
3399 Q_ASSERT(!isShort());
3400 }
3401}
3402
3403inline QDateTime::Data::Data(const Data &other) noexcept
3404 : data(other.data)
3405{
3406 if (!isShort()) {
3407 // check if we could shrink
3409 ShortData sd;
3410 sd.msecs = qintptr(d->m_msecs);
3411 sd.status = (d->m_status | QDateTimePrivate::ShortData).toInt();
3412 data = sd;
3413 } else {
3414 // no, have to keep it big
3415 d->ref.ref();
3416 }
3417 }
3418}
3419
3420inline QDateTime::Data::Data(Data &&other) noexcept
3421 : data(other.data)
3422{
3423 // reset the other to a short state
3424 Data dummy;
3425 Q_ASSERT(dummy.isShort());
3426 other.data = dummy.data;
3427}
3428
3429inline QDateTime::Data &QDateTime::Data::operator=(const Data &other) noexcept
3430{
3431 if (isShort() ? data == other.data : d == other.d)
3432 return *this;
3433
3434 auto x = d;
3435 d = other.d;
3436 if (!other.isShort()) {
3437 // check if we could shrink
3438 if (specCanBeSmall(extractSpec(other.d->m_status)) && msecsCanBeSmall(other.d->m_msecs)) {
3439 ShortData sd;
3440 sd.msecs = qintptr(other.d->m_msecs);
3441 sd.status = (other.d->m_status | QDateTimePrivate::ShortData).toInt();
3442 data = sd;
3443 } else {
3444 // no, have to keep it big
3445 other.d->ref.ref();
3446 }
3447 }
3448
3449 if (!(quintptr(x) & QDateTimePrivate::ShortData) && !x->ref.deref())
3450 delete x;
3451 return *this;
3452}
3453
3454inline QDateTime::Data::~Data()
3455{
3456 if (!isShort() && !d->ref.deref())
3457 delete d;
3458}
3459
3460inline bool QDateTime::Data::isShort() const
3461{
3463
3464 // sanity check:
3466
3467 // even if CanBeSmall = false, we have short data for a default-constructed
3468 // QDateTime object. But it's unlikely.
3469 if constexpr (CanBeSmall)
3470 return Q_LIKELY(b);
3471 return Q_UNLIKELY(b);
3472}
3473
3474inline void QDateTime::Data::detach()
3475{
3477 bool wasShort = isShort();
3478 if (wasShort) {
3479 // force enlarging
3480 x = new QDateTimePrivate;
3481 x->m_status = QDateTimePrivate::StatusFlags::fromInt(data.status) & ~QDateTimePrivate::ShortData;
3482 x->m_msecs = data.msecs;
3483 } else {
3484 if (d->ref.loadRelaxed() == 1)
3485 return;
3486
3487 x = new QDateTimePrivate(*d);
3488 }
3489
3490 x->ref.storeRelaxed(1);
3491 if (!wasShort && !d->ref.deref())
3492 delete d;
3493 d = x;
3494}
3495
3496void QDateTime::Data::invalidate()
3497{
3498 if (isShort()) {
3499 data.status &= ~int(QDateTimePrivate::ValidityMask);
3500 } else {
3501 detach();
3502 d->m_status &= ~QDateTimePrivate::ValidityMask;
3503 }
3504}
3505
3506QTimeZone QDateTime::Data::timeZone() const
3507{
3508 switch (getSpec(*this)) {
3509 case Qt::UTC:
3510 return QTimeZone::UTC;
3511 case Qt::OffsetFromUTC:
3513 case Qt::TimeZone:
3514#if QT_CONFIG(timezone)
3515 if (d->m_timeZone.isValid())
3516 return d->m_timeZone;
3517#endif
3518 break;
3519 case Qt::LocalTime:
3520 return QTimeZone::LocalTime;
3521 }
3522 return QTimeZone();
3523}
3524
3525inline const QDateTimePrivate *QDateTime::Data::operator->() const
3526{
3527 Q_ASSERT(!isShort());
3528 return d;
3529}
3530
3531inline QDateTimePrivate *QDateTime::Data::operator->()
3532{
3533 // should we attempt to detach here?
3534 Q_ASSERT(!isShort());
3535 Q_ASSERT(d->ref.loadRelaxed() == 1);
3536 return d;
3537}
3538
3539/*****************************************************************************
3540 QDateTimePrivate member functions
3541 *****************************************************************************/
3542
3546{
3547 QDateTime::Data result(zone);
3549 if (zone.isUtcOrFixedOffset())
3551 else
3553 return result;
3554}
3555
3556/*****************************************************************************
3557 QDateTime member functions
3558 *****************************************************************************/
3559
3944{
3945#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) || defined(QT_BOOTSTRAPPED) || QT_POINTER_SIZE == 8
3946 static_assert(sizeof(ShortData) == sizeof(qint64));
3947 static_assert(sizeof(Data) == sizeof(qint64));
3948#endif
3949 static_assert(sizeof(ShortData) >= sizeof(void*), "oops, Data::swap() is broken!");
3950}
3951
3952#if QT_DEPRECATED_SINCE(6, 9)
3973QDateTime::QDateTime(QDate date, QTime time, Qt::TimeSpec spec, int offsetSeconds)
3974 : d(QDateTimePrivate::create(date, time, asTimeZone(spec, offsetSeconds, "QDateTime"),
3976{
3977}
3978#endif // 6.9 deprecation
3979
3992
3995
4001
4019
4024 : d(other.d)
4025{
4026}
4027
4034 : d(std::move(other.d))
4035{
4036}
4037
4044
4050{
4051 d = other.d;
4052 return *this;
4053}
4070{
4071 // If date or time is invalid, we don't set datetime valid.
4072 return !getStatus(d).testAnyFlag(QDateTimePrivate::ValidityMask);
4073}
4074
4090{
4091 return getStatus(d).testFlag(QDateTimePrivate::ValidDateTime);
4092}
4093
4101{
4102 return getStatus(d).testFlag(QDateTimePrivate::ValidDate) ? msecsToDate(getMSecs(d)) : QDate();
4103}
4104
4112{
4113 return getStatus(d).testFlag(QDateTimePrivate::ValidTime) ? msecsToTime(getMSecs(d)) : QTime();
4114}
4115
4128{
4129 return getSpec(d);
4130}
4131
4147{
4148 return d.timeZone();
4149}
4150
4151#if QT_CONFIG(timezone)
4167QTimeZone QDateTime::timeZone() const
4168{
4169 return d.timeZone().asBackendZone();
4170}
4171#endif // timezone
4172
4196{
4197 const auto status = getStatus(d);
4199 return 0;
4200 // But allow invalid date-time (e.g. gap's resolution) to report its offset.
4201 if (!d.isShort())
4202 return d->m_offsetFromUtc;
4203
4204 auto spec = extractSpec(status);
4205 if (spec == Qt::LocalTime) {
4206 // We didn't cache the value, so we need to calculate it:
4209 }
4210
4211 Q_ASSERT(spec == Qt::UTC);
4212 return 0;
4213}
4214
4237{
4238 if (!isValid())
4239 return QString();
4240
4241 switch (getSpec(d)) {
4242 case Qt::UTC:
4243 return "UTC"_L1;
4244 case Qt::OffsetFromUTC:
4245 return "UTC"_L1 + toOffsetString(Qt::ISODate, d->m_offsetFromUtc);
4246 case Qt::TimeZone:
4247#if !QT_CONFIG(timezone)
4248 break;
4249#else
4251 return d->m_timeZone.abbreviation(*this);
4252#endif // timezone
4253 case Qt::LocalTime:
4256 }
4257 return QString();
4258}
4259
4272{
4273 if (!isValid())
4274 return false;
4275
4276 switch (getSpec(d)) {
4277 case Qt::UTC:
4278 case Qt::OffsetFromUTC:
4279 return false;
4280 case Qt::TimeZone:
4281#if !QT_CONFIG(timezone)
4282 break;
4283#else
4285 if (auto dst = extractDaylightStatus(getStatus(d));
4288 }
4289 return d->m_timeZone.d->isDaylightTime(toMSecsSinceEpoch());
4290#endif // timezone
4291 case Qt::LocalTime: {
4296 }
4298 }
4299 }
4300 return false;
4301}
4302
4323
4348
4349#if QT_DEPRECATED_SINCE(6, 9)
4368void QDateTime::setTimeSpec(Qt::TimeSpec spec)
4369{
4370 reviseTimeZone(d, asTimeZone(spec, 0, "QDateTime::setTimeSpec"),
4372}
4373
4390void QDateTime::setOffsetFromUtc(int offsetSeconds)
4391{
4394}
4395#endif // 6.9 deprecations
4396
4418{
4419 reviseTimeZone(d, toZone, resolve);
4420}
4421
4438{
4439 // Note: QDateTimeParser relies on this producing a useful result, even when
4440 // !isValid(), at least when the invalidity is a time in a fall-back (that
4441 // we'll have adjusted to lie outside it, but marked invalid because it's
4442 // not what was asked for). Other things may be doing similar. But that's
4443 // only relevant when we got enough data for resolution to find it invalid.
4444 const auto status = getStatus(d);
4446 return 0;
4447
4448 switch (extractSpec(status)) {
4449 case Qt::UTC:
4450 return getMSecs(d);
4451
4452 case Qt::OffsetFromUTC:
4453 Q_ASSERT(!d.isShort());
4454 return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC;
4455
4456 case Qt::LocalTime:
4457 if (status.testFlag(QDateTimePrivate::ShortData)) {
4458 // Short form has nowhere to cache the offset, so recompute.
4461 return state.when - state.offset * MSECS_PER_SEC;
4462 }
4463 // Use the offset saved by refreshZonedDateTime() on creation.
4464 return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC;
4465
4466 case Qt::TimeZone:
4467 Q_ASSERT(!d.isShort());
4468#if QT_CONFIG(timezone)
4469 // Use offset refreshZonedDateTime() saved on creation:
4470 if (d->m_timeZone.isValid())
4471 return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC;
4472#endif
4473 return 0;
4474 }
4475 Q_UNREACHABLE_RETURN(0);
4476}
4477
4497
4514{
4515 auto status = getStatus(d);
4516 const auto spec = extractSpec(status);
4517 Q_ASSERT(specCanBeSmall(spec) || !d.isShort());
4519
4520 status &= ~QDateTimePrivate::ValidityMask;
4522 if (spec == Qt::OffsetFromUTC)
4523 state.offset = d->m_offsetFromUtc;
4524 if (!state.offset || !qAddOverflow(msecs, state.offset * MSECS_PER_SEC, &state.when))
4526 } else if (spec == Qt::LocalTime) {
4528 if (state.valid)
4530#if QT_CONFIG(timezone)
4531 } else if (spec == Qt::TimeZone && (d.detach(), d->m_timeZone.isValid())) {
4532 const auto data = d->m_timeZone.d->data(msecs);
4533 if (Q_LIKELY(data.offsetFromUtc != QTimeZonePrivate::invalidSeconds())) {
4534 state.offset = data.offsetFromUtc;
4535 Q_ASSERT(state.offset >= -SECS_PER_DAY && state.offset <= SECS_PER_DAY);
4536 if (!state.offset
4537 || !Q_UNLIKELY(qAddOverflow(msecs, state.offset * MSECS_PER_SEC, &state.when))) {
4539 data.daylightTimeOffset
4542 d->m_msecs = state.when;
4543 d->m_offsetFromUtc = state.offset;
4544 return;
4545 } // else: zone can't represent this UTC time
4546 } // else: zone unable to represent given UTC time (should only happen on overflow).
4547#endif // timezone
4548 }
4550 || (state.offset >= -SECS_PER_DAY && state.offset <= SECS_PER_DAY));
4551
4552 if (msecsCanBeSmall(state.when) && d.isShort()) {
4553 // we can keep short
4554 d.data.msecs = qintptr(state.when);
4555 d.data.status = status.toInt();
4556 } else {
4557 d.detach();
4559 d->m_msecs = state.when;
4560 d->m_offsetFromUtc = state.offset;
4561 }
4562}
4563
4576{
4577 qint64 msecs;
4578 if (!qMulOverflow(secs, std::integral_constant<qint64, MSECS_PER_SEC>(), &msecs))
4579 setMSecsSinceEpoch(msecs);
4580 else
4581 d.invalidate();
4582}
4583
4584#if QT_CONFIG(datestring) // depends on, so implies, textdate
4615QString QDateTime::toString(Qt::DateFormat format) const
4616{
4617 QString buf;
4618 if (!isValid())
4619 return buf;
4620
4621 switch (format) {
4622 case Qt::RFC2822Date:
4623 buf = QLocale::c().toString(*this, u"dd MMM yyyy hh:mm:ss ");
4624 buf += toOffsetString(Qt::TextDate, offsetFromUtc());
4625 return buf;
4626 default:
4627 case Qt::TextDate: {
4628 const std::pair<QDate, QTime> p = getDateTime(d);
4629 buf = toStringTextDate(p.first);
4630 // Insert time between date's day and year:
4631 buf.insert(buf.lastIndexOf(u' '),
4632 u' ' + p.second.toString(Qt::TextDate));
4633 // Append zone/offset indicator, as appropriate:
4634 switch (timeSpec()) {
4635 case Qt::LocalTime:
4636 break;
4637#if QT_CONFIG(timezone)
4638 case Qt::TimeZone:
4639 buf += u' ' + d->m_timeZone.displayName(
4640 *this, QTimeZone::OffsetName, QLocale::c());
4641 break;
4642#endif
4643 default:
4644#if 0 // ### Qt 7 GMT: use UTC instead, see qnamespace.qdoc documentation
4645 buf += " UTC"_L1;
4646#else
4647 buf += " GMT"_L1;
4648#endif
4649 if (getSpec(d) == Qt::OffsetFromUTC)
4650 buf += toOffsetString(Qt::TextDate, offsetFromUtc());
4651 }
4652 return buf;
4653 }
4654 case Qt::ISODate:
4655 case Qt::ISODateWithMs: {
4656 const std::pair<QDate, QTime> p = getDateTime(d);
4657 buf = toStringIsoDate(p.first);
4658 if (buf.isEmpty())
4659 return QString(); // failed to convert
4660 buf += u'T' + p.second.toString(format);
4661 switch (getSpec(d)) {
4662 case Qt::UTC:
4663 buf += u'Z';
4664 break;
4665 case Qt::OffsetFromUTC:
4666 case Qt::TimeZone:
4667 buf += toOffsetString(Qt::ISODate, offsetFromUtc());
4668 break;
4669 default:
4670 break;
4671 }
4672 return buf;
4673 }
4674 }
4675}
4676
4720QString QDateTime::toString(QStringView format, QCalendar cal) const
4721{
4722 return QLocale::c().toString(*this, format, cal);
4723}
4724
4725// Out-of-line no-calendar overloads, since QCalendar is a non-trivial type
4730QString QDateTime::toString(QStringView format) const
4731{
4732 return QLocale::c().toString(*this, format, QCalendar());
4733}
4734
4739QString QDateTime::toString(const QString &format) const
4740{
4741 return QLocale::c().toString(*this, qToStringViewIgnoringNull(format), QCalendar());
4742}
4743#endif // datestring
4744
4745static inline void massageAdjustedDateTime(QDateTimeData &d, QDate date, QTime time, bool forward)
4746{
4747 const QDateTimePrivate::TransitionOptions resolve = toTransitionOptions(
4750 auto status = getStatus(d);
4753 auto spec = extractSpec(status);
4757 return;
4758 }
4759 qint64 local = timeToMSecs(date, time);
4760 const QDateTimePrivate::ZoneState state = stateAtMillis(d.timeZone(), local, resolve);
4763 status.setFlag(QDateTimePrivate::ValidDateTime, false);
4764 else
4766
4767 if (status & QDateTimePrivate::ShortData) {
4768 d.data.msecs = state.when;
4769 d.data.status = status.toInt();
4770 } else {
4771 d.detach();
4772 d->m_status = status;
4773 if (state.valid) {
4774 d->m_msecs = state.when;
4775 d->m_offsetFromUtc = state.offset;
4776 }
4777 }
4778}
4779
4796{
4797 if (isNull())
4798 return QDateTime();
4799
4800 QDateTime dt(*this);
4801 std::pair<QDate, QTime> p = getDateTime(d);
4802 massageAdjustedDateTime(dt.d, p.first.addDays(ndays), p.second, ndays >= 0);
4803 return dt;
4804}
4805
4822{
4823 if (isNull())
4824 return QDateTime();
4825
4826 QDateTime dt(*this);
4827 std::pair<QDate, QTime> p = getDateTime(d);
4828 massageAdjustedDateTime(dt.d, p.first.addMonths(nmonths), p.second, nmonths >= 0);
4829 return dt;
4830}
4831
4848{
4849 if (isNull())
4850 return QDateTime();
4851
4852 QDateTime dt(*this);
4853 std::pair<QDate, QTime> p = getDateTime(d);
4854 massageAdjustedDateTime(dt.d, p.first.addYears(nyears), p.second, nyears >= 0);
4855 return dt;
4856}
4857
4869{
4870 qint64 msecs;
4871 if (qMulOverflow(s, std::integral_constant<qint64, MSECS_PER_SEC>(), &msecs))
4872 return QDateTime();
4873 return addMSecs(msecs);
4874}
4875
4886{
4887 if (!isValid())
4888 return QDateTime();
4889
4890 QDateTime dt(*this);
4891 switch (getSpec(d)) {
4892 case Qt::LocalTime:
4893 case Qt::TimeZone:
4894 // Convert to real UTC first in case this crosses a DST transition:
4895 if (!qAddOverflow(toMSecsSinceEpoch(), msecs, &msecs))
4896 dt.setMSecsSinceEpoch(msecs);
4897 else
4898 dt.d.invalidate();
4899 break;
4900 case Qt::UTC:
4901 case Qt::OffsetFromUTC:
4902 // No need to convert, just add on
4903 if (qAddOverflow(getMSecs(d), msecs, &msecs)) {
4904 dt.d.invalidate();
4905 } else if (d.isShort() && msecsCanBeSmall(msecs)) {
4906 dt.d.data.msecs = qintptr(msecs);
4907 } else {
4908 dt.d.detach();
4909 dt.d->m_msecs = msecs;
4910 }
4911 break;
4912 }
4913 return dt;
4914}
4915
4953{
4954 return date().daysTo(other.date());
4955}
4956
4975{
4976 return msecsTo(other) / MSECS_PER_SEC;
4977}
4978
4994{
4995 if (!isValid() || !other.isValid())
4996 return 0;
4997
4998 return other.toMSecsSinceEpoch() - toMSecsSinceEpoch();
4999}
5000
5070#if QT_DEPRECATED_SINCE(6, 9)
5090QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const
5091{
5092 return toTimeZone(asTimeZone(spec, 0, "toTimeSpec"));
5093}
5094#endif // 6.9 deprecation
5095
5111{
5112 return toTimeZone(QTimeZone::fromSecondsAheadOfUtc(offsetSeconds));
5113}
5114
5130
5143{
5144 return toTimeZone(QTimeZone::UTC);
5145}
5146
5166{
5167 if (timeRepresentation() == timeZone)
5168 return *this;
5169
5170 if (!isValid()) {
5171 QDateTime ret = *this;
5172 ret.setTimeZone(timeZone);
5173 return ret;
5174 }
5175
5176 return fromMSecsSinceEpoch(toMSecsSinceEpoch(), timeZone);
5177}
5178
5187bool QDateTime::equals(const QDateTime &other) const
5188{
5189 if (!isValid())
5190 return !other.isValid();
5191 if (!other.isValid())
5192 return false;
5193
5194 if (usesSameOffset(d, other.d))
5195 return getMSecs(d) == getMSecs(other.d);
5196
5197 // Convert to UTC and compare
5198 return toMSecsSinceEpoch() == other.toMSecsSinceEpoch();
5199}
5200
5208
5218
5234{
5235 if (!lhs.isValid())
5237
5238 if (!rhs.isValid())
5239 return Qt::weak_ordering::greater; // we know that lhs is valid here
5240
5241 if (usesSameOffset(lhs.d, rhs.d))
5242 return Qt::compareThreeWay(getMSecs(lhs.d), getMSecs(rhs.d));
5243
5244 // Convert to UTC and compare
5246}
5247
5309
5324
5427#if defined(Q_OS_WIN)
5428static inline uint msecsFromDecomposed(int hour, int minute, int sec, int msec = 0)
5429{
5430 return MSECS_PER_HOUR * hour + MSECS_PER_MIN * minute + MSECS_PER_SEC * sec + msec;
5431}
5432
5434{
5435 SYSTEMTIME st = {};
5436 GetLocalTime(&st);
5437 return QDate(st.wYear, st.wMonth, st.wDay);
5438}
5439
5441{
5442 QTime ct;
5443 SYSTEMTIME st = {};
5444 GetLocalTime(&st);
5445 ct.setHMS(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
5446 return ct;
5447}
5448
5450{
5451 // We can get local time or "system" time (which is UTC); otherwise, we must
5452 // convert, which is most efficiently done from UTC.
5453 const Qt::TimeSpec spec = zone.timeSpec();
5454 SYSTEMTIME st = {};
5455 // GetSystemTime()'s page links to its partner page for GetLocalTime().
5456 // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtime
5457 (spec == Qt::LocalTime ? GetLocalTime : GetSystemTime)(&st);
5458 QDate d(st.wYear, st.wMonth, st.wDay);
5459 QTime t(msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds));
5460 if (spec == Qt::LocalTime)
5461 return QDateTime(d, t);
5462 QDateTime utc(d, t, QTimeZone::UTC);
5463 return spec == Qt::UTC ? utc : utc.toTimeZone(zone);
5464}
5465
5467{
5468 SYSTEMTIME st = {};
5469 GetSystemTime(&st);
5470 const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay));
5471
5472 return msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds) +
5473 daysAfterEpoch * MSECS_PER_DAY;
5474}
5475
5477{
5478 SYSTEMTIME st = {};
5479 GetSystemTime(&st);
5480 const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay));
5481
5482 return st.wHour * SECS_PER_HOUR + st.wMinute * SECS_PER_MIN + st.wSecond +
5483 daysAfterEpoch * SECS_PER_DAY;
5484}
5485
5486#elif defined(Q_OS_UNIX) // Assume POSIX-compliant
5488{
5489 return QDateTime::currentDateTime().date();
5490}
5491
5493{
5494 return QDateTime::currentDateTime().time();
5495}
5496
5498{
5500}
5501
5503{
5504 struct timespec when;
5505 if (clock_gettime(CLOCK_REALTIME, &when) == 0) // should always succeed
5506 return when.tv_sec * MSECS_PER_SEC + (when.tv_nsec + 500'000) / 1'000'000;
5507 Q_UNREACHABLE_RETURN(0);
5508}
5509
5511{
5512 struct timespec when;
5513 if (clock_gettime(CLOCK_REALTIME, &when) == 0) // should always succeed
5514 return when.tv_sec;
5515 Q_UNREACHABLE_RETURN(0);
5516}
5517#else
5518#error "What system is this?"
5519#endif
5520
5521#if QT_DEPRECATED_SINCE(6, 9)
5545QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec, int offsetSeconds)
5546{
5547 return fromMSecsSinceEpoch(msecs,
5548 asTimeZone(spec, offsetSeconds, "QDateTime::fromMSecsSinceEpoch"));
5549}
5550
5574QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec, int offsetSeconds)
5575{
5576 return fromSecsSinceEpoch(secs,
5577 asTimeZone(spec, offsetSeconds, "QDateTime::fromSecsSinceEpoch"));
5578}
5579#endif // 6.9 deprecations
5580
5595{
5596 QDateTime dt;
5598 if (timeZone.isValid())
5599 dt.setMSecsSinceEpoch(msecs);
5600 return dt;
5601}
5602
5611
5626{
5627 QDateTime dt;
5629 if (timeZone.isValid())
5630 dt.setSecsSinceEpoch(secs);
5631 return dt;
5632}
5633
5642
5643#if QT_CONFIG(datestring) // depends on, so implies, textdate
5644
5661QDateTime QDateTime::fromString(QStringView string, Qt::DateFormat format)
5662{
5663 if (string.isEmpty())
5664 return QDateTime();
5665
5666 switch (format) {
5667 case Qt::RFC2822Date: {
5668 const ParsedRfcDateTime rfc = rfcDateImpl(string);
5669
5670 if (!rfc.date.isValid() || !rfc.time.isValid())
5671 return QDateTime();
5672
5673 QDateTime dateTime(rfc.date, rfc.time, QTimeZone::UTC);
5675 return dateTime;
5676 }
5677 case Qt::ISODate:
5678 case Qt::ISODateWithMs: {
5679 const int size = string.size();
5680 if (size < 10)
5681 return QDateTime();
5682
5683 QDate date = QDate::fromString(string.first(10), Qt::ISODate);
5684 if (!date.isValid())
5685 return QDateTime();
5686 if (size == 10)
5687 return date.startOfDay();
5688
5690 QStringView isoString = string.sliced(10); // trim "yyyy-MM-dd"
5691
5692 // Must be left with T (or space) and at least one digit for the hour:
5693 if (isoString.size() < 2
5694 || !(isoString.startsWith(u'T', Qt::CaseInsensitive)
5695 // RFC 3339 (section 5.6) allows a space here. (It actually
5696 // allows any separator one considers more readable, merely
5697 // giving space as an example - but let's not go wild !)
5698 || isoString.startsWith(u' '))) {
5699 return QDateTime();
5700 }
5701 isoString = isoString.sliced(1); // trim 'T' (or space)
5702
5703 // Check end of string for Time Zone definition, either Z for UTC or ±HH:mm for Offset
5704 if (isoString.endsWith(u'Z', Qt::CaseInsensitive)) {
5705 zone = QTimeZone::UTC;
5706 isoString.chop(1); // trim 'Z'
5707 } else {
5708 // the loop below is faster but functionally equal to:
5709 // const int signIndex = isoString.indexOf(QRegulargExpression(QStringLiteral("[+-]")));
5710 int signIndex = isoString.size() - 1;
5711 Q_ASSERT(signIndex >= 0);
5712 bool found = false;
5713 do {
5714 QChar character(isoString[signIndex]);
5715 found = character == u'+' || character == u'-';
5716 } while (!found && --signIndex >= 0);
5717
5718 if (found) {
5719 bool ok;
5720 int offset = fromOffsetString(isoString.sliced(signIndex), &ok);
5721 if (!ok)
5722 return QDateTime();
5723 isoString = isoString.first(signIndex);
5725 }
5726 }
5727
5728 // Might be end of day (24:00, including variants), which QTime considers invalid.
5729 // ISO 8601 (section 4.2.3) says that 24:00 is equivalent to 00:00 the next day.
5730 bool isMidnight24 = false;
5731 QTime time = fromIsoTimeString(isoString, format, &isMidnight24);
5732 if (!time.isValid())
5733 return QDateTime();
5734 if (isMidnight24) // time is 0:0, but we want the start of next day:
5735 return date.addDays(1).startOfDay(zone);
5736 return QDateTime(date, time, zone);
5737 }
5738 case Qt::TextDate: {
5739 QVarLengthArray<QStringView, 6> parts;
5740
5741 auto tokens = string.tokenize(u' ', Qt::SkipEmptyParts);
5742 auto it = tokens.begin();
5743 for (int i = 0; i < 6 && it != tokens.end(); ++i, ++it)
5744 parts.emplace_back(*it);
5745
5746 // Documented as "ddd MMM d HH:mm:ss yyyy" with optional offset-suffix;
5747 // and allow time either before or after year.
5748 if (parts.size() < 5 || it != tokens.end())
5749 return QDateTime();
5750
5751 // Year and time can be in either order.
5752 // Guess which by looking for ':' in the time
5753 int yearPart = 3;
5754 int timePart = 3;
5755 if (parts.at(3).contains(u':'))
5756 yearPart = 4;
5757 else if (parts.at(4).contains(u':'))
5758 timePart = 4;
5759 else
5760 return QDateTime();
5761
5762 bool ok = false;
5763 int day = parts.at(2).toInt(&ok);
5764 int year = ok ? parts.at(yearPart).toInt(&ok) : 0;
5765 int month = fromShortMonthName(parts.at(1));
5766 if (!ok || year == 0 || day == 0 || month < 1)
5767 return QDateTime();
5768
5769 const QDate date(year, month, day);
5770 if (!date.isValid())
5771 return QDateTime();
5772
5773 const QTime time = fromIsoTimeString(parts.at(timePart), format, nullptr);
5774 if (!time.isValid())
5775 return QDateTime();
5776
5777 if (parts.size() == 5)
5778 return QDateTime(date, time);
5779
5780 QStringView tz = parts.at(5);
5781 if (tz.startsWith("UTC"_L1)
5782 // GMT has long been deprecated as an alias for UTC.
5783 || tz.startsWith("GMT"_L1, Qt::CaseInsensitive)) {
5784 tz = tz.sliced(3);
5785 if (tz.isEmpty())
5787
5788 int offset = fromOffsetString(tz, &ok);
5790 : QDateTime();
5791 }
5792 return QDateTime();
5793 }
5794 }
5795
5796 return QDateTime();
5797}
5798
5896QDateTime QDateTime::fromString(const QString &string, QStringView format, int baseYear,
5897 QCalendar cal)
5898{
5899#if QT_CONFIG(datetimeparser)
5900 QDateTime datetime;
5901
5902 QDateTimeParser dt(QMetaType::QDateTime, QDateTimeParser::FromString, cal);
5903 dt.setDefaultLocale(QLocale::c());
5904 if (dt.parseFormat(format) && (dt.fromString(string, &datetime, baseYear)
5905 || !datetime.isValid())) {
5906 return datetime;
5907 }
5908#else
5909 Q_UNUSED(string);
5911 Q_UNUSED(baseYear);
5912 Q_UNUSED(cal);
5913#endif
5914 return QDateTime();
5915}
5916
5949QDateTime QDateTime::fromString(const QString &string, QStringView format, int baseYear)
5950{
5951 return fromString(string, format, baseYear, QCalendar());
5952}
5953
5961#endif // datestring
5962
5963/*****************************************************************************
5964 Date/time stream functions
5965 *****************************************************************************/
5966
5967#ifndef QT_NO_DATASTREAM
5977{
5978 if (out.version() < QDataStream::Qt_5_0)
5979 return out << quint32(date.jd);
5980 else
5981 return out << date.jd;
5982}
5983
5993{
5994 if (in.version() < QDataStream::Qt_5_0) {
5995 quint32 jd;
5996 in >> jd;
5997 // Older versions consider 0 an invalid jd.
5998 date.jd = (jd != 0 ? jd : QDate::nullJd());
5999 } else {
6000 in >> date.jd;
6001 }
6002
6003 return in;
6004}
6005
6015{
6016 if (out.version() >= QDataStream::Qt_4_0) {
6017 return out << quint32(time.mds);
6018 } else {
6019 // Qt3 had no support for reading -1, QTime() was valid and serialized as 0
6020 return out << quint32(time.isNull() ? 0 : time.mds);
6021 }
6022}
6023
6033{
6034 quint32 ds;
6035 in >> ds;
6036 if (in.version() >= QDataStream::Qt_4_0) {
6037 time.mds = int(ds);
6038 } else {
6039 // Qt3 would write 0 for a null time
6040 time.mds = (ds == 0) ? QTime::NullTime : int(ds);
6041 }
6042 return in;
6043}
6044
6053{
6054 std::pair<QDate, QTime> dateAndTime;
6055
6056 // TODO: new version, route spec and details via QTimeZone
6057 if (out.version() >= QDataStream::Qt_5_2) {
6058
6059 // In 5.2 we switched to using Qt::TimeSpec and added offset and zone support
6060 dateAndTime = getDateTime(dateTime.d);
6061 out << dateAndTime << qint8(dateTime.timeSpec());
6064#if QT_CONFIG(timezone)
6065 else if (dateTime.timeSpec() == Qt::TimeZone)
6066 out << dateTime.timeZone();
6067#endif // timezone
6068
6069 } else if (out.version() == QDataStream::Qt_5_0) {
6070
6071 // In Qt 5.0 we incorrectly serialised all datetimes as UTC.
6072 // This approach is wrong and should not be used again; it breaks
6073 // the guarantee that a deserialised local datetime is the same time
6074 // of day, regardless of which timezone it was serialised in.
6075 dateAndTime = getDateTime((dateTime.isValid() ? dateTime.toUTC() : dateTime).d);
6076 out << dateAndTime << qint8(dateTime.timeSpec());
6077
6078 } else if (out.version() >= QDataStream::Qt_4_0) {
6079
6080 // From 4.0 to 5.1 (except 5.0) we used QDateTimePrivate::Spec
6081 dateAndTime = getDateTime(dateTime.d);
6082 out << dateAndTime;
6083 switch (dateTime.timeSpec()) {
6084 case Qt::UTC:
6086 break;
6087 case Qt::OffsetFromUTC:
6089 break;
6090 case Qt::TimeZone:
6092 break;
6093 case Qt::LocalTime:
6095 break;
6096 }
6097
6098 } else { // version < QDataStream::Qt_4_0
6099
6100 // Before 4.0 there was no TimeSpec, only Qt::LocalTime was supported
6101 dateAndTime = getDateTime(dateTime.d);
6102 out << dateAndTime;
6103
6104 }
6105
6106 return out;
6107}
6108
6118{
6119 QDate dt;
6120 QTime tm;
6121 qint8 ts = 0;
6123
6124 if (in.version() >= QDataStream::Qt_5_2) {
6125
6126 // In 5.2 we switched to using Qt::TimeSpec and added offset and zone support
6127 in >> dt >> tm >> ts;
6128 switch (static_cast<Qt::TimeSpec>(ts)) {
6129 case Qt::UTC:
6130 zone = QTimeZone::UTC;
6131 break;
6132 case Qt::OffsetFromUTC: {
6133 qint32 offset = 0;
6134 in >> offset;
6136 break;
6137 }
6138 case Qt::LocalTime:
6139 break;
6140 case Qt::TimeZone:
6141 in >> zone;
6142 break;
6143 }
6144 // Note: no way to resolve transition ambiguity, when relevant; use default.
6145 dateTime = QDateTime(dt, tm, zone);
6146
6147 } else if (in.version() == QDataStream::Qt_5_0) {
6148
6149 // In Qt 5.0 we incorrectly serialised all datetimes as UTC
6150 in >> dt >> tm >> ts;
6152 if (static_cast<Qt::TimeSpec>(ts) == Qt::LocalTime)
6154
6155 } else if (in.version() >= QDataStream::Qt_4_0) {
6156
6157 // From 4.0 to 5.1 (except 5.0) we used QDateTimePrivate::Spec
6158 in >> dt >> tm >> ts;
6159 switch (static_cast<QDateTimePrivate::Spec>(ts)) {
6160 case QDateTimePrivate::OffsetFromUTC: // No offset was stored, so treat as UTC.
6162 zone = QTimeZone::UTC;
6163 break;
6164 case QDateTimePrivate::TimeZone: // No zone was stored, so treat as LocalTime:
6168 break;
6169 }
6170 dateTime = QDateTime(dt, tm, zone);
6171
6172 } else { // version < QDataStream::Qt_4_0
6173
6174 // Before 4.0 there was no TimeSpec, only Qt::LocalTime was supported
6175 in >> dt >> tm;
6176 dateTime = QDateTime(dt, tm);
6177
6178 }
6179
6180 return in;
6181}
6182#endif // QT_NO_DATASTREAM
6183
6184/*****************************************************************************
6185 Date / Time Debug Streams
6186*****************************************************************************/
6187
6188#if !defined(QT_NO_DEBUG_STREAM) && QT_CONFIG(datestring)
6190{
6191 QDebugStateSaver saver(dbg);
6192 dbg.nospace() << "QDate(";
6193 if (date.isValid())
6194 // QTBUG-91070, ISODate only supports years in the range 0-9999
6195 if (int y = date.year(); y > 0 && y <= 9999)
6196 dbg.nospace() << date.toString(Qt::ISODate);
6197 else
6198 dbg.nospace() << date.toString(Qt::TextDate);
6199 else
6200 dbg.nospace() << "Invalid";
6201 dbg.nospace() << ')';
6202 return dbg;
6203}
6204
6206{
6207 QDebugStateSaver saver(dbg);
6208 dbg.nospace() << "QTime(";
6209 if (time.isValid())
6210 dbg.nospace() << time.toString(u"HH:mm:ss.zzz");
6211 else
6212 dbg.nospace() << "Invalid";
6213 dbg.nospace() << ')';
6214 return dbg;
6215}
6216
6218{
6219 QDebugStateSaver saver(dbg);
6220 dbg.nospace() << "QDateTime(";
6221 if (date.isValid()) {
6222 const Qt::TimeSpec ts = date.timeSpec();
6223 dbg.noquote() << date.toString(u"yyyy-MM-dd HH:mm:ss.zzz t")
6224 << ' ' << ts;
6225 switch (ts) {
6226 case Qt::UTC:
6227 break;
6228 case Qt::OffsetFromUTC:
6229 dbg.space() << date.offsetFromUtc() << 's';
6230 break;
6231 case Qt::TimeZone:
6232#if QT_CONFIG(timezone)
6233 dbg.space() << date.timeZone().id();
6234#endif // timezone
6235 break;
6236 case Qt::LocalTime:
6237 break;
6238 }
6239 } else {
6240 dbg.nospace() << "Invalid";
6241 }
6242 return dbg.nospace() << ')';
6243}
6244#endif // debug_stream && datestring
6245
6252size_t qHash(const QDateTime &key, size_t seed)
6253{
6254 // Use to toMSecsSinceEpoch instead of individual qHash functions for
6255 // QDate/QTime/spec/offset because QDateTime::operator== converts both arguments
6256 // to the same timezone. If we don't, qHash would return different hashes for
6257 // two QDateTimes that are equivalent once converted to the same timezone.
6258 return key.isValid() ? qHash(key.toMSecsSinceEpoch(), seed) : seed;
6259}
6260
6267size_t qHash(QDate key, size_t seed) noexcept
6268{
6269 return qHash(key.toJulianDay(), seed);
6270}
6271
6278size_t qHash(QTime key, size_t seed) noexcept
6279{
6280 return qHash(key.msecsSinceStartOfDay(), seed);
6281}
6282
bool ref() noexcept
bool deref() noexcept
T loadRelaxed() const noexcept
The QCalendar class describes calendar systems.
Definition qcalendar.h:53
YearMonthDay partsFromDate(QDate date) const
Converts a QDate to a year, month, and day of the month.
\inmodule QtCore
\inmodule QtCore\reentrant
Definition qdatastream.h:46
QTimeZone m_timeZone
QDateTime::ShortData QDateTimeShortData
Definition qdatetime_p.h:38
static QDateTime::Data create(QDate toDate, QTime toTime, const QTimeZone &timeZone, QDateTime::TransitionResolution resolve)
static ZoneState localStateAtMillis(qint64 millis, TransitionOptions resolve)
StatusFlags m_status
QDateTime::Data QDateTimeData
Definition qdatetime_p.h:39
static ZoneState expressUtcAsLocal(qint64 utcMSecs)
static QString localNameAtMillis(qint64 millis, DaylightStatus dst)
\inmodule QtCore\reentrant
Definition qdatetime.h:283
void setDate(QDate date, TransitionResolution resolve=TransitionResolution::LegacyBehavior)
Sets the date part of this datetime to date.
int offsetFromUtc() const
~QDateTime()
Destroys the datetime.
static QDateTime currentDateTime()
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setMSecsSinceEpoch(qint64 msecs)
qint64 toMSecsSinceEpoch() const
static QDateTime fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone)
qint64 secsTo(const QDateTime &) const
Returns the number of seconds from this datetime to the other datetime.
QDateTime addMSecs(qint64 msecs) const
Returns a QDateTime object containing a datetime msecs milliseconds later than the datetime of this o...
QDateTime addMonths(int months) const
Returns a QDateTime object containing a datetime nmonths months later than the datetime of this objec...
bool isNull() const
Returns true if both the date and the time are null; otherwise returns false.
QDateTime & operator=(const QDateTime &other) noexcept
Copies the other datetime into this and returns this copy.
static QDateTime fromSecsSinceEpoch(qint64 secs, const QTimeZone &timeZone)
QDateTime toTimeZone(const QTimeZone &toZone) const
qint64 msecsTo(const QDateTime &) const
Returns the number of milliseconds from this datetime to the other datetime.
TransitionResolution
Definition qdatetime.h:341
QDateTime toUTC() const
Returns a copy of this datetime converted to UTC.
void setTimeZone(const QTimeZone &toZone, TransitionResolution resolve=TransitionResolution::LegacyBehavior)
QTime time() const
Returns the time part of the datetime.
QDateTime() noexcept
Constructs a null datetime, nominally using local time.
QDateTime toLocalTime() const
Returns a copy of this datetime converted to local time.
QString timeZoneAbbreviation() const
QDateTime addSecs(qint64 secs) const
Returns a QDateTime object containing a datetime s seconds later than the datetime of this object (or...
static qint64 currentSecsSinceEpoch() noexcept
Qt::TimeSpec timeSpec() const
Returns the time specification of the datetime.
bool isDaylightTime() const
bool isValid() const
Returns true if this datetime represents a definite moment, otherwise false.
static QDateTime currentDateTimeUtc()
QDateTime toOffsetFromUtc(int offsetSeconds) const
qint64 daysTo(const QDateTime &) const
Returns the number of days from this datetime to the other datetime.
static qint64 currentMSecsSinceEpoch() noexcept
qint64 toSecsSinceEpoch() const
friend class QDateTimePrivate
Definition qdatetime.h:601
void setTime(QTime time, TransitionResolution resolve=TransitionResolution::LegacyBehavior)
Sets the time part of this datetime to time.
QTimeZone timeRepresentation() const
QDateTime addYears(int years) const
Returns a QDateTime object containing a datetime nyears years later than the datetime of this object ...
QDate date() const
Returns the date part of the datetime.
QDateTime addDays(qint64 days) const
Returns a QDateTime object containing a datetime ndays days later than the datetime of this object (o...
void setSecsSinceEpoch(qint64 secs)
\inmodule QtCore \reentrant
Definition qdatetime.h:29
int weekNumber(int *yearNum=nullptr) const
Returns the ISO 8601 week number (1 to 53).
QDateTime endOfDay() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
qint64 daysTo(QDate d) const
Returns the number of days from this date to d (which is negative if d is earlier than this date).
constexpr bool isValid() const
Returns true if this date is valid; otherwise returns false.
Definition qdatetime.h:71
constexpr qint64 toJulianDay() const
Converts the date to a Julian day.
Definition qdatetime.h:170
int month() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
static constexpr QDate fromJulianDay(qint64 jd_)
Converts the Julian day jd to a QDate.
Definition qdatetime.h:168
int day() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QDate addDays(qint64 days) const
Returns a QDate object containing a date ndays later than the date of this object (or earlier if nday...
QDate addYears(int years) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
constexpr bool isNull() const
Returns true if the date is null; otherwise returns false.
Definition qdatetime.h:70
int year() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QDate addMonths(int months) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void getDate(int *year, int *month, int *day) const
friend class QDateTime
Definition qdatetime.h:196
static QDate currentDate()
Returns the system clock's current date.
int daysInMonth() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QDateTime startOfDay(const QTimeZone &zone) const
int daysInYear() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
constexpr QDate()
Constructs a null date.
Definition qdatetime.h:32
int dayOfYear() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QDateTime startOfDay() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
int dayOfWeek() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool setDate(int year, int month, int day)
static bool isLeapYear(int year)
Returns true if the specified year is a leap year in the Gregorian calendar; otherwise returns false.
\inmodule QtCore
\inmodule QtCore
static std::optional< qint64 > julianFromParts(int year, int month, int day)
static int yearStartWeekDay(int year)
static int monthLength(int month, int year)
static bool leapTest(int year)
static QCalendar::YearMonthDay partsFromJulian(qint64 jd)
static bool validParts(int year, int month, int day)
static int weekDayOfJulian(qint64 jd)
constexpr const char * data() const noexcept
@ ShortFormat
Definition qlocale.h:875
static QLocale c()
Returns a QLocale object initialized to the "C" locale.
Definition qlocale.h:1146
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
QAtomicInt ref
Definition qshareddata.h:21
\inmodule QtCore
Definition qstringview.h:78
constexpr bool isEmpty() const noexcept
Returns whether this string view is empty - that is, whether {size() == 0}.
constexpr qsizetype size() const noexcept
Returns the size of this string view, in UTF-16 code units (that is, surrogate pairs count as two for...
constexpr QStringView first(qsizetype n) const noexcept
constexpr QStringView chopped(qsizetype n) const noexcept
Returns the substring of length size() - length starting at the beginning of this object.
constexpr QStringView last(qsizetype n) const noexcept
constexpr QChar at(qsizetype n) const noexcept
Returns the character at position n in this string view.
constexpr QChar front() const
qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
constexpr QStringView sliced(qsizetype pos) const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
QChar front() const
Definition qstring.h:230
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1240
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
static constexpr qint64 invalidSeconds()
\inmodule QtCore
Definition qtimezone.h:26
bool isValid() const
Returns true if this time zone is valid.
static QTimeZone fromSecondsAheadOfUtc(int offset)
Definition qtimezone.h:132
constexpr bool isUtcOrFixedOffset() const noexcept
Definition qtimezone.h:142
\inmodule QtCore \reentrant
Definition qdatetime.h:215
int secsTo(QTime t) const
Returns the number of seconds from this time to t.
constexpr bool isNull() const
Returns true if the time is null (i.e., the QTime object was constructed using the default constructo...
Definition qdatetime.h:223
static QTime currentTime()
Returns the current time as reported by the system clock.
QTime addMSecs(int ms) const
Returns a QTime object containing a time ms milliseconds later than the time of this object (or earli...
int hour() const
Returns the hour part (0 to 23) of the time.
int minute() const
Returns the minute part (0 to 59) of the time.
bool isValid() const
Returns true if the time is valid; otherwise returns false.
static constexpr QTime fromMSecsSinceStartOfDay(int msecs)
Returns a new QTime instance with the time set to the number of msecs since the start of the day,...
Definition qdatetime.h:243
int msecsTo(QTime t) const
Returns the number of milliseconds from this time to t.
int msec() const
Returns the millisecond part (0 to 999) of the time.
bool setHMS(int h, int m, int s, int ms=0)
Sets the time to hour h, minute m, seconds s and milliseconds ms.
constexpr int msecsSinceStartOfDay() const
Returns the number of msecs since the start of the day, i.e.
Definition qdatetime.h:244
int second() const
Returns the second part (0 to 59) of the time.
constexpr QTime()
Constructs a null time object.
Definition qdatetime.h:219
QTime addSecs(int secs) const
Returns a QTime object containing a time s seconds later than the time of this object (or earlier if ...
\variable Qt::strong_ordering::less
Definition qcompare.h:221
static const weak_ordering less
Definition qcompare.h:223
static const weak_ordering greater
Definition qcompare.h:225
static const weak_ordering equivalent
Definition qcompare.h:224
#define this
Definition dialogs.cpp:9
QString text
QDate date
[1]
QSet< QString >::iterator it
else opt state
[0]
QString localTimeAbbbreviationAt(qint64 local, QDateTimePrivate::TransitionOptions resolve)
SystemMillisRange computeSystemMillisRange()
QDateTimePrivate::ZoneState utcToLocal(qint64 utcMillis)
QDateTimePrivate::ZoneState mapLocalTime(qint64 local, QDateTimePrivate::TransitionOptions resolve)
Combined button and popup list for selecting options.
constexpr bool isAsciiDigit(char32_t c) noexcept
Definition qtools_p.h:67
constexpr qint64 JulianDayMax
constexpr qint64 SECS_PER_HOUR
constexpr qint64 MSECS_PER_MIN
constexpr qint64 SECS_PER_MIN
constexpr qint64 SECS_PER_DAY
constexpr qint64 JulianDayMin
constexpr qint64 MSECS_PER_DAY
constexpr qint64 JULIAN_DAY_FOR_EPOCH
constexpr qint64 MINS_PER_HOUR
constexpr qint64 MSECS_PER_SEC
constexpr qint64 MSECS_PER_HOUR
@ UTC
@ OffsetFromUTC
@ LocalTime
@ TimeZone
constexpr Qt::strong_ordering compareThreeWay(LeftInt lhs, RightInt rhs) noexcept
DateFormat
@ RFC2822Date
@ ISODate
@ ISODateWithMs
@ TextDate
@ CaseInsensitive
@ SkipEmptyParts
Definition qnamespace.h:128
#define Q_UNLIKELY(x)
#define Q_NEVER_INLINE
#define Q_LIKELY(x)
static QTime msecsToTime(qint64 msecs)
static auto millisToWithinRange(qint64 millis)
static QDateTime toLatest(QDate day, const QTimeZone &zone)
static constexpr QDateTimePrivate::StatusFlags mergeDaylightStatus(QDateTimePrivate::StatusFlags sf, QDateTimePrivate::DaylightStatus status)
static qint64 timeToMSecs(QDate date, QTime time)
static std::pair< QDate, QTime > getDateTime(const QDateTimeData &d)
static constexpr QDateTimePrivate::DaylightStatus extractDaylightStatus(QDateTimePrivate::StatusFlags status)
size_t qHash(const QDateTime &key, size_t seed)
static Qt::TimeSpec getSpec(const QDateTimeData &d)
QDateTimePrivate::QDateTimeShortData ShortData
static void reviseTimeZone(QDateTimeData &d, const QTimeZone &zone, QDateTime::TransitionResolution resolve)
static QDateTimePrivate::StatusFlags getStatus(const QDateTimeData &d)
static qint64 getMSecs(const QDateTimeData &d)
static void massageAdjustedDateTime(QDateTimeData &d, QDate date, QTime time, bool forward)
static bool inDateTimeRange(qint64 jd, DaySide side)
QDateTimePrivate::QDateTimeData QDateTimeData
static bool specCanBeSmall(Qt::TimeSpec spec)
static int systemTimeYearMatching(int year)
static constexpr QDateTimePrivate::StatusFlags mergeSpec(QDateTimePrivate::StatusFlags status, Qt::TimeSpec spec)
static QDate msecsToDate(qint64 msecs)
static QString toOffsetString(Qt::DateFormat format, int offset)
static bool daysAndMillisOverflow(qint64 days, qint64 millisInDay, qint64 *sumMillis)
static QDate fixedDate(QCalendar::YearMonthDay parts, QCalendar cal)
Definition qdatetime.cpp:53
static constexpr QDateTimePrivate::TransitionOptions toTransitionOptions(QDateTime::TransitionResolution res)
static void refreshSimpleDateTime(QDateTimeData &d)
static void setDateTime(QDateTimeData &d, QDate date, QTime time)
static void refreshZonedDateTime(QDateTimeData &d, const QTimeZone &zone, QDateTimePrivate::TransitionOptions resolve)
static bool msecsCanBeSmall(qint64 msecs)
static constexpr Qt::TimeSpec extractSpec(QDateTimePrivate::StatusFlags status)
static bool usesSameOffset(const QDateTimeData &a, const QDateTimeData &b)
static void checkValidDateTime(QDateTimeData &d, QDateTime::TransitionResolution resolve)
Qt::weak_ordering compareThreeWay(const QDateTime &lhs, const QDateTime &rhs)
static QDateTime toEarliest(QDate day, const QTimeZone &zone)
static QDateTimePrivate::ZoneState stateAtMillis(const QTimeZone &zone, qint64 millis, QDateTimePrivate::TransitionOptions resolve)
static bool millisInSystemRange(qint64 millis, qint64 slack=0)
static qint64 msecsToJulianDay(qint64 msecs)
DaySide
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
QSimpleParsedNumber< qulonglong > qstrntoull(const char *begin, qsizetype size, int base)
#define qWarning
Definition qlogging.h:166
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
Definition qnumeric.h:113
std::enable_if_t< std::is_unsigned_v< T >, bool > qSubOverflow(T v1, T v2, T *r)
Definition qnumeric.h:153
std::enable_if_t< std::is_unsigned_v< T >||std::is_signed_v< T >, bool > qMulOverflow(T v1, T v2, T *r)
Definition qnumeric.h:182
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLenum dst
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum GLuint GLintptr offset
GLuint name
GLint first
GLint GLsizei GLsizei GLenum format
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLbyte GLbyte tz
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint in
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
static qreal dot(const QPointF &a, const QPointF &b)
static bool fromString(const QMetaObject *mo, QString s, Allocate &&allocate)
static const QQmlJSScope * resolve(const QQmlJSScope *current, const QStringList &names)
static QT_BEGIN_NAMESPACE bool isDigit(ushort ch)
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static ISC_DATE toDate(QDate t)
static ISC_TIME toTime(QTime t)
QStringView qToStringViewIgnoringNull(const QStringLike &s) noexcept
#define sp
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
size_t quintptr
Definition qtypes.h:167
int qint32
Definition qtypes.h:49
quint64 qulonglong
Definition qtypes.h:64
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
QT_BEGIN_NAMESPACE typedef signed char qint8
Definition qtypes.h:45
ptrdiff_t qintptr
Definition qtypes.h:166
static double LocalTime(double t, double localTZA)
static int toInt(const QChar &qc, int R)
static int sign(int x)
QTextStream out(stdout)
[7]
QDataStream & operator<<(QDataStream &out, const MyClass &myObj)
[4]
QDataStream & operator>>(QDataStream &in, MyClass &myObj)
QDateTime dateTime
[12]
QSharedPointer< T > other(t)
[5]
QAction * at
view create()
bool isValid() const
Definition qcalendar.h:63
\inmodule QtCore \reentrant
Definition qchar.h:18
static char * convertFromUnicode(char *out, QStringView in, QStringConverter::State *state) noexcept