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 | |
38 | QT_BEGIN_NAMESPACE |
39 | |
40 | using namespace Qt::StringLiterals; |
41 | using namespace QtPrivate::DateTimeConstants; |
42 | using namespace QtMiscUtils; |
43 | |
44 | /***************************************************************************** |
45 | Date/Time Constants |
46 | *****************************************************************************/ |
47 | |
48 | /***************************************************************************** |
49 | QDate static helper functions |
50 | *****************************************************************************/ |
51 | static_assert(std::is_trivially_copyable_v<QCalendar::YearMonthDay>); |
52 | |
53 | static inline QDate fixedDate(QCalendar::YearMonthDay parts, QCalendar cal) |
54 | { |
55 | if ((parts.year < 0 && !cal.isProleptic()) || (parts.year == 0 && !cal.hasYearZero())) |
56 | return QDate(); |
57 | |
58 | parts.day = qMin(a: parts.day, b: cal.daysInMonth(month: parts.month, year: parts.year)); |
59 | return cal.dateFromParts(parts); |
60 | } |
61 | |
62 | static inline QDate fixedDate(QCalendar::YearMonthDay parts) |
63 | { |
64 | if (parts.year) { |
65 | parts.day = qMin(a: parts.day, b: QGregorianCalendar::monthLength(month: parts.month, year: parts.year)); |
66 | const auto jd = QGregorianCalendar::julianFromParts(year: parts.year, month: parts.month, day: parts.day); |
67 | if (jd) |
68 | return QDate::fromJulianDay(jd_: *jd); |
69 | } |
70 | return QDate(); |
71 | } |
72 | |
73 | /***************************************************************************** |
74 | Date/Time formatting helper functions |
75 | *****************************************************************************/ |
76 | |
77 | #if QT_CONFIG(textdate) |
78 | static const char qt_shortMonthNames[][4] = { |
79 | "Jan" , "Feb" , "Mar" , "Apr" , "May" , "Jun" , |
80 | "Jul" , "Aug" , "Sep" , "Oct" , "Nov" , "Dec" |
81 | }; |
82 | |
83 | static 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 |
94 | namespace { |
95 | using ParsedInt = QSimpleParsedNumber<qulonglong>; |
96 | |
97 | /* |
98 | Reads a whole number that must be the whole text. |
99 | */ |
100 | ParsedInt readInt(QLatin1StringView text) |
101 | { |
102 | // Various date formats' fields (e.g. all in ISO) should not accept spaces |
103 | // or signs, so check that the string starts with a digit and that qstrntoull() |
104 | // converted the whole string. |
105 | |
106 | if (text.isEmpty() || !isAsciiDigit(c: text.front().toLatin1())) |
107 | return {}; |
108 | |
109 | QSimpleParsedNumber res = qstrntoull(nptr: text.data(), size: text.size(), base: 10); |
110 | return res.used == text.size() ? res : ParsedInt{}; |
111 | } |
112 | |
113 | ParsedInt readInt(QStringView text) |
114 | { |
115 | if (text.isEmpty()) |
116 | return {}; |
117 | |
118 | // Converting to Latin-1 because QStringView::toULongLong() works with |
119 | // US-ASCII only by design anyway. |
120 | // Also QStringView::toULongLong() can't be used here as it will happily ignore |
121 | // spaces and accept signs; but various date formats' fields (e.g. all in ISO) |
122 | // should not. |
123 | QVarLengthArray<char> latin1(text.size()); |
124 | QLatin1::convertFromUnicode(out: latin1.data(), in: text); |
125 | return readInt(text: QLatin1StringView{latin1.data(), latin1.size()}); |
126 | } |
127 | |
128 | } // namespace |
129 | |
130 | struct ParsedRfcDateTime { |
131 | QDate date; |
132 | QTime time; |
133 | int utcOffset; |
134 | }; |
135 | |
136 | static 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 | |
146 | static ParsedRfcDateTime rfcDateImpl(QStringView s) |
147 | { |
148 | // Matches "[ddd,] dd MMM yyyy[ hh:mm[:ss]] [±hhmm]" - correct RFC 822, 2822, 5322 format - |
149 | // or "ddd MMM dd[ hh:mm:ss] yyyy [±hhmm]" - permissive RFC 850, 1036 (read only) |
150 | ParsedRfcDateTime result; |
151 | |
152 | QVarLengthArray<QStringView, 6> words; |
153 | |
154 | auto tokens = s.tokenize(needle: u' ', flags: Qt::SkipEmptyParts); |
155 | auto it = tokens.begin(); |
156 | for (int i = 0; i < 6 && it != tokens.end(); ++i, ++it) |
157 | words.emplace_back(args: *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(c: u',')) { |
179 | dayName = maybeDayName.chopped(n: 1); |
180 | words.erase(pos: words.begin()); |
181 | } else if (!maybeDayName.front().isDigit()) { |
182 | dayName = maybeDayName; |
183 | words.erase(pos: 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(idx: 2).contains(c: colon) ? 3 : 2; |
201 | } |
202 | |
203 | int dayOfWeek = 0; |
204 | if (!dayName.isEmpty()) { |
205 | if (!isShortName(dayName)) |
206 | return result; |
207 | dayOfWeek = shortDayFromName(name: dayName); |
208 | if (!dayOfWeek) |
209 | break; |
210 | } |
211 | |
212 | const int day = words.at(idx: dayIndex).toInt(ok: &ok); |
213 | if (!ok) |
214 | return result; |
215 | const int year = words.at(idx: yearIndex).toInt(ok: &ok); |
216 | if (!ok) |
217 | return result; |
218 | const QStringView monthName = words.at(idx: 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(i: yearIndex); |
230 | words.remove(i: 0, n: 2); // month and day-of-month, in some order |
231 | |
232 | // Time: [hh:mm[:ss]] |
233 | QTime time; |
234 | if (words.size() && words.at(idx: 0).contains(c: colon)) { |
235 | const QStringView when = words.front(); |
236 | words.erase(pos: 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(n: 2).toInt(ok: &ok); |
242 | if (!ok) |
243 | return result; |
244 | const int minute = when.sliced(pos: 3, n: 2).toInt(ok: &ok); |
245 | if (!ok) |
246 | return result; |
247 | const auto secs = when.size() == 8 ? when.last(n: 2).toInt(ok: &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(pos: 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(pos: 1, n: 2).toInt(ok: &ok); |
266 | if (!ok) |
267 | return result; |
268 | const auto minute = zone.size() == 5 ? zone.last(n: 2).toInt(ok: &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 |
284 | static QString toOffsetString(Qt::DateFormat format, int offset) |
285 | { |
286 | return QString::asprintf(format: "%c%02d%s%02d" , |
287 | offset >= 0 ? '+' : '-', |
288 | qAbs(t: 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(t: offset) / 60) % 60); |
292 | } |
293 | |
294 | #if QT_CONFIG(datestring) |
295 | // Parse offset in [+-]HH[[:]mm] format |
296 | static 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(pos: 1); |
318 | qsizetype hhLen = time.indexOf(c: 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(n: qMin(a: hhLen, b: time.size())); |
326 | bool ok = false; |
327 | const int hour = hhRef.toInt(ok: &ok); |
328 | if (!ok || hour > 23) // More generous than QTimeZone::MaxUtcOffsetSecs |
329 | return 0; |
330 | |
331 | const QStringView mmRef = time.sliced(pos: qMin(a: mmIndex, b: time.size())); |
332 | const int minute = mmRef.isEmpty() ? 0 : mmRef.toInt(ok: &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 | |
345 | /*! |
346 | \class QDate |
347 | \inmodule QtCore |
348 | \reentrant |
349 | \brief The QDate class provides date functions. |
350 | |
351 | A QDate object represents a particular day, regardless of calendar, locale |
352 | or other settings used when creating it or supplied by the system. It can |
353 | report the year, month and day of the month that represent the day with |
354 | respect to the proleptic Gregorian calendar or any calendar supplied as a |
355 | QCalendar object. QDate objects should be passed by value rather than by |
356 | reference to const; they simply package \c qint64. |
357 | |
358 | A QDate object is typically created by giving the year, month, and day |
359 | numbers explicitly. Note that QDate interprets year numbers less than 100 as |
360 | presented, i.e., as years 1 through 99, without adding any offset. The |
361 | static function currentDate() creates a QDate object containing the date |
362 | read from the system clock. An explicit date can also be set using |
363 | setDate(). The fromString() function returns a QDate given a string and a |
364 | date format which is used to interpret the date within the string. |
365 | |
366 | The year(), month(), and day() functions provide access to the year, month, |
367 | and day numbers. When more than one of these values is needed, it is more |
368 | efficient to call QCalendar::partsFromDate(), to save repeating (potentially |
369 | expensive) calendrical calculations. |
370 | |
371 | Also, dayOfWeek() and dayOfYear() functions are provided. The same |
372 | information is provided in textual format by toString(). QLocale can map the |
373 | day numbers to names, QCalendar can map month numbers to names. |
374 | |
375 | QDate provides a full set of operators to compare two QDate |
376 | objects where smaller means earlier, and larger means later. |
377 | |
378 | You can increment (or decrement) a date by a given number of days |
379 | using addDays(). Similarly you can use addMonths() and addYears(). |
380 | The daysTo() function returns the number of days between two |
381 | dates. |
382 | |
383 | The daysInMonth() and daysInYear() functions return how many days there are |
384 | in this date's month and year, respectively. The isLeapYear() function |
385 | indicates whether a date is in a leap year. QCalendar can also supply this |
386 | information, in some cases more conveniently. |
387 | |
388 | \section1 Remarks |
389 | |
390 | \note All conversion to and from string formats is done using the C locale. |
391 | For localized conversions, see QLocale. |
392 | |
393 | In the Gregorian calendar, there is no year 0. Dates in that year are |
394 | considered invalid. The year -1 is the year "1 before Christ" or "1 before |
395 | common era." The day before 1 January 1 CE, QDate(1, 1, 1), is 31 December |
396 | 1 BCE, QDate(-1, 12, 31). Various other calendars behave similarly; see |
397 | QCalendar::hasYearZero(). |
398 | |
399 | \section2 Range of Valid Dates |
400 | |
401 | Dates are stored internally as a Julian Day number, an integer count of |
402 | every day in a contiguous range, with 24 November 4714 BCE in the Gregorian |
403 | calendar being Julian Day 0 (1 January 4713 BCE in the Julian calendar). |
404 | As well as being an efficient and accurate way of storing an absolute date, |
405 | it is suitable for converting a date into other calendar systems such as |
406 | Hebrew, Islamic or Chinese. The Julian Day number can be obtained using |
407 | QDate::toJulianDay() and can be set using QDate::fromJulianDay(). |
408 | |
409 | The range of Julian Day numbers that QDate can represent is, for technical |
410 | reasons, limited to between -784350574879 and 784354017364, which means from |
411 | before 2 billion BCE to after 2 billion CE. This is more than seven times as |
412 | wide as the range of dates a QDateTime can represent. |
413 | |
414 | \sa QTime, QDateTime, QCalendar, QDateTime::YearRange, QDateEdit, QDateTimeEdit, QCalendarWidget |
415 | */ |
416 | |
417 | /*! |
418 | \fn QDate::QDate() |
419 | |
420 | Constructs a null date. Null dates are invalid. |
421 | |
422 | \sa isNull(), isValid() |
423 | */ |
424 | |
425 | /*! |
426 | Constructs a date with year \a y, month \a m and day \a d. |
427 | |
428 | The date is understood in terms of the Gregorian calendar. If the specified |
429 | date is invalid, the date is not set and isValid() returns \c false. |
430 | |
431 | \warning Years 1 to 99 are interpreted as is. Year 0 is invalid. |
432 | |
433 | \sa isValid(), QCalendar::dateFromParts() |
434 | */ |
435 | |
436 | QDate::QDate(int y, int m, int d) |
437 | { |
438 | static_assert(QDate::maxJd() == JulianDayMax); |
439 | static_assert(QDate::minJd() == JulianDayMin); |
440 | jd = QGregorianCalendar::julianFromParts(year: y, month: m, day: d).value_or(u: nullJd()); |
441 | } |
442 | |
443 | QDate::QDate(int y, int m, int d, QCalendar cal) |
444 | { |
445 | *this = cal.dateFromParts(year: y, month: m, day: d); |
446 | } |
447 | |
448 | /*! |
449 | \fn QDate::QDate(std::chrono::year_month_day ymd) |
450 | \fn QDate::QDate(std::chrono::year_month_day_last ymd) |
451 | \fn QDate::QDate(std::chrono::year_month_weekday ymd) |
452 | \fn QDate::QDate(std::chrono::year_month_weekday_last ymd) |
453 | |
454 | \since 6.4 |
455 | |
456 | Constructs a QDate representing the same date as \a ymd. This allows for |
457 | easy interoperability between the Standard Library calendaring classes and |
458 | Qt datetime classes. |
459 | |
460 | For example: |
461 | |
462 | \snippet code/src_corelib_time_qdatetime.cpp 22 |
463 | |
464 | \note Unlike QDate, std::chrono::year and the related classes feature the |
465 | year zero. This means that if \a ymd is in the year zero or before, the |
466 | resulting QDate object will have an year one less than the one specified by |
467 | \a ymd. |
468 | |
469 | \note This function requires C++20. |
470 | */ |
471 | |
472 | /*! |
473 | \fn QDate QDate::fromStdSysDays(const std::chrono::sys_days &days) |
474 | \since 6.4 |
475 | |
476 | Returns a QDate \a days days after January 1st, 1970 (the UNIX epoch). If |
477 | \a days is negative, the returned date will be before the epoch. |
478 | |
479 | \note This function requires C++20. |
480 | |
481 | \sa toStdSysDays() |
482 | */ |
483 | |
484 | /*! |
485 | \fn std::chrono::sys_days QDate::toStdSysDays() const |
486 | |
487 | Returns the number of days between January 1st, 1970 (the UNIX epoch) and |
488 | this date, represented as a \c{std::chrono::sys_days} object. If this date |
489 | is before the epoch, the number of days will be negative. |
490 | |
491 | \note This function requires C++20. |
492 | |
493 | \sa fromStdSysDays(), daysTo() |
494 | */ |
495 | |
496 | /*! |
497 | \fn bool QDate::isNull() const |
498 | |
499 | Returns \c true if the date is null; otherwise returns \c false. A null |
500 | date is invalid. |
501 | |
502 | \note The behavior of this function is equivalent to isValid(). |
503 | |
504 | \sa isValid() |
505 | */ |
506 | |
507 | /*! |
508 | \fn bool QDate::isValid() const |
509 | |
510 | Returns \c true if this date is valid; otherwise returns \c false. |
511 | |
512 | \sa isNull(), QCalendar::isDateValid() |
513 | */ |
514 | |
515 | /*! |
516 | Returns the year of this date. |
517 | |
518 | Uses \a cal as calendar, if supplied, else the Gregorian calendar. |
519 | |
520 | Returns 0 if the date is invalid. For some calendars, dates before their |
521 | first year may all be invalid. |
522 | |
523 | If using a calendar which has a year 0, check using isValid() if the return |
524 | is 0. Such calendars use negative year numbers in the obvious way, with |
525 | year 1 preceded by year 0, in turn preceded by year -1 and so on. |
526 | |
527 | Some calendars, despite having no year 0, have a conventional numbering of |
528 | the years before their first year, counting backwards from 1. For example, |
529 | in the proleptic Gregorian calendar, successive years before 1 CE (the first |
530 | year) are identified as 1 BCE, 2 BCE, 3 BCE and so on. For such calendars, |
531 | negative year numbers are used to indicate these years before year 1, with |
532 | -1 indicating the year before 1. |
533 | |
534 | \sa month(), day(), QCalendar::hasYearZero(), QCalendar::isProleptic(), QCalendar::partsFromDate() |
535 | */ |
536 | |
537 | int QDate::year(QCalendar cal) const |
538 | { |
539 | if (isValid()) { |
540 | const auto parts = cal.partsFromDate(date: *this); |
541 | if (parts.isValid()) |
542 | return parts.year; |
543 | } |
544 | return 0; |
545 | } |
546 | |
547 | /*! |
548 | \overload |
549 | */ |
550 | |
551 | int 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 | |
561 | /*! |
562 | Returns the month-number for the date. |
563 | |
564 | Numbers the months of the year starting with 1 for the first. Uses \a cal |
565 | as calendar if supplied, else the Gregorian calendar, for which the month |
566 | numbering is as follows: |
567 | |
568 | \list |
569 | \li 1 = "January" |
570 | \li 2 = "February" |
571 | \li 3 = "March" |
572 | \li 4 = "April" |
573 | \li 5 = "May" |
574 | \li 6 = "June" |
575 | \li 7 = "July" |
576 | \li 8 = "August" |
577 | \li 9 = "September" |
578 | \li 10 = "October" |
579 | \li 11 = "November" |
580 | \li 12 = "December" |
581 | \endlist |
582 | |
583 | Returns 0 if the date is invalid. Note that some calendars may have more |
584 | than 12 months in some years. |
585 | |
586 | \sa year(), day(), QCalendar::partsFromDate() |
587 | */ |
588 | |
589 | int QDate::month(QCalendar cal) const |
590 | { |
591 | if (isValid()) { |
592 | const auto parts = cal.partsFromDate(date: *this); |
593 | if (parts.isValid()) |
594 | return parts.month; |
595 | } |
596 | return 0; |
597 | } |
598 | |
599 | /*! |
600 | \overload |
601 | */ |
602 | |
603 | int 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 | |
613 | /*! |
614 | Returns the day of the month for this date. |
615 | |
616 | Uses \a cal as calendar if supplied, else the Gregorian calendar (for which |
617 | the return ranges from 1 to 31). Returns 0 if the date is invalid. |
618 | |
619 | \sa year(), month(), dayOfWeek(), QCalendar::partsFromDate() |
620 | */ |
621 | |
622 | int QDate::day(QCalendar cal) const |
623 | { |
624 | if (isValid()) { |
625 | const auto parts = cal.partsFromDate(date: *this); |
626 | if (parts.isValid()) |
627 | return parts.day; |
628 | } |
629 | return 0; |
630 | } |
631 | |
632 | /*! |
633 | \overload |
634 | */ |
635 | |
636 | int 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 | |
646 | /*! |
647 | Returns the weekday (1 = Monday to 7 = Sunday) for this date. |
648 | |
649 | Uses \a cal as calendar if supplied, else the Gregorian calendar. Returns 0 |
650 | if the date is invalid. Some calendars may give special meaning |
651 | (e.g. intercallary days) to values greater than 7. |
652 | |
653 | \sa day(), dayOfYear(), QCalendar::dayOfWeek(), Qt::DayOfWeek |
654 | */ |
655 | |
656 | int QDate::dayOfWeek(QCalendar cal) const |
657 | { |
658 | if (isNull()) |
659 | return 0; |
660 | |
661 | return cal.dayOfWeek(date: *this); |
662 | } |
663 | |
664 | /*! |
665 | \overload |
666 | */ |
667 | |
668 | int QDate::dayOfWeek() const |
669 | { |
670 | return isValid() ? QGregorianCalendar::weekDayOfJulian(jd) : 0; |
671 | } |
672 | |
673 | /*! |
674 | Returns the day of the year (1 for the first day) for this date. |
675 | |
676 | Uses \a cal as calendar if supplied, else the Gregorian calendar. |
677 | Returns 0 if either the date or the first day of its year is invalid. |
678 | |
679 | \sa day(), dayOfWeek(), QCalendar::daysInYear() |
680 | */ |
681 | |
682 | int QDate::dayOfYear(QCalendar cal) const |
683 | { |
684 | if (isValid()) { |
685 | QDate firstDay = cal.dateFromParts(year: year(cal), month: 1, day: 1); |
686 | if (firstDay.isValid()) |
687 | return firstDay.daysTo(d: *this) + 1; |
688 | } |
689 | return 0; |
690 | } |
691 | |
692 | /*! |
693 | \overload |
694 | */ |
695 | |
696 | int QDate::dayOfYear() const |
697 | { |
698 | if (isValid()) { |
699 | if (const auto first = QGregorianCalendar::julianFromParts(year: year(), month: 1, day: 1)) |
700 | return jd - *first + 1; |
701 | } |
702 | return 0; |
703 | } |
704 | |
705 | /*! |
706 | Returns the number of days in the month for this date. |
707 | |
708 | Uses \a cal as calendar if supplied, else the Gregorian calendar (for which |
709 | the result ranges from 28 to 31). Returns 0 if the date is invalid. |
710 | |
711 | \sa day(), daysInYear(), QCalendar::daysInMonth(), |
712 | QCalendar::maximumDaysInMonth(), QCalendar::minimumDaysInMonth() |
713 | */ |
714 | |
715 | int QDate::daysInMonth(QCalendar cal) const |
716 | { |
717 | if (isValid()) { |
718 | const auto parts = cal.partsFromDate(date: *this); |
719 | if (parts.isValid()) |
720 | return cal.daysInMonth(month: parts.month, year: parts.year); |
721 | } |
722 | return 0; |
723 | } |
724 | |
725 | /*! |
726 | \overload |
727 | */ |
728 | |
729 | int QDate::daysInMonth() const |
730 | { |
731 | if (isValid()) { |
732 | const auto parts = QGregorianCalendar::partsFromJulian(jd); |
733 | if (parts.isValid()) |
734 | return QGregorianCalendar::monthLength(month: parts.month, year: parts.year); |
735 | } |
736 | return 0; |
737 | } |
738 | |
739 | /*! |
740 | Returns the number of days in the year for this date. |
741 | |
742 | Uses \a cal as calendar if supplied, else the Gregorian calendar (for which |
743 | the result is 365 or 366). Returns 0 if the date is invalid. |
744 | |
745 | \sa day(), daysInMonth(), QCalendar::daysInYear(), QCalendar::maximumMonthsInYear() |
746 | */ |
747 | |
748 | int QDate::daysInYear(QCalendar cal) const |
749 | { |
750 | if (isNull()) |
751 | return 0; |
752 | |
753 | return cal.daysInYear(year: year(cal)); |
754 | } |
755 | |
756 | /*! |
757 | \overload |
758 | */ |
759 | |
760 | int QDate::daysInYear() const |
761 | { |
762 | return isValid() ? QGregorianCalendar::leapTest(year: year()) ? 366 : 365 : 0; |
763 | } |
764 | |
765 | /*! |
766 | Returns the ISO 8601 week number (1 to 53). |
767 | |
768 | Returns 0 if the date is invalid. Otherwise, returns the week number for the |
769 | date. If \a yearNumber is not \nullptr (its default), stores the year as |
770 | *\a{yearNumber}. |
771 | |
772 | In accordance with ISO 8601, each week falls in the year to which most of |
773 | its days belong, in the Gregorian calendar. As ISO 8601's week starts on |
774 | Monday, this is the year in which the week's Thursday falls. Most years have |
775 | 52 weeks, but some have 53. |
776 | |
777 | \note *\a{yearNumber} is not always the same as year(). For example, 1 |
778 | January 2000 has week number 52 in the year 1999, and 31 December |
779 | 2002 has week number 1 in the year 2003. |
780 | |
781 | \sa isValid() |
782 | */ |
783 | |
784 | int 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(days: 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). |
801 | static QTimeZone asTimeZone(Qt::TimeSpec spec, int offset, const char *warner) |
802 | { |
803 | if (warner) { |
804 | switch (spec) { |
805 | case Qt::TimeZone: |
806 | qWarning(msg: "%s: Pass a QTimeZone instead of Qt::TimeZone." , warner); |
807 | break; |
808 | case Qt::LocalTime: |
809 | if (offset) { |
810 | qWarning(msg: "%s: Ignoring offset (%d seconds) passed with Qt::LocalTime" , |
811 | warner, offset); |
812 | } |
813 | break; |
814 | case Qt::UTC: |
815 | if (offset) { |
816 | qWarning(msg: "%s: Ignoring offset (%d seconds) passed with Qt::UTC" , |
817 | warner, offset); |
818 | offset = 0; |
819 | } |
820 | break; |
821 | case Qt::OffsetFromUTC: |
822 | break; |
823 | } |
824 | } |
825 | return QTimeZone::isUtcOrFixedOffset(spec) |
826 | ? QTimeZone::fromSecondsAheadOfUtc(offset) |
827 | : QTimeZone(QTimeZone::LocalTime); |
828 | } |
829 | #endif // Helper for 6.9 deprecation |
830 | |
831 | enum class DaySide { Start, End }; |
832 | |
833 | static 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; |
838 | jd -= JULIAN_DAY_FOR_EPOCH; |
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 | |
853 | static QDateTime toEarliest(QDate day, const QTimeZone &zone) |
854 | { |
855 | Q_ASSERT(!zone.isUtcOrFixedOffset()); |
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(secs: -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 | |
903 | /*! |
904 | \since 5.14 |
905 | |
906 | Returns the start-moment of the day. |
907 | |
908 | When a day starts depends on a how time is described: each day starts and |
909 | ends earlier for those in time-zones further west and later for those in |
910 | time-zones further east. The time representation to use can be specified by |
911 | an optional time \a zone. The default time representation is the system's |
912 | local time. |
913 | |
914 | Usually, the start of the day is midnight, 00:00: however, if a time-zone |
915 | transition causes the given date to skip over that midnight (e.g. a DST |
916 | spring-forward skipping over the first hour of the day day), the actual |
917 | earliest time in the day is returned. This can only arise when the time |
918 | representation is a time-zone or local time. |
919 | |
920 | When \a zone has a timeSpec() of is Qt::OffsetFromUTC or Qt::UTC, the time |
921 | representation has no transitions so the start of the day is QTime(0, 0). |
922 | |
923 | In the rare case of a date that was entirely skipped (this happens when a |
924 | zone east of the international date-line switches to being west of it), the |
925 | return shall be invalid. Passing an invalid time-zone as \a zone will also |
926 | produce an invalid result, as shall dates that start outside the range |
927 | representable by QDateTime. |
928 | |
929 | \sa endOfDay() |
930 | */ |
931 | QDateTime QDate::startOfDay(const QTimeZone &zone) const |
932 | { |
933 | if (!inDateTimeRange(jd, side: 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(beforeDateTime: QDateTime(addDays(days: 1), QTime(12, 0), zone)); |
947 | const QDateTime &at = tran.atUtc.toTimeZone(toZone: zone); |
948 | if (at.isValid() && at.date() == *this) |
949 | return at; |
950 | } |
951 | #endif |
952 | |
953 | return toEarliest(day: *this, zone); |
954 | } |
955 | |
956 | /*! |
957 | \overload |
958 | \since 6.5 |
959 | */ |
960 | QDateTime QDate::startOfDay() const |
961 | { |
962 | return startOfDay(zone: QTimeZone::LocalTime); |
963 | } |
964 | |
965 | #if QT_DEPRECATED_SINCE(6, 9) |
966 | /*! |
967 | \overload |
968 | \since 5.14 |
969 | \deprecated [6.9] Use \c{startOfDay(const QTimeZone &)} instead. |
970 | |
971 | Returns the start-moment of the day. |
972 | |
973 | When a day starts depends on a how time is described: each day starts and |
974 | ends earlier for those with higher offsets from UTC and later for those with |
975 | lower offsets from UTC. The time representation to use can be specified |
976 | either by a \a spec and \a offsetSeconds (ignored unless \a spec is |
977 | Qt::OffsetSeconds) or by a time zone. |
978 | |
979 | Usually, the start of the day is midnight, 00:00: however, if a local time |
980 | transition causes the given date to skip over that midnight (e.g. a DST |
981 | spring-forward skipping over the first hour of the day day), the actual |
982 | earliest time in the day is returned. |
983 | |
984 | When \a spec is Qt::OffsetFromUTC, \a offsetSeconds gives an implied zone's |
985 | offset from UTC. As UTC and such zones have no transitions, the start of the |
986 | day is QTime(0, 0) in these cases. |
987 | |
988 | In the rare case of a date that was entirely skipped (this happens when a |
989 | zone east of the international date-line switches to being west of it), the |
990 | return shall be invalid. Passing Qt::TimeZone as \a spec (instead of passing |
991 | a QTimeZone) will also produce an invalid result, as shall dates that start |
992 | outside the range representable by QDateTime. |
993 | */ |
994 | QDateTime QDate::startOfDay(Qt::TimeSpec spec, int offsetSeconds) const |
995 | { |
996 | QTimeZone zone = asTimeZone(spec, offset: offsetSeconds, warner: "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 | |
1002 | static QDateTime toLatest(QDate day, const QTimeZone &zone) |
1003 | { |
1004 | Q_ASSERT(!zone.isUtcOrFixedOffset()); |
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(secs: 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 | |
1052 | /*! |
1053 | \since 5.14 |
1054 | |
1055 | Returns the end-moment of the day. |
1056 | |
1057 | When a day ends depends on a how time is described: each day starts and ends |
1058 | earlier for those in time-zones further west and later for those in |
1059 | time-zones further east. The time representation to use can be specified by |
1060 | an optional time \a zone. The default time representation is the system's |
1061 | local time. |
1062 | |
1063 | Usually, the end of the day is one millisecond before the midnight, 24:00: |
1064 | however, if a time-zone transition causes the given date to skip over that |
1065 | moment (e.g. a DST spring-forward skipping over 23:00 and the following |
1066 | hour), the actual latest time in the day is returned. This can only arise |
1067 | when the time representation is a time-zone or local time. |
1068 | |
1069 | When \a zone has a timeSpec() of Qt::OffsetFromUTC or Qt::UTC, the time |
1070 | representation has no transitions so the end of the day is QTime(23, 59, 59, |
1071 | 999). |
1072 | |
1073 | In the rare case of a date that was entirely skipped (this happens when a |
1074 | zone east of the international date-line switches to being west of it), the |
1075 | return shall be invalid. Passing an invalid time-zone as \a zone will also |
1076 | produce an invalid result, as shall dates that end outside the range |
1077 | representable by QDateTime. |
1078 | |
1079 | \sa startOfDay() |
1080 | */ |
1081 | QDateTime QDate::endOfDay(const QTimeZone &zone) const |
1082 | { |
1083 | if (!inDateTimeRange(jd, side: 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(afterDateTime: QDateTime(addDays(days: -1), QTime(12, 0), zone)); |
1097 | const QDateTime &at = tran.atUtc.toTimeZone(toZone: zone); |
1098 | if (at.isValid() && at.date() == *this) |
1099 | return at; |
1100 | } |
1101 | #endif |
1102 | |
1103 | return toLatest(day: *this, zone); |
1104 | } |
1105 | |
1106 | /*! |
1107 | \overload |
1108 | \since 6.5 |
1109 | */ |
1110 | QDateTime QDate::endOfDay() const |
1111 | { |
1112 | return endOfDay(zone: QTimeZone::LocalTime); |
1113 | } |
1114 | |
1115 | #if QT_DEPRECATED_SINCE(6, 9) |
1116 | /*! |
1117 | \overload |
1118 | \since 5.14 |
1119 | \deprecated [6.9] Use \c{endOfDay(const QTimeZone &) instead. |
1120 | |
1121 | Returns the end-moment of the day. |
1122 | |
1123 | When a day ends depends on a how time is described: each day starts and ends |
1124 | earlier for those with higher offsets from UTC and later for those with |
1125 | lower offsets from UTC. The time representation to use can be specified |
1126 | either by a \a spec and \a offsetSeconds (ignored unless \a spec is |
1127 | Qt::OffsetSeconds) or by a time zone. |
1128 | |
1129 | Usually, the end of the day is one millisecond before the midnight, 24:00: |
1130 | however, if a local time transition causes the given date to skip over that |
1131 | moment (e.g. a DST spring-forward skipping over 23:00 and the following |
1132 | hour), the actual latest time in the day is returned. |
1133 | |
1134 | When \a spec is Qt::OffsetFromUTC, \a offsetSeconds gives the implied zone's |
1135 | offset from UTC. As UTC and such zones have no transitions, the end of the |
1136 | day is QTime(23, 59, 59, 999) in these cases. |
1137 | |
1138 | In the rare case of a date that was entirely skipped (this happens when a |
1139 | zone east of the international date-line switches to being west of it), the |
1140 | return shall be invalid. Passing Qt::TimeZone as \a spec (instead of passing |
1141 | a QTimeZone) will also produce an invalid result, as shall dates that end |
1142 | outside the range representable by QDateTime. |
1143 | */ |
1144 | QDateTime QDate::endOfDay(Qt::TimeSpec spec, int offsetSeconds) const |
1145 | { |
1146 | QTimeZone zone = asTimeZone(spec, offset: offsetSeconds, warner: "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 | |
1154 | static 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(' '); |
1161 | return QLocale::c().dayName(cal.dayOfWeek(date), format: QLocale::ShortFormat) + sp |
1162 | + cal.monthName(locale: QLocale::c(), month: parts.month, year: parts.year, format: QLocale::ShortFormat) |
1163 | // Documented to use 4-digit year |
1164 | + sp + QString::asprintf(format: "%d %04d" , parts.day, parts.year); |
1165 | } |
1166 | } |
1167 | return QString(); |
1168 | } |
1169 | |
1170 | static 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(format: "%04d-%02d-%02d" , parts.year, parts.month, parts.day); |
1175 | return QString(); |
1176 | } |
1177 | |
1178 | /*! |
1179 | \overload |
1180 | |
1181 | Returns the date as a string. The \a format parameter determines the format |
1182 | of the string. |
1183 | |
1184 | If the \a format is Qt::TextDate, the string is formatted in the default |
1185 | way. The day and month names will be in English. An example of this |
1186 | formatting is "Sat May 20 1995". For localized formatting, see |
1187 | \l{QLocale::toString()}. |
1188 | |
1189 | If the \a format is Qt::ISODate, the string format corresponds |
1190 | to the ISO 8601 extended specification for representations of |
1191 | dates and times, taking the form yyyy-MM-dd, where yyyy is the |
1192 | year, MM is the month of the year (between 01 and 12), and dd is |
1193 | the day of the month between 01 and 31. |
1194 | |
1195 | If the \a format is Qt::RFC2822Date, the string is formatted in |
1196 | an \l{RFC 2822} compatible way. An example of this formatting is |
1197 | "20 May 1995". |
1198 | |
1199 | If the date is invalid, an empty string will be returned. |
1200 | |
1201 | \warning The Qt::ISODate format is only valid for years in the |
1202 | range 0 to 9999. |
1203 | |
1204 | \sa fromString(), QLocale::toString() |
1205 | */ |
1206 | QString 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(date: *this, format: u"dd MMM yyyy" ); |
1214 | default: |
1215 | case Qt::TextDate: |
1216 | return toStringTextDate(date: *this); |
1217 | case Qt::ISODate: |
1218 | case Qt::ISODateWithMs: |
1219 | // No calendar dependence |
1220 | return toStringIsoDate(date: *this); |
1221 | } |
1222 | } |
1223 | |
1224 | /*! |
1225 | \fn QString QDate::toString(const QString &format, QCalendar cal) const |
1226 | \fn QString QDate::toString(QStringView format, QCalendar cal) const |
1227 | |
1228 | Returns the date as a string. The \a format parameter determines the format |
1229 | of the result string. If \a cal is supplied, it determines the calendar used |
1230 | to represent the date; it defaults to Gregorian. |
1231 | |
1232 | These expressions may be used: |
1233 | |
1234 | \table |
1235 | \header \li Expression \li Output |
1236 | \row \li d \li The day as a number without a leading zero (1 to 31) |
1237 | \row \li dd \li The day as a number with a leading zero (01 to 31) |
1238 | \row \li ddd \li The abbreviated day name ('Mon' to 'Sun'). |
1239 | \row \li dddd \li The long day name ('Monday' to 'Sunday'). |
1240 | \row \li M \li The month as a number without a leading zero (1 to 12) |
1241 | \row \li MM \li The month as a number with a leading zero (01 to 12) |
1242 | \row \li MMM \li The abbreviated month name ('Jan' to 'Dec'). |
1243 | \row \li MMMM \li The long month name ('January' to 'December'). |
1244 | \row \li yy \li The year as a two digit number (00 to 99) |
1245 | \row \li yyyy \li The year as a four digit number. If the year is negative, |
1246 | a minus sign is prepended, making five characters. |
1247 | \endtable |
1248 | |
1249 | Any sequence of characters enclosed in single quotes will be included |
1250 | verbatim in the output string (stripped of the quotes), even if it contains |
1251 | formatting characters. Two consecutive single quotes ("''") are replaced by |
1252 | a single quote in the output. All other characters in the format string are |
1253 | included verbatim in the output string. |
1254 | |
1255 | Formats without separators (e.g. "ddMM") are supported but must be used with |
1256 | care, as the resulting strings aren't always reliably readable (e.g. if "dM" |
1257 | produces "212" it could mean either the 2nd of December or the 21st of |
1258 | February). |
1259 | |
1260 | Example format strings (assuming that the QDate is the 20 July |
1261 | 1969): |
1262 | |
1263 | \table |
1264 | \header \li Format \li Result |
1265 | \row \li dd.MM.yyyy \li 20.07.1969 |
1266 | \row \li ddd MMMM d yy \li Sun July 20 69 |
1267 | \row \li 'The day is' dddd \li The day is Sunday |
1268 | \endtable |
1269 | |
1270 | If the datetime is invalid, an empty string will be returned. |
1271 | |
1272 | \note Day and month names are given in English (C locale). To get localized |
1273 | month and day names, use QLocale::system().toString(). |
1274 | |
1275 | \note If a format character is repeated more times than the longest |
1276 | expression in the table above using it, this part of the format will be read |
1277 | as several expressions with no separator between them; the longest above, |
1278 | possibly repeated as many times as there are copies of it, ending with a |
1279 | residue that may be a shorter expression. Thus \c{'MMMMMMMMMM'} for a date |
1280 | in May will contribute \c{"MayMay05"} to the output. |
1281 | |
1282 | \sa fromString(), QDateTime::toString(), QTime::toString(), QLocale::toString() |
1283 | |
1284 | */ |
1285 | QString QDate::toString(QStringView format, QCalendar cal) const |
1286 | { |
1287 | return QLocale::c().toString(date: *this, format, cal); |
1288 | } |
1289 | #endif // datestring |
1290 | |
1291 | /*! |
1292 | \since 4.2 |
1293 | |
1294 | Sets this to represent the date, in the Gregorian calendar, with the given |
1295 | \a year, \a month and \a day numbers. Returns true if the resulting date is |
1296 | valid, otherwise it sets this to represent an invalid date and returns |
1297 | false. |
1298 | |
1299 | \sa isValid(), QCalendar::dateFromParts() |
1300 | */ |
1301 | bool QDate::setDate(int year, int month, int day) |
1302 | { |
1303 | const auto maybe = QGregorianCalendar::julianFromParts(year, month, day); |
1304 | jd = maybe.value_or(u: nullJd()); |
1305 | return bool(maybe); |
1306 | } |
1307 | |
1308 | /*! |
1309 | \since 5.14 |
1310 | |
1311 | Sets this to represent the date, in the given calendar \a cal, with the |
1312 | given \a year, \a month and \a day numbers. Returns true if the resulting |
1313 | date is valid, otherwise it sets this to represent an invalid date and |
1314 | returns false. |
1315 | |
1316 | \sa isValid(), QCalendar::dateFromParts() |
1317 | */ |
1318 | |
1319 | bool QDate::setDate(int year, int month, int day, QCalendar cal) |
1320 | { |
1321 | *this = QDate(year, month, day, cal); |
1322 | return isValid(); |
1323 | } |
1324 | |
1325 | /*! |
1326 | \since 4.5 |
1327 | |
1328 | Extracts the date's year, month, and day, and assigns them to |
1329 | *\a year, *\a month, and *\a day. The pointers may be null. |
1330 | |
1331 | Returns 0 if the date is invalid. |
1332 | |
1333 | \note In Qt versions prior to 5.7, this function is marked as non-\c{const}. |
1334 | |
1335 | \sa year(), month(), day(), isValid(), QCalendar::partsFromDate() |
1336 | */ |
1337 | void QDate::getDate(int *year, int *month, int *day) const |
1338 | { |
1339 | QCalendar::YearMonthDay parts; // invalid by default |
1340 | if (isValid()) |
1341 | parts = QGregorianCalendar::partsFromJulian(jd); |
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 | |
1352 | /*! |
1353 | Returns a QDate object containing a date \a ndays later than the |
1354 | date of this object (or earlier if \a ndays is negative). |
1355 | |
1356 | Returns a null date if the current date is invalid or the new date is |
1357 | out of range. |
1358 | |
1359 | \sa addMonths(), addYears(), daysTo() |
1360 | */ |
1361 | |
1362 | QDate QDate::addDays(qint64 ndays) const |
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(jd_: r); |
1371 | } |
1372 | |
1373 | /*! |
1374 | \fn QDate QDate::addDuration(std::chrono::days ndays) const |
1375 | |
1376 | \since 6.4 |
1377 | |
1378 | Returns a QDate object containing a date \a ndays later than the |
1379 | date of this object (or earlier if \a ndays is negative). |
1380 | |
1381 | Returns a null date if the current date is invalid or the new date is |
1382 | out of range. |
1383 | |
1384 | \note Adding durations expressed in \c{std::chrono::months} or |
1385 | \c{std::chrono::years} does not yield the same result obtained by using |
1386 | addMonths() or addYears(). The former are fixed durations, calculated in |
1387 | relation to the solar year; the latter use the Gregorian calendar definitions |
1388 | of months/years. |
1389 | |
1390 | \note This function requires C++20. |
1391 | |
1392 | \sa addMonths(), addYears(), daysTo() |
1393 | */ |
1394 | |
1395 | /*! |
1396 | Returns a QDate object containing a date \a nmonths later than the |
1397 | date of this object (or earlier if \a nmonths is negative). |
1398 | |
1399 | Uses \a cal as calendar, if supplied, else the Gregorian calendar. |
1400 | |
1401 | \note If the ending day/month combination does not exist in the resulting |
1402 | month/year, this function will return a date that is the latest valid date |
1403 | in the selected month. |
1404 | |
1405 | \sa addDays(), addYears() |
1406 | */ |
1407 | |
1408 | QDate 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(date: *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(year: parts.year); |
1426 | } |
1427 | int count = cal.monthsInYear(year: parts.year); |
1428 | while (parts.month > count) { |
1429 | parts.month -= count; |
1430 | count = (++parts.year || cal.hasYearZero()) ? cal.monthsInYear(year: parts.year) : 0; |
1431 | } |
1432 | |
1433 | return fixedDate(parts, cal); |
1434 | } |
1435 | |
1436 | /*! |
1437 | \overload |
1438 | */ |
1439 | |
1440 | QDate 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 | |
1468 | /*! |
1469 | Returns a QDate object containing a date \a nyears later than the |
1470 | date of this object (or earlier if \a nyears is negative). |
1471 | |
1472 | Uses \a cal as calendar, if supplied, else the Gregorian calendar. |
1473 | |
1474 | \note If the ending day/month combination does not exist in the resulting |
1475 | year (e.g., for the Gregorian calendar, if the date was Feb 29 and the final |
1476 | year is not a leap year), this function will return a date that is the |
1477 | latest valid date in the given month (in the example, Feb 28). |
1478 | |
1479 | \sa addDays(), addMonths() |
1480 | */ |
1481 | |
1482 | QDate QDate::addYears(int nyears, QCalendar cal) const |
1483 | { |
1484 | if (!isValid()) |
1485 | return QDate(); |
1486 | |
1487 | auto parts = cal.partsFromDate(date: *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 | |
1501 | /*! |
1502 | \overload |
1503 | */ |
1504 | |
1505 | QDate 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 | |
1524 | /*! |
1525 | Returns the number of days from this date to \a d (which is |
1526 | negative if \a d is earlier than this date). |
1527 | |
1528 | Returns 0 if either date is invalid. |
1529 | |
1530 | Example: |
1531 | \snippet code/src_corelib_time_qdatetime.cpp 0 |
1532 | |
1533 | \sa addDays() |
1534 | */ |
1535 | |
1536 | qint64 QDate::daysTo(QDate d) const |
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 | |
1546 | /*! |
1547 | \fn bool QDate::operator==(QDate lhs, QDate rhs) |
1548 | |
1549 | Returns \c true if \a lhs and \a rhs represent the same day, otherwise |
1550 | \c false. |
1551 | */ |
1552 | |
1553 | /*! |
1554 | \fn bool QDate::operator!=(QDate lhs, QDate rhs) |
1555 | |
1556 | Returns \c true if \a lhs and \a rhs represent distinct days; otherwise |
1557 | returns \c false. |
1558 | |
1559 | \sa operator==() |
1560 | */ |
1561 | |
1562 | /*! |
1563 | \fn bool QDate::operator<(QDate lhs, QDate rhs) |
1564 | |
1565 | Returns \c true if \a lhs is earlier than \a rhs; otherwise returns \c false. |
1566 | */ |
1567 | |
1568 | /*! |
1569 | \fn bool QDate::operator<=(QDate lhs, QDate rhs) |
1570 | |
1571 | Returns \c true if \a lhs is earlier than or equal to \a rhs; |
1572 | otherwise returns \c false. |
1573 | */ |
1574 | |
1575 | /*! |
1576 | \fn bool QDate::operator>(QDate lhs, QDate rhs) |
1577 | |
1578 | Returns \c true if \a lhs is later than \a rhs; otherwise returns \c false. |
1579 | */ |
1580 | |
1581 | /*! |
1582 | \fn bool QDate::operator>=(QDate lhs, QDate rhs) |
1583 | |
1584 | Returns \c true if \a lhs is later than or equal to \a rhs; |
1585 | otherwise returns \c false. |
1586 | */ |
1587 | |
1588 | /*! |
1589 | \fn QDate::currentDate() |
1590 | Returns the system clock's current date. |
1591 | |
1592 | \sa QTime::currentTime(), QDateTime::currentDateTime() |
1593 | */ |
1594 | |
1595 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
1596 | |
1597 | /*! |
1598 | \fn QDate QDate::fromString(const QString &string, Qt::DateFormat format) |
1599 | |
1600 | Returns the QDate represented by the \a string, using the |
1601 | \a format given, or an invalid date if the string cannot be |
1602 | parsed. |
1603 | |
1604 | Note for Qt::TextDate: only English month names (e.g. "Jan" in short form or |
1605 | "January" in long form) are recognized. |
1606 | |
1607 | \sa toString(), QLocale::toDate() |
1608 | */ |
1609 | |
1610 | /*! |
1611 | \overload |
1612 | \since 6.0 |
1613 | */ |
1614 | QDate 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(s: string).date; |
1622 | default: |
1623 | case Qt::TextDate: { |
1624 | // Documented as "ddd MMM d yyyy" |
1625 | QVarLengthArray<QStringView, 4> parts; |
1626 | auto tokens = string.tokenize(needle: u' ', flags: Qt::SkipEmptyParts); |
1627 | auto it = tokens.begin(); |
1628 | for (int i = 0; i < 4 && it != tokens.end(); ++i, ++it) |
1629 | parts.emplace_back(args: *it); |
1630 | |
1631 | if (parts.size() != 4 || it != tokens.end()) |
1632 | return QDate(); |
1633 | |
1634 | bool ok = false; |
1635 | int year = parts.at(idx: 3).toInt(ok: &ok); |
1636 | int day = ok ? parts.at(idx: 2).toInt(ok: &ok) : 0; |
1637 | if (!ok || !day) |
1638 | return QDate(); |
1639 | |
1640 | const int month = fromShortMonthName(monthName: parts.at(idx: 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(text: string.first(n: 4)); |
1651 | const ParsedInt month = readInt(text: string.sliced(pos: 5, n: 2)); |
1652 | const ParsedInt day = readInt(text: string.sliced(pos: 8, n: 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 | |
1661 | /*! |
1662 | \fn QDate QDate::fromString(const QString &string, const QString &format, QCalendar cal) |
1663 | |
1664 | Returns the QDate represented by the \a string, using the \a |
1665 | format given, or an invalid date if the string cannot be parsed. |
1666 | |
1667 | Uses \a cal as calendar if supplied, else the Gregorian calendar. Ranges of |
1668 | values in the format descriptions below are for the latter; they may be |
1669 | different for other calendars. |
1670 | |
1671 | These expressions may be used for the format: |
1672 | |
1673 | \table |
1674 | \header \li Expression \li Output |
1675 | \row \li d \li The day as a number without a leading zero (1 to 31) |
1676 | \row \li dd \li The day as a number with a leading zero (01 to 31) |
1677 | \row \li ddd \li The abbreviated day name ('Mon' to 'Sun'). |
1678 | \row \li dddd \li The long day name ('Monday' to 'Sunday'). |
1679 | \row \li M \li The month as a number without a leading zero (1 to 12) |
1680 | \row \li MM \li The month as a number with a leading zero (01 to 12) |
1681 | \row \li MMM \li The abbreviated month name ('Jan' to 'Dec'). |
1682 | \row \li MMMM \li The long month name ('January' to 'December'). |
1683 | \row \li yy \li The year as a two digit number (00 to 99) |
1684 | \row \li yyyy \li The year as a four digit number, possibly plus a leading |
1685 | minus sign for negative years. |
1686 | \endtable |
1687 | |
1688 | \note Day and month names must be given in English (C locale). If localized |
1689 | month and day names are to be recognized, use QLocale::system().toDate(). |
1690 | |
1691 | All other input characters will be treated as text. Any non-empty sequence |
1692 | of characters enclosed in single quotes will also be treated (stripped of |
1693 | the quotes) as text and not be interpreted as expressions. For example: |
1694 | |
1695 | \snippet code/src_corelib_time_qdatetime.cpp 1 |
1696 | |
1697 | If the format is not satisfied, an invalid QDate is returned. The |
1698 | expressions that don't expect leading zeroes (d, M) will be |
1699 | greedy. This means that they will use two digits even if this |
1700 | will put them outside the accepted range of values and leaves too |
1701 | few digits for other sections. For example, the following format |
1702 | string could have meant January 30 but the M will grab two |
1703 | digits, resulting in an invalid date: |
1704 | |
1705 | \snippet code/src_corelib_time_qdatetime.cpp 2 |
1706 | |
1707 | For any field that is not represented in the format the following |
1708 | defaults are used: |
1709 | |
1710 | \table |
1711 | \header \li Field \li Default value |
1712 | \row \li Year \li 1900 |
1713 | \row \li Month \li 1 (January) |
1714 | \row \li Day \li 1 |
1715 | \endtable |
1716 | |
1717 | The following examples demonstrate the default values: |
1718 | |
1719 | \snippet code/src_corelib_time_qdatetime.cpp 3 |
1720 | |
1721 | \note If a format character is repeated more times than the longest |
1722 | expression in the table above using it, this part of the format will be read |
1723 | as several expressions with no separator between them; the longest above, |
1724 | possibly repeated as many times as there are copies of it, ending with a |
1725 | residue that may be a shorter expression. Thus \c{'MMMMMMMMMM'} would match |
1726 | \c{"MayMay05"} and set the month to May. Likewise, \c{'MMMMMM'} would match |
1727 | \c{"May08"} and find it inconsistent, leading to an invalid date. |
1728 | |
1729 | \sa toString(), QDateTime::fromString(), QTime::fromString(), |
1730 | QLocale::toDate() |
1731 | */ |
1732 | |
1733 | /*! |
1734 | \fn QDate QDate::fromString(QStringView string, QStringView format, QCalendar cal) |
1735 | \overload |
1736 | \since 6.0 |
1737 | */ |
1738 | |
1739 | /*! |
1740 | \overload |
1741 | \since 6.0 |
1742 | */ |
1743 | QDate 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(text: string, date: &date, time: nullptr); |
1751 | #else |
1752 | Q_UNUSED(string); |
1753 | Q_UNUSED(format); |
1754 | Q_UNUSED(cal); |
1755 | #endif |
1756 | return date; |
1757 | } |
1758 | #endif // datestring |
1759 | |
1760 | /*! |
1761 | \overload |
1762 | |
1763 | Returns \c true if the specified date (\a year, \a month, and \a day) is |
1764 | valid in the Gregorian calendar; otherwise returns \c false. |
1765 | |
1766 | Example: |
1767 | \snippet code/src_corelib_time_qdatetime.cpp 4 |
1768 | |
1769 | \sa isNull(), setDate(), QCalendar::isDateValid() |
1770 | */ |
1771 | |
1772 | bool QDate::isValid(int year, int month, int day) |
1773 | { |
1774 | return QGregorianCalendar::validParts(year, month, day); |
1775 | } |
1776 | |
1777 | /*! |
1778 | \fn bool QDate::isLeapYear(int year) |
1779 | |
1780 | Returns \c true if the specified \a year is a leap year in the Gregorian |
1781 | calendar; otherwise returns \c false. |
1782 | |
1783 | \sa QCalendar::isLeapYear() |
1784 | */ |
1785 | |
1786 | bool QDate::isLeapYear(int y) |
1787 | { |
1788 | return QGregorianCalendar::leapTest(year: y); |
1789 | } |
1790 | |
1791 | /*! \fn static QDate QDate::fromJulianDay(qint64 jd) |
1792 | |
1793 | Converts the Julian day \a jd to a QDate. |
1794 | |
1795 | \sa toJulianDay() |
1796 | */ |
1797 | |
1798 | /*! \fn int QDate::toJulianDay() const |
1799 | |
1800 | Converts the date to a Julian day. |
1801 | |
1802 | \sa fromJulianDay() |
1803 | */ |
1804 | |
1805 | /***************************************************************************** |
1806 | QTime member functions |
1807 | *****************************************************************************/ |
1808 | |
1809 | /*! |
1810 | \class QTime |
1811 | \inmodule QtCore |
1812 | \reentrant |
1813 | |
1814 | \brief The QTime class provides clock time functions. |
1815 | |
1816 | A QTime object contains a clock time, which it can express as the numbers of |
1817 | hours, minutes, seconds, and milliseconds since midnight. It provides |
1818 | functions for comparing times and for manipulating a time by adding a number |
1819 | of milliseconds. QTime objects should be passed by value rather than by |
1820 | reference to const; they simply package \c int. |
1821 | |
1822 | QTime uses the 24-hour clock format; it has no concept of AM/PM. |
1823 | Unlike QDateTime, QTime knows nothing about time zones or |
1824 | daylight-saving time (DST). |
1825 | |
1826 | A QTime object is typically created either by giving the number of hours, |
1827 | minutes, seconds, and milliseconds explicitly, or by using the static |
1828 | function currentTime(), which creates a QTime object that represents the |
1829 | system's local time. |
1830 | |
1831 | The hour(), minute(), second(), and msec() functions provide |
1832 | access to the number of hours, minutes, seconds, and milliseconds |
1833 | of the time. The same information is provided in textual format by |
1834 | the toString() function. |
1835 | |
1836 | The addSecs() and addMSecs() functions provide the time a given |
1837 | number of seconds or milliseconds later than a given time. |
1838 | Correspondingly, the number of seconds or milliseconds |
1839 | between two times can be found using secsTo() or msecsTo(). |
1840 | |
1841 | QTime provides a full set of operators to compare two QTime |
1842 | objects; an earlier time is considered smaller than a later one; |
1843 | if A.msecsTo(B) is positive, then A < B. |
1844 | |
1845 | QTime objects can also be created from a text representation using |
1846 | fromString() and converted to a string representation using toString(). All |
1847 | conversion to and from string formats is done using the C locale. For |
1848 | localized conversions, see QLocale. |
1849 | |
1850 | \sa QDate, QDateTime |
1851 | */ |
1852 | |
1853 | /*! |
1854 | \fn QTime::QTime() |
1855 | |
1856 | Constructs a null time object. For a null time, isNull() returns \c true and |
1857 | isValid() returns \c false. If you need a zero time, use QTime(0, 0). For |
1858 | the start of a day, see QDate::startOfDay(). |
1859 | |
1860 | \sa isNull(), isValid() |
1861 | */ |
1862 | |
1863 | /*! |
1864 | Constructs a time with hour \a h, minute \a m, seconds \a s and |
1865 | milliseconds \a ms. |
1866 | |
1867 | \a h must be in the range 0 to 23, \a m and \a s must be in the |
1868 | range 0 to 59, and \a ms must be in the range 0 to 999. |
1869 | |
1870 | \sa isValid() |
1871 | */ |
1872 | |
1873 | QTime::QTime(int h, int m, int s, int ms) |
1874 | { |
1875 | setHMS(h, m, s, ms); |
1876 | } |
1877 | |
1878 | |
1879 | /*! |
1880 | \fn bool QTime::isNull() const |
1881 | |
1882 | Returns \c true if the time is null (i.e., the QTime object was |
1883 | constructed using the default constructor); otherwise returns |
1884 | false. A null time is also an invalid time. |
1885 | |
1886 | \sa isValid() |
1887 | */ |
1888 | |
1889 | /*! |
1890 | Returns \c true if the time is valid; otherwise returns \c false. For example, |
1891 | the time 23:30:55.746 is valid, but 24:12:30 is invalid. |
1892 | |
1893 | \sa isNull() |
1894 | */ |
1895 | |
1896 | bool QTime::isValid() const |
1897 | { |
1898 | return mds > NullTime && mds < MSECS_PER_DAY; |
1899 | } |
1900 | |
1901 | |
1902 | /*! |
1903 | Returns the hour part (0 to 23) of the time. |
1904 | |
1905 | Returns -1 if the time is invalid. |
1906 | |
1907 | \sa minute(), second(), msec() |
1908 | */ |
1909 | |
1910 | int QTime::hour() const |
1911 | { |
1912 | if (!isValid()) |
1913 | return -1; |
1914 | |
1915 | return ds() / MSECS_PER_HOUR; |
1916 | } |
1917 | |
1918 | /*! |
1919 | Returns the minute part (0 to 59) of the time. |
1920 | |
1921 | Returns -1 if the time is invalid. |
1922 | |
1923 | \sa hour(), second(), msec() |
1924 | */ |
1925 | |
1926 | int QTime::minute() const |
1927 | { |
1928 | if (!isValid()) |
1929 | return -1; |
1930 | |
1931 | return (ds() % MSECS_PER_HOUR) / MSECS_PER_MIN; |
1932 | } |
1933 | |
1934 | /*! |
1935 | Returns the second part (0 to 59) of the time. |
1936 | |
1937 | Returns -1 if the time is invalid. |
1938 | |
1939 | \sa hour(), minute(), msec() |
1940 | */ |
1941 | |
1942 | int QTime::second() const |
1943 | { |
1944 | if (!isValid()) |
1945 | return -1; |
1946 | |
1947 | return (ds() / MSECS_PER_SEC) % SECS_PER_MIN; |
1948 | } |
1949 | |
1950 | /*! |
1951 | Returns the millisecond part (0 to 999) of the time. |
1952 | |
1953 | Returns -1 if the time is invalid. |
1954 | |
1955 | \sa hour(), minute(), second() |
1956 | */ |
1957 | |
1958 | int 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 |
1967 | /*! |
1968 | \overload |
1969 | |
1970 | Returns the time as a string. The \a format parameter determines |
1971 | the format of the string. |
1972 | |
1973 | If \a format is Qt::TextDate, the string format is HH:mm:ss; |
1974 | e.g. 1 second before midnight would be "23:59:59". |
1975 | |
1976 | If \a format is Qt::ISODate, the string format corresponds to the |
1977 | ISO 8601 extended specification for representations of dates, |
1978 | represented by HH:mm:ss. To include milliseconds in the ISO 8601 |
1979 | date, use the \a format Qt::ISODateWithMs, which corresponds to |
1980 | HH:mm:ss.zzz. |
1981 | |
1982 | If the \a format is Qt::RFC2822Date, the string is formatted in |
1983 | an \l{RFC 2822} compatible way. An example of this formatting is |
1984 | "23:59:20". |
1985 | |
1986 | If the time is invalid, an empty string will be returned. |
1987 | |
1988 | \sa fromString(), QDate::toString(), QDateTime::toString(), QLocale::toString() |
1989 | */ |
1990 | |
1991 | QString 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(format: "%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(format: "%02d:%02d:%02d" , hour(), minute(), second()); |
2004 | } |
2005 | } |
2006 | |
2007 | /*! |
2008 | \fn QString QTime::toString(const QString &format) const |
2009 | \fn QString QTime::toString(QStringView format) const |
2010 | |
2011 | Returns the time as a string. The \a format parameter determines |
2012 | the format of the result string. |
2013 | |
2014 | These expressions may be used: |
2015 | |
2016 | \table |
2017 | \header \li Expression \li Output |
2018 | \row \li h |
2019 | \li The hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) |
2020 | \row \li hh |
2021 | \li The hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) |
2022 | \row \li H |
2023 | \li The hour without a leading zero (0 to 23, even with AM/PM display) |
2024 | \row \li HH |
2025 | \li The hour with a leading zero (00 to 23, even with AM/PM display) |
2026 | \row \li m \li The minute without a leading zero (0 to 59) |
2027 | \row \li mm \li The minute with a leading zero (00 to 59) |
2028 | \row \li s \li The whole second, without any leading zero (0 to 59) |
2029 | \row \li ss \li The whole second, with a leading zero where applicable (00 to 59) |
2030 | \row \li z or zz |
2031 | \li The fractional part of the second, to go after a decimal point, |
2032 | without trailing zeroes. Thus \c{"s.z"} reports the seconds to full |
2033 | available (millisecond) precision without trailing zeroes (0 to |
2034 | 999). For example, \c{"s.z"} would produce \c{"0.25"} for a time a |
2035 | quarter second into a minute. |
2036 | \row \li zzz |
2037 | \li The fractional part of the second, to millisecond precision, |
2038 | including trailing zeroes where applicable (000 to 999). For |
2039 | example, \c{"ss.zzz"} would produce \c{"00.250"} for a time a |
2040 | quarter second into a minute. |
2041 | \row \li AP or A |
2042 | \li Use AM/PM display. \c A/AP will be replaced by 'AM' or 'PM'. In |
2043 | localized forms (only relevant to \l{QLocale::toString()}), the |
2044 | locale-appropriate text is converted to upper-case. |
2045 | \row \li ap or a |
2046 | \li Use am/pm display. \c a/ap will be replaced by 'am' or 'pm'. In |
2047 | localized forms (only relevant to \l{QLocale::toString()}), the |
2048 | locale-appropriate text is converted to lower-case. |
2049 | \row \li aP or Ap |
2050 | \li Use AM/PM display (since 6.3). \c aP/Ap will be replaced by 'AM' or |
2051 | 'PM'. In localized forms (only relevant to |
2052 | \l{QLocale::toString()}), the locale-appropriate text (returned by |
2053 | \l{QLocale::amText()} or \l{QLocale::pmText()}) is used without |
2054 | change of case. |
2055 | \row \li t |
2056 | \li The timezone abbreviation (for example "CEST"). Note that time zone |
2057 | abbreviations are not unique. In particular, \l toString() cannot |
2058 | parse this. |
2059 | \row \li tt |
2060 | \li The timezone's offset from UTC with no colon between the hours and |
2061 | minutes (for example "+0200"). |
2062 | \row \li ttt |
2063 | \li The timezone's offset from UTC with a colon between the hours and |
2064 | minutes (for example "+02:00"). |
2065 | \row \li tttt |
2066 | \li The timezone name (for example "Europe/Berlin"). Note that this |
2067 | gives no indication of whether the datetime was in daylight-saving |
2068 | time or standard time, which may lead to ambiguity if the datetime |
2069 | falls in an hour repeated by a transition between the two. The name |
2070 | used is the one provided by \l QTimeZone::displayName() with the \l |
2071 | QTimeZone::LongName type. This may depend on the operating system |
2072 | in use. |
2073 | \endtable |
2074 | |
2075 | Any non-empty sequence of characters enclosed in single quotes will be |
2076 | included verbatim in the output string (stripped of the quotes), even if it |
2077 | contains formatting characters. Two consecutive single quotes ("''") are |
2078 | replaced by a single quote in the output. All other characters in the format |
2079 | string are included verbatim in the output string. |
2080 | |
2081 | Formats without separators (e.g. "ddMM") are supported but must be used with |
2082 | care, as the resulting strings aren't always reliably readable (e.g. if "dM" |
2083 | produces "212" it could mean either the 2nd of December or the 21st of |
2084 | February). |
2085 | |
2086 | Example format strings (assuming that the QTime is 14:13:09.042) |
2087 | |
2088 | \table |
2089 | \header \li Format \li Result |
2090 | \row \li hh:mm:ss.zzz \li 14:13:09.042 |
2091 | \row \li h:m:s ap \li 2:13:9 pm |
2092 | \row \li H:m:s a \li 14:13:9 pm |
2093 | \endtable |
2094 | |
2095 | If the time is invalid, an empty string will be returned. |
2096 | |
2097 | \note To get localized forms of AM or PM (the AP, ap, A, a, aP or Ap |
2098 | formats), use QLocale::system().toString(). |
2099 | |
2100 | \note If a format character is repeated more times than the longest |
2101 | expression in the table above using it, this part of the format will be read |
2102 | as several expressions with no separator between them; the longest above, |
2103 | possibly repeated as many times as there are copies of it, ending with a |
2104 | residue that may be a shorter expression. Thus \c{'HHHHH'} for the time |
2105 | 08:00 will contribute \c{"08088"} to the output. |
2106 | |
2107 | \sa fromString(), QDate::toString(), QDateTime::toString(), QLocale::toString() |
2108 | */ |
2109 | // ### Qt 7 The 't' format specifiers should be specific to QDateTime (compare fromString). |
2110 | QString QTime::toString(QStringView format) const |
2111 | { |
2112 | return QLocale::c().toString(time: *this, format); |
2113 | } |
2114 | #endif // datestring |
2115 | |
2116 | /*! |
2117 | Sets the time to hour \a h, minute \a m, seconds \a s and |
2118 | milliseconds \a ms. |
2119 | |
2120 | \a h must be in the range 0 to 23, \a m and \a s must be in the |
2121 | range 0 to 59, and \a ms must be in the range 0 to 999. |
2122 | Returns \c true if the set time is valid; otherwise returns \c false. |
2123 | |
2124 | \sa isValid() |
2125 | */ |
2126 | |
2127 | bool 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 | |
2138 | /*! |
2139 | Returns a QTime object containing a time \a s seconds later |
2140 | than the time of this object (or earlier if \a s is negative). |
2141 | |
2142 | Note that the time will wrap if it passes midnight. |
2143 | |
2144 | Returns a null time if this time is invalid. |
2145 | |
2146 | Example: |
2147 | |
2148 | \snippet code/src_corelib_time_qdatetime.cpp 5 |
2149 | |
2150 | \sa addMSecs(), secsTo(), QDateTime::addSecs() |
2151 | */ |
2152 | |
2153 | QTime QTime::addSecs(int s) const |
2154 | { |
2155 | s %= SECS_PER_DAY; |
2156 | return addMSecs(ms: s * MSECS_PER_SEC); |
2157 | } |
2158 | |
2159 | /*! |
2160 | Returns the number of seconds from this time to \a t. |
2161 | If \a t is earlier than this time, the number of seconds returned |
2162 | is negative. |
2163 | |
2164 | Because QTime measures time within a day and there are 86400 |
2165 | seconds in a day, the result is always between -86400 and 86400. |
2166 | |
2167 | secsTo() does not take into account any milliseconds. |
2168 | |
2169 | Returns 0 if either time is invalid. |
2170 | |
2171 | \sa addSecs(), QDateTime::secsTo() |
2172 | */ |
2173 | |
2174 | int QTime::secsTo(QTime t) const |
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 | |
2185 | /*! |
2186 | Returns a QTime object containing a time \a ms milliseconds later |
2187 | than the time of this object (or earlier if \a ms is negative). |
2188 | |
2189 | Note that the time will wrap if it passes midnight. See addSecs() |
2190 | for an example. |
2191 | |
2192 | Returns a null time if this time is invalid. |
2193 | |
2194 | \sa addSecs(), msecsTo(), QDateTime::addMSecs() |
2195 | */ |
2196 | |
2197 | QTime QTime::addMSecs(int ms) const |
2198 | { |
2199 | QTime t; |
2200 | if (isValid()) |
2201 | t.mds = QRoundingDown::qMod<MSECS_PER_DAY>(a: ds() + ms); |
2202 | return t; |
2203 | } |
2204 | |
2205 | /*! |
2206 | Returns the number of milliseconds from this time to \a t. |
2207 | If \a t is earlier than this time, the number of milliseconds returned |
2208 | is negative. |
2209 | |
2210 | Because QTime measures time within a day and there are 86400 |
2211 | seconds in a day, the result is always between -86400000 and |
2212 | 86400000 ms. |
2213 | |
2214 | Returns 0 if either time is invalid. |
2215 | |
2216 | \sa secsTo(), addMSecs(), QDateTime::msecsTo() |
2217 | */ |
2218 | |
2219 | int QTime::msecsTo(QTime t) const |
2220 | { |
2221 | if (!isValid() || !t.isValid()) |
2222 | return 0; |
2223 | return t.ds() - ds(); |
2224 | } |
2225 | |
2226 | |
2227 | /*! |
2228 | \fn bool QTime::operator==(QTime lhs, QTime rhs) |
2229 | |
2230 | Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false. |
2231 | */ |
2232 | |
2233 | /*! |
2234 | \fn bool QTime::operator!=(QTime lhs, QTime rhs) |
2235 | |
2236 | Returns \c true if \a lhs is different from \a rhs; otherwise returns \c false. |
2237 | */ |
2238 | |
2239 | /*! |
2240 | \fn bool QTime::operator<(QTime lhs, QTime rhs) |
2241 | |
2242 | Returns \c true if \a lhs is earlier than \a rhs; otherwise returns \c false. |
2243 | */ |
2244 | |
2245 | /*! |
2246 | \fn bool QTime::operator<=(QTime lhs, QTime rhs) |
2247 | |
2248 | Returns \c true if \a lhs is earlier than or equal to \a rhs; |
2249 | otherwise returns \c false. |
2250 | */ |
2251 | |
2252 | /*! |
2253 | \fn bool QTime::operator>(QTime lhs, QTime rhs) |
2254 | |
2255 | Returns \c true if \a lhs is later than \a rhs; otherwise returns \c false. |
2256 | */ |
2257 | |
2258 | /*! |
2259 | \fn bool QTime::operator>=(QTime lhs, QTime rhs) |
2260 | |
2261 | Returns \c true if \a lhs is later than or equal to \a rhs; |
2262 | otherwise returns \c false. |
2263 | */ |
2264 | |
2265 | /*! |
2266 | \fn QTime QTime::fromMSecsSinceStartOfDay(int msecs) |
2267 | |
2268 | Returns a new QTime instance with the time set to the number of \a msecs |
2269 | since the start of the day, i.e. since 00:00:00. |
2270 | |
2271 | If \a msecs falls outside the valid range an invalid QTime will be returned. |
2272 | |
2273 | \sa msecsSinceStartOfDay() |
2274 | */ |
2275 | |
2276 | /*! |
2277 | \fn int QTime::msecsSinceStartOfDay() const |
2278 | |
2279 | Returns the number of msecs since the start of the day, i.e. since 00:00:00. |
2280 | |
2281 | \sa fromMSecsSinceStartOfDay() |
2282 | */ |
2283 | |
2284 | /*! |
2285 | \fn QTime::currentTime() |
2286 | |
2287 | Returns the current time as reported by the system clock. |
2288 | |
2289 | Note that the accuracy depends on the accuracy of the underlying |
2290 | operating system; not all systems provide 1-millisecond accuracy. |
2291 | |
2292 | Furthermore, currentTime() only increases within each day; it shall drop by |
2293 | 24 hours each time midnight passes; and, beside this, changes in it may not |
2294 | correspond to elapsed time, if a daylight-saving transition intervenes. |
2295 | |
2296 | \sa QDateTime::currentDateTime(), QDateTime::currentDateTimeUtc() |
2297 | */ |
2298 | |
2299 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
2300 | |
2301 | static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *isMidnight24) |
2302 | { |
2303 | Q_ASSERT(format == Qt::TextDate || format == Qt::ISODate || format == Qt::ISODateWithMs); |
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(c: u'.'), comma = string.indexOf(c: u','); |
2312 | if (dot != -1) { |
2313 | tail = string.sliced(pos: dot + 1); |
2314 | if (tail.indexOf(c: u'.') != -1) // Forbid second dot: |
2315 | return QTime(); |
2316 | string = string.first(n: dot); |
2317 | } else if (comma != -1) { |
2318 | tail = string.sliced(pos: comma + 1); |
2319 | string = string.first(n: comma); |
2320 | } |
2321 | if (tail.indexOf(c: u',') != -1) // Forbid comma after first dot-or-comma: |
2322 | return QTime(); |
2323 | |
2324 | const ParsedInt frac = readInt(text: 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(x: 0.1, y: 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(text: string.first(n: 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(text: string.sliced(pos: 3, n: 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(text: string.sliced(pos: 6, n: 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(d: 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 | |
2404 | /*! |
2405 | \fn QTime QTime::fromString(const QString &string, Qt::DateFormat format) |
2406 | |
2407 | Returns the time represented in the \a string as a QTime using the |
2408 | \a format given, or an invalid time if this is not possible. |
2409 | |
2410 | \sa toString(), QLocale::toTime() |
2411 | */ |
2412 | |
2413 | /*! |
2414 | \overload |
2415 | \since 6.0 |
2416 | */ |
2417 | QTime 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(s: string).time; |
2425 | case Qt::ISODate: |
2426 | case Qt::ISODateWithMs: |
2427 | case Qt::TextDate: |
2428 | default: |
2429 | return fromIsoTimeString(string, format, isMidnight24: nullptr); |
2430 | } |
2431 | } |
2432 | |
2433 | /*! |
2434 | \fn QTime QTime::fromString(const QString &string, const QString &format) |
2435 | |
2436 | Returns the QTime represented by the \a string, using the \a |
2437 | format given, or an invalid time if the string cannot be parsed. |
2438 | |
2439 | These expressions may be used for the format: |
2440 | |
2441 | \table |
2442 | \header \li Expression \li Output |
2443 | \row \li h |
2444 | \li The hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) |
2445 | \row \li hh |
2446 | \li The hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) |
2447 | \row \li H |
2448 | \li The hour without a leading zero (0 to 23, even with AM/PM display) |
2449 | \row \li HH |
2450 | \li The hour with a leading zero (00 to 23, even with AM/PM display) |
2451 | \row \li m \li The minute without a leading zero (0 to 59) |
2452 | \row \li mm \li The minute with a leading zero (00 to 59) |
2453 | \row \li s \li The whole second, without any leading zero (0 to 59) |
2454 | \row \li ss \li The whole second, with a leading zero where applicable (00 to 59) |
2455 | \row \li z or zz |
2456 | \li The fractional part of the second, as would usually follow a |
2457 | decimal point, without requiring trailing zeroes (0 to 999). Thus |
2458 | \c{"s.z"} matches the seconds with up to three digits of fractional |
2459 | part supplying millisecond precision, without needing trailing |
2460 | zeroes. For example, \c{"s.z"} would recognize either \c{"00.250"} |
2461 | or \c{"0.25"} as representing a time a quarter second into its |
2462 | minute. |
2463 | \row \li zzz |
2464 | \li Three digit fractional part of the second, to millisecond |
2465 | precision, including trailing zeroes where applicable (000 to 999). |
2466 | For example, \c{"ss.zzz"} would reject \c{"0.25"} but recognize |
2467 | \c{"00.250"} as representing a time a quarter second into its |
2468 | minute. |
2469 | \row \li AP, A, ap, a, aP or Ap |
2470 | \li Either 'AM' indicating a time before 12:00 or 'PM' for later times, |
2471 | matched case-insensitively. |
2472 | \endtable |
2473 | |
2474 | All other input characters will be treated as text. Any non-empty sequence |
2475 | of characters enclosed in single quotes will also be treated (stripped of |
2476 | the quotes) as text and not be interpreted as expressions. |
2477 | |
2478 | \snippet code/src_corelib_time_qdatetime.cpp 6 |
2479 | |
2480 | If the format is not satisfied, an invalid QTime is returned. |
2481 | Expressions that do not expect leading zeroes to be given (h, m, s |
2482 | and z) are greedy. This means that they will use two digits (or three, for z) even if |
2483 | this puts them outside the range of accepted values and leaves too |
2484 | few digits for other sections. For example, the following string |
2485 | could have meant 00:07:10, but the m will grab two digits, resulting |
2486 | in an invalid time: |
2487 | |
2488 | \snippet code/src_corelib_time_qdatetime.cpp 7 |
2489 | |
2490 | Any field that is not represented in the format will be set to zero. |
2491 | For example: |
2492 | |
2493 | \snippet code/src_corelib_time_qdatetime.cpp 8 |
2494 | |
2495 | \note If localized forms of am or pm (the AP, ap, Ap, aP, A or a formats) |
2496 | are to be recognized, use QLocale::system().toTime(). |
2497 | |
2498 | \note If a format character is repeated more times than the longest |
2499 | expression in the table above using it, this part of the format will be read |
2500 | as several expressions with no separator between them; the longest above, |
2501 | possibly repeated as many times as there are copies of it, ending with a |
2502 | residue that may be a shorter expression. Thus \c{'HHHHH'} would match |
2503 | \c{"08088"} or \c{"080808"} and set the hour to 8; if the time string |
2504 | contained "070809" it would "match" but produce an inconsistent result, |
2505 | leading to an invalid time. |
2506 | |
2507 | \sa toString(), QDateTime::fromString(), QDate::fromString(), |
2508 | QLocale::toTime(), QLocale::toDateTime() |
2509 | */ |
2510 | |
2511 | /*! |
2512 | \fn QTime QTime::fromString(QStringView string, QStringView format) |
2513 | \overload |
2514 | \since 6.0 |
2515 | */ |
2516 | |
2517 | /*! |
2518 | \overload |
2519 | \since 6.0 |
2520 | */ |
2521 | QTime 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(text: string, date: nullptr, time: &time); |
2529 | #else |
2530 | Q_UNUSED(string); |
2531 | Q_UNUSED(format); |
2532 | #endif |
2533 | return time; |
2534 | } |
2535 | #endif // datestring |
2536 | |
2537 | |
2538 | /*! |
2539 | \overload |
2540 | |
2541 | Returns \c true if the specified time is valid; otherwise returns |
2542 | false. |
2543 | |
2544 | The time is valid if \a h is in the range 0 to 23, \a m and |
2545 | \a s are in the range 0 to 59, and \a ms is in the range 0 to 999. |
2546 | |
2547 | Example: |
2548 | |
2549 | \snippet code/src_corelib_time_qdatetime.cpp 9 |
2550 | */ |
2551 | |
2552 | bool 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) |
2563 | typedef QDateTimePrivate::QDateTimeShortData ShortData; |
2564 | typedef QDateTimePrivate::QDateTimeData QDateTimeData; |
2565 | |
2566 | // Converts milliseconds since the start of 1970 into a date and/or time: |
2567 | static qint64 msecsToJulianDay(qint64 msecs) |
2568 | { |
2569 | return JULIAN_DAY_FOR_EPOCH + QRoundingDown::qDiv<MSECS_PER_DAY>(a: msecs); |
2570 | } |
2571 | |
2572 | static QDate msecsToDate(qint64 msecs) |
2573 | { |
2574 | return QDate::fromJulianDay(jd_: msecsToJulianDay(msecs)); |
2575 | } |
2576 | |
2577 | static QTime msecsToTime(qint64 msecs) |
2578 | { |
2579 | return QTime::fromMSecsSinceStartOfDay(msecs: QRoundingDown::qMod<MSECS_PER_DAY>(a: msecs)); |
2580 | } |
2581 | |
2582 | // True if combining days with millis overflows; otherwise, stores result in *sumMillis |
2583 | // The inputs should not have opposite signs. |
2584 | static inline bool daysAndMillisOverflow(qint64 days, qint64 millisInDay, qint64 *sumMillis) |
2585 | { |
2586 | return qMulOverflow(v1: days, std::integral_constant<qint64, MSECS_PER_DAY>(), r: sumMillis) |
2587 | || qAddOverflow(v1: *sumMillis, v2: millisInDay, r: sumMillis); |
2588 | } |
2589 | |
2590 | // Converts a date/time value into msecs |
2591 | static qint64 timeToMSecs(QDate date, QTime time) |
2592 | { |
2593 | qint64 days = date.toJulianDay() - JULIAN_DAY_FOR_EPOCH; |
2594 | qint64 msecs, dayms = time.msecsSinceStartOfDay(); |
2595 | if (days < 0 && dayms > 0) { |
2596 | ++days; |
2597 | dayms -= MSECS_PER_DAY; |
2598 | } |
2599 | if (daysAndMillisOverflow(days, millisInDay: dayms, sumMillis: &msecs)) { |
2600 | using Bound = std::numeric_limits<qint64>; |
2601 | return days < 0 ? Bound::min() : Bound::max(); |
2602 | } |
2603 | return msecs; |
2604 | } |
2605 | |
2606 | /*! |
2607 | \internal |
2608 | Tests whether system functions can handle a given time. |
2609 | |
2610 | The range of milliseconds for which the time_t-based functions work depends |
2611 | somewhat on platform (see computeSystemMillisRange() for details). This |
2612 | function tests whether the UTC time \a millis milliseconds from the epoch is |
2613 | in the supported range. |
2614 | |
2615 | To test a local time, pass an upper bound on the magnitude of time-zone |
2616 | correction potentially needed as \a slack: in this case the range is |
2617 | extended by this many milliseconds at each end (where applicable). The |
2618 | function then returns true precisely if \a millis is within this (possibly) |
2619 | widened range. This doesn't guarantee that the time_t functions can handle |
2620 | the time, so check their returns to be sure. Values for which the function |
2621 | returns false should be assumed unrepresentable. |
2622 | */ |
2623 | static 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 | |
2630 | /*! |
2631 | \internal |
2632 | Returns a year, in the system range, with the same day-of-week pattern |
2633 | |
2634 | Returns the number of a year, in the range supported by system time_t |
2635 | functions, that starts and ends on the same days of the week as \a year. |
2636 | This implies it is a leap year precisely if \a year is. If year is before |
2637 | the epoch, a year early in the supported range is used; otherwise, one late |
2638 | in that range. For a leap year, this may be as much as 26 years years from |
2639 | the range's relevant end; for normal years at most a decade from the end. |
2640 | |
2641 | This ensures that any DST rules based on, e.g., the last Sunday in a |
2642 | particular month will select the same date in the returned year as they |
2643 | would if applied to \a year. Of course, the zone's rules may be different in |
2644 | \a year than in the selected year, but it's hard to do better. |
2645 | */ |
2646 | static 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: |
2668 | QDateTimePrivate::ZoneState QDateTimePrivate::expressUtcAsLocal(qint64 utcMSecs) |
2669 | { |
2670 | ZoneState result{utcMSecs}; |
2671 | // Within the time_t supported range, localtime() can handle it: |
2672 | if (millisInSystemRange(millis: utcMSecs)) { |
2673 | result = QLocalTime::utcToLocal(utcMillis: 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(atMSecsSinceEpoch: utcMSecs); |
2683 | if (qAddOverflow(v1: utcMSecs, v2: result.offset * MSECS_PER_SEC, r: &result.when)) |
2684 | return result; |
2685 | result.dst = sys.d->isDaylightTime(atMSecsSinceEpoch: 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(msecs: utcMSecs); |
2695 | const auto ymd = QGregorianCalendar::partsFromJulian(jd); |
2696 | qint64 diffMillis, fakeUtc; |
2697 | const auto fakeJd = QGregorianCalendar::julianFromParts(year: systemTimeYearMatching(year: ymd.year), |
2698 | month: ymd.month, day: 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(utcMillis: fakeUtc); |
2707 | // Now correct result.when for the use of the fake date: |
2708 | if (!result.valid || qAddOverflow(v1: result.when, v2: diffMillis, r: &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 | |
2717 | static auto millisToWithinRange(qint64 millis) |
2718 | { |
2719 | struct R { |
2720 | qint64 shifted = 0; |
2721 | bool good = false; |
2722 | } result; |
2723 | qint64 jd = msecsToJulianDay(msecs: millis); |
2724 | auto ymd = QGregorianCalendar::partsFromJulian(jd); |
2725 | const auto fakeJd = QGregorianCalendar::julianFromParts(year: systemTimeYearMatching(year: ymd.year), |
2726 | month: ymd.month, day: ymd.day); |
2727 | result.good = fakeJd && !daysAndMillisOverflow(days: *fakeJd - jd, millisInDay: millis, sumMillis: &result.shifted); |
2728 | return result; |
2729 | } |
2730 | |
2731 | QString QDateTimePrivate::localNameAtMillis(qint64 millis, DaylightStatus dst) |
2732 | { |
2733 | QString abbreviation; |
2734 | if (millisInSystemRange(millis, slack: MSECS_PER_DAY)) { |
2735 | abbreviation = QLocalTime::localTimeAbbbreviationAt(local: 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(zone: sys, millis, dst); |
2746 | if (state.valid) |
2747 | return sys.d->abbreviation(atMSecsSinceEpoch: 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(local: fake.shifted, dst); |
2756 | |
2757 | // Overflow, apparently. |
2758 | return {}; |
2759 | } |
2760 | |
2761 | // Determine the offset from UTC at the given local time as millis. |
2762 | QDateTimePrivate::ZoneState QDateTimePrivate::localStateAtMillis(qint64 millis, DaylightStatus dst) |
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, slack: MSECS_PER_DAY)) { |
2767 | auto result = QLocalTime::mapLocalTime(local: 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(zone: 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(local: 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. |
2805 | QDateTimePrivate::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(forLocalMSecs: millis, hint: 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 | |
2826 | static inline QDateTimePrivate::ZoneState stateAtMillis(QTimeZone zone, qint64 millis, |
2827 | QDateTimePrivate::DaylightStatus dst) |
2828 | { |
2829 | if (zone.timeSpec() == Qt::LocalTime) |
2830 | return QDateTimePrivate::localStateAtMillis(millis, dst); |
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 | |
2838 | static inline bool specCanBeSmall(Qt::TimeSpec spec) |
2839 | { |
2840 | return spec == Qt::LocalTime || spec == Qt::UTC; |
2841 | } |
2842 | |
2843 | static 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 | |
2853 | static constexpr inline |
2854 | QDateTimePrivate::StatusFlags mergeSpec(QDateTimePrivate::StatusFlags status, Qt::TimeSpec spec) |
2855 | { |
2856 | status &= ~QDateTimePrivate::TimeSpecMask; |
2857 | status |= QDateTimePrivate::StatusFlags::fromInt(i: int(spec) << QDateTimePrivate::TimeSpecShift); |
2858 | return status; |
2859 | } |
2860 | |
2861 | static constexpr inline Qt::TimeSpec (QDateTimePrivate::StatusFlags status) |
2862 | { |
2863 | return Qt::TimeSpec((status & QDateTimePrivate::TimeSpecMask).toInt() >> QDateTimePrivate::TimeSpecShift); |
2864 | } |
2865 | |
2866 | // Set the Daylight Status if LocalTime set via msecs |
2867 | static constexpr inline QDateTimePrivate::StatusFlags |
2868 | mergeDaylightStatus(QDateTimePrivate::StatusFlags sf, QDateTimePrivate::DaylightStatus status) |
2869 | { |
2870 | sf &= ~QDateTimePrivate::DaylightMask; |
2871 | if (status == QDateTimePrivate::DaylightTime) { |
2872 | sf |= QDateTimePrivate::SetToDaylightTime; |
2873 | } else if (status == QDateTimePrivate::StandardTime) { |
2874 | sf |= QDateTimePrivate::SetToStandardTime; |
2875 | } |
2876 | return sf; |
2877 | } |
2878 | |
2879 | // Get the DST Status if LocalTime set via msecs |
2880 | static constexpr inline |
2881 | QDateTimePrivate::DaylightStatus (QDateTimePrivate::StatusFlags status) |
2882 | { |
2883 | if (status.testFlag(flag: QDateTimePrivate::SetToDaylightTime)) |
2884 | return QDateTimePrivate::DaylightTime; |
2885 | if (status.testFlag(flag: QDateTimePrivate::SetToStandardTime)) |
2886 | return QDateTimePrivate::StandardTime; |
2887 | return QDateTimePrivate::UnknownDaylightTime; |
2888 | } |
2889 | |
2890 | static 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 | |
2900 | static 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 | |
2910 | static inline Qt::TimeSpec getSpec(const QDateTimeData &d) |
2911 | { |
2912 | return extractSpec(status: 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 | */ |
2920 | static inline bool usesSameOffset(const QDateTimeData &a, const QDateTimeData &b) |
2921 | { |
2922 | const auto status = getStatus(d: a); |
2923 | if (status != getStatus(d: 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 |
2946 | static void refreshZonedDateTime(QDateTimeData &d, const QTimeZone &zone) |
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 |
2954 | if (!status.testFlags(flags: QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime)) { |
2955 | status.setFlag(flag: QDateTimePrivate::ValidDateTime, on: 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); |
2961 | QDateTimePrivate::ZoneState state = stateAtMillis(zone, millis: msecs, |
2962 | dst: 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) |
2969 | status = mergeDaylightStatus(sf: status | QDateTimePrivate::ValidDateTime, status: state.dst); |
2970 | else // msecs changed or failed to convert (e.g. overflow) |
2971 | status.setFlag(flag: QDateTimePrivate::ValidDateTime, on: false); |
2972 | } |
2973 | |
2974 | if (status.testFlag(flag: 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 |
2983 | static void refreshSimpleDateTime(QDateTimeData &d) |
2984 | { |
2985 | auto status = getStatus(d); |
2986 | Q_ASSERT(QTimeZone::isUtcOrFixedOffset(extractSpec(status))); |
2987 | status.setFlag(flag: QDateTimePrivate::ValidDateTime, |
2988 | on: status.testFlags(flags: QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime)); |
2989 | |
2990 | if (status.testFlag(flag: 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: |
2997 | static void checkValidDateTime(QDateTimeData &d) |
2998 | { |
2999 | auto spec = extractSpec(status: 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 |
3004 | refreshSimpleDateTime(d); |
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, zone: d.timeZone()); |
3011 | break; |
3012 | } |
3013 | } |
3014 | |
3015 | static void reviseTimeZone(QDateTimeData &d, QTimeZone zone) |
3016 | { |
3017 | Qt::TimeSpec spec = zone.timeSpec(); |
3018 | auto status = mergeSpec(status: 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; |
3028 | offset = zone.fixedSecondsAheadOfUtc(); |
3029 | Q_ASSERT(offset); |
3030 | break; |
3031 | case Qt::TimeZone: |
3032 | reuse = false; |
3033 | break; |
3034 | case Qt::LocalTime: |
3035 | break; |
3036 | } |
3037 | |
3038 | status &= ~(QDateTimePrivate::ValidDateTime | QDateTimePrivate::DaylightMask); |
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 | |
3051 | if (QTimeZone::isUtcOrFixedOffset(spec)) |
3052 | refreshSimpleDateTime(d); |
3053 | else |
3054 | refreshZonedDateTime(d, zone); |
3055 | } |
3056 | |
3057 | static void setDateTime(QDateTimeData &d, QDate date, QTime time) |
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()) |
3061 | time = QTime::fromMSecsSinceStartOfDay(msecs: 0); |
3062 | |
3063 | QDateTimePrivate::StatusFlags newStatus = { }; |
3064 | |
3065 | // Set date value and status |
3066 | qint64 days = 0; |
3067 | if (date.isValid()) { |
3068 | days = date.toJulianDay() - JULIAN_DAY_FOR_EPOCH; |
3069 | newStatus = QDateTimePrivate::ValidDate; |
3070 | } |
3071 | |
3072 | // Set time value and status |
3073 | int ds = 0; |
3074 | if (time.isValid()) { |
3075 | ds = time.msecsSinceStartOfDay(); |
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, millisInDay: qint64(ds), sumMillis: &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); |
3097 | d.data.status &= ~(QDateTimePrivate::ValidityMask | QDateTimePrivate::DaylightMask).toInt(); |
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; |
3107 | d->m_status &= ~(QDateTimePrivate::ValidityMask | QDateTimePrivate::DaylightMask); |
3108 | d->m_status |= newStatus; |
3109 | } |
3110 | } |
3111 | |
3112 | static QPair<QDate, QTime> getDateTime(const QDateTimeData &d) |
3113 | { |
3114 | auto status = getStatus(d); |
3115 | const qint64 msecs = getMSecs(d); |
3116 | const auto dayMilli = QRoundingDown::qDivMod<MSECS_PER_DAY>(a: msecs); |
3117 | return { status.testFlag(flag: QDateTimePrivate::ValidDate) |
3118 | ? QDate::fromJulianDay(jd_: JULIAN_DAY_FOR_EPOCH + dayMilli.quotient) |
3119 | : QDate(), |
3120 | status.testFlag(flag: QDateTimePrivate::ValidTime) |
3121 | ? QTime::fromMSecsSinceStartOfDay(msecs: dayMilli.remainder) |
3122 | : QTime() }; |
3123 | } |
3124 | |
3125 | /***************************************************************************** |
3126 | QDateTime::Data member functions |
3127 | *****************************************************************************/ |
3128 | |
3129 | inline 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) |
3134 | quintptr value = mergeSpec(status: QDateTimePrivate::ShortData, spec: Qt::LocalTime).toInt(); |
3135 | d = reinterpret_cast<QDateTimePrivate *>(value); |
3136 | } |
3137 | |
3138 | inline QDateTime::Data::Data(const QTimeZone &zone) |
3139 | { |
3140 | Qt::TimeSpec spec = zone.timeSpec(); |
3141 | if (CanBeSmall && Q_LIKELY(specCanBeSmall(spec))) { |
3142 | quintptr value = mergeSpec(status: QDateTimePrivate::ShortData, spec).toInt(); |
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(status: {}, 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 | |
3158 | inline QDateTime::Data::Data(const Data &other) noexcept |
3159 | : data(other.data) |
3160 | { |
3161 | if (!isShort()) { |
3162 | // check if we could shrink |
3163 | if (specCanBeSmall(spec: extractSpec(status: d->m_status)) && msecsCanBeSmall(msecs: 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 | |
3175 | inline 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 | |
3184 | inline 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(spec: extractSpec(status: other.d->m_status)) && msecsCanBeSmall(msecs: 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 | |
3209 | inline QDateTime::Data::~Data() |
3210 | { |
3211 | if (!isShort() && !d->ref.deref()) |
3212 | delete d; |
3213 | } |
3214 | |
3215 | inline bool QDateTime::Data::isShort() const |
3216 | { |
3217 | bool b = quintptr(d) & QDateTimePrivate::ShortData; |
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 | |
3229 | inline void QDateTime::Data::detach() |
3230 | { |
3231 | QDateTimePrivate *x; |
3232 | bool wasShort = isShort(); |
3233 | if (wasShort) { |
3234 | // force enlarging |
3235 | x = new QDateTimePrivate; |
3236 | x->m_status = QDateTimePrivate::StatusFlags::fromInt(i: 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(newValue: 1); |
3246 | if (!wasShort && !d->ref.deref()) |
3247 | delete d; |
3248 | d = x; |
3249 | } |
3250 | |
3251 | QTimeZone QDateTime::Data::timeZone() const |
3252 | { |
3253 | switch (getSpec(d: *this)) { |
3254 | case Qt::UTC: |
3255 | return QTimeZone::UTC; |
3256 | case Qt::OffsetFromUTC: |
3257 | return QTimeZone::fromSecondsAheadOfUtc(offset: 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 | |
3270 | inline const QDateTimePrivate *QDateTime::Data::operator->() const |
3271 | { |
3272 | Q_ASSERT(!isShort()); |
3273 | return d; |
3274 | } |
3275 | |
3276 | inline 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 | |
3288 | Q_NEVER_INLINE |
3289 | QDateTime::Data QDateTimePrivate::create(QDate toDate, QTime toTime, const QTimeZone &zone) |
3290 | { |
3291 | QDateTime::Data result(zone); |
3292 | setDateTime(d&: result, date: toDate, time: toTime); |
3293 | if (zone.isUtcOrFixedOffset()) |
3294 | refreshSimpleDateTime(d&: result); |
3295 | else |
3296 | refreshZonedDateTime(d&: result, zone); |
3297 | return result; |
3298 | } |
3299 | |
3300 | /***************************************************************************** |
3301 | QDateTime member functions |
3302 | *****************************************************************************/ |
3303 | |
3304 | /*! |
3305 | \class QDateTime |
3306 | \inmodule QtCore |
3307 | \ingroup shared |
3308 | \reentrant |
3309 | \brief The QDateTime class provides date and time functions. |
3310 | |
3311 | A QDateTime object encodes a calendar date and a clock time (a "datetime") |
3312 | in accordance with a time representation. It combines features of the QDate |
3313 | and QTime classes. It can read the current datetime from the system |
3314 | clock. It provides functions for comparing datetimes and for manipulating a |
3315 | datetime by adding a number of seconds, days, months, or years. |
3316 | |
3317 | QDateTime can describe datetimes with respect to \l{Qt::LocalTime}{local |
3318 | time}, to \l{Qt::UTC}{UTC}, to a specified \l{Qt::OffsetFromUTC}{offset from |
3319 | UTC} or to a specified \l{Qt::TimeZone}{time zone}. Each of these time |
3320 | representations can be encapsulated in a suitable instance of the QTimeZone |
3321 | class. For example, a time zone of "Europe/Berlin" will apply the |
3322 | daylight-saving rules as used in Germany. In contrast, a fixed offset from |
3323 | UTC of +3600 seconds is one hour ahead of UTC (usually written in ISO |
3324 | standard notation as "UTC+01:00"), with no daylight-saving |
3325 | complications. When using either local time or a specified time zone, |
3326 | time-zone transitions (see \l {Daylight-Saving Time (DST)}{below}) are taken |
3327 | into account. A QDateTime's timeSpec() will tell you which of the four types |
3328 | of time representation is in use; its timeRepresentation() provides a full |
3329 | representation of that time representation, as a QTimeZone. |
3330 | |
3331 | A QDateTime object is typically created either by giving a date and time |
3332 | explicitly in the constructor, or by using a static function such as |
3333 | currentDateTime() or fromMSecsSinceEpoch(). The date and time can be changed |
3334 | with setDate() and setTime(). A datetime can also be set using the |
3335 | setMSecsSinceEpoch() function that takes the time, in milliseconds, since |
3336 | the start, in UTC of the year 1970. The fromString() function returns a |
3337 | QDateTime, given a string and a date format used to interpret the date |
3338 | within the string. |
3339 | |
3340 | QDateTime::currentDateTime() returns a QDateTime that expresses the current |
3341 | date and time with respect to a specific time representation, such as local |
3342 | time (its default). QDateTime::currentDateTimeUtc() returns a QDateTime that |
3343 | expresses the current date and time with respect to UTC; it is equivalent to |
3344 | \c {QDateTime::currentDateTime(QTimeZone::UTC)}. |
3345 | |
3346 | The date() and time() functions provide access to the date and |
3347 | time parts of the datetime. The same information is provided in |
3348 | textual format by the toString() function. |
3349 | |
3350 | QDateTime provides a full set of operators to compare two |
3351 | QDateTime objects, where smaller means earlier and larger means |
3352 | later. |
3353 | |
3354 | You can increment (or decrement) a datetime by a given number of |
3355 | milliseconds using addMSecs(), seconds using addSecs(), or days using |
3356 | addDays(). Similarly, you can use addMonths() and addYears(). The daysTo() |
3357 | function returns the number of days between two datetimes, secsTo() returns |
3358 | the number of seconds between two datetimes, and msecsTo() returns the |
3359 | number of milliseconds between two datetimes. These operations are aware of |
3360 | daylight-saving time (DST) and other time-zone transitions, where |
3361 | applicable. |
3362 | |
3363 | Use toTimeZone() to re-express a datetime in terms of a different time |
3364 | representation. By passing a lightweight QTimeZone that represents local |
3365 | time, UTC or a fixed offset from UTC, you can convert the datetime to use |
3366 | the corresponding time representation; or you can pass a full time zone |
3367 | (whose \l {QTimeZone::timeSpec()}{timeSpec()} is \c {Qt::TimeZone}) to use |
3368 | that instead. |
3369 | |
3370 | \note QDateTime does not account for leap seconds. |
3371 | |
3372 | \section1 Remarks |
3373 | |
3374 | \note All conversion to and from string formats is done using the C locale. |
3375 | For localized conversions, see QLocale. |
3376 | |
3377 | \note There is no year 0 in the Gregorian calendar. Dates in that year are |
3378 | considered invalid. The year -1 is the year "1 before Christ" or "1 before |
3379 | common era." The day before 1 January 1 CE is 31 December 1 BCE. |
3380 | |
3381 | \section2 Range of Valid Dates |
3382 | |
3383 | The range of values that QDateTime can represent is dependent on the |
3384 | internal storage implementation. QDateTime is currently stored in a qint64 |
3385 | as a serial msecs value encoding the date and time. This restricts the date |
3386 | range to about +/- 292 million years, compared to the QDate range of +/- 2 |
3387 | billion years. Care must be taken when creating a QDateTime with extreme |
3388 | values that you do not overflow the storage. The exact range of supported |
3389 | values varies depending on the time representation used. |
3390 | |
3391 | \section2 Use of Timezones |
3392 | |
3393 | QDateTime uses the system's time zone information to determine the current |
3394 | local time zone and its offset from UTC. If the system is not configured |
3395 | correctly or not up-to-date, QDateTime will give wrong results. |
3396 | |
3397 | QDateTime likewise uses system-provided information to determine the offsets |
3398 | of other timezones from UTC. If this information is incomplete or out of |
3399 | date, QDateTime will give wrong results. See the QTimeZone documentation for |
3400 | more details. |
3401 | |
3402 | On modern Unix systems, this means QDateTime usually has accurate |
3403 | information about historical transitions (including DST, see below) whenever |
3404 | possible. On Windows, where the system doesn't support historical timezone |
3405 | data, historical accuracy is not maintained with respect to timezone |
3406 | transitions, notably including DST. However, building Qt with the ICU |
3407 | library will equipe QTimeZone with the same timezone database as is used on |
3408 | Unix. |
3409 | |
3410 | \section2 Daylight-Saving Time (DST) |
3411 | |
3412 | QDateTime takes into account transitions between Standard Time and |
3413 | Daylight-Saving Time. For example, if the transition is at 2am and the clock |
3414 | goes forward to 3am, then there is a "missing" hour from 02:00:00 to |
3415 | 02:59:59.999 which QDateTime considers to be invalid. Any date arithmetic |
3416 | performed will take this missing hour into account and return a valid |
3417 | result. For example, adding one second to 01:59:59 will get 03:00:00. |
3418 | |
3419 | For datetimes that the system \c time_t can represent (from 1901-12-14 to |
3420 | 2038-01-18 on systems with 32-bit \c time_t; for the full range QDateTime |
3421 | can represent if the type is 64-bit), the standard system APIs are used to |
3422 | determine local time's offset from UTC. For datetimes not handled by these |
3423 | system APIs, QTimeZone::systemTimeZone() is used. In either case, the offset |
3424 | information used depends on the system and may be incomplete or, for past |
3425 | times, historically inaccurate. In any case, for future dates, the local |
3426 | time zone's offsets and DST rules may change before that date comes around. |
3427 | |
3428 | \section2 Offsets From UTC |
3429 | |
3430 | Offsets from UTC are measured in seconds east of Greenwich. The moment |
3431 | described by a particular date and time, such as noon on a particular day, |
3432 | depends on the time representation used. Those with a higher offset from UTC |
3433 | describe an earlier moment, and those with a lower offset a later moment, by |
3434 | any given combination of date and time. |
3435 | |
3436 | There is no explicit size restriction on an offset from UTC, but there is an |
3437 | implicit limit imposed when using the toString() and fromString() methods |
3438 | which use a [+|-]hh:mm format, effectively limiting the range to +/- 99 |
3439 | hours and 59 minutes and whole minutes only. Note that currently no time |
3440 | zone has an offset outside the range of ±14 hours and all known offsets are |
3441 | multiples of five minutes. |
3442 | |
3443 | \sa QDate, QTime, QDateTimeEdit, QTimeZone |
3444 | */ |
3445 | |
3446 | /*! |
3447 | \since 5.14 |
3448 | \enum QDateTime::YearRange |
3449 | |
3450 | This enumerated type describes the range of years (in the Gregorian |
3451 | calendar) representable by QDateTime: |
3452 | |
3453 | \value First The later parts of this year are representable |
3454 | \value Last The earlier parts of this year are representable |
3455 | |
3456 | All dates strictly between these two years are also representable. |
3457 | Note, however, that the Gregorian Calendar has no year zero. |
3458 | |
3459 | \note QDate can describe dates in a wider range of years. For most |
3460 | purposes, this makes little difference, as the range of years that QDateTime |
3461 | can support reaches 292 million years either side of 1970. |
3462 | |
3463 | \sa isValid(), QDate |
3464 | */ |
3465 | |
3466 | /*! |
3467 | Constructs a null datetime, nominally using local time. |
3468 | |
3469 | A null datetime is invalid, since its date and time are invalid. |
3470 | |
3471 | \sa isValid(), setMSecsSinceEpoch(), setDate(), setTime(), setTimeZone() |
3472 | */ |
3473 | QDateTime::QDateTime() noexcept |
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) |
3483 | /*! |
3484 | \deprecated [6.9] Use \c{QDateTime(date, time)} or \c{QDateTime(date, time, QTimeZone::fromSecondsAheadOfUtc(offsetSeconds))}. |
3485 | |
3486 | Constructs a datetime with the given \a date and \a time, using the time |
3487 | representation implied by \a spec and \a offsetSeconds seconds. |
3488 | |
3489 | If \a date is valid and \a time is not, the time will be set to midnight. |
3490 | |
3491 | If \a spec is not Qt::OffsetFromUTC then \a offsetSeconds will be |
3492 | ignored. If \a spec is Qt::OffsetFromUTC and \a offsetSeconds is 0 then the |
3493 | timeSpec() will be set to Qt::UTC, i.e. an offset of 0 seconds. |
3494 | |
3495 | If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime, |
3496 | i.e. the current system time zone. To create a Qt::TimeZone datetime |
3497 | use the correct constructor. |
3498 | |
3499 | If \a date lies outside the range of dates representable by QDateTime, the |
3500 | result is invalid. If \a spec is Qt::LocalTime and the system's time-zone |
3501 | skipped over the given date and time, the result is invalid. |
3502 | */ |
3503 | QDateTime::QDateTime(QDate date, QTime time, Qt::TimeSpec spec, int offsetSeconds) |
3504 | : d(QDateTimePrivate::create(toDate: date, toTime: time, zone: asTimeZone(spec, offset: offsetSeconds, warner: "QDateTime" ))) |
3505 | { |
3506 | } |
3507 | #endif // 6.9 deprecation |
3508 | |
3509 | /*! |
3510 | \since 5.2 |
3511 | |
3512 | Constructs a datetime with the given \a date and \a time, using the time |
3513 | representation described by \a timeZone. |
3514 | |
3515 | If \a date is valid and \a time is not, the time will be set to midnight. |
3516 | If \a timeZone is invalid then the datetime will be invalid. |
3517 | */ |
3518 | |
3519 | QDateTime::QDateTime(QDate date, QTime time, const QTimeZone &timeZone) |
3520 | : d(QDateTimePrivate::create(toDate: date, toTime: time, zone: timeZone)) |
3521 | { |
3522 | } |
3523 | |
3524 | /*! |
3525 | \since 6.5 |
3526 | |
3527 | Constructs a datetime with the given \a date and \a time, using local time. |
3528 | |
3529 | If \a date is valid and \a time is not, midnight will be used as the time. |
3530 | */ |
3531 | |
3532 | QDateTime::QDateTime(QDate date, QTime time) |
3533 | : d(QDateTimePrivate::create(toDate: date, toTime: time, zone: QTimeZone::LocalTime)) |
3534 | { |
3535 | } |
3536 | |
3537 | /*! |
3538 | Constructs a copy of the \a other datetime. |
3539 | */ |
3540 | QDateTime::QDateTime(const QDateTime &other) noexcept |
3541 | : d(other.d) |
3542 | { |
3543 | } |
3544 | |
3545 | /*! |
3546 | \since 5.8 |
3547 | Moves the content of the temporary \a other datetime to this object and |
3548 | leaves \a other in an unspecified (but proper) state. |
3549 | */ |
3550 | QDateTime::QDateTime(QDateTime &&other) noexcept |
3551 | : d(std::move(other.d)) |
3552 | { |
3553 | } |
3554 | |
3555 | /*! |
3556 | Destroys the datetime. |
3557 | */ |
3558 | QDateTime::~QDateTime() |
3559 | { |
3560 | } |
3561 | |
3562 | /*! |
3563 | Copies the \a other datetime into this and returns this copy. |
3564 | */ |
3565 | |
3566 | QDateTime &QDateTime::operator=(const QDateTime &other) noexcept |
3567 | { |
3568 | d = other.d; |
3569 | return *this; |
3570 | } |
3571 | /*! |
3572 | \fn void QDateTime::swap(QDateTime &other) |
3573 | \since 5.0 |
3574 | |
3575 | Swaps this datetime with \a other. This operation is very fast |
3576 | and never fails. |
3577 | */ |
3578 | |
3579 | /*! |
3580 | Returns \c true if both the date and the time are null; otherwise |
3581 | returns \c false. A null datetime is invalid. |
3582 | |
3583 | \sa QDate::isNull(), QTime::isNull(), isValid() |
3584 | */ |
3585 | |
3586 | bool QDateTime::isNull() const |
3587 | { |
3588 | // If date or time is invalid, we don't set datetime valid. |
3589 | return !getStatus(d).testAnyFlag(flag: QDateTimePrivate::ValidityMask); |
3590 | } |
3591 | |
3592 | /*! |
3593 | Returns \c true if this datetime represents a definite moment, otherwise \c false. |
3594 | |
3595 | A datetime is valid if both its date and its time are valid and the time |
3596 | representation used gives a valid meaning to their combination. When the |
3597 | time representation is a specific time-zone or local time, there may be |
3598 | times on some dates that the zone skips in its representation, as when a |
3599 | daylight-saving transition skips an hour (typically during a night in |
3600 | spring). For example, if DST ends at 2am with the clock advancing to 3am, |
3601 | then datetimes from 02:00:00 to 02:59:59.999 on that day are invalid. |
3602 | |
3603 | \sa QDateTime::YearRange, QDate::isValid(), QTime::isValid() |
3604 | */ |
3605 | |
3606 | bool QDateTime::isValid() const |
3607 | { |
3608 | return getStatus(d).testFlag(flag: QDateTimePrivate::ValidDateTime); |
3609 | } |
3610 | |
3611 | /*! |
3612 | Returns the date part of the datetime. |
3613 | |
3614 | \sa setDate(), time(), timeRepresentation() |
3615 | */ |
3616 | |
3617 | QDate QDateTime::date() const |
3618 | { |
3619 | return getStatus(d).testFlag(flag: QDateTimePrivate::ValidDate) ? msecsToDate(msecs: getMSecs(d)) : QDate(); |
3620 | } |
3621 | |
3622 | /*! |
3623 | Returns the time part of the datetime. |
3624 | |
3625 | \sa setTime(), date(), timeRepresentation() |
3626 | */ |
3627 | |
3628 | QTime QDateTime::time() const |
3629 | { |
3630 | return getStatus(d).testFlag(flag: QDateTimePrivate::ValidTime) ? msecsToTime(msecs: getMSecs(d)) : QTime(); |
3631 | } |
3632 | |
3633 | /*! |
3634 | Returns the time specification of the datetime. |
3635 | |
3636 | This classifies its time representation as local time, UTC, a fixed offset |
3637 | from UTC (without indicating the offset) or a time zone (without giving the |
3638 | details of that time zone). Equivalent to |
3639 | \c{timeRepresentation().timeSpec()}. |
3640 | |
3641 | \sa setTimeSpec(), timeRepresentation(), date(), time() |
3642 | */ |
3643 | |
3644 | Qt::TimeSpec QDateTime::timeSpec() const |
3645 | { |
3646 | return getSpec(d); |
3647 | } |
3648 | |
3649 | /*! |
3650 | \since 6.5 |
3651 | Returns a QTimeZone identifying how this datetime represents time. |
3652 | |
3653 | The timeSpec() of the returned QTimeZone will coincide with that of this |
3654 | datetime; if it is not Qt::TimeZone then the returned QTimeZone is a time |
3655 | representation. When their timeSpec() is Qt::OffsetFromUTC, the returned |
3656 | QTimeZone's fixedSecondsAheadOfUtc() supplies the offset. When timeSpec() |
3657 | is Qt::TimeZone, the QTimeZone object itself is the full representation of |
3658 | that time zone. |
3659 | |
3660 | \sa timeZone(), setTimeZone(), QTimeZone::asBackendZone() |
3661 | */ |
3662 | |
3663 | QTimeZone QDateTime::timeRepresentation() const |
3664 | { |
3665 | return d.timeZone(); |
3666 | } |
3667 | |
3668 | #if QT_CONFIG(timezone) |
3669 | /*! |
3670 | \since 5.2 |
3671 | |
3672 | Returns the time zone of the datetime. |
3673 | |
3674 | The result is the same as \c{timeRepresentation().asBackendZone()}. In all |
3675 | cases, the result's \l {QTimeZone::timeSpec()}{timeSpec()} is Qt::TimeZone. |
3676 | |
3677 | When timeSpec() is Qt::LocalTime, the result will describe local time at the |
3678 | time this method was called. It will not reflect subsequent changes to the |
3679 | system time zone, even when the QDateTime from which it was obtained does. |
3680 | |
3681 | \sa timeRepresentation(), setTimeZone(), Qt::TimeSpec, QTimeZone::asBackendZone() |
3682 | */ |
3683 | |
3684 | QTimeZone QDateTime::timeZone() const |
3685 | { |
3686 | return d.timeZone().asBackendZone(); |
3687 | } |
3688 | #endif // timezone |
3689 | |
3690 | /*! |
3691 | \since 5.2 |
3692 | |
3693 | Returns this datetime's Offset From UTC in seconds. |
3694 | |
3695 | The result depends on timeSpec(): |
3696 | \list |
3697 | \li \c Qt::UTC The offset is 0. |
3698 | \li \c Qt::OffsetFromUTC The offset is the value originally set. |
3699 | \li \c Qt::LocalTime The local time's offset from UTC is returned. |
3700 | \li \c Qt::TimeZone The offset used by the time-zone is returned. |
3701 | \endlist |
3702 | |
3703 | For the last two, the offset at this date and time will be returned, taking |
3704 | account of Daylight-Saving Offset. The offset is the difference between the |
3705 | local time or time in the given time-zone and UTC time; it is positive in |
3706 | time-zones ahead of UTC (East of The Prime Meridian), negative for those |
3707 | behind UTC (West of The Prime Meridian). |
3708 | |
3709 | \sa setOffsetFromUtc() |
3710 | */ |
3711 | |
3712 | int QDateTime::offsetFromUtc() const |
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 | |
3730 | /*! |
3731 | \since 5.2 |
3732 | |
3733 | Returns the Time Zone Abbreviation for this datetime. |
3734 | |
3735 | The returned string depends on timeSpec(): |
3736 | |
3737 | \list |
3738 | \li For Qt::UTC it is "UTC". |
3739 | \li For Qt::OffsetFromUTC it will be in the format "UTC[+-]00:00". |
3740 | \li For Qt::LocalTime, the host system is queried. |
3741 | \li For Qt::TimeZone, the associated QTimeZone object is queried. |
3742 | \endlist |
3743 | |
3744 | \note The abbreviation is not guaranteed to be unique, i.e. different time |
3745 | zones may have the same abbreviation. For Qt::LocalTime and Qt::TimeZone, |
3746 | when returned by the host system, the abbreviation may be localized. |
3747 | |
3748 | \sa timeSpec(), QTimeZone::abbreviation() |
3749 | */ |
3750 | |
3751 | QString QDateTime::timeZoneAbbreviation() const |
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(format: Qt::ISODate, offset: 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(atDateTime: *this); |
3767 | #endif // timezone |
3768 | case Qt::LocalTime: |
3769 | return QDateTimePrivate::localNameAtMillis(millis: getMSecs(d), |
3770 | dst: extractDaylightStatus(status: getStatus(d))); |
3771 | } |
3772 | return QString(); |
3773 | } |
3774 | |
3775 | /*! |
3776 | \since 5.2 |
3777 | |
3778 | Returns if this datetime falls in Daylight-Saving Time. |
3779 | |
3780 | If the Qt::TimeSpec is not Qt::LocalTime or Qt::TimeZone then will always |
3781 | return false. |
3782 | |
3783 | \sa timeSpec() |
3784 | */ |
3785 | |
3786 | bool QDateTime::isDaylightTime() const |
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(status: getStatus(d)); |
3801 | dst != QDateTimePrivate::UnknownDaylightTime) { |
3802 | return dst == QDateTimePrivate::DaylightTime; |
3803 | } |
3804 | return d->m_timeZone.d->isDaylightTime(atMSecsSinceEpoch: toMSecsSinceEpoch()); |
3805 | #endif // timezone |
3806 | case Qt::LocalTime: { |
3807 | auto dst = extractDaylightStatus(status: getStatus(d)); |
3808 | if (dst == QDateTimePrivate::UnknownDaylightTime) |
3809 | dst = QDateTimePrivate::localStateAtMillis(millis: getMSecs(d), dst).dst; |
3810 | return dst == QDateTimePrivate::DaylightTime; |
3811 | } |
3812 | } |
3813 | return false; |
3814 | } |
3815 | |
3816 | /*! |
3817 | Sets the date part of this datetime to \a date. If no time is set yet, it |
3818 | is set to midnight. If \a date is invalid, this QDateTime becomes invalid. |
3819 | |
3820 | \sa date(), setTime(), setTimeZone() |
3821 | */ |
3822 | |
3823 | void QDateTime::setDate(QDate date) |
3824 | { |
3825 | setDateTime(d, date, time: time()); |
3826 | checkValidDateTime(d); |
3827 | } |
3828 | |
3829 | /*! |
3830 | Sets the time part of this datetime to \a time. If \a time is not valid, |
3831 | this function sets it to midnight. Therefore, it's possible to clear any |
3832 | set time in a QDateTime by setting it to a default QTime: |
3833 | |
3834 | \code |
3835 | QDateTime dt = QDateTime::currentDateTime(); |
3836 | dt.setTime(QTime()); |
3837 | \endcode |
3838 | |
3839 | \sa time(), setDate(), setTimeZone() |
3840 | */ |
3841 | |
3842 | void QDateTime::setTime(QTime time) |
3843 | { |
3844 | setDateTime(d, date: date(), time); |
3845 | checkValidDateTime(d); |
3846 | } |
3847 | |
3848 | #if QT_DEPRECATED_SINCE(6, 9) |
3849 | /*! |
3850 | \deprecated [6.9] Use setTimeZone() instead |
3851 | |
3852 | Sets the time specification used in this datetime to \a spec. |
3853 | The datetime may refer to a different point in time. |
3854 | |
3855 | If \a spec is Qt::OffsetFromUTC then the timeSpec() will be set |
3856 | to Qt::UTC, i.e. an effective offset of 0. |
3857 | |
3858 | If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime, |
3859 | i.e. the current system time zone. |
3860 | |
3861 | Example: |
3862 | \snippet code/src_corelib_time_qdatetime.cpp 19 |
3863 | |
3864 | \sa setTimeZone(), timeSpec(), toTimeSpec(), setDate(), setTime() |
3865 | */ |
3866 | |
3867 | void QDateTime::setTimeSpec(Qt::TimeSpec spec) |
3868 | { |
3869 | reviseTimeZone(d, zone: asTimeZone(spec, offset: 0, warner: "QDateTime::setTimeSpec" )); |
3870 | } |
3871 | |
3872 | /*! |
3873 | \since 5.2 |
3874 | \deprecated [6.9] Use setTimeZone(QTimeZone::fromSecondsAheadOfUtc(offsetSeconds)) instead |
3875 | |
3876 | Sets the timeSpec() to Qt::OffsetFromUTC and the offset to \a offsetSeconds. |
3877 | The datetime may refer to a different point in time. |
3878 | |
3879 | The maximum and minimum offset is 14 positive or negative hours. If |
3880 | \a offsetSeconds is larger or smaller than that, then the result is |
3881 | undefined. |
3882 | |
3883 | If \a offsetSeconds is 0 then the timeSpec() will be set to Qt::UTC. |
3884 | |
3885 | \sa setTimeZone(), isValid(), offsetFromUtc(), toOffsetFromUtc() |
3886 | */ |
3887 | |
3888 | void QDateTime::setOffsetFromUtc(int offsetSeconds) |
3889 | { |
3890 | reviseTimeZone(d, zone: QTimeZone::fromSecondsAheadOfUtc(offset: offsetSeconds)); |
3891 | } |
3892 | #endif // 6.9 deprecations |
3893 | |
3894 | /*! |
3895 | \since 5.2 |
3896 | |
3897 | Sets the time zone used in this datetime to \a toZone. |
3898 | |
3899 | The datetime may refer to a different point in time. It uses the time |
3900 | representation of \a toZone, which may change the meaning of its unchanged |
3901 | date() and time(). |
3902 | |
3903 | If \a toZone is invalid then the datetime will be invalid. Otherwise, this |
3904 | datetime's timeSpec() after the call will match \c{toZone.timeSpec()}. |
3905 | |
3906 | \sa timeRepresentation(), timeZone(), Qt::TimeSpec |
3907 | */ |
3908 | |
3909 | void QDateTime::setTimeZone(const QTimeZone &toZone) |
3910 | { |
3911 | reviseTimeZone(d, zone: toZone); |
3912 | } |
3913 | |
3914 | /*! |
3915 | \since 4.7 |
3916 | |
3917 | Returns the datetime as a number of milliseconds after the start, in UTC, of |
3918 | the year 1970. |
3919 | |
3920 | On systems that do not support time zones, this function will |
3921 | behave as if local time were Qt::UTC. |
3922 | |
3923 | The behavior for this function is undefined if the datetime stored in |
3924 | this object is not valid. However, for all valid dates, this function |
3925 | returns a unique value. |
3926 | |
3927 | \sa toSecsSinceEpoch(), setMSecsSinceEpoch(), fromMSecsSinceEpoch() |
3928 | */ |
3929 | qint64 QDateTime::toMSecsSinceEpoch() const |
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. |
3946 | auto dst = extractDaylightStatus(status: getStatus(d)); |
3947 | auto state = QDateTimePrivate::localStateAtMillis(millis: getMSecs(d), dst); |
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 | |
3965 | /*! |
3966 | \since 5.8 |
3967 | |
3968 | Returns the datetime as a number of seconds after the start, in UTC, of the |
3969 | year 1970. |
3970 | |
3971 | On systems that do not support time zones, this function will |
3972 | behave as if local time were Qt::UTC. |
3973 | |
3974 | The behavior for this function is undefined if the datetime stored in |
3975 | this object is not valid. However, for all valid dates, this function |
3976 | returns a unique value. |
3977 | |
3978 | \sa toMSecsSinceEpoch(), fromSecsSinceEpoch(), setSecsSinceEpoch() |
3979 | */ |
3980 | qint64 QDateTime::toSecsSinceEpoch() const |
3981 | { |
3982 | return toMSecsSinceEpoch() / MSECS_PER_SEC; |
3983 | } |
3984 | |
3985 | /*! |
3986 | \since 4.7 |
3987 | |
3988 | Sets the datetime to represent a moment a given number, \a msecs, of |
3989 | milliseconds after the start, in UTC, of the year 1970. |
3990 | |
3991 | On systems that do not support time zones, this function will |
3992 | behave as if local time were Qt::UTC. |
3993 | |
3994 | Note that passing the minimum of \c qint64 |
3995 | (\c{std::numeric_limits<qint64>::min()}) to \a msecs will result in |
3996 | undefined behavior. |
3997 | |
3998 | \sa setSecsSinceEpoch(), toMSecsSinceEpoch(), fromMSecsSinceEpoch() |
3999 | */ |
4000 | void QDateTime::setMSecsSinceEpoch(qint64 msecs) |
4001 | { |
4002 | auto status = getStatus(d); |
4003 | const auto spec = extractSpec(status); |
4004 | Q_ASSERT(specCanBeSmall(spec) || !d.isShort()); |
4005 | QDateTimePrivate::ZoneState state(msecs); |
4006 | |
4007 | status &= ~QDateTimePrivate::ValidityMask; |
4008 | if (QTimeZone::isUtcOrFixedOffset(spec)) { |
4009 | if (spec == Qt::OffsetFromUTC) |
4010 | state.offset = d->m_offsetFromUtc; |
4011 | if (!state.offset || !qAddOverflow(v1: msecs, v2: state.offset * MSECS_PER_SEC, r: &state.when)) |
4012 | status |= QDateTimePrivate::ValidityMask; |
4013 | } else if (spec == Qt::LocalTime) { |
4014 | state = QDateTimePrivate::expressUtcAsLocal(utcMSecs: msecs); |
4015 | if (state.valid) |
4016 | status = mergeDaylightStatus(sf: status | QDateTimePrivate::ValidityMask, status: state.dst); |
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(forMSecsSinceEpoch: 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))) { |
4025 | d->m_status = mergeDaylightStatus(sf: status | QDateTimePrivate::ValidityMask, |
4026 | status: data.daylightTimeOffset |
4027 | ? QDateTimePrivate::DaylightTime |
4028 | : QDateTimePrivate::StandardTime); |
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 | } |
4036 | Q_ASSERT(!status.testFlag(QDateTimePrivate::ValidDateTime) |
4037 | || (state.offset >= -SECS_PER_DAY && state.offset <= SECS_PER_DAY)); |
4038 | |
4039 | if (msecsCanBeSmall(msecs: 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 | |
4051 | /*! |
4052 | \since 5.8 |
4053 | |
4054 | Sets the datetime to represent a moment a given number, \a secs, of seconds |
4055 | after the start, in UTC, of the year 1970. |
4056 | |
4057 | On systems that do not support time zones, this function will |
4058 | behave as if local time were Qt::UTC. |
4059 | |
4060 | \sa setMSecsSinceEpoch(), toSecsSinceEpoch(), fromSecsSinceEpoch() |
4061 | */ |
4062 | void QDateTime::setSecsSinceEpoch(qint64 secs) |
4063 | { |
4064 | qint64 msecs; |
4065 | if (!qMulOverflow(v1: secs, std::integral_constant<qint64, MSECS_PER_SEC>(), r: &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 |
4076 | /*! |
4077 | \overload |
4078 | |
4079 | Returns the datetime as a string in the \a format given. |
4080 | |
4081 | If the \a format is Qt::TextDate, the string is formatted in the default |
4082 | way. The day and month names will be in English. An example of this |
4083 | formatting is "Wed May 20 03:40:13 1998". For localized formatting, see |
4084 | \l{QLocale::toString()}. |
4085 | |
4086 | If the \a format is Qt::ISODate, the string format corresponds |
4087 | to the ISO 8601 extended specification for representations of |
4088 | dates and times, taking the form yyyy-MM-ddTHH:mm:ss[Z|[+|-]HH:mm], |
4089 | depending on the timeSpec() of the QDateTime. If the timeSpec() |
4090 | is Qt::UTC, Z will be appended to the string; if the timeSpec() is |
4091 | Qt::OffsetFromUTC, the offset in hours and minutes from UTC will |
4092 | be appended to the string. To include milliseconds in the ISO 8601 |
4093 | date, use the \a format Qt::ISODateWithMs, which corresponds to |
4094 | yyyy-MM-ddTHH:mm:ss.zzz[Z|[+|-]HH:mm]. |
4095 | |
4096 | If the \a format is Qt::RFC2822Date, the string is formatted |
4097 | following \l{RFC 2822}. |
4098 | |
4099 | If the datetime is invalid, an empty string will be returned. |
4100 | |
4101 | \warning The Qt::ISODate format is only valid for years in the |
4102 | range 0 to 9999. |
4103 | |
4104 | \sa fromString(), QDate::toString(), QTime::toString(), |
4105 | QLocale::toString() |
4106 | */ |
4107 | QString 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(dateTime: *this, format: u"dd MMM yyyy hh:mm:ss " ); |
4116 | buf += toOffsetString(format: Qt::TextDate, offset: offsetFromUtc()); |
4117 | return buf; |
4118 | default: |
4119 | case Qt::TextDate: { |
4120 | const QPair<QDate, QTime> p = getDateTime(d); |
4121 | buf = toStringTextDate(date: p.first); |
4122 | // Insert time between date's day and year: |
4123 | buf.insert(i: buf.lastIndexOf(c: u' '), |
4124 | s: u' ' + p.second.toString(format: 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 | atDateTime: *this, nameType: QTimeZone::OffsetName, locale: 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(format: Qt::TextDate, offset: offsetFromUtc()); |
4143 | } |
4144 | return buf; |
4145 | } |
4146 | case Qt::ISODate: |
4147 | case Qt::ISODateWithMs: { |
4148 | const QPair<QDate, QTime> p = getDateTime(d); |
4149 | buf = toStringIsoDate(date: 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(format: Qt::ISODate, offset: offsetFromUtc()); |
4160 | break; |
4161 | default: |
4162 | break; |
4163 | } |
4164 | return buf; |
4165 | } |
4166 | } |
4167 | } |
4168 | |
4169 | /*! |
4170 | \fn QString QDateTime::toString(const QString &format, QCalendar cal) const |
4171 | \fn QString QDateTime::toString(QStringView format, QCalendar cal) const |
4172 | |
4173 | Returns the datetime as a string. The \a format parameter determines the |
4174 | format of the result string. If \a cal is supplied, it determines the calendar |
4175 | used to represent the date; it defaults to Gregorian. See QTime::toString() |
4176 | and QDate::toString() for the supported specifiers for time and date, |
4177 | respectively. |
4178 | |
4179 | Any sequence of characters enclosed in single quotes will be included |
4180 | verbatim in the output string (stripped of the quotes), even if it contains |
4181 | formatting characters. Two consecutive single quotes ("''") are replaced by |
4182 | a single quote in the output. All other characters in the format string are |
4183 | included verbatim in the output string. |
4184 | |
4185 | Formats without separators (e.g. "ddMM") are supported but must be used with |
4186 | care, as the resulting strings aren't always reliably readable (e.g. if "dM" |
4187 | produces "212" it could mean either the 2nd of December or the 21st of |
4188 | February). |
4189 | |
4190 | Example format strings (assumed that the QDateTime is 21 May 2001 |
4191 | 14:13:09.120): |
4192 | |
4193 | \table |
4194 | \header \li Format \li Result |
4195 | \row \li dd.MM.yyyy \li 21.05.2001 |
4196 | \row \li ddd MMMM d yy \li Tue May 21 01 |
4197 | \row \li hh:mm:ss.zzz \li 14:13:09.120 |
4198 | \row \li hh:mm:ss.z \li 14:13:09.12 |
4199 | \row \li h:m:s ap \li 2:13:9 pm |
4200 | \endtable |
4201 | |
4202 | If the datetime is invalid, an empty string will be returned. |
4203 | |
4204 | \note Day and month names as well as AM/PM indicators are given in English |
4205 | (C locale). To get localized month and day names and localized forms of |
4206 | AM/PM, use QLocale::system().toDateTime(). |
4207 | |
4208 | \sa fromString(), QDate::toString(), QTime::toString(), QLocale::toString() |
4209 | */ |
4210 | QString QDateTime::toString(QStringView format, QCalendar cal) const |
4211 | { |
4212 | return QLocale::c().toString(dateTime: *this, format, cal); |
4213 | } |
4214 | #endif // datestring |
4215 | |
4216 | static inline void massageAdjustedDateTime(QDateTimeData &d, QDate date, QTime time) |
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); |
4230 | Q_ASSERT(status.testFlags(QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime |
4231 | | QDateTimePrivate::ValidDateTime)); |
4232 | auto spec = extractSpec(status); |
4233 | if (QTimeZone::isUtcOrFixedOffset(spec)) { |
4234 | setDateTime(d, date, time); |
4235 | refreshSimpleDateTime(d); |
4236 | return; |
4237 | } |
4238 | auto dst = extractDaylightStatus(status); |
4239 | qint64 local = timeToMSecs(date, time); |
4240 | const QDateTimePrivate::ZoneState state = stateAtMillis(zone: d.timeZone(), millis: local, dst); |
4241 | if (state.valid) |
4242 | status = mergeDaylightStatus(sf: status | QDateTimePrivate::ValidDateTime, status: state.dst); |
4243 | else |
4244 | status.setFlag(flag: QDateTimePrivate::ValidDateTime, on: 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 | |
4259 | /*! |
4260 | Returns a QDateTime object containing a datetime \a ndays days |
4261 | later than the datetime of this object (or earlier if \a ndays is |
4262 | negative). |
4263 | |
4264 | If the timeSpec() is Qt::LocalTime or Qt::TimeZone and the resulting |
4265 | date and time fall in the Standard Time to Daylight-Saving Time transition |
4266 | hour then the result will be adjusted accordingly, i.e. if the transition |
4267 | is at 2am and the clock goes forward to 3am and the result falls between |
4268 | 2am and 3am then the result will be adjusted to fall after 3am. |
4269 | |
4270 | \sa daysTo(), addMonths(), addYears(), addSecs() |
4271 | */ |
4272 | |
4273 | QDateTime QDateTime::addDays(qint64 ndays) const |
4274 | { |
4275 | if (isNull()) |
4276 | return QDateTime(); |
4277 | |
4278 | QDateTime dt(*this); |
4279 | QPair<QDate, QTime> p = getDateTime(d); |
4280 | massageAdjustedDateTime(d&: dt.d, date: p.first.addDays(ndays), time: p.second); |
4281 | return dt; |
4282 | } |
4283 | |
4284 | /*! |
4285 | Returns a QDateTime object containing a datetime \a nmonths months |
4286 | later than the datetime of this object (or earlier if \a nmonths |
4287 | is negative). |
4288 | |
4289 | If the timeSpec() is Qt::LocalTime or Qt::TimeZone and the resulting |
4290 | date and time fall in the Standard Time to Daylight-Saving Time transition |
4291 | hour then the result will be adjusted accordingly, i.e. if the transition |
4292 | is at 2am and the clock goes forward to 3am and the result falls between |
4293 | 2am and 3am then the result will be adjusted to fall after 3am. |
4294 | |
4295 | \sa daysTo(), addDays(), addYears(), addSecs() |
4296 | */ |
4297 | |
4298 | QDateTime QDateTime::addMonths(int nmonths) const |
4299 | { |
4300 | if (isNull()) |
4301 | return QDateTime(); |
4302 | |
4303 | QDateTime dt(*this); |
4304 | QPair<QDate, QTime> p = getDateTime(d); |
4305 | massageAdjustedDateTime(d&: dt.d, date: p.first.addMonths(nmonths), time: p.second); |
4306 | return dt; |
4307 | } |
4308 | |
4309 | /*! |
4310 | Returns a QDateTime object containing a datetime \a nyears years |
4311 | later than the datetime of this object (or earlier if \a nyears is |
4312 | negative). |
4313 | |
4314 | If the timeSpec() is Qt::LocalTime or Qt::TimeZone and the resulting |
4315 | date and time fall in the Standard Time to Daylight-Saving Time transition |
4316 | hour then the result will be adjusted accordingly, i.e. if the transition |
4317 | is at 2am and the clock goes forward to 3am and the result falls between |
4318 | 2am and 3am then the result will be adjusted to fall after 3am. |
4319 | |
4320 | \sa daysTo(), addDays(), addMonths(), addSecs() |
4321 | */ |
4322 | |
4323 | QDateTime QDateTime::addYears(int nyears) const |
4324 | { |
4325 | if (isNull()) |
4326 | return QDateTime(); |
4327 | |
4328 | QDateTime dt(*this); |
4329 | QPair<QDate, QTime> p = getDateTime(d); |
4330 | massageAdjustedDateTime(d&: dt.d, date: p.first.addYears(nyears), time: p.second); |
4331 | return dt; |
4332 | } |
4333 | |
4334 | /*! |
4335 | Returns a QDateTime object containing a datetime \a s seconds |
4336 | later than the datetime of this object (or earlier if \a s is |
4337 | negative). |
4338 | |
4339 | If this datetime is invalid, an invalid datetime will be returned. |
4340 | |
4341 | \sa addMSecs(), secsTo(), addDays(), addMonths(), addYears() |
4342 | */ |
4343 | |
4344 | QDateTime QDateTime::addSecs(qint64 s) const |
4345 | { |
4346 | qint64 msecs; |
4347 | if (qMulOverflow(v1: s, std::integral_constant<qint64, MSECS_PER_SEC>(), r: &msecs)) |
4348 | return QDateTime(); |
4349 | return addMSecs(msecs); |
4350 | } |
4351 | |
4352 | /*! |
4353 | Returns a QDateTime object containing a datetime \a msecs milliseconds |
4354 | later than the datetime of this object (or earlier if \a msecs is |
4355 | negative). |
4356 | |
4357 | If this datetime is invalid, an invalid datetime will be returned. |
4358 | |
4359 | \sa addSecs(), msecsTo(), addDays(), addMonths(), addYears() |
4360 | */ |
4361 | QDateTime QDateTime::addMSecs(qint64 msecs) const |
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(v1: toMSecsSinceEpoch(), v2: msecs, r: &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(v1: getMSecs(d), v2: msecs, r: &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 | |
4407 | /*! |
4408 | \fn QDateTime QDateTime::addDuration(std::chrono::milliseconds msecs) const |
4409 | |
4410 | \since 6.4 |
4411 | |
4412 | Returns a QDateTime object containing a datetime \a msecs milliseconds |
4413 | later than the datetime of this object (or earlier if \a msecs is |
4414 | negative). |
4415 | |
4416 | If this datetime is invalid, an invalid datetime will be returned. |
4417 | |
4418 | \note Adding durations expressed in \c{std::chrono::months} or |
4419 | \c{std::chrono::years} does not yield the same result obtained by using |
4420 | addMonths() or addYears(). The former are fixed durations, calculated in |
4421 | relation to the solar year; the latter use the Gregorian calendar definitions |
4422 | of months/years. |
4423 | |
4424 | \sa addMSecs(), msecsTo(), addDays(), addMonths(), addYears() |
4425 | */ |
4426 | |
4427 | /*! |
4428 | Returns the number of days from this datetime to the \a other |
4429 | datetime. The number of days is counted as the number of times |
4430 | midnight is reached between this datetime to the \a other |
4431 | datetime. This means that a 10 minute difference from 23:55 to |
4432 | 0:05 the next day counts as one day. |
4433 | |
4434 | If the \a other datetime is earlier than this datetime, |
4435 | the value returned is negative. |
4436 | |
4437 | Example: |
4438 | \snippet code/src_corelib_time_qdatetime.cpp 15 |
4439 | |
4440 | \sa addDays(), secsTo(), msecsTo() |
4441 | */ |
4442 | |
4443 | qint64 QDateTime::daysTo(const QDateTime &other) const |
4444 | { |
4445 | return date().daysTo(d: other.date()); |
4446 | } |
4447 | |
4448 | /*! |
4449 | Returns the number of seconds from this datetime to the \a other |
4450 | datetime. If the \a other datetime is earlier than this datetime, |
4451 | the value returned is negative. |
4452 | |
4453 | Before performing the comparison, the two datetimes are converted |
4454 | to Qt::UTC to ensure that the result is correct if daylight-saving |
4455 | (DST) applies to one of the two datetimes but not the other. |
4456 | |
4457 | Returns 0 if either datetime is invalid. |
4458 | |
4459 | Example: |
4460 | \snippet code/src_corelib_time_qdatetime.cpp 11 |
4461 | |
4462 | \sa addSecs(), daysTo(), QTime::secsTo() |
4463 | */ |
4464 | |
4465 | qint64 QDateTime::secsTo(const QDateTime &other) const |
4466 | { |
4467 | return msecsTo(other) / MSECS_PER_SEC; |
4468 | } |
4469 | |
4470 | /*! |
4471 | Returns the number of milliseconds from this datetime to the \a other |
4472 | datetime. If the \a other datetime is earlier than this datetime, |
4473 | the value returned is negative. |
4474 | |
4475 | Before performing the comparison, the two datetimes are converted |
4476 | to Qt::UTC to ensure that the result is correct if daylight-saving |
4477 | (DST) applies to one of the two datetimes and but not the other. |
4478 | |
4479 | Returns 0 if either datetime is invalid. |
4480 | |
4481 | \sa addMSecs(), daysTo(), QTime::msecsTo() |
4482 | */ |
4483 | |
4484 | qint64 QDateTime::msecsTo(const QDateTime &other) const |
4485 | { |
4486 | if (!isValid() || !other.isValid()) |
4487 | return 0; |
4488 | |
4489 | return other.toMSecsSinceEpoch() - toMSecsSinceEpoch(); |
4490 | } |
4491 | |
4492 | /*! |
4493 | \fn std::chrono::milliseconds QDateTime::operator-(const QDateTime &lhs, const QDateTime &rhs) |
4494 | \since 6.4 |
4495 | |
4496 | Returns the number of milliseconds between \a lhs and \a rhs. |
4497 | If \a lhs is earlier than \a rhs, the result will be negative. |
4498 | |
4499 | Returns 0 if either datetime is invalid. |
4500 | |
4501 | \sa msecsTo() |
4502 | */ |
4503 | |
4504 | /*! |
4505 | \fn QDateTime QDateTime::operator+(const QDateTime &dateTime, std::chrono::milliseconds duration) |
4506 | \fn QDateTime QDateTime::operator+(std::chrono::milliseconds duration, const QDateTime &dateTime) |
4507 | |
4508 | \since 6.4 |
4509 | |
4510 | Returns a QDateTime object containing a datetime \a duration milliseconds |
4511 | later than \a dateTime (or earlier if \a duration is negative). |
4512 | |
4513 | If \a dateTime is invalid, an invalid datetime will be returned. |
4514 | |
4515 | \sa addMSecs() |
4516 | */ |
4517 | |
4518 | /*! |
4519 | \fn QDateTime &QDateTime::operator+=(std::chrono::milliseconds duration) |
4520 | \since 6.4 |
4521 | |
4522 | Modifies this datetime object by adding the given \a duration. |
4523 | The updated object will be later if \a duration is positive, |
4524 | or earlier if it is negative. |
4525 | |
4526 | If this datetime is invalid, this function has no effect. |
4527 | |
4528 | Returns a reference to this datetime object. |
4529 | |
4530 | \sa addMSecs() |
4531 | */ |
4532 | |
4533 | /*! |
4534 | \fn QDateTime QDateTime::operator-(const QDateTime &dateTime, std::chrono::milliseconds duration) |
4535 | |
4536 | \since 6.4 |
4537 | |
4538 | Returns a QDateTime object containing a datetime \a duration milliseconds |
4539 | earlier than \a dateTime (or later if \a duration is negative). |
4540 | |
4541 | If \a dateTime is invalid, an invalid datetime will be returned. |
4542 | |
4543 | \sa addMSecs() |
4544 | */ |
4545 | |
4546 | /*! |
4547 | \fn QDateTime &QDateTime::operator-=(std::chrono::milliseconds duration) |
4548 | \since 6.4 |
4549 | |
4550 | Modifies this datetime object by subtracting the given \a duration. |
4551 | The updated object will be earlier if \a duration is positive, |
4552 | or later if it is negative. |
4553 | |
4554 | If this datetime is invalid, this function has no effect. |
4555 | |
4556 | Returns a reference to this datetime object. |
4557 | |
4558 | \sa addMSecs |
4559 | */ |
4560 | |
4561 | #if QT_DEPRECATED_SINCE(6, 9) |
4562 | /*! |
4563 | \deprecated [6.9] Use \l toTimeZone() instead. |
4564 | |
4565 | Returns a copy of this datetime converted to the given time \a spec. |
4566 | |
4567 | The result represents the same moment in time as, and is equal to, this datetime. |
4568 | |
4569 | If \a spec is Qt::OffsetFromUTC then it is set to Qt::UTC. To set to a fixed |
4570 | offset from UTC, use toTimeZone() or toOffsetFromUtc(). |
4571 | |
4572 | If \a spec is Qt::TimeZone then it is set to Qt::LocalTime, i.e. the local |
4573 | Time Zone. To set a specified time-zone, use toTimeZone(). |
4574 | |
4575 | Example: |
4576 | \snippet code/src_corelib_time_qdatetime.cpp 16 |
4577 | |
4578 | \sa setTimeSpec(), timeSpec(), toTimeZone() |
4579 | */ |
4580 | |
4581 | QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const |
4582 | { |
4583 | return toTimeZone(toZone: asTimeZone(spec, offset: 0, warner: "toTimeSpec" )); |
4584 | } |
4585 | #endif // 6.9 deprecation |
4586 | |
4587 | /*! |
4588 | \since 5.2 |
4589 | |
4590 | Returns a copy of this datetime converted to a spec of Qt::OffsetFromUTC |
4591 | with the given \a offsetSeconds. Equivalent to |
4592 | \c{toTimeZone(QTimeZone::fromSecondsAheadOfUtc(offsetSeconds))}. |
4593 | |
4594 | If the \a offsetSeconds equals 0 then a UTC datetime will be returned. |
4595 | |
4596 | The result represents the same moment in time as, and is equal to, this datetime. |
4597 | |
4598 | \sa setOffsetFromUtc(), offsetFromUtc(), toTimeZone() |
4599 | */ |
4600 | |
4601 | QDateTime QDateTime::toOffsetFromUtc(int offsetSeconds) const |
4602 | { |
4603 | return toTimeZone(toZone: QTimeZone::fromSecondsAheadOfUtc(offset: offsetSeconds)); |
4604 | } |
4605 | |
4606 | /*! |
4607 | Returns a copy of this datetime converted to local time. |
4608 | |
4609 | The result represents the same moment in time as, and is equal to, this datetime. |
4610 | |
4611 | Example: |
4612 | |
4613 | \snippet code/src_corelib_time_qdatetime.cpp 17 |
4614 | |
4615 | \sa toTimeZone(), toUTC(), toOffsetFromUtc() |
4616 | */ |
4617 | QDateTime QDateTime::toLocalTime() const |
4618 | { |
4619 | return toTimeZone(toZone: QTimeZone::LocalTime); |
4620 | } |
4621 | |
4622 | /*! |
4623 | Returns a copy of this datetime converted to UTC. |
4624 | |
4625 | The result represents the same moment in time as, and is equal to, this datetime. |
4626 | |
4627 | Example: |
4628 | |
4629 | \snippet code/src_corelib_time_qdatetime.cpp 18 |
4630 | |
4631 | \sa toTimeZone(), toLocalTime(), toOffsetFromUtc() |
4632 | */ |
4633 | QDateTime QDateTime::toUTC() const |
4634 | { |
4635 | return toTimeZone(toZone: QTimeZone::UTC); |
4636 | } |
4637 | |
4638 | /*! |
4639 | \since 5.2 |
4640 | |
4641 | Returns a copy of this datetime converted to the given \a timeZone. |
4642 | |
4643 | The result represents the same moment in time as, and is equal to, this datetime. |
4644 | |
4645 | The result describes the moment in time in terms of \a timeZone's time |
4646 | representation. For example: |
4647 | |
4648 | \snippet code/src_corelib_time_qdatetime.cpp 23 |
4649 | |
4650 | If \a timeZone is invalid then the datetime will be invalid. Otherwise the |
4651 | returned datetime's timeSpec() will match \c{timeZone.timeSpec()}. |
4652 | |
4653 | \sa timeRepresentation(), toLocalTime(), toUTC(), toOffsetFromUtc() |
4654 | */ |
4655 | |
4656 | QDateTime QDateTime::toTimeZone(const QTimeZone &timeZone) const |
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(msecs: toMSecsSinceEpoch(), timeZone); |
4668 | } |
4669 | |
4670 | /*! |
4671 | \internal |
4672 | Returns \c true if this datetime is equal to the \a other datetime; |
4673 | otherwise returns \c false. |
4674 | |
4675 | \sa precedes(), operator==() |
4676 | */ |
4677 | |
4678 | bool 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(a: d, b: other.d)) |
4686 | return getMSecs(d) == getMSecs(d: other.d); |
4687 | |
4688 | // Convert to UTC and compare |
4689 | return toMSecsSinceEpoch() == other.toMSecsSinceEpoch(); |
4690 | } |
4691 | |
4692 | /*! |
4693 | \fn bool QDateTime::operator==(const QDateTime &lhs, const QDateTime &rhs) |
4694 | |
4695 | Returns \c true if \a lhs is the same as \a rhs; otherwise returns \c false. |
4696 | |
4697 | Two datetimes are different if either the date, the time, or the time zone |
4698 | components are different. Since 5.14, all invalid datetime are equal (and |
4699 | less than all valid datetimes). |
4700 | |
4701 | \sa operator!=(), operator<(), operator<=(), operator>(), operator>=() |
4702 | */ |
4703 | |
4704 | /*! |
4705 | \fn bool QDateTime::operator!=(const QDateTime &lhs, const QDateTime &rhs) |
4706 | |
4707 | Returns \c true if \a lhs is different from \a rhs; otherwise returns \c |
4708 | false. |
4709 | |
4710 | Two datetimes are different if either the date, the time, or the time zone |
4711 | components are different. Since 5.14, all invalid datetime are equal (and |
4712 | less than all valid datetimes). |
4713 | |
4714 | \sa operator==() |
4715 | */ |
4716 | |
4717 | /*! |
4718 | \internal |
4719 | Returns \c true if \a lhs is earlier than the \a rhs |
4720 | datetime; otherwise returns \c false. |
4721 | |
4722 | \sa equals(), operator<() |
4723 | */ |
4724 | |
4725 | bool 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(a: d, b: other.d)) |
4733 | return getMSecs(d) < getMSecs(d: other.d); |
4734 | |
4735 | // Convert to UTC and compare |
4736 | return toMSecsSinceEpoch() < other.toMSecsSinceEpoch(); |
4737 | } |
4738 | |
4739 | /*! |
4740 | \fn bool QDateTime::operator<(const QDateTime &lhs, const QDateTime &rhs) |
4741 | |
4742 | Returns \c true if \a lhs is earlier than \a rhs; |
4743 | otherwise returns \c false. |
4744 | |
4745 | \sa operator==() |
4746 | */ |
4747 | |
4748 | /*! |
4749 | \fn bool QDateTime::operator<=(const QDateTime &lhs, const QDateTime &rhs) |
4750 | |
4751 | Returns \c true if \a lhs is earlier than or equal to \a rhs; otherwise |
4752 | returns \c false. |
4753 | |
4754 | \sa operator==() |
4755 | */ |
4756 | |
4757 | /*! |
4758 | \fn bool QDateTime::operator>(const QDateTime &lhs, const QDateTime &rhs) |
4759 | |
4760 | Returns \c true if \a lhs is later than \a rhs; otherwise returns \c false. |
4761 | |
4762 | \sa operator==() |
4763 | */ |
4764 | |
4765 | /*! |
4766 | \fn bool QDateTime::operator>=(const QDateTime &lhs, const QDateTime &rhs) |
4767 | |
4768 | Returns \c true if \a lhs is later than or equal to \a rhs; |
4769 | otherwise returns \c false. |
4770 | |
4771 | \sa operator==() |
4772 | */ |
4773 | |
4774 | /*! |
4775 | \since 6.5 |
4776 | \fn QDateTime QDateTime::currentDateTime(const QTimeZone &zone) |
4777 | |
4778 | Returns the system clock's current datetime, using the time representation |
4779 | described by \a zone. If \a zone is omitted, local time is used. |
4780 | |
4781 | \sa currentDateTimeUtc(), QDate::currentDate(), QTime::currentTime(), toTimeSpec() |
4782 | */ |
4783 | |
4784 | /*! |
4785 | \overload |
4786 | \since 0.90 |
4787 | */ |
4788 | QDateTime QDateTime::currentDateTime() |
4789 | { |
4790 | return currentDateTime(zone: QTimeZone::LocalTime); |
4791 | } |
4792 | |
4793 | /*! |
4794 | \fn QDateTime QDateTime::currentDateTimeUtc() |
4795 | \since 4.7 |
4796 | Returns the system clock's current datetime, expressed in terms of UTC. |
4797 | |
4798 | Equivalent to \c{currentDateTime(QTimeZone::UTC)}. |
4799 | |
4800 | \sa currentDateTime(), QDate::currentDate(), QTime::currentTime(), toTimeSpec() |
4801 | */ |
4802 | |
4803 | QDateTime QDateTime::currentDateTimeUtc() |
4804 | { |
4805 | return currentDateTime(zone: QTimeZone::UTC); |
4806 | } |
4807 | |
4808 | /*! |
4809 | \fn qint64 QDateTime::currentMSecsSinceEpoch() |
4810 | \since 4.7 |
4811 | |
4812 | Returns the current number of milliseconds since the start, in UTC, of the year 1970. |
4813 | |
4814 | This number is like the POSIX time_t variable, but expressed in milliseconds |
4815 | instead of seconds. |
4816 | |
4817 | \sa currentDateTime(), currentDateTimeUtc(), toTimeSpec() |
4818 | */ |
4819 | |
4820 | /*! |
4821 | \fn qint64 QDateTime::currentSecsSinceEpoch() |
4822 | \since 5.8 |
4823 | |
4824 | Returns the number of seconds since the start, in UTC, of the year 1970. |
4825 | |
4826 | This number is like the POSIX time_t variable. |
4827 | |
4828 | \sa currentMSecsSinceEpoch() |
4829 | */ |
4830 | |
4831 | /*! |
4832 | \fn template <typename Clock, typename Duration> QDateTime QDateTime::fromStdTimePoint(const std::chrono::time_point<Clock, Duration> &time) |
4833 | \since 6.4 |
4834 | |
4835 | Constructs a datetime representing the same point in time as \a time, |
4836 | using Qt::UTC as its specification. |
4837 | |
4838 | The clock of \a time must be compatible with \c{std::chrono::system_clock}, |
4839 | and the duration type must be convertible to \c{std::chrono::milliseconds}. |
4840 | |
4841 | \note This function requires C++20. |
4842 | |
4843 | \sa toStdSysMilliseconds(), fromMSecsSinceEpoch() |
4844 | */ |
4845 | |
4846 | /*! |
4847 | \fn QDateTime QDateTime::fromStdTimePoint(const std::chrono::local_time<std::chrono::milliseconds> &time) |
4848 | \since 6.4 |
4849 | |
4850 | Constructs a datetime whose date and time are the number of milliseconds |
4851 | represented by \a time, counted since 1970-01-01T00:00:00.000 in local |
4852 | time (Qt::LocalTime). |
4853 | |
4854 | \note This function requires C++20. |
4855 | |
4856 | \sa toStdSysMilliseconds(), fromMSecsSinceEpoch() |
4857 | */ |
4858 | |
4859 | /*! |
4860 | \fn QDateTime QDateTime::fromStdLocalTime(const std::chrono::local_time<std::chrono::milliseconds> &time) |
4861 | \since 6.4 |
4862 | |
4863 | Constructs a datetime whose date and time are the number of milliseconds |
4864 | represented by \a time, counted since 1970-01-01T00:00:00.000 in local |
4865 | time (Qt::LocalTime). |
4866 | |
4867 | \note This function requires C++20. |
4868 | |
4869 | \sa toStdSysMilliseconds(), fromMSecsSinceEpoch() |
4870 | */ |
4871 | |
4872 | /*! |
4873 | \fn QDateTime QDateTime::fromStdZonedTime(const std::chrono::zoned_time<std::chrono::milliseconds, const std::chrono::time_zone *> &time); |
4874 | \since 6.4 |
4875 | |
4876 | Constructs a datetime representing the same point in time as \a time. |
4877 | The result will be expressed in \a{time}'s time zone. |
4878 | |
4879 | \note This function requires C++20. |
4880 | |
4881 | \sa QTimeZone |
4882 | |
4883 | \sa toStdSysMilliseconds(), fromMSecsSinceEpoch() |
4884 | */ |
4885 | |
4886 | /*! |
4887 | \fn std::chrono::sys_time<std::chrono::milliseconds> QDateTime::toStdSysMilliseconds() const |
4888 | \since 6.4 |
4889 | |
4890 | Converts this datetime object to the equivalent time point expressed in |
4891 | milliseconds, using \c{std::chrono::system_clock} as a clock. |
4892 | |
4893 | \note This function requires C++20. |
4894 | |
4895 | \sa fromStdTimePoint(), toMSecsSinceEpoch() |
4896 | */ |
4897 | |
4898 | /*! |
4899 | \fn std::chrono::sys_seconds QDateTime::toStdSysSeconds() const |
4900 | \since 6.4 |
4901 | |
4902 | Converts this datetime object to the equivalent time point expressed in |
4903 | seconds, using \c{std::chrono::system_clock} as a clock. |
4904 | |
4905 | \note This function requires C++20. |
4906 | |
4907 | \sa fromStdTimePoint(), toSecsSinceEpoch() |
4908 | */ |
4909 | |
4910 | #if defined(Q_OS_WIN) |
4911 | static 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 | |
4916 | QDate QDate::currentDate() |
4917 | { |
4918 | SYSTEMTIME st = {}; |
4919 | GetLocalTime(&st); |
4920 | return QDate(st.wYear, st.wMonth, st.wDay); |
4921 | } |
4922 | |
4923 | QTime QTime::currentTime() |
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 | |
4932 | QDateTime QDateTime::currentDateTime(const QTimeZone &zone) |
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 | |
4949 | qint64 QDateTime::currentMSecsSinceEpoch() noexcept |
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 | |
4959 | qint64 QDateTime::currentSecsSinceEpoch() noexcept |
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) |
4970 | QDate QDate::currentDate() |
4971 | { |
4972 | return QDateTime::currentDateTime().date(); |
4973 | } |
4974 | |
4975 | QTime QTime::currentTime() |
4976 | { |
4977 | return QDateTime::currentDateTime().time(); |
4978 | } |
4979 | |
4980 | QDateTime QDateTime::currentDateTime(const QTimeZone &zone) |
4981 | { |
4982 | return fromMSecsSinceEpoch(msecs: currentMSecsSinceEpoch(), timeZone: zone); |
4983 | } |
4984 | |
4985 | qint64 QDateTime::currentMSecsSinceEpoch() noexcept |
4986 | { |
4987 | // posix compliant system |
4988 | // we have milliseconds |
4989 | struct timeval tv; |
4990 | gettimeofday(tv: &tv, tz: nullptr); |
4991 | return tv.tv_sec * MSECS_PER_SEC + tv.tv_usec / 1000; |
4992 | } |
4993 | |
4994 | qint64 QDateTime::currentSecsSinceEpoch() noexcept |
4995 | { |
4996 | struct timeval tv; |
4997 | gettimeofday(tv: &tv, tz: 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) |
5005 | /*! |
5006 | \since 5.2 |
5007 | \overload |
5008 | \deprecated [6.9] Pass a \l QTimeZone instead, or omit \a spec and \a offsetSeconds. |
5009 | |
5010 | Returns a datetime representing a moment the given number \a msecs of |
5011 | milliseconds after the start, in UTC, of the year 1970, described as |
5012 | specified by \a spec and \a offsetSeconds. |
5013 | |
5014 | Note that there are possible values for \a msecs that lie outside the valid |
5015 | range of QDateTime, both negative and positive. The behavior of this |
5016 | function is undefined for those values. |
5017 | |
5018 | If the \a spec is not Qt::OffsetFromUTC then the \a offsetSeconds will be |
5019 | ignored. If the \a spec is Qt::OffsetFromUTC and the \a offsetSeconds is 0 |
5020 | then Qt::UTC will be used as the \a spec, since UTC has zero offset. |
5021 | |
5022 | If \a spec is Qt::TimeZone then Qt::LocalTime will be used in its place, |
5023 | equivalent to using the current system time zone (but differently |
5024 | represented). |
5025 | |
5026 | \sa fromSecsSinceEpoch(), toMSecsSinceEpoch(), setMSecsSinceEpoch() |
5027 | */ |
5028 | QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec, int offsetSeconds) |
5029 | { |
5030 | return fromMSecsSinceEpoch(msecs, |
5031 | timeZone: asTimeZone(spec, offset: offsetSeconds, warner: "QDateTime::fromMSecsSinceEpoch" )); |
5032 | } |
5033 | |
5034 | /*! |
5035 | \since 5.8 |
5036 | \overload |
5037 | \deprecated [6.9] Pass a \l QTimeZone instead, or omit \a spec and \a offsetSeconds. |
5038 | |
5039 | Returns a datetime representing a moment the given number \a secs of seconds |
5040 | after the start, in UTC, of the year 1970, described as specified by \a spec |
5041 | and \a offsetSeconds. |
5042 | |
5043 | Note that there are possible values for \a secs that lie outside the valid |
5044 | range of QDateTime, both negative and positive. The behavior of this |
5045 | function is undefined for those values. |
5046 | |
5047 | If the \a spec is not Qt::OffsetFromUTC then the \a offsetSeconds will be |
5048 | ignored. If the \a spec is Qt::OffsetFromUTC and the \a offsetSeconds is 0 |
5049 | then Qt::UTC will be used as the \a spec, since UTC has zero offset. |
5050 | |
5051 | If \a spec is Qt::TimeZone then Qt::LocalTime will be used in its place, |
5052 | equivalent to using the current system time zone (but differently |
5053 | represented). |
5054 | |
5055 | \sa fromMSecsSinceEpoch(), toSecsSinceEpoch(), setSecsSinceEpoch() |
5056 | */ |
5057 | QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec, int offsetSeconds) |
5058 | { |
5059 | return fromSecsSinceEpoch(secs, |
5060 | timeZone: asTimeZone(spec, offset: offsetSeconds, warner: "QDateTime::fromSecsSinceEpoch" )); |
5061 | } |
5062 | #endif // 6.9 deprecations |
5063 | |
5064 | /*! |
5065 | \since 5.2 |
5066 | \overload |
5067 | |
5068 | Returns a datetime representing a moment the given number \a msecs of |
5069 | milliseconds after the start, in UTC, of the year 1970, described as |
5070 | specified by \a timeZone. The default time representation is local time. |
5071 | |
5072 | Note that there are possible values for \a msecs that lie outside the valid |
5073 | range of QDateTime, both negative and positive. The behavior of this |
5074 | function is undefined for those values. |
5075 | |
5076 | \sa fromSecsSinceEpoch(), toMSecsSinceEpoch(), setMSecsSinceEpoch() |
5077 | */ |
5078 | QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone) |
5079 | { |
5080 | QDateTime dt; |
5081 | reviseTimeZone(d&: dt.d, zone: timeZone); |
5082 | if (timeZone.isValid()) |
5083 | dt.setMSecsSinceEpoch(msecs); |
5084 | return dt; |
5085 | } |
5086 | |
5087 | /*! |
5088 | \since 6.5 |
5089 | \overload |
5090 | */ |
5091 | QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs) |
5092 | { |
5093 | return fromMSecsSinceEpoch(msecs, timeZone: QTimeZone::LocalTime); |
5094 | } |
5095 | |
5096 | /*! |
5097 | \since 5.8 |
5098 | \overload |
5099 | |
5100 | Returns a datetime representing a moment the given number \a secs of seconds |
5101 | after the start, in UTC, of the year 1970, described as specified by \a |
5102 | timeZone. The default time representation is local time. |
5103 | |
5104 | Note that there are possible values for \a secs that lie outside the valid |
5105 | range of QDateTime, both negative and positive. The behavior of this |
5106 | function is undefined for those values. |
5107 | |
5108 | \sa fromMSecsSinceEpoch(), toSecsSinceEpoch(), setSecsSinceEpoch() |
5109 | */ |
5110 | QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, const QTimeZone &timeZone) |
5111 | { |
5112 | QDateTime dt; |
5113 | reviseTimeZone(d&: dt.d, zone: timeZone); |
5114 | if (timeZone.isValid()) |
5115 | dt.setSecsSinceEpoch(secs); |
5116 | return dt; |
5117 | } |
5118 | |
5119 | /*! |
5120 | \since 6.5 |
5121 | \overload |
5122 | */ |
5123 | QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs) |
5124 | { |
5125 | return fromSecsSinceEpoch(secs, timeZone: QTimeZone::LocalTime); |
5126 | } |
5127 | |
5128 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
5129 | |
5130 | /*! |
5131 | \fn QDateTime QDateTime::fromString(const QString &string, Qt::DateFormat format) |
5132 | |
5133 | Returns the QDateTime represented by the \a string, using the |
5134 | \a format given, or an invalid datetime if this is not possible. |
5135 | |
5136 | Note for Qt::TextDate: only English short month names (e.g. "Jan" in short |
5137 | form or "January" in long form) are recognized. |
5138 | |
5139 | \sa toString(), QLocale::toDateTime() |
5140 | */ |
5141 | |
5142 | /*! |
5143 | \overload |
5144 | \since 6.0 |
5145 | */ |
5146 | QDateTime 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(s: string); |
5154 | |
5155 | if (!rfc.date.isValid() || !rfc.time.isValid()) |
5156 | return QDateTime(); |
5157 | |
5158 | QDateTime dateTime(rfc.date, rfc.time, QTimeZone::UTC); |
5159 | dateTime.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(offset: rfc.utcOffset)); |
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: string.first(n: 10), format: Qt::ISODate); |
5169 | if (!date.isValid()) |
5170 | return QDateTime(); |
5171 | if (size == 10) |
5172 | return date.startOfDay(); |
5173 | |
5174 | QTimeZone zone = QTimeZone::LocalTime; |
5175 | QStringView isoString = string.sliced(pos: 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(c: u'T', cs: 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(c: u' '))) { |
5184 | return QDateTime(); |
5185 | } |
5186 | isoString = isoString.sliced(pos: 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(c: u'Z', cs: Qt::CaseInsensitive)) { |
5190 | zone = QTimeZone::UTC; |
5191 | isoString.chop(n: 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(offsetString: isoString.sliced(pos: signIndex), valid: &ok); |
5206 | if (!ok) |
5207 | return QDateTime(); |
5208 | isoString = isoString.first(n: signIndex); |
5209 | zone = QTimeZone::fromSecondsAheadOfUtc(offset); |
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(string: isoString, format, isMidnight24: &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(ndays: 1).startOfDay(zone); |
5221 | return QDateTime(date, time, zone); |
5222 | } |
5223 | case Qt::TextDate: { |
5224 | QVarLengthArray<QStringView, 6> parts; |
5225 | |
5226 | auto tokens = string.tokenize(needle: u' ', flags: Qt::SkipEmptyParts); |
5227 | auto it = tokens.begin(); |
5228 | for (int i = 0; i < 6 && it != tokens.end(); ++i, ++it) |
5229 | parts.emplace_back(args: *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(idx: 3).contains(c: u':')) |
5241 | yearPart = 4; |
5242 | else if (parts.at(idx: 4).contains(c: u':')) |
5243 | timePart = 4; |
5244 | else |
5245 | return QDateTime(); |
5246 | |
5247 | bool ok = false; |
5248 | int day = parts.at(idx: 2).toInt(ok: &ok); |
5249 | int year = ok ? parts.at(idx: yearPart).toInt(ok: &ok) : 0; |
5250 | int month = fromShortMonthName(monthName: parts.at(idx: 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(string: parts.at(idx: timePart), format, isMidnight24: 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(idx: 5); |
5266 | if (tz.startsWith(s: "UTC"_L1 ) |
5267 | // GMT has long been deprecated as an alias for UTC. |
5268 | || tz.startsWith(s: "GMT"_L1 , cs: Qt::CaseInsensitive)) { |
5269 | tz = tz.sliced(pos: 3); |
5270 | if (tz.isEmpty()) |
5271 | return QDateTime(date, time, QTimeZone::UTC); |
5272 | |
5273 | int offset = fromOffsetString(offsetString: tz, valid: &ok); |
5274 | return ok ? QDateTime(date, time, QTimeZone::fromSecondsAheadOfUtc(offset)) |
5275 | : QDateTime(); |
5276 | } |
5277 | return QDateTime(); |
5278 | } |
5279 | } |
5280 | |
5281 | return QDateTime(); |
5282 | } |
5283 | |
5284 | /*! |
5285 | \fn QDateTime QDateTime::fromString(const QString &string, const QString &format, QCalendar cal) |
5286 | |
5287 | Returns the QDateTime represented by the \a string, using the \a |
5288 | format given, or an invalid datetime if the string cannot be parsed. |
5289 | |
5290 | Uses the calendar \a cal if supplied, else Gregorian. |
5291 | |
5292 | In addition to the expressions, recognized in the format string to represent |
5293 | parts of the date and time, by QDate::fromString() and QTime::fromString(), |
5294 | this method supports: |
5295 | |
5296 | \table |
5297 | \header \li Expression \li Output |
5298 | \row \li t |
5299 | \li the timezone (offset, name, "Z" or offset with "UTC" prefix) |
5300 | \row \li tt |
5301 | \li the timezone in offset format with no colon between hours and |
5302 | minutes (for example "+0200") |
5303 | \row \li ttt |
5304 | \li the timezone in offset format with a colon between hours and |
5305 | minutes (for example "+02:00") |
5306 | \row \li tttt |
5307 | \li the timezone name (for example "Europe/Berlin"). The name |
5308 | recognized are those known to \l QTimeZone, which may depend on the |
5309 | operating system in use. |
5310 | \endtable |
5311 | |
5312 | If no 't' format specifier is present, the system's local time-zone is used. |
5313 | For the defaults of all other fields, see QDate::fromString() and QTime::fromString(). |
5314 | |
5315 | For example: |
5316 | |
5317 | \snippet code/src_corelib_time_qdatetime.cpp 14 |
5318 | |
5319 | All other input characters will be treated as text. Any non-empty sequence |
5320 | of characters enclosed in single quotes will also be treated (stripped of |
5321 | the quotes) as text and not be interpreted as expressions. |
5322 | |
5323 | \snippet code/src_corelib_time_qdatetime.cpp 12 |
5324 | |
5325 | If the format is not satisfied, an invalid QDateTime is returned. If the |
5326 | format is satisfied but \a string represents an invalid datetime (e.g. in a |
5327 | gap skipped by a time-zone transition), an invalid QDateTime is returned, |
5328 | whose toMSecsSinceEpoch() represents a near-by datetime that is |
5329 | valid. Passing that to fromMSecsSinceEpoch() will produce a valid datetime |
5330 | that isn't faithfully represented by the string parsed. |
5331 | |
5332 | The expressions that don't have leading zeroes (d, M, h, m, s, z) will be |
5333 | greedy. This means that they will use two digits (or three, for z) even if this will |
5334 | put them outside the range and/or leave too few digits for other |
5335 | sections. |
5336 | |
5337 | \snippet code/src_corelib_time_qdatetime.cpp 13 |
5338 | |
5339 | This could have meant 1 January 00:30.00 but the M will grab |
5340 | two digits. |
5341 | |
5342 | Incorrectly specified fields of the \a string will cause an invalid |
5343 | QDateTime to be returned. For example, consider the following code, |
5344 | where the two digit year 12 is read as 1912 (see the table below for all |
5345 | field defaults); the resulting datetime is invalid because 23 April 1912 |
5346 | was a Tuesday, not a Monday: |
5347 | |
5348 | \snippet code/src_corelib_time_qdatetime.cpp 20 |
5349 | |
5350 | The correct code is: |
5351 | |
5352 | \snippet code/src_corelib_time_qdatetime.cpp 21 |
5353 | |
5354 | \note Day and month names as well as AM/PM indicators must be given in |
5355 | English (C locale). If localized month and day names or localized forms of |
5356 | AM/PM are to be recognized, use QLocale::system().toDateTime(). |
5357 | |
5358 | \note If a format character is repeated more times than the longest |
5359 | expression in the table above using it, this part of the format will be read |
5360 | as several expressions with no separator between them; the longest above, |
5361 | possibly repeated as many times as there are copies of it, ending with a |
5362 | residue that may be a shorter expression. Thus \c{'tttttt'} would match |
5363 | \c{"Europe/BerlinEurope/Berlin"} and set the zone to Berlin time; if the |
5364 | datetime string contained "Europe/BerlinZ" it would "match" but produce an |
5365 | inconsistent result, leading to an invalid datetime. |
5366 | |
5367 | \sa toString(), QDate::fromString(), QTime::fromString(), |
5368 | QLocale::toDateTime() |
5369 | */ |
5370 | |
5371 | /*! |
5372 | \fn QDateTime QDateTime::fromString(QStringView string, QStringView format, QCalendar cal) |
5373 | \overload |
5374 | \since 6.0 |
5375 | */ |
5376 | |
5377 | /*! |
5378 | \overload |
5379 | \since 6.0 |
5380 | */ |
5381 | QDateTime 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(text: string, datetime: &datetime) || !datetime.isValid())) |
5389 | return datetime; |
5390 | #else |
5391 | Q_UNUSED(string); |
5392 | Q_UNUSED(format); |
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 |
5405 | /*! |
5406 | \relates QDate |
5407 | |
5408 | Writes the \a date to stream \a out. |
5409 | |
5410 | \sa {Serializing Qt Data Types} |
5411 | */ |
5412 | |
5413 | QDataStream &operator<<(QDataStream &out, QDate date) |
5414 | { |
5415 | if (out.version() < QDataStream::Qt_5_0) |
5416 | return out << quint32(date.jd); |
5417 | else |
5418 | return out << date.jd; |
5419 | } |
5420 | |
5421 | /*! |
5422 | \relates QDate |
5423 | |
5424 | Reads a date from stream \a in into the \a date. |
5425 | |
5426 | \sa {Serializing Qt Data Types} |
5427 | */ |
5428 | |
5429 | QDataStream &operator>>(QDataStream &in, QDate &date) |
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 | |
5443 | /*! |
5444 | \relates QTime |
5445 | |
5446 | Writes \a time to stream \a out. |
5447 | |
5448 | \sa {Serializing Qt Data Types} |
5449 | */ |
5450 | |
5451 | QDataStream &operator<<(QDataStream &out, QTime time) |
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 | |
5461 | /*! |
5462 | \relates QTime |
5463 | |
5464 | Reads a time from stream \a in into the given \a time. |
5465 | |
5466 | \sa {Serializing Qt Data Types} |
5467 | */ |
5468 | |
5469 | QDataStream &operator>>(QDataStream &in, QTime &time) |
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 | |
5482 | /*! |
5483 | \relates QDateTime |
5484 | |
5485 | Writes \a dateTime to the \a out stream. |
5486 | |
5487 | \sa {Serializing Qt Data Types} |
5488 | */ |
5489 | QDataStream &operator<<(QDataStream &out, const QDateTime &dateTime) |
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(d: dateTime.d); |
5498 | out << dateAndTime << qint8(dateTime.timeSpec()); |
5499 | if (dateTime.timeSpec() == Qt::OffsetFromUTC) |
5500 | out << qint32(dateTime.offsetFromUtc()); |
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(d: (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(d: dateTime.d); |
5519 | out << dateAndTime; |
5520 | switch (dateTime.timeSpec()) { |
5521 | case Qt::UTC: |
5522 | out << (qint8)QDateTimePrivate::UTC; |
5523 | break; |
5524 | case Qt::OffsetFromUTC: |
5525 | out << (qint8)QDateTimePrivate::OffsetFromUTC; |
5526 | break; |
5527 | case Qt::TimeZone: |
5528 | out << (qint8)QDateTimePrivate::TimeZone; |
5529 | break; |
5530 | case Qt::LocalTime: |
5531 | out << (qint8)QDateTimePrivate::LocalUnknown; |
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(d: dateTime.d); |
5539 | out << dateAndTime; |
5540 | |
5541 | } |
5542 | |
5543 | return out; |
5544 | } |
5545 | |
5546 | /*! |
5547 | \relates QDateTime |
5548 | |
5549 | Reads a datetime from the stream \a in into \a dateTime. |
5550 | |
5551 | \sa {Serializing Qt Data Types} |
5552 | */ |
5553 | |
5554 | QDataStream &operator>>(QDataStream &in, QDateTime &dateTime) |
5555 | { |
5556 | QDate dt; |
5557 | QTime tm; |
5558 | qint8 ts = 0; |
5559 | QTimeZone zone(QTimeZone::LocalTime); |
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; |
5572 | zone = QTimeZone::fromSecondsAheadOfUtc(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; |
5587 | dateTime = QDateTime(dt, tm, QTimeZone::UTC); |
5588 | if (static_cast<Qt::TimeSpec>(ts) == Qt::LocalTime) |
5589 | dateTime = dateTime.toTimeZone(timeZone: zone); |
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. |
5597 | case QDateTimePrivate::UTC: |
5598 | zone = QTimeZone::UTC; |
5599 | break; |
5600 | case QDateTimePrivate::TimeZone: // No zone was stored, so treat as LocalTime: |
5601 | case QDateTimePrivate::LocalUnknown: |
5602 | case QDateTimePrivate::LocalStandard: |
5603 | case QDateTimePrivate::LocalDST: |
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) |
5625 | QDebug operator<<(QDebug dbg, QDate date) |
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(format: Qt::ISODate); |
5633 | else |
5634 | dbg.nospace() << date.toString(format: Qt::TextDate); |
5635 | else |
5636 | dbg.nospace() << "Invalid" ; |
5637 | dbg.nospace() << ')'; |
5638 | return dbg; |
5639 | } |
5640 | |
5641 | QDebug operator<<(QDebug dbg, QTime time) |
5642 | { |
5643 | QDebugStateSaver saver(dbg); |
5644 | dbg.nospace() << "QTime(" ; |
5645 | if (time.isValid()) |
5646 | dbg.nospace() << time.toString(format: u"HH:mm:ss.zzz" ); |
5647 | else |
5648 | dbg.nospace() << "Invalid" ; |
5649 | dbg.nospace() << ')'; |
5650 | return dbg; |
5651 | } |
5652 | |
5653 | QDebug operator<<(QDebug dbg, const QDateTime &date) |
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(format: 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 | |
5682 | /*! \fn size_t qHash(const QDateTime &key, size_t seed = 0) |
5683 | \relates QHash |
5684 | \since 5.0 |
5685 | |
5686 | Returns the hash value for the \a key, using \a seed to seed the calculation. |
5687 | */ |
5688 | size_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: key.toMSecsSinceEpoch(), seed) : seed; |
5695 | } |
5696 | |
5697 | /*! \fn size_t qHash(QDate key, size_t seed = 0) |
5698 | \relates QHash |
5699 | \since 5.0 |
5700 | |
5701 | Returns the hash value for the \a key, using \a seed to seed the calculation. |
5702 | */ |
5703 | size_t qHash(QDate key, size_t seed) noexcept |
5704 | { |
5705 | return qHash(key: key.toJulianDay(), seed); |
5706 | } |
5707 | |
5708 | /*! \fn size_t qHash(QTime key, size_t seed = 0) |
5709 | \relates QHash |
5710 | \since 5.0 |
5711 | |
5712 | Returns the hash value for the \a key, using \a seed to seed the calculation. |
5713 | */ |
5714 | size_t qHash(QTime key, size_t seed) noexcept |
5715 | { |
5716 | return qHash(key: key.msecsSinceStartOfDay(), seed); |
5717 | } |
5718 | |
5719 | QT_END_NAMESPACE |
5720 | |