Qt 6.x
The Qt SDK
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
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.
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;
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
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 int 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
436QDate::QDate(int y, int m, int d)
437{
438 static_assert(QDate::maxJd() == JulianDayMax);
439 static_assert(QDate::minJd() == JulianDayMin);
440 jd = QGregorianCalendar::julianFromParts(y, m, d).value_or(nullJd());
441}
442
443QDate::QDate(int y, int m, int d, QCalendar cal)
444{
445 *this = cal.dateFromParts(y, m, d);
446}
447
537int QDate::year(QCalendar cal) const
538{
539 if (isValid()) {
540 const auto parts = cal.partsFromDate(*this);
541 if (parts.isValid())
542 return parts.year;
543 }
544 return 0;
545}
546
551int QDate::year() const
552{
553 if (isValid()) {
554 const auto parts = QGregorianCalendar::partsFromJulian(jd);
555 if (parts.isValid())
556 return parts.year;
557 }
558 return 0;
559}
560
590{
591 if (isValid()) {
592 const auto parts = cal.partsFromDate(*this);
593 if (parts.isValid())
594 return parts.month;
595 }
596 return 0;
597}
598
603int QDate::month() const
604{
605 if (isValid()) {
606 const auto parts = QGregorianCalendar::partsFromJulian(jd);
607 if (parts.isValid())
608 return parts.month;
609 }
610 return 0;
611}
612
622int QDate::day(QCalendar cal) const
623{
624 if (isValid()) {
625 const auto parts = cal.partsFromDate(*this);
626 if (parts.isValid())
627 return parts.day;
628 }
629 return 0;
630}
631
636int QDate::day() const
637{
638 if (isValid()) {
639 const auto parts = QGregorianCalendar::partsFromJulian(jd);
640 if (parts.isValid())
641 return parts.day;
642 }
643 return 0;
644}
645
657{
658 if (isNull())
659 return 0;
660
661 return cal.dayOfWeek(*this);
662}
663
669{
671}
672
683{
684 if (isValid()) {
685 QDate firstDay = cal.dateFromParts(year(cal), 1, 1);
686 if (firstDay.isValid())
687 return firstDay.daysTo(*this) + 1;
688 }
689 return 0;
690}
691
697{
698 if (isValid()) {
699 if (const auto first = QGregorianCalendar::julianFromParts(year(), 1, 1))
700 return jd - *first + 1;
701 }
702 return 0;
703}
704
716{
717 if (isValid()) {
718 const auto parts = cal.partsFromDate(*this);
719 if (parts.isValid())
720 return cal.daysInMonth(parts.month, parts.year);
721 }
722 return 0;
723}
724
730{
731 if (isValid()) {
732 const auto parts = QGregorianCalendar::partsFromJulian(jd);
733 if (parts.isValid())
734 return QGregorianCalendar::monthLength(parts.month, parts.year);
735 }
736 return 0;
737}
738
749{
750 if (isNull())
751 return 0;
752
753 return cal.daysInYear(year(cal));
754}
755
761{
762 return isValid() ? QGregorianCalendar::leapTest(year()) ? 366 : 365 : 0;
763}
764
784int QDate::weekNumber(int *yearNumber) const
785{
786 if (!isValid())
787 return 0;
788
789 // This could be replaced by use of QIso8601Calendar, once we implement it.
790 // The Thursday of the same week determines our answer:
791 const QDate thursday(addDays(4 - dayOfWeek()));
792 if (yearNumber)
793 *yearNumber = thursday.year();
794
795 // Week n's Thurs's DOY has 1 <= DOY - 7*(n-1) < 8, so 0 <= DOY + 6 - 7*n < 7:
796 return (thursday.dayOfYear() + 6) / 7;
797}
798
799#if QT_DEPRECATED_SINCE(6, 9)
800// Only called by deprecated methods (so bootstrap builds warn unused without this #if).
801static QTimeZone asTimeZone(Qt::TimeSpec spec, int offset, const char *warner)
802{
803 if (warner) {
804 switch (spec) {
805 case Qt::TimeZone:
806 qWarning("%s: Pass a QTimeZone instead of Qt::TimeZone.", warner);
807 break;
808 case Qt::LocalTime:
809 if (offset) {
810 qWarning("%s: Ignoring offset (%d seconds) passed with Qt::LocalTime",
811 warner, offset);
812 }
813 break;
814 case Qt::UTC:
815 if (offset) {
816 qWarning("%s: Ignoring offset (%d seconds) passed with Qt::UTC",
817 warner, offset);
818 offset = 0;
819 }
820 break;
822 break;
823 }
824 }
828}
829#endif // Helper for 6.9 deprecation
830
831enum class DaySide { Start, End };
832
833static bool inDateTimeRange(qint64 jd, DaySide side)
834{
835 using Bounds = std::numeric_limits<qint64>;
836 if (jd < Bounds::min() + JULIAN_DAY_FOR_EPOCH)
837 return false;
839 const qint64 maxDay = Bounds::max() / MSECS_PER_DAY;
840 const qint64 minDay = Bounds::min() / MSECS_PER_DAY - 1;
841 // (Divisions rounded towards zero, as MSECS_PER_DAY is even - so doesn't
842 // divide max() - and has factors other than two, so doesn't divide min().)
843 // Range includes start of last day and end of first:
844 switch (side) {
845 case DaySide::Start:
846 return jd > minDay && jd <= maxDay;
847 case DaySide::End:
848 return jd >= minDay && jd < maxDay;
849 }
850 Q_UNREACHABLE_RETURN(false);
851}
852
853static QDateTime toEarliest(QDate day, const QTimeZone &zone)
854{
856 const auto moment = [=](QTime time) { return QDateTime(day, time, zone); };
857 // Longest routine time-zone transition is 2 hours:
858 QDateTime when = moment(QTime(2, 0));
859 if (!when.isValid()) {
860 // Noon should be safe ...
861 when = moment(QTime(12, 0));
862 if (!when.isValid()) {
863 // ... unless it's a 24-hour jump (moving the date-line)
864 when = moment(QTime(23, 59, 59, 999));
865 if (!when.isValid())
866 return QDateTime();
867 }
868 }
869 int high = when.time().msecsSinceStartOfDay() / 60000;
870 int low = 0;
871 // Binary chop to the right minute
872 while (high > low + 1) {
873 const int mid = (high + low) / 2;
874 const QDateTime probe = moment(QTime(mid / 60, mid % 60));
875 if (probe.isValid() && probe.date() == day) {
876 high = mid;
877 when = probe;
878 } else {
879 low = mid;
880 }
881 }
882 // Transitions out of local solar mean time, and the few international
883 // date-line crossings before that (Alaska, Philippines), may have happened
884 // between minute boundaries. Don't try to fix milliseconds.
885 if (QDateTime p = moment(when.time().addSecs(-1)); Q_UNLIKELY(p.isValid() && p.date() == day)) {
886 high *= 60;
887 low *= 60;
888 while (high > low + 1) {
889 const int mid = (high + low) / 2;
890 const int min = mid / 60;
891 const QDateTime probe = moment(QTime(min / 60, min % 60, mid % 60));
892 if (probe.isValid() && probe.date() == day) {
893 high = mid;
894 when = probe;
895 } else {
896 low = mid;
897 }
898 }
899 }
900 return when.isValid() ? when : QDateTime();
901}
902
932{
933 if (!inDateTimeRange(jd, DaySide::Start) || !zone.isValid())
934 return QDateTime();
935
936 QDateTime when(*this, QTime(0, 0), zone);
937 if (Q_LIKELY(when.isValid()))
938 return when;
939
940#if QT_CONFIG(timezone)
941 // The start of the day must have fallen in a spring-forward's gap; find the spring-forward:
942 if (zone.timeSpec() == Qt::TimeZone && zone.hasTransitions()) {
943 QTimeZone::OffsetData tran
944 // There's unlikely to be another transition before noon tomorrow.
945 // However, the whole of today may have been skipped !
946 = zone.previousTransition(QDateTime(addDays(1), QTime(12, 0), zone));
947 const QDateTime &at = tran.atUtc.toTimeZone(zone);
948 if (at.isValid() && at.date() == *this)
949 return at;
950 }
951#endif
952
953 return toEarliest(*this, zone);
954}
955
961{
963}
964
965#if QT_DEPRECATED_SINCE(6, 9)
994QDateTime QDate::startOfDay(Qt::TimeSpec spec, int offsetSeconds) const
995{
996 QTimeZone zone = asTimeZone(spec, offsetSeconds, "QDate::startOfDay");
997 // If spec was Qt::TimeZone, zone's is Qt::LocalTime.
998 return zone.timeSpec() == spec ? startOfDay(zone) : QDateTime();
999}
1000#endif // 6.9 deprecation
1001
1002static QDateTime toLatest(QDate day, const QTimeZone &zone)
1003{
1005 const auto moment = [=](QTime time) { return QDateTime(day, time, zone); };
1006 // Longest routine time-zone transition is 2 hours:
1007 QDateTime when = moment(QTime(21, 59, 59, 999));
1008 if (!when.isValid()) {
1009 // Noon should be safe ...
1010 when = moment(QTime(12, 0));
1011 if (!when.isValid()) {
1012 // ... unless it's a 24-hour jump (moving the date-line)
1013 when = moment(QTime(0, 0));
1014 if (!when.isValid())
1015 return QDateTime();
1016 }
1017 }
1018 int high = 24 * 60;
1019 int low = when.time().msecsSinceStartOfDay() / 60000;
1020 // Binary chop to the right minute
1021 while (high > low + 1) {
1022 const int mid = (high + low) / 2;
1023 const QDateTime probe = moment(QTime(mid / 60, mid % 60, 59, 999));
1024 if (probe.isValid() && probe.date() == day) {
1025 low = mid;
1026 when = probe;
1027 } else {
1028 high = mid;
1029 }
1030 }
1031 // Transitions out of local solar mean time, and the few international
1032 // date-line crossings before that (Alaska, Philippines), may have happened
1033 // between minute boundaries. Don't try to fix milliseconds.
1034 if (QDateTime p = moment(when.time().addSecs(1)); Q_UNLIKELY(p.isValid() && p.date() == day)) {
1035 high *= 60;
1036 low *= 60;
1037 while (high > low + 1) {
1038 const int mid = (high + low) / 2;
1039 const int min = mid / 60;
1040 const QDateTime probe = moment(QTime(min / 60, min % 60, mid % 60, 999));
1041 if (probe.isValid() && probe.date() == day) {
1042 low = mid;
1043 when = probe;
1044 } else {
1045 high = mid;
1046 }
1047 }
1048 }
1049 return when.isValid() ? when : QDateTime();
1050}
1051
1082{
1083 if (!inDateTimeRange(jd, DaySide::End) || !zone.isValid())
1084 return QDateTime();
1085
1086 QDateTime when(*this, QTime(23, 59, 59, 999), zone);
1087 if (Q_LIKELY(when.isValid()))
1088 return when;
1089
1090#if QT_CONFIG(timezone)
1091 // The end of the day must have fallen in a spring-forward's gap; find the spring-forward:
1092 if (zone.timeSpec() == Qt::TimeZone && zone.hasTransitions()) {
1093 QTimeZone::OffsetData tran
1094 // It's unlikely there's been another transition since yesterday noon.
1095 // However, the whole of today may have been skipped !
1096 = zone.nextTransition(QDateTime(addDays(-1), QTime(12, 0), zone));
1097 const QDateTime &at = tran.atUtc.toTimeZone(zone);
1098 if (at.isValid() && at.date() == *this)
1099 return at;
1100 }
1101#endif
1102
1103 return toLatest(*this, zone);
1104}
1105
1111{
1113}
1114
1115#if QT_DEPRECATED_SINCE(6, 9)
1144QDateTime QDate::endOfDay(Qt::TimeSpec spec, int offsetSeconds) const
1145{
1146 QTimeZone zone = asTimeZone(spec, offsetSeconds, "QDate::endOfDay");
1147 // If spec was Qt::TimeZone, zone's is Qt::LocalTime.
1148 return endOfDay(zone);
1149}
1150#endif // 6.9 deprecation
1151
1152#if QT_CONFIG(datestring) // depends on, so implies, textdate
1153
1154static QString toStringTextDate(QDate date)
1155{
1156 if (date.isValid()) {
1157 QCalendar cal; // Always Gregorian
1158 const auto parts = cal.partsFromDate(date);
1159 if (parts.isValid()) {
1160 const QLatin1Char sp(' ');
1162 + cal.monthName(QLocale::c(), parts.month, parts.year, QLocale::ShortFormat)
1163 // Documented to use 4-digit year
1164 + sp + QString::asprintf("%d %04d", parts.day, parts.year);
1165 }
1166 }
1167 return QString();
1168}
1169
1170static QString toStringIsoDate(QDate date)
1171{
1172 const auto parts = QCalendar().partsFromDate(date);
1173 if (parts.isValid() && parts.year >= 0 && parts.year <= 9999)
1174 return QString::asprintf("%04d-%02d-%02d", parts.year, parts.month, parts.day);
1175 return QString();
1176}
1177
1206QString QDate::toString(Qt::DateFormat format) const
1207{
1208 if (!isValid())
1209 return QString();
1210
1211 switch (format) {
1212 case Qt::RFC2822Date:
1213 return QLocale::c().toString(*this, u"dd MMM yyyy");
1214 default:
1215 case Qt::TextDate:
1216 return toStringTextDate(*this);
1217 case Qt::ISODate:
1218 case Qt::ISODateWithMs:
1219 // No calendar dependence
1220 return toStringIsoDate(*this);
1221 }
1222}
1223
1285QString QDate::toString(QStringView format, QCalendar cal) const
1286{
1287 return QLocale::c().toString(*this, format, cal);
1288}
1289#endif // datestring
1290
1301bool QDate::setDate(int year, int month, int day)
1302{
1303 const auto maybe = QGregorianCalendar::julianFromParts(year, month, day);
1304 jd = maybe.value_or(nullJd());
1305 return bool(maybe);
1306}
1307
1319bool QDate::setDate(int year, int month, int day, QCalendar cal)
1320{
1321 *this = QDate(year, month, day, cal);
1322 return isValid();
1323}
1324
1337void QDate::getDate(int *year, int *month, int *day) const
1338{
1339 QCalendar::YearMonthDay parts; // invalid by default
1340 if (isValid())
1342
1343 const bool ok = parts.isValid();
1344 if (year)
1345 *year = ok ? parts.year : 0;
1346 if (month)
1347 *month = ok ? parts.month : 0;
1348 if (day)
1349 *day = ok ? parts.day : 0;
1350}
1351
1363{
1364 if (isNull())
1365 return QDate();
1366
1367 if (qint64 r; Q_UNLIKELY(qAddOverflow(jd, ndays, &r)))
1368 return QDate();
1369 else
1370 return fromJulianDay(r);
1371}
1372
1408QDate QDate::addMonths(int nmonths, QCalendar cal) const
1409{
1410 if (!isValid())
1411 return QDate();
1412
1413 if (nmonths == 0)
1414 return *this;
1415
1416 auto parts = cal.partsFromDate(*this);
1417
1418 if (!parts.isValid())
1419 return QDate();
1420 Q_ASSERT(parts.year || cal.hasYearZero());
1421
1422 parts.month += nmonths;
1423 while (parts.month <= 0) {
1424 if (--parts.year || cal.hasYearZero())
1425 parts.month += cal.monthsInYear(parts.year);
1426 }
1427 int count = cal.monthsInYear(parts.year);
1428 while (parts.month > count) {
1429 parts.month -= count;
1430 count = (++parts.year || cal.hasYearZero()) ? cal.monthsInYear(parts.year) : 0;
1431 }
1432
1433 return fixedDate(parts, cal);
1434}
1435
1440QDate QDate::addMonths(int nmonths) const
1441{
1442 if (isNull())
1443 return QDate();
1444
1445 if (nmonths == 0)
1446 return *this;
1447
1448 auto parts = QGregorianCalendar::partsFromJulian(jd);
1449
1450 if (!parts.isValid())
1451 return QDate();
1452 Q_ASSERT(parts.year);
1453
1454 parts.month += nmonths;
1455 while (parts.month <= 0) {
1456 if (--parts.year) // skip over year 0
1457 parts.month += 12;
1458 }
1459 while (parts.month > 12) {
1460 parts.month -= 12;
1461 if (!++parts.year) // skip over year 0
1462 ++parts.year;
1463 }
1464
1465 return fixedDate(parts);
1466}
1467
1482QDate QDate::addYears(int nyears, QCalendar cal) const
1483{
1484 if (!isValid())
1485 return QDate();
1486
1487 auto parts = cal.partsFromDate(*this);
1488 if (!parts.isValid())
1489 return QDate();
1490
1491 int old_y = parts.year;
1492 parts.year += nyears;
1493
1494 // If we just crossed (or hit) a missing year zero, adjust year by +/- 1:
1495 if (!cal.hasYearZero() && ((old_y > 0) != (parts.year > 0) || !parts.year))
1496 parts.year += nyears > 0 ? +1 : -1;
1497
1498 return fixedDate(parts, cal);
1499}
1500
1505QDate QDate::addYears(int nyears) const
1506{
1507 if (isNull())
1508 return QDate();
1509
1510 auto parts = QGregorianCalendar::partsFromJulian(jd);
1511 if (!parts.isValid())
1512 return QDate();
1513
1514 int old_y = parts.year;
1515 parts.year += nyears;
1516
1517 // If we just crossed (or hit) a missing year zero, adjust year by +/- 1:
1518 if ((old_y > 0) != (parts.year > 0) || !parts.year)
1519 parts.year += nyears > 0 ? +1 : -1;
1520
1521 return fixedDate(parts);
1522}
1523
1537{
1538 if (isNull() || d.isNull())
1539 return 0;
1540
1541 // Due to limits on minJd() and maxJd() we know this will never overflow
1542 return d.jd - jd;
1543}
1544
1545
1595#if QT_CONFIG(datestring) // depends on, so implies, textdate
1596
1614QDate QDate::fromString(QStringView string, Qt::DateFormat format)
1615{
1616 if (string.isEmpty())
1617 return QDate();
1618
1619 switch (format) {
1620 case Qt::RFC2822Date:
1621 return rfcDateImpl(string).date;
1622 default:
1623 case Qt::TextDate: {
1624 // Documented as "ddd MMM d yyyy"
1626 auto tokens = string.tokenize(u' ', Qt::SkipEmptyParts);
1627 auto it = tokens.begin();
1628 for (int i = 0; i < 4 && it != tokens.end(); ++i, ++it)
1629 parts.emplace_back(*it);
1630
1631 if (parts.size() != 4 || it != tokens.end())
1632 return QDate();
1633
1634 bool ok = false;
1635 int year = parts.at(3).toInt(&ok);
1636 int day = ok ? parts.at(2).toInt(&ok) : 0;
1637 if (!ok || !day)
1638 return QDate();
1639
1640 const int month = fromShortMonthName(parts.at(1));
1641 if (month == -1) // Month name matches no English or localised name.
1642 return QDate();
1643
1644 return QDate(year, month, day);
1645 }
1646 case Qt::ISODate:
1647 // Semi-strict parsing, must be long enough and have punctuators as separators
1648 if (string.size() >= 10 && string[4].isPunct() && string[7].isPunct()
1649 && (string.size() == 10 || !string[10].isDigit())) {
1650 const ParsedInt year = readInt(string.first(4));
1651 const ParsedInt month = readInt(string.sliced(5, 2));
1652 const ParsedInt day = readInt(string.sliced(8, 2));
1653 if (year.ok() && year.result > 0 && year.result <= 9999 && month.ok() && day.ok())
1654 return QDate(year.result, month.result, day.result);
1655 }
1656 break;
1657 }
1658 return QDate();
1659}
1660
1743QDate QDate::fromString(const QString &string, QStringView format, QCalendar cal)
1744{
1745 QDate date;
1746#if QT_CONFIG(datetimeparser)
1747 QDateTimeParser dt(QMetaType::QDate, QDateTimeParser::FromString, cal);
1748 dt.setDefaultLocale(QLocale::c());
1749 if (dt.parseFormat(format))
1750 dt.fromString(string, &date, nullptr);
1751#else
1752 Q_UNUSED(string);
1754 Q_UNUSED(cal);
1755#endif
1756 return date;
1757}
1758#endif // datestring
1759
1772bool QDate::isValid(int year, int month, int day)
1773{
1775}
1776
1787{
1789}
1790
1805/*****************************************************************************
1806 QTime member functions
1807 *****************************************************************************/
1808
1873QTime::QTime(int h, int m, int s, int ms)
1874{
1875 setHMS(h, m, s, ms);
1876}
1877
1878
1896bool QTime::isValid() const
1897{
1898 return mds > NullTime && mds < MSECS_PER_DAY;
1899}
1900
1901
1910int QTime::hour() const
1911{
1912 if (!isValid())
1913 return -1;
1914
1915 return ds() / MSECS_PER_HOUR;
1916}
1917
1926int QTime::minute() const
1927{
1928 if (!isValid())
1929 return -1;
1930
1931 return (ds() % MSECS_PER_HOUR) / MSECS_PER_MIN;
1932}
1933
1942int QTime::second() const
1943{
1944 if (!isValid())
1945 return -1;
1946
1947 return (ds() / MSECS_PER_SEC) % SECS_PER_MIN;
1948}
1949
1958int QTime::msec() const
1959{
1960 if (!isValid())
1961 return -1;
1962
1963 return ds() % MSECS_PER_SEC;
1964}
1965
1966#if QT_CONFIG(datestring) // depends on, so implies, textdate
1991QString QTime::toString(Qt::DateFormat format) const
1992{
1993 if (!isValid())
1994 return QString();
1995
1996 switch (format) {
1997 case Qt::ISODateWithMs:
1998 return QString::asprintf("%02d:%02d:%02d.%03d", hour(), minute(), second(), msec());
1999 case Qt::RFC2822Date:
2000 case Qt::ISODate:
2001 case Qt::TextDate:
2002 default:
2003 return QString::asprintf("%02d:%02d:%02d", hour(), minute(), second());
2004 }
2005}
2006
2109// ### Qt 7 The 't' format specifiers should be specific to QDateTime (compare fromString).
2110QString QTime::toString(QStringView format) const
2111{
2112 return QLocale::c().toString(*this, format);
2113}
2114#endif // datestring
2115
2127bool QTime::setHMS(int h, int m, int s, int ms)
2128{
2129 if (!isValid(h,m,s,ms)) {
2130 mds = NullTime; // make this invalid
2131 return false;
2132 }
2133 mds = ((h * MINS_PER_HOUR + m) * SECS_PER_MIN + s) * MSECS_PER_SEC + ms;
2134 Q_ASSERT(mds >= 0 && mds < MSECS_PER_DAY);
2135 return true;
2136}
2137
2154{
2155 s %= SECS_PER_DAY;
2156 return addMSecs(s * MSECS_PER_SEC);
2157}
2158
2175{
2176 if (!isValid() || !t.isValid())
2177 return 0;
2178
2179 // Truncate milliseconds as we do not want to consider them.
2180 int ourSeconds = ds() / MSECS_PER_SEC;
2181 int theirSeconds = t.ds() / MSECS_PER_SEC;
2182 return theirSeconds - ourSeconds;
2183}
2184
2198{
2199 QTime t;
2200 if (isValid())
2201 t.mds = QRoundingDown::qMod<MSECS_PER_DAY>(ds() + ms);
2202 return t;
2203}
2204
2220{
2221 if (!isValid() || !t.isValid())
2222 return 0;
2223 return t.ds() - ds();
2224}
2225
2226
2299#if QT_CONFIG(datestring) // depends on, so implies, textdate
2300
2301static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *isMidnight24)
2302{
2304 if (isMidnight24)
2305 *isMidnight24 = false;
2306 // Match /\d\d(:\d\d(:\d\d)?)?([,.]\d+)?/ as "HH[:mm[:ss]][.zzz]"
2307 // The fractional part, if present, is in the same units as the field it follows.
2308 // TextDate restricts fractional parts to the seconds field.
2309
2310 QStringView tail;
2311 const int dot = string.indexOf(u'.'), comma = string.indexOf(u',');
2312 if (dot != -1) {
2313 tail = string.sliced(dot + 1);
2314 if (tail.indexOf(u'.') != -1) // Forbid second dot:
2315 return QTime();
2316 string = string.first(dot);
2317 } else if (comma != -1) {
2318 tail = string.sliced(comma + 1);
2319 string = string.first(comma);
2320 }
2321 if (tail.indexOf(u',') != -1) // Forbid comma after first dot-or-comma:
2322 return QTime();
2323
2324 const ParsedInt frac = readInt(tail);
2325 // There must be *some* digits in a fractional part; and it must be all digits:
2326 if (tail.isEmpty() ? dot != -1 || comma != -1 : !frac.ok())
2327 return QTime();
2328 Q_ASSERT(frac.ok() ^ tail.isEmpty());
2329 double fraction = frac.ok() ? frac.result * std::pow(0.1, tail.size()) : 0.0;
2330
2331 const int size = string.size();
2332 if (size < 2 || size > 8)
2333 return QTime();
2334
2335 ParsedInt hour = readInt(string.first(2));
2336 if (!hour.ok() || hour.result > (format == Qt::TextDate ? 23 : 24))
2337 return QTime();
2338
2339 ParsedInt minute{};
2340 if (string.size() > 2) {
2341 if (string[2] == u':' && string.size() > 4)
2342 minute = readInt(string.sliced(3, 2));
2343 if (!minute.ok() || minute.result >= MINS_PER_HOUR)
2344 return QTime();
2345 } else if (format == Qt::TextDate) { // Requires minutes
2346 return QTime();
2347 } else if (frac.ok()) {
2348 Q_ASSERT(!(fraction < 0.0) && fraction < 1.0);
2349 fraction *= MINS_PER_HOUR;
2350 minute.result = qulonglong(fraction);
2351 fraction -= minute.result;
2352 }
2353
2354 ParsedInt second{};
2355 if (string.size() > 5) {
2356 if (string[5] == u':' && string.size() == 8)
2357 second = readInt(string.sliced(6, 2));
2358 if (!second.ok() || second.result >= SECS_PER_MIN)
2359 return QTime();
2360 } else if (frac.ok()) {
2361 if (format == Qt::TextDate) // Doesn't allow fraction of minutes
2362 return QTime();
2363 Q_ASSERT(!(fraction < 0.0) && fraction < 1.0);
2364 fraction *= SECS_PER_MIN;
2365 second.result = qulonglong(fraction);
2366 fraction -= second.result;
2367 }
2368
2369 Q_ASSERT(!(fraction < 0.0) && fraction < 1.0);
2370 // Round millis to nearest (unlike minutes and seconds, rounded down):
2371 int msec = frac.ok() ? qRound(MSECS_PER_SEC * fraction) : 0;
2372 // But handle overflow gracefully:
2373 if (msec == MSECS_PER_SEC) {
2374 // If we can (when data were otherwise valid) validly propagate overflow
2375 // into other fields, do so:
2376 if (isMidnight24 || hour.result < 23 || minute.result < 59 || second.result < 59) {
2377 msec = 0;
2378 if (++second.result == SECS_PER_MIN) {
2379 second.result = 0;
2380 if (++minute.result == MINS_PER_HOUR) {
2381 minute.result = 0;
2382 ++hour.result;
2383 // May need to propagate further via isMidnight24, see below
2384 }
2385 }
2386 } else {
2387 // QTime::fromString() or Qt::TextDate: rounding up would cause
2388 // 23:59:59.999... to become invalid; clip to 999 ms instead:
2389 msec = MSECS_PER_SEC - 1;
2390 }
2391 }
2392
2393 // For ISO date format, 24:0:0 means 0:0:0 on the next day:
2394 if (hour.result == 24 && minute.result == 0 && second.result == 0 && msec == 0) {
2395 Q_ASSERT(format != Qt::TextDate); // It clipped hour at 23, above.
2396 if (isMidnight24)
2397 *isMidnight24 = true;
2398 hour.result = 0;
2399 }
2400
2401 return QTime(hour.result, minute.result, second.result, msec);
2402}
2403
2417QTime QTime::fromString(QStringView string, Qt::DateFormat format)
2418{
2419 if (string.isEmpty())
2420 return QTime();
2421
2422 switch (format) {
2423 case Qt::RFC2822Date:
2424 return rfcDateImpl(string).time;
2425 case Qt::ISODate:
2426 case Qt::ISODateWithMs:
2427 case Qt::TextDate:
2428 default:
2429 return fromIsoTimeString(string, format, nullptr);
2430 }
2431}
2432
2521QTime QTime::fromString(const QString &string, QStringView format)
2522{
2523 QTime time;
2524#if QT_CONFIG(datetimeparser)
2525 QDateTimeParser dt(QMetaType::QTime, QDateTimeParser::FromString, QCalendar());
2526 dt.setDefaultLocale(QLocale::c());
2527 if (dt.parseFormat(format))
2528 dt.fromString(string, nullptr, &time);
2529#else
2530 Q_UNUSED(string);
2532#endif
2533 return time;
2534}
2535#endif // datestring
2536
2537
2552bool QTime::isValid(int h, int m, int s, int ms)
2553{
2554 return (uint(h) < 24 && uint(m) < MINS_PER_HOUR && uint(s) < SECS_PER_MIN
2555 && uint(ms) < MSECS_PER_SEC);
2556}
2557
2558/*****************************************************************************
2559 QDateTime static helper functions
2560 *****************************************************************************/
2561
2562// get the types from QDateTime (through QDateTimePrivate)
2565
2566// Converts milliseconds since the start of 1970 into a date and/or time:
2568{
2569 return JULIAN_DAY_FOR_EPOCH + QRoundingDown::qDiv<MSECS_PER_DAY>(msecs);
2570}
2571
2573{
2575}
2576
2578{
2579 return QTime::fromMSecsSinceStartOfDay(QRoundingDown::qMod<MSECS_PER_DAY>(msecs));
2580}
2581
2582// True if combining days with millis overflows; otherwise, stores result in *sumMillis
2583// The inputs should not have opposite signs.
2584static inline bool daysAndMillisOverflow(qint64 days, qint64 millisInDay, qint64 *sumMillis)
2585{
2586 return qMulOverflow(days, std::integral_constant<qint64, MSECS_PER_DAY>(), sumMillis)
2587 || qAddOverflow(*sumMillis, millisInDay, sumMillis);
2588}
2589
2590// Converts a date/time value into msecs
2592{
2594 qint64 msecs, dayms = time.msecsSinceStartOfDay();
2595 if (days < 0 && dayms > 0) {
2596 ++days;
2597 dayms -= MSECS_PER_DAY;
2598 }
2599 if (daysAndMillisOverflow(days, dayms, &msecs)) {
2600 using Bound = std::numeric_limits<qint64>;
2601 return days < 0 ? Bound::min() : Bound::max();
2602 }
2603 return msecs;
2604}
2605
2623static inline bool millisInSystemRange(qint64 millis, qint64 slack = 0)
2624{
2625 static const auto bounds = QLocalTime::computeSystemMillisRange();
2626 return (bounds.minClip || millis >= bounds.min - slack)
2627 && (bounds.maxClip || millis <= bounds.max + slack);
2628}
2629
2646static int systemTimeYearMatching(int year)
2647{
2648#if defined(Q_OS_WIN) || defined(Q_OS_WASM)// They don't support times before the epoch
2649 static constexpr int forLeapEarly[] = { 1984, 1996, 1980, 1992, 1976, 1988, 1972 };
2650 static constexpr int regularEarly[] = { 1978, 1973, 1974, 1975, 1970, 1971, 1977 };
2651#else // First year fully in 32-bit time_t range is 1902
2652 static constexpr int forLeapEarly[] = { 1928, 1912, 1924, 1908, 1920, 1904, 1916 };
2653 static constexpr int regularEarly[] = { 1905, 1906, 1907, 1902, 1903, 1909, 1910 };
2654#endif
2655 static constexpr int forLeapLate[] = { 2012, 2024, 2036, 2020, 2032, 2016, 2028 };
2656 static constexpr int regularLate[] = { 2034, 2035, 2030, 2031, 2037, 2027, 2033 };
2657 const int dow = QGregorianCalendar::yearStartWeekDay(year);
2658 Q_ASSERT(dow == QDate(year, 1, 1).dayOfWeek());
2659 const int res = (QGregorianCalendar::leapTest(year)
2660 ? (year < 1970 ? forLeapEarly : forLeapLate)
2661 : (year < 1970 ? regularEarly : regularLate))[dow == 7 ? 0 : dow];
2662 Q_ASSERT(QDate(res, 1, 1).dayOfWeek() == dow);
2663 Q_ASSERT(QDate(res, 12, 31).dayOfWeek() == QDate(year, 12, 31).dayOfWeek());
2664 return res;
2665}
2666
2667// Sets up d and status to represent local time at the given UTC msecs since epoch:
2669{
2670 ZoneState result{utcMSecs};
2671 // Within the time_t supported range, localtime() can handle it:
2672 if (millisInSystemRange(utcMSecs)) {
2673 result = QLocalTime::utcToLocal(utcMSecs);
2674 if (result.valid)
2675 return result;
2676 }
2677
2678 // Docs state any LocalTime after 2038-01-18 *will* have any DST applied.
2679 // When this falls outside the supported range, we need to fake it.
2680#if QT_CONFIG(timezone) // Use the system time-zone.
2681 if (const auto sys = QTimeZone::systemTimeZone(); sys.isValid()) {
2682 result.offset = sys.d->offsetFromUtc(utcMSecs);
2683 if (qAddOverflow(utcMSecs, result.offset * MSECS_PER_SEC, &result.when))
2684 return result;
2685 result.dst = sys.d->isDaylightTime(utcMSecs) ? DaylightTime : StandardTime;
2686 result.valid = true;
2687 return result;
2688 }
2689#endif // timezone
2690
2691 // Kludge
2692 // Do the conversion in a year with the same days of the week, so DST
2693 // dates might be right, and adjust by the number of days that was off:
2694 const qint64 jd = msecsToJulianDay(utcMSecs);
2695 const auto ymd = QGregorianCalendar::partsFromJulian(jd);
2696 qint64 diffMillis, fakeUtc;
2697 const auto fakeJd = QGregorianCalendar::julianFromParts(systemTimeYearMatching(ymd.year),
2698 ymd.month, ymd.day);
2699 if (Q_UNLIKELY(!fakeJd
2700 || qMulOverflow(jd - *fakeJd, std::integral_constant<qint64, MSECS_PER_DAY>(),
2701 &diffMillis)
2702 || qSubOverflow(utcMSecs, diffMillis, &fakeUtc))) {
2703 return result;
2704 }
2705
2706 result = QLocalTime::utcToLocal(fakeUtc);
2707 // Now correct result.when for the use of the fake date:
2708 if (!result.valid || qAddOverflow(result.when, diffMillis, &result.when)) {
2709 // If utcToLocal() failed, its return has the fake when; restore utcMSecs.
2710 // Fail on overflow, but preserve offset and DST-ness.
2711 result.when = utcMSecs;
2712 result.valid = false;
2713 }
2714 return result;
2715}
2716
2717static auto millisToWithinRange(qint64 millis)
2718{
2719 struct R {
2720 qint64 shifted = 0;
2721 bool good = false;
2722 } result;
2723 qint64 jd = msecsToJulianDay(millis);
2725 const auto fakeJd = QGregorianCalendar::julianFromParts(systemTimeYearMatching(ymd.year),
2726 ymd.month, ymd.day);
2727 result.good = fakeJd && !daysAndMillisOverflow(*fakeJd - jd, millis, &result.shifted);
2728 return result;
2729}
2730
2732{
2733 QString abbreviation;
2734 if (millisInSystemRange(millis, MSECS_PER_DAY)) {
2735 abbreviation = QLocalTime::localTimeAbbbreviationAt(millis, dst);
2736 if (!abbreviation.isEmpty())
2737 return abbreviation;
2738 }
2739
2740 // Otherwise, outside the system range.
2741#if QT_CONFIG(timezone)
2742 // Use the system zone:
2743 const auto sys = QTimeZone::systemTimeZone();
2744 if (sys.isValid()) {
2745 ZoneState state = zoneStateAtMillis(sys, millis, dst);
2746 if (state.valid)
2747 return sys.d->abbreviation(state.when - state.offset * MSECS_PER_SEC);
2748 }
2749#endif // timezone
2750
2751 // Kludge
2752 // Use a time in the system range with the same day-of-week pattern to its year:
2753 auto fake = millisToWithinRange(millis);
2754 if (Q_LIKELY(fake.good))
2755 return QLocalTime::localTimeAbbbreviationAt(fake.shifted, dst);
2756
2757 // Overflow, apparently.
2758 return {};
2759}
2760
2761// Determine the offset from UTC at the given local time as millis.
2763{
2764 // First, if millis is within a day of the viable range, try mktime() in
2765 // case it does fall in the range and gets useful information:
2766 if (millisInSystemRange(millis, MSECS_PER_DAY)) {
2767 auto result = QLocalTime::mapLocalTime(millis, dst);
2768 if (result.valid)
2769 return result;
2770 }
2771
2772 // Otherwise, outside the system range.
2773#if QT_CONFIG(timezone)
2774 // Use the system zone:
2775 const auto sys = QTimeZone::systemTimeZone();
2776 if (sys.isValid())
2777 return zoneStateAtMillis(sys, millis, dst);
2778#endif // timezone
2779
2780 // Kludge
2781 // Use a time in the system range with the same day-of-week pattern to its year:
2782 auto fake = millisToWithinRange(millis);
2783 if (Q_LIKELY(fake.good)) {
2784 auto result = QLocalTime::mapLocalTime(fake.shifted, dst);
2785 if (result.valid) {
2786 qint64 adjusted;
2787 if (Q_UNLIKELY(qAddOverflow(result.when, millis - fake.shifted, &adjusted))) {
2788 using Bound = std::numeric_limits<qint64>;
2789 adjusted = millis < fake.shifted ? Bound::min() : Bound::max();
2790 }
2791 result.when = adjusted;
2792 } else {
2793 result.when = millis;
2794 }
2795 return result;
2796 }
2797 // Overflow, apparently.
2798 return {millis};
2799}
2800
2801#if QT_CONFIG(timezone)
2802// For a TimeZone and a time expressed in zone msecs encoding, possibly with a
2803// hint to DST-ness, compute the actual DST-ness and offset, adjusting the time
2804// if needed to escape a spring-forward.
2805QDateTimePrivate::ZoneState QDateTimePrivate::zoneStateAtMillis(const QTimeZone &zone,
2806 qint64 millis, DaylightStatus dst)
2807{
2808 Q_ASSERT(zone.isValid());
2809 Q_ASSERT(zone.timeSpec() == Qt::TimeZone);
2810 // Get the effective data from QTimeZone
2811 QTimeZonePrivate::Data data = zone.d->dataForLocalTime(millis, int(dst));
2812 if (data.offsetFromUtc == QTimeZonePrivate::invalidSeconds())
2813 return {millis};
2814 Q_ASSERT(zone.d->offsetFromUtc(data.atMSecsSinceEpoch) == data.offsetFromUtc);
2815 ZoneState state(data.atMSecsSinceEpoch + data.offsetFromUtc * MSECS_PER_SEC,
2816 data.offsetFromUtc,
2817 data.daylightTimeOffset ? DaylightTime : StandardTime);
2818 // Revise offset, when stepping out of a spring-forward, to what makes a
2819 // fromMSecsSinceEpoch(toMSecsSinceEpoch()) of the resulting QDT work:
2820 if (millis != state.when)
2821 state.offset += (millis - state.when) / MSECS_PER_SEC;
2822 return state;
2823}
2824#endif // timezone
2825
2828{
2829 if (zone.timeSpec() == Qt::LocalTime)
2831#if QT_CONFIG(timezone)
2832 if (zone.timeSpec() == Qt::TimeZone && zone.isValid())
2833 return QDateTimePrivate::zoneStateAtMillis(zone, millis, dst);
2834#endif
2835 return {millis};
2836}
2837
2838static inline bool specCanBeSmall(Qt::TimeSpec spec)
2839{
2840 return spec == Qt::LocalTime || spec == Qt::UTC;
2841}
2842
2843static inline bool msecsCanBeSmall(qint64 msecs)
2844{
2845 if constexpr (!QDateTimeData::CanBeSmall)
2846 return false;
2847
2848 ShortData sd;
2849 sd.msecs = qintptr(msecs);
2850 return sd.msecs == msecs;
2851}
2852
2853static constexpr inline
2854QDateTimePrivate::StatusFlags mergeSpec(QDateTimePrivate::StatusFlags status, Qt::TimeSpec spec)
2855{
2856 status &= ~QDateTimePrivate::TimeSpecMask;
2857 status |= QDateTimePrivate::StatusFlags::fromInt(int(spec) << QDateTimePrivate::TimeSpecShift);
2858 return status;
2859}
2860
2861static constexpr inline Qt::TimeSpec extractSpec(QDateTimePrivate::StatusFlags status)
2862{
2864}
2865
2866// Set the Daylight Status if LocalTime set via msecs
2867static constexpr inline QDateTimePrivate::StatusFlags
2868mergeDaylightStatus(QDateTimePrivate::StatusFlags sf, QDateTimePrivate::DaylightStatus status)
2869{
2870 sf &= ~QDateTimePrivate::DaylightMask;
2871 if (status == QDateTimePrivate::DaylightTime) {
2873 } else if (status == QDateTimePrivate::StandardTime) {
2875 }
2876 return sf;
2877}
2878
2879// Get the DST Status if LocalTime set via msecs
2880static constexpr inline
2881QDateTimePrivate::DaylightStatus extractDaylightStatus(QDateTimePrivate::StatusFlags status)
2882{
2883 if (status.testFlag(QDateTimePrivate::SetToDaylightTime))
2885 if (status.testFlag(QDateTimePrivate::SetToStandardTime))
2888}
2889
2890static inline qint64 getMSecs(const QDateTimeData &d)
2891{
2892 if (d.isShort()) {
2893 // same as, but producing better code
2894 //return d.data.msecs;
2895 return qintptr(d.d) >> 8;
2896 }
2897 return d->m_msecs;
2898}
2899
2900static inline QDateTimePrivate::StatusFlags getStatus(const QDateTimeData &d)
2901{
2902 if (d.isShort()) {
2903 // same as, but producing better code
2904 //return StatusFlag(d.data.status);
2905 return QDateTimePrivate::StatusFlag(qintptr(d.d) & 0xFF);
2906 }
2907 return d->m_status;
2908}
2909
2911{
2912 return extractSpec(getStatus(d));
2913}
2914
2915/* True if we *can cheaply determine* that a and b use the same offset.
2916 If they use different offsets or it would be expensive to find out, false.
2917 Calls to toMSecsSinceEpoch() are expensive, for these purposes.
2918 See QDateTime's comparison operators.
2919*/
2920static inline bool usesSameOffset(const QDateTimeData &a, const QDateTimeData &b)
2921{
2922 const auto status = getStatus(a);
2923 if (status != getStatus(b))
2924 return false;
2925 // Status includes DST-ness, so we now know they match in it.
2926
2927 switch (extractSpec(status)) {
2928 case Qt::LocalTime:
2929 case Qt::UTC:
2930 return true;
2931
2932 case Qt::TimeZone:
2933 /* TimeZone always determines its offset during construction of the
2934 private data. Even if we're in different zones, what matters is the
2935 offset actually in effect at the specific time. (DST can cause things
2936 with the same time-zone to use different offsets, but we already
2937 checked their DSTs match.) */
2938 case Qt::OffsetFromUTC: // always knows its offset, which is all that matters.
2939 Q_ASSERT(!a.isShort() && !b.isShort());
2940 return a->m_offsetFromUtc == b->m_offsetFromUtc;
2941 }
2942 Q_UNREACHABLE_RETURN(false);
2943}
2944
2945// Refresh the LocalTime or TimeZone validity and offset
2947{
2948 Q_ASSERT(zone.timeSpec() == Qt::TimeZone || zone.timeSpec() == Qt::LocalTime);
2949 auto status = getStatus(d);
2950 Q_ASSERT(extractSpec(status) == zone.timeSpec());
2951 int offsetFromUtc = 0;
2952
2953 // If not valid date and time then is invalid
2955 status.setFlag(QDateTimePrivate::ValidDateTime, false);
2956 } else {
2957 // We have a valid date and time and a Qt::LocalTime or Qt::TimeZone that needs calculating
2958 // LocalTime and TimeZone might fall into a "missing" DST transition hour
2959 // Calling toEpochMSecs will adjust the returned date/time if it does
2960 qint64 msecs = getMSecs(d);
2962 extractDaylightStatus(status));
2963 // Save the offset to use in offsetFromUtc() &c., even if the next check
2964 // marks invalid; this lets fromMSecsSinceEpoch() give a useful fallback
2965 // for times in spring-forward gaps.
2966 offsetFromUtc = state.offset;
2967 Q_ASSERT(!state.valid || (state.offset >= -SECS_PER_DAY && state.offset <= SECS_PER_DAY));
2968 if (state.valid && msecs == state.when)
2970 else // msecs changed or failed to convert (e.g. overflow)
2971 status.setFlag(QDateTimePrivate::ValidDateTime, false);
2972 }
2973
2974 if (status.testFlag(QDateTimePrivate::ShortData)) {
2975 d.data.status = status.toInt();
2976 } else {
2977 d->m_status = status;
2978 d->m_offsetFromUtc = offsetFromUtc;
2979 }
2980}
2981
2982// Check the UTC / offsetFromUTC validity
2984{
2985 auto status = getStatus(d);
2987 status.setFlag(QDateTimePrivate::ValidDateTime,
2989
2990 if (status.testFlag(QDateTimePrivate::ShortData))
2991 d.data.status = status.toInt();
2992 else
2993 d->m_status = status;
2994}
2995
2996// Clean up and set status after assorted set-up or reworking:
2998{
2999 auto spec = extractSpec(getStatus(d));
3000 switch (spec) {
3001 case Qt::OffsetFromUTC:
3002 case Qt::UTC:
3003 // for these, a valid date and a valid time imply a valid QDateTime
3005 break;
3006 case Qt::TimeZone:
3007 case Qt::LocalTime:
3008 // For these, we need to check whether (the zone is valid and) the time
3009 // is valid for the zone. Expensive, but we have no other option.
3010 refreshZonedDateTime(d, d.timeZone());
3011 break;
3012 }
3013}
3014
3016{
3017 Qt::TimeSpec spec = zone.timeSpec();
3018 auto status = mergeSpec(getStatus(d), spec);
3019 bool reuse = d.isShort();
3020 int offset = 0;
3021
3022 switch (spec) {
3023 case Qt::UTC:
3024 Q_ASSERT(zone.fixedSecondsAheadOfUtc() == 0);
3025 break;
3026 case Qt::OffsetFromUTC:
3027 reuse = false;
3030 break;
3031 case Qt::TimeZone:
3032 reuse = false;
3033 break;
3034 case Qt::LocalTime:
3035 break;
3036 }
3037
3039 if (reuse) {
3040 d.data.status = status.toInt();
3041 } else {
3042 d.detach();
3043 d->m_status = status & ~QDateTimePrivate::ShortData;
3044 d->m_offsetFromUtc = offset;
3045#if QT_CONFIG(timezone)
3046 if (spec == Qt::TimeZone)
3047 d->m_timeZone = zone;
3048#endif // timezone
3049 }
3050
3053 else
3054 refreshZonedDateTime(d, zone);
3055}
3056
3058{
3059 // If the date is valid and the time is not we set time to 00:00:00
3060 if (!time.isValid() && date.isValid())
3062
3063 QDateTimePrivate::StatusFlags newStatus = { };
3064
3065 // Set date value and status
3066 qint64 days = 0;
3067 if (date.isValid()) {
3069 newStatus = QDateTimePrivate::ValidDate;
3070 }
3071
3072 // Set time value and status
3073 int ds = 0;
3074 if (time.isValid()) {
3076 newStatus |= QDateTimePrivate::ValidTime;
3077 }
3078 Q_ASSERT(ds < MSECS_PER_DAY);
3079 // Only the later parts of the very first day are representable - its start
3080 // would overflow - so get ds the same side of 0 as days:
3081 if (days < 0 && ds > 0) {
3082 days++;
3083 ds -= MSECS_PER_DAY;
3084 }
3085
3086 // Check in representable range:
3087 qint64 msecs = 0;
3088 if (daysAndMillisOverflow(days, qint64(ds), &msecs)) {
3089 newStatus = QDateTimePrivate::StatusFlags{};
3090 msecs = 0;
3091 }
3092 if (d.isShort()) {
3093 // let's see if we can keep this short
3094 if (msecsCanBeSmall(msecs)) {
3095 // yes, we can
3096 d.data.msecs = qintptr(msecs);
3098 d.data.status |= newStatus.toInt();
3099 } else {
3100 // nope...
3101 d.detach();
3102 }
3103 }
3104 if (!d.isShort()) {
3105 d.detach();
3106 d->m_msecs = msecs;
3108 d->m_status |= newStatus;
3109 }
3110}
3111
3113{
3114 auto status = getStatus(d);
3115 const qint64 msecs = getMSecs(d);
3116 const auto dayMilli = QRoundingDown::qDivMod<MSECS_PER_DAY>(msecs);
3117 return { status.testFlag(QDateTimePrivate::ValidDate)
3118 ? QDate::fromJulianDay(JULIAN_DAY_FOR_EPOCH + dayMilli.quotient)
3119 : QDate(),
3120 status.testFlag(QDateTimePrivate::ValidTime)
3121 ? QTime::fromMSecsSinceStartOfDay(dayMilli.remainder)
3122 : QTime() };
3123}
3124
3125/*****************************************************************************
3126 QDateTime::Data member functions
3127 *****************************************************************************/
3128
3129inline QDateTime::Data::Data() noexcept
3130{
3131 // default-constructed data has a special exception:
3132 // it can be small even if CanBeSmall == false
3133 // (optimization so we don't allocate memory in the default constructor)
3135 d = reinterpret_cast<QDateTimePrivate *>(value);
3136}
3137
3138inline QDateTime::Data::Data(const QTimeZone &zone)
3139{
3140 Qt::TimeSpec spec = zone.timeSpec();
3141 if (CanBeSmall && Q_LIKELY(specCanBeSmall(spec))) {
3143 d = reinterpret_cast<QDateTimePrivate *>(value);
3144 Q_ASSERT(isShort());
3145 } else {
3146 // the structure is too small, we need to detach
3147 d = new QDateTimePrivate;
3148 d->ref.ref();
3149 d->m_status = mergeSpec({}, spec);
3150 if (spec == Qt::OffsetFromUTC)
3151 d->m_offsetFromUtc = zone.fixedSecondsAheadOfUtc();
3152 else if (spec == Qt::TimeZone)
3153 d->m_timeZone = zone;
3154 Q_ASSERT(!isShort());
3155 }
3156}
3157
3158inline QDateTime::Data::Data(const Data &other) noexcept
3159 : data(other.data)
3160{
3161 if (!isShort()) {
3162 // check if we could shrink
3163 if (specCanBeSmall(extractSpec(d->m_status)) && msecsCanBeSmall(d->m_msecs)) {
3164 ShortData sd;
3165 sd.msecs = qintptr(d->m_msecs);
3166 sd.status = (d->m_status | QDateTimePrivate::ShortData).toInt();
3167 data = sd;
3168 } else {
3169 // no, have to keep it big
3170 d->ref.ref();
3171 }
3172 }
3173}
3174
3175inline QDateTime::Data::Data(Data &&other) noexcept
3176 : data(other.data)
3177{
3178 // reset the other to a short state
3179 Data dummy;
3180 Q_ASSERT(dummy.isShort());
3181 other.data = dummy.data;
3182}
3183
3184inline QDateTime::Data &QDateTime::Data::operator=(const Data &other) noexcept
3185{
3186 if (isShort() ? data == other.data : d == other.d)
3187 return *this;
3188
3189 auto x = d;
3190 d = other.d;
3191 if (!other.isShort()) {
3192 // check if we could shrink
3193 if (specCanBeSmall(extractSpec(other.d->m_status)) && msecsCanBeSmall(other.d->m_msecs)) {
3194 ShortData sd;
3195 sd.msecs = qintptr(other.d->m_msecs);
3196 sd.status = (other.d->m_status | QDateTimePrivate::ShortData).toInt();
3197 data = sd;
3198 } else {
3199 // no, have to keep it big
3200 other.d->ref.ref();
3201 }
3202 }
3203
3204 if (!(quintptr(x) & QDateTimePrivate::ShortData) && !x->ref.deref())
3205 delete x;
3206 return *this;
3207}
3208
3209inline QDateTime::Data::~Data()
3210{
3211 if (!isShort() && !d->ref.deref())
3212 delete d;
3213}
3214
3215inline bool QDateTime::Data::isShort() const
3216{
3218
3219 // sanity check:
3220 Q_ASSERT(b || !d->m_status.testFlag(QDateTimePrivate::ShortData));
3221
3222 // even if CanBeSmall = false, we have short data for a default-constructed
3223 // QDateTime object. But it's unlikely.
3224 if constexpr (CanBeSmall)
3225 return Q_LIKELY(b);
3226 return Q_UNLIKELY(b);
3227}
3228
3229inline void QDateTime::Data::detach()
3230{
3232 bool wasShort = isShort();
3233 if (wasShort) {
3234 // force enlarging
3235 x = new QDateTimePrivate;
3236 x->m_status = QDateTimePrivate::StatusFlags::fromInt(data.status) & ~QDateTimePrivate::ShortData;
3237 x->m_msecs = data.msecs;
3238 } else {
3239 if (d->ref.loadRelaxed() == 1)
3240 return;
3241
3242 x = new QDateTimePrivate(*d);
3243 }
3244
3245 x->ref.storeRelaxed(1);
3246 if (!wasShort && !d->ref.deref())
3247 delete d;
3248 d = x;
3249}
3250
3251QTimeZone QDateTime::Data::timeZone() const
3252{
3253 switch (getSpec(*this)) {
3254 case Qt::UTC:
3255 return QTimeZone::UTC;
3256 case Qt::OffsetFromUTC:
3257 return QTimeZone::fromSecondsAheadOfUtc(d->m_offsetFromUtc);
3258 case Qt::TimeZone:
3259#if QT_CONFIG(timezone)
3260 if (d->m_timeZone.isValid())
3261 return d->m_timeZone;
3262#endif
3263 break;
3264 case Qt::LocalTime:
3265 return QTimeZone::LocalTime;
3266 }
3267 return QTimeZone();
3268}
3269
3270inline const QDateTimePrivate *QDateTime::Data::operator->() const
3271{
3272 Q_ASSERT(!isShort());
3273 return d;
3274}
3275
3276inline QDateTimePrivate *QDateTime::Data::operator->()
3277{
3278 // should we attempt to detach here?
3279 Q_ASSERT(!isShort());
3280 Q_ASSERT(d->ref.loadRelaxed() == 1);
3281 return d;
3282}
3283
3284/*****************************************************************************
3285 QDateTimePrivate member functions
3286 *****************************************************************************/
3287
3290{
3291 QDateTime::Data result(zone);
3293 if (zone.isUtcOrFixedOffset())
3295 else
3297 return result;
3298}
3299
3300/*****************************************************************************
3301 QDateTime member functions
3302 *****************************************************************************/
3303
3474{
3475#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) || defined(QT_BOOTSTRAPPED) || QT_POINTER_SIZE == 8
3476 static_assert(sizeof(ShortData) == sizeof(qint64));
3477 static_assert(sizeof(Data) == sizeof(qint64));
3478#endif
3479 static_assert(sizeof(ShortData) >= sizeof(void*), "oops, Data::swap() is broken!");
3480}
3481
3482#if QT_DEPRECATED_SINCE(6, 9)
3503QDateTime::QDateTime(QDate date, QTime time, Qt::TimeSpec spec, int offsetSeconds)
3504 : d(QDateTimePrivate::create(date, time, asTimeZone(spec, offsetSeconds, "QDateTime")))
3505{
3506}
3507#endif // 6.9 deprecation
3508
3520 : d(QDateTimePrivate::create(date, time, timeZone))
3521{
3522}
3523
3534{
3535}
3536
3541 : d(other.d)
3542{
3543}
3544
3551 : d(std::move(other.d))
3552{
3553}
3554
3559{
3560}
3561
3567{
3568 d = other.d;
3569 return *this;
3570}
3587{
3588 // If date or time is invalid, we don't set datetime valid.
3589 return !getStatus(d).testAnyFlag(QDateTimePrivate::ValidityMask);
3590}
3591
3607{
3608 return getStatus(d).testFlag(QDateTimePrivate::ValidDateTime);
3609}
3610
3618{
3619 return getStatus(d).testFlag(QDateTimePrivate::ValidDate) ? msecsToDate(getMSecs(d)) : QDate();
3620}
3621
3629{
3630 return getStatus(d).testFlag(QDateTimePrivate::ValidTime) ? msecsToTime(getMSecs(d)) : QTime();
3631}
3632
3645{
3646 return getSpec(d);
3647}
3648
3664{
3665 return d.timeZone();
3666}
3667
3668#if QT_CONFIG(timezone)
3684QTimeZone QDateTime::timeZone() const
3685{
3686 return d.timeZone().asBackendZone();
3687}
3688#endif // timezone
3689
3713{
3714 if (!d.isShort())
3715 return d->m_offsetFromUtc;
3716 if (!isValid())
3717 return 0;
3718
3719 auto spec = getSpec(d);
3720 if (spec == Qt::LocalTime) {
3721 // we didn't cache the value, so we need to calculate it now...
3722 qint64 msecs = getMSecs(d);
3723 return (msecs - toMSecsSinceEpoch()) / MSECS_PER_SEC;
3724 }
3725
3726 Q_ASSERT(spec == Qt::UTC);
3727 return 0;
3728}
3729
3752{
3753 if (!isValid())
3754 return QString();
3755
3756 switch (getSpec(d)) {
3757 case Qt::UTC:
3758 return "UTC"_L1;
3759 case Qt::OffsetFromUTC:
3760 return "UTC"_L1 + toOffsetString(Qt::ISODate, d->m_offsetFromUtc);
3761 case Qt::TimeZone:
3762#if !QT_CONFIG(timezone)
3763 break;
3764#else
3765 Q_ASSERT(d->m_timeZone.isValid());
3766 return d->m_timeZone.abbreviation(*this);
3767#endif // timezone
3768 case Qt::LocalTime:
3771 }
3772 return QString();
3773}
3774
3787{
3788 if (!isValid())
3789 return false;
3790
3791 switch (getSpec(d)) {
3792 case Qt::UTC:
3793 case Qt::OffsetFromUTC:
3794 return false;
3795 case Qt::TimeZone:
3796#if !QT_CONFIG(timezone)
3797 break;
3798#else
3799 Q_ASSERT(d->m_timeZone.isValid());
3800 if (auto dst = extractDaylightStatus(getStatus(d));
3803 }
3804 return d->m_timeZone.d->isDaylightTime(toMSecsSinceEpoch());
3805#endif // timezone
3806 case Qt::LocalTime: {
3811 }
3812 }
3813 return false;
3814}
3815
3824{
3825 setDateTime(d, date, time());
3827}
3828
3843{
3844 setDateTime(d, date(), time);
3846}
3847
3848#if QT_DEPRECATED_SINCE(6, 9)
3867void QDateTime::setTimeSpec(Qt::TimeSpec spec)
3868{
3869 reviseTimeZone(d, asTimeZone(spec, 0, "QDateTime::setTimeSpec"));
3870}
3871
3888void QDateTime::setOffsetFromUtc(int offsetSeconds)
3889{
3891}
3892#endif // 6.9 deprecations
3893
3910{
3911 reviseTimeZone(d, toZone);
3912}
3913
3930{
3931 // Note: QDateTimeParser relies on this producing a useful result, even when
3932 // !isValid(), at least when the invalidity is a time in a fall-back (that
3933 // we'll have adjusted to lie outside it, but marked invalid because it's
3934 // not what was asked for). Other things may be doing similar.
3935 switch (getSpec(d)) {
3936 case Qt::UTC:
3937 return getMSecs(d);
3938
3939 case Qt::OffsetFromUTC:
3940 Q_ASSERT(!d.isShort());
3941 return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC;
3942
3943 case Qt::LocalTime:
3944 if (d.isShort()) {
3945 // Short form has nowhere to cache the offset, so recompute.
3948 return state.when - state.offset * MSECS_PER_SEC;
3949 }
3950 // Use the offset saved by refreshZonedDateTime() on creation.
3951 return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC;
3952
3953 case Qt::TimeZone:
3954 Q_ASSERT(!d.isShort());
3955#if QT_CONFIG(timezone)
3956 // Use offset refreshZonedDateTime() saved on creation:
3957 if (d->m_timeZone.isValid())
3958 return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC;
3959#endif
3960 return 0;
3961 }
3962 Q_UNREACHABLE_RETURN(0);
3963}
3964
3981{
3983}
3984
4001{
4002 auto status = getStatus(d);
4003 const auto spec = extractSpec(status);
4004 Q_ASSERT(specCanBeSmall(spec) || !d.isShort());
4006
4007 status &= ~QDateTimePrivate::ValidityMask;
4009 if (spec == Qt::OffsetFromUTC)
4010 state.offset = d->m_offsetFromUtc;
4011 if (!state.offset || !qAddOverflow(msecs, state.offset * MSECS_PER_SEC, &state.when))
4013 } else if (spec == Qt::LocalTime) {
4015 if (state.valid)
4017#if QT_CONFIG(timezone)
4018 } else if (spec == Qt::TimeZone && (d.detach(), d->m_timeZone.isValid())) {
4019 const auto data = d->m_timeZone.d->data(msecs);
4020 if (Q_LIKELY(data.offsetFromUtc != QTimeZonePrivate::invalidSeconds())) {
4021 state.offset = data.offsetFromUtc;
4022 Q_ASSERT(state.offset >= -SECS_PER_DAY && state.offset <= SECS_PER_DAY);
4023 if (!state.offset
4024 || !Q_UNLIKELY(qAddOverflow(msecs, state.offset * MSECS_PER_SEC, &state.when))) {
4026 data.daylightTimeOffset
4029 d->m_msecs = state.when;
4030 d->m_offsetFromUtc = state.offset;
4031 return;
4032 } // else: zone can't represent this UTC time
4033 } // else: zone unable to represent given UTC time (should only happen on overflow).
4034#endif // timezone
4035 }
4037 || (state.offset >= -SECS_PER_DAY && state.offset <= SECS_PER_DAY));
4038
4039 if (msecsCanBeSmall(state.when) && d.isShort()) {
4040 // we can keep short
4041 d.data.msecs = qintptr(state.when);
4042 d.data.status = status.toInt();
4043 } else {
4044 d.detach();
4045 d->m_status = status & ~QDateTimePrivate::ShortData;
4046 d->m_msecs = state.when;
4047 d->m_offsetFromUtc = state.offset;
4048 }
4049}
4050
4063{
4064 qint64 msecs;
4065 if (!qMulOverflow(secs, std::integral_constant<qint64, MSECS_PER_SEC>(), &msecs)) {
4066 setMSecsSinceEpoch(msecs);
4067 } else if (d.isShort()) {
4068 d.data.status &= ~int(QDateTimePrivate::ValidityMask);
4069 } else {
4070 d.detach();
4071 d->m_status &= ~QDateTimePrivate::ValidityMask;
4072 }
4073}
4074
4075#if QT_CONFIG(datestring) // depends on, so implies, textdate
4107QString QDateTime::toString(Qt::DateFormat format) const
4108{
4109 QString buf;
4110 if (!isValid())
4111 return buf;
4112
4113 switch (format) {
4114 case Qt::RFC2822Date:
4115 buf = QLocale::c().toString(*this, u"dd MMM yyyy hh:mm:ss ");
4116 buf += toOffsetString(Qt::TextDate, offsetFromUtc());
4117 return buf;
4118 default:
4119 case Qt::TextDate: {
4121 buf = toStringTextDate(p.first);
4122 // Insert time between date's day and year:
4123 buf.insert(buf.lastIndexOf(u' '),
4124 u' ' + p.second.toString(Qt::TextDate));
4125 // Append zone/offset indicator, as appropriate:
4126 switch (timeSpec()) {
4127 case Qt::LocalTime:
4128 break;
4129#if QT_CONFIG(timezone)
4130 case Qt::TimeZone:
4131 buf += u' ' + d->m_timeZone.displayName(
4132 *this, QTimeZone::OffsetName, QLocale::c());
4133 break;
4134#endif
4135 default:
4136#if 0 // ### Qt 7 GMT: use UTC instead, see qnamespace.qdoc documentation
4137 buf += " UTC"_L1;
4138#else
4139 buf += " GMT"_L1;
4140#endif
4141 if (getSpec(d) == Qt::OffsetFromUTC)
4142 buf += toOffsetString(Qt::TextDate, offsetFromUtc());
4143 }
4144 return buf;
4145 }
4146 case Qt::ISODate:
4147 case Qt::ISODateWithMs: {
4149 buf = toStringIsoDate(p.first);
4150 if (buf.isEmpty())
4151 return QString(); // failed to convert
4152 buf += u'T' + p.second.toString(format);
4153 switch (getSpec(d)) {
4154 case Qt::UTC:
4155 buf += u'Z';
4156 break;
4157 case Qt::OffsetFromUTC:
4158 case Qt::TimeZone:
4159 buf += toOffsetString(Qt::ISODate, offsetFromUtc());
4160 break;
4161 default:
4162 break;
4163 }
4164 return buf;
4165 }
4166 }
4167}
4168
4210QString QDateTime::toString(QStringView format, QCalendar cal) const
4211{
4212 return QLocale::c().toString(*this, format, cal);
4213}
4214#endif // datestring
4215
4217{
4218 /*
4219 If we have just adjusted to a day with a DST transition, our given time
4220 may lie in the transition hour (either missing or duplicated). For any
4221 other time, telling mktime() or QTimeZone what we know about DST-ness, of
4222 the time we adjusted from, will make no difference; it'll just tell us the
4223 actual DST-ness of the given time. When landing in a transition that
4224 repeats an hour, passing the prior DST-ness - when known - will get us the
4225 indicated side of the duplicate (either local or zone). When landing in a
4226 gap, the zone gives us the other side of the gap and mktime() is wrapped
4227 to coax it into doing the same (which it does by default on Unix).
4228 */
4229 auto status = getStatus(d);
4232 auto spec = extractSpec(status);
4236 return;
4237 }
4238 auto dst = extractDaylightStatus(status);
4239 qint64 local = timeToMSecs(date, time);
4240 const QDateTimePrivate::ZoneState state = stateAtMillis(d.timeZone(), local, dst);
4241 if (state.valid)
4243 else
4244 status.setFlag(QDateTimePrivate::ValidDateTime, false);
4245
4246 if (status & QDateTimePrivate::ShortData) {
4247 d.data.msecs = state.when;
4248 d.data.status = status.toInt();
4249 } else {
4250 d.detach();
4251 d->m_status = status;
4252 if (state.valid) {
4253 d->m_msecs = state.when;
4254 d->m_offsetFromUtc = state.offset;
4255 }
4256 }
4257}
4258
4274{
4275 if (isNull())
4276 return QDateTime();
4277
4278 QDateTime dt(*this);
4280 massageAdjustedDateTime(dt.d, p.first.addDays(ndays), p.second);
4281 return dt;
4282}
4283
4299{
4300 if (isNull())
4301 return QDateTime();
4302
4303 QDateTime dt(*this);
4305 massageAdjustedDateTime(dt.d, p.first.addMonths(nmonths), p.second);
4306 return dt;
4307}
4308
4324{
4325 if (isNull())
4326 return QDateTime();
4327
4328 QDateTime dt(*this);
4330 massageAdjustedDateTime(dt.d, p.first.addYears(nyears), p.second);
4331 return dt;
4332}
4333
4345{
4346 qint64 msecs;
4347 if (qMulOverflow(s, std::integral_constant<qint64, MSECS_PER_SEC>(), &msecs))
4348 return QDateTime();
4349 return addMSecs(msecs);
4350}
4351
4362{
4363 if (!isValid())
4364 return QDateTime();
4365
4366 QDateTime dt(*this);
4367 switch (getSpec(d)) {
4368 case Qt::LocalTime:
4369 case Qt::TimeZone:
4370 // Convert to real UTC first in case this crosses a DST transition:
4371 if (!qAddOverflow(toMSecsSinceEpoch(), msecs, &msecs)) {
4372 dt.setMSecsSinceEpoch(msecs);
4373 } else if (dt.d.isShort()) {
4374 dt.d.data.status &= ~int(QDateTimePrivate::ValidityMask);
4375 } else {
4376 dt.d.detach();
4377 dt.d->m_status &= ~QDateTimePrivate::ValidityMask;
4378 }
4379 break;
4380 case Qt::UTC:
4381 case Qt::OffsetFromUTC:
4382 // No need to convert, just add on
4383 if (qAddOverflow(getMSecs(d), msecs, &msecs)) {
4384 if (dt.d.isShort()) {
4385 dt.d.data.status &= ~int(QDateTimePrivate::ValidityMask);
4386 } else {
4387 dt.d.detach();
4388 dt.d->m_status &= ~QDateTimePrivate::ValidityMask;
4389 }
4390 } else if (d.isShort()) {
4391 // need to check if we need to enlarge first
4392 if (msecsCanBeSmall(msecs)) {
4393 dt.d.data.msecs = qintptr(msecs);
4394 } else {
4395 dt.d.detach();
4396 dt.d->m_msecs = msecs;
4397 }
4398 } else {
4399 dt.d.detach();
4400 dt.d->m_msecs = msecs;
4401 }
4402 break;
4403 }
4404 return dt;
4405}
4406
4444{
4445 return date().daysTo(other.date());
4446}
4447
4466{
4467 return msecsTo(other) / MSECS_PER_SEC;
4468}
4469
4485{
4486 if (!isValid() || !other.isValid())
4487 return 0;
4488
4489 return other.toMSecsSinceEpoch() - toMSecsSinceEpoch();
4490}
4491
4561#if QT_DEPRECATED_SINCE(6, 9)
4581QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const
4582{
4583 return toTimeZone(asTimeZone(spec, 0, "toTimeSpec"));
4584}
4585#endif // 6.9 deprecation
4586
4602{
4603 return toTimeZone(QTimeZone::fromSecondsAheadOfUtc(offsetSeconds));
4604}
4605
4618{
4620}
4621
4634{
4635 return toTimeZone(QTimeZone::UTC);
4636}
4637
4657{
4658 if (timeRepresentation() == timeZone)
4659 return *this;
4660
4661 if (!isValid()) {
4662 QDateTime ret = *this;
4663 ret.setTimeZone(timeZone);
4664 return ret;
4665 }
4666
4667 return fromMSecsSinceEpoch(toMSecsSinceEpoch(), timeZone);
4668}
4669
4678bool QDateTime::equals(const QDateTime &other) const
4679{
4680 if (!isValid())
4681 return !other.isValid();
4682 if (!other.isValid())
4683 return false;
4684
4685 if (usesSameOffset(d, other.d))
4686 return getMSecs(d) == getMSecs(other.d);
4687
4688 // Convert to UTC and compare
4689 return toMSecsSinceEpoch() == other.toMSecsSinceEpoch();
4690}
4691
4725bool QDateTime::precedes(const QDateTime &other) const
4726{
4727 if (!isValid())
4728 return other.isValid();
4729 if (!other.isValid())
4730 return false;
4731
4732 if (usesSameOffset(d, other.d))
4733 return getMSecs(d) < getMSecs(other.d);
4734
4735 // Convert to UTC and compare
4736 return toMSecsSinceEpoch() < other.toMSecsSinceEpoch();
4737}
4738
4789{
4791}
4792
4804{
4806}
4807
4910#if defined(Q_OS_WIN)
4911static inline uint msecsFromDecomposed(int hour, int minute, int sec, int msec = 0)
4912{
4913 return MSECS_PER_HOUR * hour + MSECS_PER_MIN * minute + MSECS_PER_SEC * sec + msec;
4914}
4915
4917{
4918 SYSTEMTIME st = {};
4919 GetLocalTime(&st);
4920 return QDate(st.wYear, st.wMonth, st.wDay);
4921}
4922
4924{
4925 QTime ct;
4926 SYSTEMTIME st = {};
4927 GetLocalTime(&st);
4928 ct.setHMS(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
4929 return ct;
4930}
4931
4933{
4934 // We can get local time or "system" time (which is UTC); otherwise, we must
4935 // convert, which is most efficiently done from UTC.
4936 const Qt::TimeSpec spec = zone.timeSpec();
4937 SYSTEMTIME st = {};
4938 // GetSystemTime()'s page links to its partner page for GetLocalTime().
4939 // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtime
4940 (spec == Qt::LocalTime ? GetLocalTime : GetSystemTime)(&st);
4941 QDate d(st.wYear, st.wMonth, st.wDay);
4942 QTime t(msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds));
4943 if (spec == Qt::LocalTime)
4944 return QDateTime(d, t);
4945 QDateTime utc(d, t, QTimeZone::UTC);
4946 return spec == Qt::UTC ? utc : utc.toTimeZone(zone);
4947}
4948
4950{
4951 SYSTEMTIME st = {};
4952 GetSystemTime(&st);
4953 const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay));
4954
4955 return msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds) +
4956 daysAfterEpoch * MSECS_PER_DAY;
4957}
4958
4960{
4961 SYSTEMTIME st = {};
4962 GetSystemTime(&st);
4963 const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay));
4964
4965 return st.wHour * SECS_PER_HOUR + st.wMinute * SECS_PER_MIN + st.wSecond +
4966 daysAfterEpoch * SECS_PER_DAY;
4967}
4968
4969#elif defined(Q_OS_UNIX)
4971{
4973}
4974
4976{
4978}
4979
4981{
4983}
4984
4986{
4987 // posix compliant system
4988 // we have milliseconds
4989 struct timeval tv;
4990 gettimeofday(&tv, nullptr);
4991 return tv.tv_sec * MSECS_PER_SEC + tv.tv_usec / 1000;
4992}
4993
4995{
4996 struct timeval tv;
4997 gettimeofday(&tv, nullptr);
4998 return tv.tv_sec;
4999}
5000#else
5001#error "What system is this?"
5002#endif
5003
5004#if QT_DEPRECATED_SINCE(6, 9)
5028QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec, int offsetSeconds)
5029{
5030 return fromMSecsSinceEpoch(msecs,
5031 asTimeZone(spec, offsetSeconds, "QDateTime::fromMSecsSinceEpoch"));
5032}
5033
5057QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec, int offsetSeconds)
5058{
5059 return fromSecsSinceEpoch(secs,
5060 asTimeZone(spec, offsetSeconds, "QDateTime::fromSecsSinceEpoch"));
5061}
5062#endif // 6.9 deprecations
5063
5079{
5080 QDateTime dt;
5081 reviseTimeZone(dt.d, timeZone);
5082 if (timeZone.isValid())
5083 dt.setMSecsSinceEpoch(msecs);
5084 return dt;
5085}
5086
5092{
5094}
5095
5111{
5112 QDateTime dt;
5113 reviseTimeZone(dt.d, timeZone);
5114 if (timeZone.isValid())
5115 dt.setSecsSinceEpoch(secs);
5116 return dt;
5117}
5118
5124{
5126}
5127
5128#if QT_CONFIG(datestring) // depends on, so implies, textdate
5129
5146QDateTime QDateTime::fromString(QStringView string, Qt::DateFormat format)
5147{
5148 if (string.isEmpty())
5149 return QDateTime();
5150
5151 switch (format) {
5152 case Qt::RFC2822Date: {
5153 const ParsedRfcDateTime rfc = rfcDateImpl(string);
5154
5155 if (!rfc.date.isValid() || !rfc.time.isValid())
5156 return QDateTime();
5157
5158 QDateTime dateTime(rfc.date, rfc.time, QTimeZone::UTC);
5160 return dateTime;
5161 }
5162 case Qt::ISODate:
5163 case Qt::ISODateWithMs: {
5164 const int size = string.size();
5165 if (size < 10)
5166 return QDateTime();
5167
5168 QDate date = QDate::fromString(string.first(10), Qt::ISODate);
5169 if (!date.isValid())
5170 return QDateTime();
5171 if (size == 10)
5172 return date.startOfDay();
5173
5175 QStringView isoString = string.sliced(10); // trim "yyyy-MM-dd"
5176
5177 // Must be left with T (or space) and at least one digit for the hour:
5178 if (isoString.size() < 2
5179 || !(isoString.startsWith(u'T', Qt::CaseInsensitive)
5180 // RFC 3339 (section 5.6) allows a space here. (It actually
5181 // allows any separator one considers more readable, merely
5182 // giving space as an example - but let's not go wild !)
5183 || isoString.startsWith(u' '))) {
5184 return QDateTime();
5185 }
5186 isoString = isoString.sliced(1); // trim 'T' (or space)
5187
5188 // Check end of string for Time Zone definition, either Z for UTC or [+-]HH:mm for Offset
5189 if (isoString.endsWith(u'Z', Qt::CaseInsensitive)) {
5190 zone = QTimeZone::UTC;
5191 isoString.chop(1); // trim 'Z'
5192 } else {
5193 // the loop below is faster but functionally equal to:
5194 // const int signIndex = isoString.indexOf(QRegulargExpression(QStringLiteral("[+-]")));
5195 int signIndex = isoString.size() - 1;
5196 Q_ASSERT(signIndex >= 0);
5197 bool found = false;
5198 do {
5199 QChar character(isoString[signIndex]);
5200 found = character == u'+' || character == u'-';
5201 } while (!found && --signIndex >= 0);
5202
5203 if (found) {
5204 bool ok;
5205 int offset = fromOffsetString(isoString.sliced(signIndex), &ok);
5206 if (!ok)
5207 return QDateTime();
5208 isoString = isoString.first(signIndex);
5210 }
5211 }
5212
5213 // Might be end of day (24:00, including variants), which QTime considers invalid.
5214 // ISO 8601 (section 4.2.3) says that 24:00 is equivalent to 00:00 the next day.
5215 bool isMidnight24 = false;
5216 QTime time = fromIsoTimeString(isoString, format, &isMidnight24);
5217 if (!time.isValid())
5218 return QDateTime();
5219 if (isMidnight24) // time is 0:0, but we want the start of next day:
5220 return date.addDays(1).startOfDay(zone);
5221 return QDateTime(date, time, zone);
5222 }
5223 case Qt::TextDate: {
5225
5226 auto tokens = string.tokenize(u' ', Qt::SkipEmptyParts);
5227 auto it = tokens.begin();
5228 for (int i = 0; i < 6 && it != tokens.end(); ++i, ++it)
5229 parts.emplace_back(*it);
5230
5231 // Documented as "ddd MMM d HH:mm:ss yyyy" with optional offset-suffix;
5232 // and allow time either before or after year.
5233 if (parts.size() < 5 || it != tokens.end())
5234 return QDateTime();
5235
5236 // Year and time can be in either order.
5237 // Guess which by looking for ':' in the time
5238 int yearPart = 3;
5239 int timePart = 3;
5240 if (parts.at(3).contains(u':'))
5241 yearPart = 4;
5242 else if (parts.at(4).contains(u':'))
5243 timePart = 4;
5244 else
5245 return QDateTime();
5246
5247 bool ok = false;
5248 int day = parts.at(2).toInt(&ok);
5249 int year = ok ? parts.at(yearPart).toInt(&ok) : 0;
5250 int month = fromShortMonthName(parts.at(1));
5251 if (!ok || year == 0 || day == 0 || month < 1)
5252 return QDateTime();
5253
5254 const QDate date(year, month, day);
5255 if (!date.isValid())
5256 return QDateTime();
5257
5258 const QTime time = fromIsoTimeString(parts.at(timePart), format, nullptr);
5259 if (!time.isValid())
5260 return QDateTime();
5261
5262 if (parts.size() == 5)
5263 return QDateTime(date, time);
5264
5265 QStringView tz = parts.at(5);
5266 if (tz.startsWith("UTC"_L1)
5267 // GMT has long been deprecated as an alias for UTC.
5268 || tz.startsWith("GMT"_L1, Qt::CaseInsensitive)) {
5269 tz = tz.sliced(3);
5270 if (tz.isEmpty())
5272
5273 int offset = fromOffsetString(tz, &ok);
5275 : QDateTime();
5276 }
5277 return QDateTime();
5278 }
5279 }
5280
5281 return QDateTime();
5282}
5283
5381QDateTime QDateTime::fromString(const QString &string, QStringView format, QCalendar cal)
5382{
5383#if QT_CONFIG(datetimeparser)
5384 QDateTime datetime;
5385
5386 QDateTimeParser dt(QMetaType::QDateTime, QDateTimeParser::FromString, cal);
5387 dt.setDefaultLocale(QLocale::c());
5388 if (dt.parseFormat(format) && (dt.fromString(string, &datetime) || !datetime.isValid()))
5389 return datetime;
5390#else
5391 Q_UNUSED(string);
5393 Q_UNUSED(cal);
5394#endif
5395 return QDateTime();
5396}
5397
5398#endif // datestring
5399
5400/*****************************************************************************
5401 Date/time stream functions
5402 *****************************************************************************/
5403
5404#ifndef QT_NO_DATASTREAM
5414{
5415 if (out.version() < QDataStream::Qt_5_0)
5416 return out << quint32(date.jd);
5417 else
5418 return out << date.jd;
5419}
5420
5430{
5431 if (in.version() < QDataStream::Qt_5_0) {
5432 quint32 jd;
5433 in >> jd;
5434 // Older versions consider 0 an invalid jd.
5435 date.jd = (jd != 0 ? jd : QDate::nullJd());
5436 } else {
5437 in >> date.jd;
5438 }
5439
5440 return in;
5441}
5442
5452{
5453 if (out.version() >= QDataStream::Qt_4_0) {
5454 return out << quint32(time.mds);
5455 } else {
5456 // Qt3 had no support for reading -1, QTime() was valid and serialized as 0
5457 return out << quint32(time.isNull() ? 0 : time.mds);
5458 }
5459}
5460
5470{
5471 quint32 ds;
5472 in >> ds;
5473 if (in.version() >= QDataStream::Qt_4_0) {
5474 time.mds = int(ds);
5475 } else {
5476 // Qt3 would write 0 for a null time
5477 time.mds = (ds == 0) ? QTime::NullTime : int(ds);
5478 }
5479 return in;
5480}
5481
5490{
5491 QPair<QDate, QTime> dateAndTime;
5492
5493 // TODO: new version, route spec and details via QTimeZone
5494 if (out.version() >= QDataStream::Qt_5_2) {
5495
5496 // In 5.2 we switched to using Qt::TimeSpec and added offset and zone support
5497 dateAndTime = getDateTime(dateTime.d);
5498 out << dateAndTime << qint8(dateTime.timeSpec());
5501#if QT_CONFIG(timezone)
5502 else if (dateTime.timeSpec() == Qt::TimeZone)
5503 out << dateTime.timeZone();
5504#endif // timezone
5505
5506 } else if (out.version() == QDataStream::Qt_5_0) {
5507
5508 // In Qt 5.0 we incorrectly serialised all datetimes as UTC.
5509 // This approach is wrong and should not be used again; it breaks
5510 // the guarantee that a deserialised local datetime is the same time
5511 // of day, regardless of which timezone it was serialised in.
5512 dateAndTime = getDateTime((dateTime.isValid() ? dateTime.toUTC() : dateTime).d);
5513 out << dateAndTime << qint8(dateTime.timeSpec());
5514
5515 } else if (out.version() >= QDataStream::Qt_4_0) {
5516
5517 // From 4.0 to 5.1 (except 5.0) we used QDateTimePrivate::Spec
5518 dateAndTime = getDateTime(dateTime.d);
5519 out << dateAndTime;
5520 switch (dateTime.timeSpec()) {
5521 case Qt::UTC:
5523 break;
5524 case Qt::OffsetFromUTC:
5526 break;
5527 case Qt::TimeZone:
5529 break;
5530 case Qt::LocalTime:
5532 break;
5533 }
5534
5535 } else { // version < QDataStream::Qt_4_0
5536
5537 // Before 4.0 there was no TimeSpec, only Qt::LocalTime was supported
5538 dateAndTime = getDateTime(dateTime.d);
5539 out << dateAndTime;
5540
5541 }
5542
5543 return out;
5544}
5545
5555{
5556 QDate dt;
5557 QTime tm;
5558 qint8 ts = 0;
5560
5561 if (in.version() >= QDataStream::Qt_5_2) {
5562
5563 // In 5.2 we switched to using Qt::TimeSpec and added offset and zone support
5564 in >> dt >> tm >> ts;
5565 switch (static_cast<Qt::TimeSpec>(ts)) {
5566 case Qt::UTC:
5567 zone = QTimeZone::UTC;
5568 break;
5569 case Qt::OffsetFromUTC: {
5570 qint32 offset = 0;
5571 in >> offset;
5573 break;
5574 }
5575 case Qt::LocalTime:
5576 break;
5577 case Qt::TimeZone:
5578 in >> zone;
5579 break;
5580 }
5581 dateTime = QDateTime(dt, tm, zone);
5582
5583 } else if (in.version() == QDataStream::Qt_5_0) {
5584
5585 // In Qt 5.0 we incorrectly serialised all datetimes as UTC
5586 in >> dt >> tm >> ts;
5588 if (static_cast<Qt::TimeSpec>(ts) == Qt::LocalTime)
5590
5591 } else if (in.version() >= QDataStream::Qt_4_0) {
5592
5593 // From 4.0 to 5.1 (except 5.0) we used QDateTimePrivate::Spec
5594 in >> dt >> tm >> ts;
5595 switch (static_cast<QDateTimePrivate::Spec>(ts)) {
5596 case QDateTimePrivate::OffsetFromUTC: // No offset was stored, so treat as UTC.
5598 zone = QTimeZone::UTC;
5599 break;
5600 case QDateTimePrivate::TimeZone: // No zone was stored, so treat as LocalTime:
5604 break;
5605 }
5606 dateTime = QDateTime(dt, tm, zone);
5607
5608 } else { // version < QDataStream::Qt_4_0
5609
5610 // Before 4.0 there was no TimeSpec, only Qt::LocalTime was supported
5611 in >> dt >> tm;
5612 dateTime = QDateTime(dt, tm);
5613
5614 }
5615
5616 return in;
5617}
5618#endif // QT_NO_DATASTREAM
5619
5620/*****************************************************************************
5621 Date / Time Debug Streams
5622*****************************************************************************/
5623
5624#if !defined(QT_NO_DEBUG_STREAM) && QT_CONFIG(datestring)
5626{
5627 QDebugStateSaver saver(dbg);
5628 dbg.nospace() << "QDate(";
5629 if (date.isValid())
5630 // QTBUG-91070, ISODate only supports years in the range 0-9999
5631 if (int y = date.year(); y > 0 && y <= 9999)
5632 dbg.nospace() << date.toString(Qt::ISODate);
5633 else
5634 dbg.nospace() << date.toString(Qt::TextDate);
5635 else
5636 dbg.nospace() << "Invalid";
5637 dbg.nospace() << ')';
5638 return dbg;
5639}
5640
5642{
5643 QDebugStateSaver saver(dbg);
5644 dbg.nospace() << "QTime(";
5645 if (time.isValid())
5646 dbg.nospace() << time.toString(u"HH:mm:ss.zzz");
5647 else
5648 dbg.nospace() << "Invalid";
5649 dbg.nospace() << ')';
5650 return dbg;
5651}
5652
5654{
5655 QDebugStateSaver saver(dbg);
5656 dbg.nospace() << "QDateTime(";
5657 if (date.isValid()) {
5658 const Qt::TimeSpec ts = date.timeSpec();
5659 dbg.noquote() << date.toString(u"yyyy-MM-dd HH:mm:ss.zzz t")
5660 << ' ' << ts;
5661 switch (ts) {
5662 case Qt::UTC:
5663 break;
5664 case Qt::OffsetFromUTC:
5665 dbg.space() << date.offsetFromUtc() << 's';
5666 break;
5667 case Qt::TimeZone:
5668#if QT_CONFIG(timezone)
5669 dbg.space() << date.timeZone().id();
5670#endif // timezone
5671 break;
5672 case Qt::LocalTime:
5673 break;
5674 }
5675 } else {
5676 dbg.nospace() << "Invalid";
5677 }
5678 return dbg.nospace() << ')';
5679}
5680#endif // debug_stream && datestring
5681
5688size_t qHash(const QDateTime &key, size_t seed)
5689{
5690 // Use to toMSecsSinceEpoch instead of individual qHash functions for
5691 // QDate/QTime/spec/offset because QDateTime::operator== converts both arguments
5692 // to the same timezone. If we don't, qHash would return different hashes for
5693 // two QDateTimes that are equivalent once converted to the same timezone.
5694 return key.isValid() ? qHash(key.toMSecsSinceEpoch(), seed) : seed;
5695}
5696
5703size_t qHash(QDate key, size_t seed) noexcept
5704{
5705 return qHash(key.toJulianDay(), seed);
5706}
5707
5714size_t qHash(QTime key, size_t seed) noexcept
5715{
5716 return qHash(key.msecsSinceStartOfDay(), seed);
5717}
5718
The QCalendar class describes calendar systems.
Definition qcalendar.h:53
QDate dateFromParts(int year, int month, int day) const
QString monthName(const QLocale &locale, int month, int year=Unspecified, QLocale::FormatType format=QLocale::LongFormat) const
Returns a suitably localised name for a month.
YearMonthDay partsFromDate(QDate date) const
Converts a QDate to a year, month, and day of the month.
bool hasYearZero() const
Returns true if this calendar has a year zero.
int monthsInYear(int year) const
Returns the number of months in the given year.
int dayOfWeek(QDate date) const
Returns the day of the week number for the given date.
bool isProleptic() const
Returns true if this calendar is proleptic.
int daysInMonth(int month, int year=Unspecified) const
Returns the number of days in the given month of the given year.
int daysInYear(int year) const
Returns the number of days in the given year.
\inmodule QtCore
Definition qchar.h:48
constexpr bool isDigit() const noexcept
Returns true if the character is a decimal digit (Number_DecimalDigit); otherwise returns false.
Definition qchar.h:473
constexpr char toLatin1() const noexcept
Returns the Latin-1 character equivalent to the QChar, or 0.
Definition qchar.h:457
\inmodule QtCore\reentrant
Definition qdatastream.h:30
QDateTime::ShortData QDateTimeShortData
Definition qdatetime_p.h:38
StatusFlags m_status
QDateTime::Data QDateTimeData
Definition qdatetime_p.h:39
static ZoneState localStateAtMillis(qint64 millis, DaylightStatus dst)
static ZoneState expressUtcAsLocal(qint64 utcMSecs)
static QDateTime::Data create(QDate toDate, QTime toTime, const QTimeZone &timeZone)
static QString localNameAtMillis(qint64 millis, DaylightStatus dst)
\inmodule QtCore\reentrant
Definition qdatetime.h:257
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.
QDateTime toUTC() const
Returns a copy of this datetime converted to UTC.
void setTime(QTime time)
Sets the time part of this datetime to time.
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
void setDate(QDate date)
Sets the date part of this datetime to date.
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)
void setTimeZone(const QTimeZone &toZone)
\inmodule QtCore \reentrant
Definition qdatetime.h:27
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:86
constexpr qint64 toJulianDay() const
Converts the date to a Julian day.
Definition qdatetime.h:161
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:159
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:85
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:172
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:30
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
QString dayName(int, FormatType format=LongFormat) const
Definition qlocale.cpp:2867
@ ShortFormat
Definition qlocale.h:865
static QLocale c()
Returns a QLocale object initialized to the "C" locale.
Definition qlocale.h:1103
QString toString(qlonglong i) const
Returns a localized string representation of i.
Definition qlocale.cpp:1962
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
\inmodule QtCore
Definition qstringview.h:76
bool startsWith(QStringView s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
constexpr void chop(qsizetype n) noexcept
Truncates this string view by length characters.
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.
bool endsWith(QStringView s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
int toInt(bool *ok=nullptr, int base=10) const
Returns the string view converted to an int using base base, which is 10 by default and must be betwe...
Definition qstring.h:1025
constexpr QStringView last(qsizetype n) const noexcept
constexpr QStringView sliced(qsizetype pos) const noexcept
constexpr QChar front() const
qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QChar front() const
Definition qstring.h:214
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1095
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7005
static constexpr qint64 invalidSeconds()
\inmodule QtCore
Definition qtimezone.h:25
static constexpr bool isUtcOrFixedOffset(Qt::TimeSpec spec) noexcept
Definition qtimezone.h:137
constexpr int fixedSecondsAheadOfUtc() const noexcept
Definition qtimezone.h:134
bool isValid() const
Returns true if this time zone is valid.
static QTimeZone fromSecondsAheadOfUtc(int offset)
Definition qtimezone.h:129
constexpr bool isUtcOrFixedOffset() const noexcept
Definition qtimezone.h:139
constexpr Qt::TimeSpec timeSpec() const noexcept
Definition qtimezone.h:133
\inmodule QtCore \reentrant
Definition qdatetime.h:189
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:197
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:217
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:218
int second() const
Returns the second part (0 to 59) of the time.
constexpr QTime()
Constructs a null time object.
Definition qdatetime.h:193
QTime addSecs(int secs) const
Returns a QTime object containing a time s seconds later than the time of this object (or earlier if ...
constexpr size_type size() const noexcept
void remove(qsizetype i, qsizetype n=1)
T & emplace_back(Args &&...args)
const T & at(qsizetype idx) const
iterator erase(const_iterator begin, const_iterator end)
iterator begin() noexcept
#define this
Definition dialogs.cpp:9
QString text
QDate date
[1]
QSet< QString >::iterator it
else opt state
[0]
QString localTimeAbbbreviationAt(qint64 local, QDateTimePrivate::DaylightStatus dst)
SystemMillisRange computeSystemMillisRange()
QDateTimePrivate::ZoneState utcToLocal(qint64 utcMillis)
QDateTimePrivate::ZoneState mapLocalTime(qint64 local, QDateTimePrivate::DaylightStatus dst)
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
DateFormat
@ RFC2822Date
@ ISODate
@ ISODateWithMs
@ TextDate
@ CaseInsensitive
@ SkipEmptyParts
Definition qnamespace.h:127
#define Q_UNLIKELY(x)
#define Q_NEVER_INLINE
#define Q_LIKELY(x)
std::pair< T1, T2 > QPair
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 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 QDateTimePrivate::StatusFlags getStatus(const QDateTimeData &d)
static qint64 getMSecs(const QDateTimeData &d)
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 void reviseTimeZone(QDateTimeData &d, QTimeZone zone)
static bool daysAndMillisOverflow(qint64 days, qint64 millisInDay, qint64 *sumMillis)
static QDate fixedDate(QCalendar::YearMonthDay parts, QCalendar cal)
Definition qdatetime.cpp:53
static void massageAdjustedDateTime(QDateTimeData &d, QDate date, QTime time)
static void refreshSimpleDateTime(QDateTimeData &d)
static void setDateTime(QDateTimeData &d, QDate date, QTime time)
static bool msecsCanBeSmall(qint64 msecs)
static QPair< QDate, QTime > getDateTime(const QDateTimeData &d)
static constexpr Qt::TimeSpec extractSpec(QDateTimePrivate::StatusFlags status)
static void checkValidDateTime(QDateTimeData &d)
static bool usesSameOffset(const QDateTimeData &a, const QDateTimeData &b)
static QDateTimePrivate::ZoneState stateAtMillis(QTimeZone zone, qint64 millis, QDateTimePrivate::DaylightStatus dst)
static QDateTime toEarliest(QDate day, const QTimeZone &zone)
static bool millisInSystemRange(qint64 millis, qint64 slack=0)
static void refreshZonedDateTime(QDateTimeData &d, const QTimeZone &zone)
static qint64 msecsToJulianDay(qint64 msecs)
DaySide
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
QSimpleParsedNumber< qulonglong > qstrntoull(const char *begin, qsizetype size, int base)
#define qWarning
Definition qlogging.h:162
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
GLenum GLenum dst
GLenum GLuint GLenum GLsizei const GLchar * buf
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLuint name
GLint first
GLint GLsizei GLsizei GLenum format
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLbyte GLbyte tz
GLuint res
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint in
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
static qreal dot(const QPointF &a, const QPointF &b)
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)
#define sp
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:45
size_t quintptr
Definition qtypes.h:72
int qint32
Definition qtypes.h:44
quint64 qulonglong
Definition qtypes.h:59
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
QT_BEGIN_NAMESPACE typedef signed char qint8
Definition qtypes.h:40
ptrdiff_t qintptr
Definition qtypes.h:71
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:17
static char * convertFromUnicode(char *out, QStringView in, QStringConverter::State *state) noexcept