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 = 0; |
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 qsizetype size = offsetString.size(); |
301 | if (size < 2 || size > 6) |
302 | return 0; |
303 | |
304 | // sign will be +1 for a positive and -1 for a negative offset |
305 | int sign; |
306 | |
307 | // First char must be + or - |
308 | const QChar signChar = offsetString[0]; |
309 | if (signChar == u'+') |
310 | sign = 1; |
311 | else if (signChar == u'-') |
312 | sign = -1; |
313 | else |
314 | return 0; |
315 | |
316 | // Split the hour and minute parts |
317 | const QStringView time = offsetString.sliced(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 | \compares strong |
352 | \compareswith strong std::chrono::year_month_day std::chrono::year_month_day_last \ |
353 | std::chrono::year_month_weekday std::chrono::year_month_weekday_last |
354 | These comparison operators are only available when using C++20. |
355 | \endcompareswith |
356 | |
357 | A QDate object represents a particular day, regardless of calendar, locale |
358 | or other settings used when creating it or supplied by the system. It can |
359 | report the year, month and day of the month that represent the day with |
360 | respect to the proleptic Gregorian calendar or any calendar supplied as a |
361 | QCalendar object. QDate objects should be passed by value rather than by |
362 | reference to const; they simply package \c qint64. |
363 | |
364 | A QDate object is typically created by giving the year, month, and day |
365 | numbers explicitly. Note that QDate interprets year numbers less than 100 as |
366 | presented, i.e., as years 1 through 99, without adding any offset. The |
367 | static function currentDate() creates a QDate object containing the date |
368 | read from the system clock. An explicit date can also be set using |
369 | setDate(). The fromString() function returns a QDate given a string and a |
370 | date format which is used to interpret the date within the string. |
371 | |
372 | The year(), month(), and day() functions provide access to the year, month, |
373 | and day numbers. When more than one of these values is needed, it is more |
374 | efficient to call QCalendar::partsFromDate(), to save repeating (potentially |
375 | expensive) calendrical calculations. |
376 | |
377 | Also, dayOfWeek() and dayOfYear() functions are provided. The same |
378 | information is provided in textual format by toString(). QLocale can map the |
379 | day numbers to names, QCalendar can map month numbers to names. |
380 | |
381 | QDate provides a full set of operators to compare two QDate |
382 | objects where smaller means earlier, and larger means later. |
383 | |
384 | You can increment (or decrement) a date by a given number of days |
385 | using addDays(). Similarly you can use addMonths() and addYears(). |
386 | The daysTo() function returns the number of days between two |
387 | dates. |
388 | |
389 | The daysInMonth() and daysInYear() functions return how many days there are |
390 | in this date's month and year, respectively. The isLeapYear() function |
391 | indicates whether a date is in a leap year. QCalendar can also supply this |
392 | information, in some cases more conveniently. |
393 | |
394 | \section1 Remarks |
395 | |
396 | \note All conversion to and from string formats is done using the C locale. |
397 | For localized conversions, see QLocale. |
398 | |
399 | In the Gregorian calendar, there is no year 0. Dates in that year are |
400 | considered invalid. The year -1 is the year "1 before Christ" or "1 before |
401 | common era." The day before 1 January 1 CE, QDate(1, 1, 1), is 31 December |
402 | 1 BCE, QDate(-1, 12, 31). Various other calendars behave similarly; see |
403 | QCalendar::hasYearZero(). |
404 | |
405 | \section2 Range of Valid Dates |
406 | |
407 | Dates are stored internally as a Julian Day number, an integer count of |
408 | every day in a contiguous range, with 24 November 4714 BCE in the Gregorian |
409 | calendar being Julian Day 0 (1 January 4713 BCE in the Julian calendar). |
410 | As well as being an efficient and accurate way of storing an absolute date, |
411 | it is suitable for converting a date into other calendar systems such as |
412 | Hebrew, Islamic or Chinese. The Julian Day number can be obtained using |
413 | QDate::toJulianDay() and can be set using QDate::fromJulianDay(). |
414 | |
415 | The range of Julian Day numbers that QDate can represent is, for technical |
416 | reasons, limited to between -784350574879 and 784354017364, which means from |
417 | before 2 billion BCE to after 2 billion CE. This is more than seven times as |
418 | wide as the range of dates a QDateTime can represent. |
419 | |
420 | \sa QTime, QDateTime, QCalendar, QDateTime::YearRange, QDateEdit, QDateTimeEdit, QCalendarWidget |
421 | */ |
422 | |
423 | /*! |
424 | \fn QDate::QDate() |
425 | |
426 | Constructs a null date. Null dates are invalid. |
427 | |
428 | \sa isNull(), isValid() |
429 | */ |
430 | |
431 | /*! |
432 | Constructs a date with year \a y, month \a m and day \a d. |
433 | |
434 | The date is understood in terms of the Gregorian calendar. If the specified |
435 | date is invalid, the date is not set and isValid() returns \c false. |
436 | |
437 | \warning Years 1 to 99 are interpreted as is. Year 0 is invalid. |
438 | |
439 | \sa isValid(), QCalendar::dateFromParts() |
440 | */ |
441 | |
442 | QDate::QDate(int y, int m, int d) |
443 | { |
444 | static_assert(maxJd() == JulianDayMax); |
445 | static_assert(minJd() == JulianDayMin); |
446 | jd = QGregorianCalendar::julianFromParts(year: y, month: m, day: d).value_or(u: nullJd()); |
447 | } |
448 | |
449 | QDate::QDate(int y, int m, int d, QCalendar cal) |
450 | { |
451 | *this = cal.dateFromParts(year: y, month: m, day: d); |
452 | } |
453 | |
454 | /*! |
455 | \fn QDate::QDate(std::chrono::year_month_day date) |
456 | \fn QDate::QDate(std::chrono::year_month_day_last date) |
457 | \fn QDate::QDate(std::chrono::year_month_weekday date) |
458 | \fn QDate::QDate(std::chrono::year_month_weekday_last date) |
459 | |
460 | \since 6.4 |
461 | |
462 | Constructs a QDate representing the same date as \a date. This allows for |
463 | easy interoperability between the Standard Library calendaring classes and |
464 | Qt datetime classes. |
465 | |
466 | For example: |
467 | |
468 | \snippet code/src_corelib_time_qdatetime.cpp 22 |
469 | |
470 | \note Unlike QDate, std::chrono::year and the related classes feature the |
471 | year zero. This means that if \a date is in the year zero or before, the |
472 | resulting QDate object will have an year one less than the one specified by |
473 | \a date. |
474 | |
475 | \note This function requires C++20. |
476 | */ |
477 | |
478 | /*! |
479 | \fn QDate QDate::fromStdSysDays(const std::chrono::sys_days &days) |
480 | \since 6.4 |
481 | |
482 | Returns a QDate \a days days after January 1st, 1970 (the UNIX epoch). If |
483 | \a days is negative, the returned date will be before the epoch. |
484 | |
485 | \note This function requires C++20. |
486 | |
487 | \sa toStdSysDays() |
488 | */ |
489 | |
490 | /*! |
491 | \fn std::chrono::sys_days QDate::toStdSysDays() const |
492 | |
493 | Returns the number of days between January 1st, 1970 (the UNIX epoch) and |
494 | this date, represented as a \c{std::chrono::sys_days} object. If this date |
495 | is before the epoch, the number of days will be negative. |
496 | |
497 | \note This function requires C++20. |
498 | |
499 | \sa fromStdSysDays(), daysTo() |
500 | */ |
501 | |
502 | /*! |
503 | \fn bool QDate::isNull() const |
504 | |
505 | Returns \c true if the date is null; otherwise returns \c false. A null |
506 | date is invalid. |
507 | |
508 | \note The behavior of this function is equivalent to isValid(). |
509 | |
510 | \sa isValid() |
511 | */ |
512 | |
513 | /*! |
514 | \fn bool QDate::isValid() const |
515 | |
516 | Returns \c true if this date is valid; otherwise returns \c false. |
517 | |
518 | \sa isNull(), QCalendar::isDateValid() |
519 | */ |
520 | |
521 | /*! |
522 | Returns the year of this date. |
523 | |
524 | Uses \a cal as calendar, if supplied, else the Gregorian calendar. |
525 | |
526 | Returns 0 if the date is invalid. For some calendars, dates before their |
527 | first year may all be invalid. |
528 | |
529 | If using a calendar which has a year 0, check using isValid() if the return |
530 | is 0. Such calendars use negative year numbers in the obvious way, with |
531 | year 1 preceded by year 0, in turn preceded by year -1 and so on. |
532 | |
533 | Some calendars, despite having no year 0, have a conventional numbering of |
534 | the years before their first year, counting backwards from 1. For example, |
535 | in the proleptic Gregorian calendar, successive years before 1 CE (the first |
536 | year) are identified as 1 BCE, 2 BCE, 3 BCE and so on. For such calendars, |
537 | negative year numbers are used to indicate these years before year 1, with |
538 | -1 indicating the year before 1. |
539 | |
540 | \sa month(), day(), QCalendar::hasYearZero(), QCalendar::isProleptic(), QCalendar::partsFromDate() |
541 | */ |
542 | |
543 | int QDate::year(QCalendar cal) const |
544 | { |
545 | if (isValid()) { |
546 | const auto parts = cal.partsFromDate(date: *this); |
547 | if (parts.isValid()) |
548 | return parts.year; |
549 | } |
550 | return 0; |
551 | } |
552 | |
553 | /*! |
554 | \overload |
555 | */ |
556 | |
557 | int QDate::year() const |
558 | { |
559 | if (isValid()) { |
560 | const auto parts = QGregorianCalendar::partsFromJulian(jd); |
561 | if (parts.isValid()) |
562 | return parts.year; |
563 | } |
564 | return 0; |
565 | } |
566 | |
567 | /*! |
568 | Returns the month-number for the date. |
569 | |
570 | Numbers the months of the year starting with 1 for the first. Uses \a cal |
571 | as calendar if supplied, else the Gregorian calendar, for which the month |
572 | numbering is as follows: |
573 | |
574 | \list |
575 | \li 1 = "January" |
576 | \li 2 = "February" |
577 | \li 3 = "March" |
578 | \li 4 = "April" |
579 | \li 5 = "May" |
580 | \li 6 = "June" |
581 | \li 7 = "July" |
582 | \li 8 = "August" |
583 | \li 9 = "September" |
584 | \li 10 = "October" |
585 | \li 11 = "November" |
586 | \li 12 = "December" |
587 | \endlist |
588 | |
589 | Returns 0 if the date is invalid. Note that some calendars may have more |
590 | than 12 months in some years. |
591 | |
592 | \sa year(), day(), QCalendar::partsFromDate() |
593 | */ |
594 | |
595 | int QDate::month(QCalendar cal) const |
596 | { |
597 | if (isValid()) { |
598 | const auto parts = cal.partsFromDate(date: *this); |
599 | if (parts.isValid()) |
600 | return parts.month; |
601 | } |
602 | return 0; |
603 | } |
604 | |
605 | /*! |
606 | \overload |
607 | */ |
608 | |
609 | int QDate::month() const |
610 | { |
611 | if (isValid()) { |
612 | const auto parts = QGregorianCalendar::partsFromJulian(jd); |
613 | if (parts.isValid()) |
614 | return parts.month; |
615 | } |
616 | return 0; |
617 | } |
618 | |
619 | /*! |
620 | Returns the day of the month for this date. |
621 | |
622 | Uses \a cal as calendar if supplied, else the Gregorian calendar (for which |
623 | the return ranges from 1 to 31). Returns 0 if the date is invalid. |
624 | |
625 | \sa year(), month(), dayOfWeek(), QCalendar::partsFromDate() |
626 | */ |
627 | |
628 | int QDate::day(QCalendar cal) const |
629 | { |
630 | if (isValid()) { |
631 | const auto parts = cal.partsFromDate(date: *this); |
632 | if (parts.isValid()) |
633 | return parts.day; |
634 | } |
635 | return 0; |
636 | } |
637 | |
638 | /*! |
639 | \overload |
640 | */ |
641 | |
642 | int QDate::day() const |
643 | { |
644 | if (isValid()) { |
645 | const auto parts = QGregorianCalendar::partsFromJulian(jd); |
646 | if (parts.isValid()) |
647 | return parts.day; |
648 | } |
649 | return 0; |
650 | } |
651 | |
652 | /*! |
653 | Returns the weekday (1 = Monday to 7 = Sunday) for this date. |
654 | |
655 | Uses \a cal as calendar if supplied, else the Gregorian calendar. Returns 0 |
656 | if the date is invalid. Some calendars may give special meaning |
657 | (e.g. intercallary days) to values greater than 7. |
658 | |
659 | \sa day(), dayOfYear(), QCalendar::dayOfWeek(), Qt::DayOfWeek |
660 | */ |
661 | |
662 | int QDate::dayOfWeek(QCalendar cal) const |
663 | { |
664 | if (isNull()) |
665 | return 0; |
666 | |
667 | return cal.dayOfWeek(date: *this); |
668 | } |
669 | |
670 | /*! |
671 | \overload |
672 | */ |
673 | |
674 | int QDate::dayOfWeek() const |
675 | { |
676 | return isValid() ? QGregorianCalendar::weekDayOfJulian(jd) : 0; |
677 | } |
678 | |
679 | /*! |
680 | Returns the day of the year (1 for the first day) for this date. |
681 | |
682 | Uses \a cal as calendar if supplied, else the Gregorian calendar. |
683 | Returns 0 if either the date or the first day of its year is invalid. |
684 | |
685 | \sa day(), dayOfWeek(), QCalendar::daysInYear() |
686 | */ |
687 | |
688 | int QDate::dayOfYear(QCalendar cal) const |
689 | { |
690 | if (isValid()) { |
691 | QDate firstDay = cal.dateFromParts(year: year(cal), month: 1, day: 1); |
692 | if (firstDay.isValid()) |
693 | return firstDay.daysTo(d: *this) + 1; |
694 | } |
695 | return 0; |
696 | } |
697 | |
698 | /*! |
699 | \overload |
700 | */ |
701 | |
702 | int QDate::dayOfYear() const |
703 | { |
704 | if (isValid()) { |
705 | if (const auto first = QGregorianCalendar::julianFromParts(year: year(), month: 1, day: 1)) |
706 | return jd - *first + 1; |
707 | } |
708 | return 0; |
709 | } |
710 | |
711 | /*! |
712 | Returns the number of days in the month for this date. |
713 | |
714 | Uses \a cal as calendar if supplied, else the Gregorian calendar (for which |
715 | the result ranges from 28 to 31). Returns 0 if the date is invalid. |
716 | |
717 | \sa day(), daysInYear(), QCalendar::daysInMonth(), |
718 | QCalendar::maximumDaysInMonth(), QCalendar::minimumDaysInMonth() |
719 | */ |
720 | |
721 | int QDate::daysInMonth(QCalendar cal) const |
722 | { |
723 | if (isValid()) { |
724 | const auto parts = cal.partsFromDate(date: *this); |
725 | if (parts.isValid()) |
726 | return cal.daysInMonth(month: parts.month, year: parts.year); |
727 | } |
728 | return 0; |
729 | } |
730 | |
731 | /*! |
732 | \overload |
733 | */ |
734 | |
735 | int QDate::daysInMonth() const |
736 | { |
737 | if (isValid()) { |
738 | const auto parts = QGregorianCalendar::partsFromJulian(jd); |
739 | if (parts.isValid()) |
740 | return QGregorianCalendar::monthLength(month: parts.month, year: parts.year); |
741 | } |
742 | return 0; |
743 | } |
744 | |
745 | /*! |
746 | Returns the number of days in the year for this date. |
747 | |
748 | Uses \a cal as calendar if supplied, else the Gregorian calendar (for which |
749 | the result is 365 or 366). Returns 0 if the date is invalid. |
750 | |
751 | \sa day(), daysInMonth(), QCalendar::daysInYear(), QCalendar::maximumMonthsInYear() |
752 | */ |
753 | |
754 | int QDate::daysInYear(QCalendar cal) const |
755 | { |
756 | if (isNull()) |
757 | return 0; |
758 | |
759 | return cal.daysInYear(year: year(cal)); |
760 | } |
761 | |
762 | /*! |
763 | \overload |
764 | */ |
765 | |
766 | int QDate::daysInYear() const |
767 | { |
768 | return isValid() ? QGregorianCalendar::leapTest(year: year()) ? 366 : 365 : 0; |
769 | } |
770 | |
771 | /*! |
772 | Returns the ISO 8601 week number (1 to 53). |
773 | |
774 | Returns 0 if the date is invalid. Otherwise, returns the week number for the |
775 | date. If \a yearNumber is not \nullptr (its default), stores the year as |
776 | *\a{yearNumber}. |
777 | |
778 | In accordance with ISO 8601, each week falls in the year to which most of |
779 | its days belong, in the Gregorian calendar. As ISO 8601's week starts on |
780 | Monday, this is the year in which the week's Thursday falls. Most years have |
781 | 52 weeks, but some have 53. |
782 | |
783 | \note *\a{yearNumber} is not always the same as year(). For example, 1 |
784 | January 2000 has week number 52 in the year 1999, and 31 December |
785 | 2002 has week number 1 in the year 2003. |
786 | |
787 | \sa isValid() |
788 | */ |
789 | |
790 | int QDate::weekNumber(int *yearNumber) const |
791 | { |
792 | if (!isValid()) |
793 | return 0; |
794 | |
795 | // This could be replaced by use of QIso8601Calendar, once we implement it. |
796 | // The Thursday of the same week determines our answer: |
797 | const QDate thursday(addDays(days: 4 - dayOfWeek())); |
798 | if (yearNumber) |
799 | *yearNumber = thursday.year(); |
800 | |
801 | // Week n's Thurs's DOY has 1 <= DOY - 7*(n-1) < 8, so 0 <= DOY + 6 - 7*n < 7: |
802 | return (thursday.dayOfYear() + 6) / 7; |
803 | } |
804 | |
805 | #if QT_DEPRECATED_SINCE(6, 9) |
806 | // Only called by deprecated methods (so bootstrap builds warn unused without this #if). |
807 | static QTimeZone asTimeZone(Qt::TimeSpec spec, int offset, const char *warner) |
808 | { |
809 | if (warner) { |
810 | switch (spec) { |
811 | case Qt::TimeZone: |
812 | qWarning(msg: "%s: Pass a QTimeZone instead of Qt::TimeZone." , warner); |
813 | break; |
814 | case Qt::LocalTime: |
815 | if (offset) { |
816 | qWarning(msg: "%s: Ignoring offset (%d seconds) passed with Qt::LocalTime" , |
817 | warner, offset); |
818 | } |
819 | break; |
820 | case Qt::UTC: |
821 | if (offset) { |
822 | qWarning(msg: "%s: Ignoring offset (%d seconds) passed with Qt::UTC" , |
823 | warner, offset); |
824 | offset = 0; |
825 | } |
826 | break; |
827 | case Qt::OffsetFromUTC: |
828 | break; |
829 | } |
830 | } |
831 | return QTimeZone::isUtcOrFixedOffset(spec) |
832 | ? QTimeZone::fromSecondsAheadOfUtc(offset) |
833 | : QTimeZone(QTimeZone::LocalTime); |
834 | } |
835 | #endif // Helper for 6.9 deprecation |
836 | |
837 | enum class DaySide { Start, End }; |
838 | |
839 | static bool inDateTimeRange(qint64 jd, DaySide side) |
840 | { |
841 | using Bounds = std::numeric_limits<qint64>; |
842 | if (jd < Bounds::min() + JULIAN_DAY_FOR_EPOCH) |
843 | return false; |
844 | jd -= JULIAN_DAY_FOR_EPOCH; |
845 | const qint64 maxDay = Bounds::max() / MSECS_PER_DAY; |
846 | const qint64 minDay = Bounds::min() / MSECS_PER_DAY - 1; |
847 | // (Divisions rounded towards zero, as MSECS_PER_DAY is even - so doesn't |
848 | // divide max() - and has factors other than two, so doesn't divide min().) |
849 | // Range includes start of last day and end of first: |
850 | switch (side) { |
851 | case DaySide::Start: |
852 | return jd > minDay && jd <= maxDay; |
853 | case DaySide::End: |
854 | return jd >= minDay && jd < maxDay; |
855 | } |
856 | Q_UNREACHABLE_RETURN(false); |
857 | } |
858 | |
859 | static QDateTime toEarliest(QDate day, const QTimeZone &zone) |
860 | { |
861 | Q_ASSERT(!zone.isUtcOrFixedOffset()); |
862 | // And the day starts in a gap. First find a moment not in that gap. |
863 | const auto moment = [=](QTime time) { |
864 | return QDateTime(day, time, zone, QDateTime::TransitionResolution::Reject); |
865 | }; |
866 | // Longest routine time-zone transition is 2 hours: |
867 | QDateTime when = moment(QTime(2, 0)); |
868 | if (!when.isValid()) { |
869 | // Noon should be safe ... |
870 | when = moment(QTime(12, 0)); |
871 | if (!when.isValid()) { |
872 | // ... unless it's a 24-hour jump (moving the date-line) |
873 | when = moment(QTime(23, 59, 59, 999)); |
874 | if (!when.isValid()) |
875 | return QDateTime(); |
876 | } |
877 | } |
878 | int high = when.time().msecsSinceStartOfDay() / 60000; |
879 | int low = 0; |
880 | // Binary chop to the right minute |
881 | while (high > low + 1) { |
882 | const int mid = (high + low) / 2; |
883 | const QDateTime probe = QDateTime(day, QTime(mid / 60, mid % 60), zone, |
884 | QDateTime::TransitionResolution::PreferBefore); |
885 | if (probe.isValid() && probe.date() == day) { |
886 | high = mid; |
887 | when = probe; |
888 | } else { |
889 | low = mid; |
890 | } |
891 | } |
892 | // Transitions out of local solar mean time, and the few international |
893 | // date-line crossings before that (Alaska, Philippines), may have happened |
894 | // between minute boundaries. Don't try to fix milliseconds. |
895 | if (QDateTime p = moment(when.time().addSecs(secs: -1)); Q_UNLIKELY(p.isValid() && p.date() == day)) { |
896 | high *= 60; |
897 | low *= 60; |
898 | while (high > low + 1) { |
899 | const int mid = (high + low) / 2; |
900 | const int min = mid / 60; |
901 | const QDateTime probe = moment(QTime(min / 60, min % 60, mid % 60)); |
902 | if (probe.isValid() && probe.date() == day) { |
903 | high = mid; |
904 | when = probe; |
905 | } else { |
906 | low = mid; |
907 | } |
908 | } |
909 | } |
910 | return when.isValid() ? when : QDateTime(); |
911 | } |
912 | |
913 | /*! |
914 | \since 5.14 |
915 | |
916 | Returns the start-moment of the day. |
917 | |
918 | When a day starts depends on a how time is described: each day starts and |
919 | ends earlier for those in time-zones further west and later for those in |
920 | time-zones further east. The time representation to use can be specified by |
921 | an optional time \a zone. The default time representation is the system's |
922 | local time. |
923 | |
924 | Usually, the start of the day is midnight, 00:00: however, if a time-zone |
925 | transition causes the given date to skip over that midnight (e.g. a DST |
926 | spring-forward skipping over the first hour of the day day), the actual |
927 | earliest time in the day is returned. This can only arise when the time |
928 | representation is a time-zone or local time. |
929 | |
930 | When \a zone has a timeSpec() of is Qt::OffsetFromUTC or Qt::UTC, the time |
931 | representation has no transitions so the start of the day is QTime(0, 0). |
932 | |
933 | In the rare case of a date that was entirely skipped (this happens when a |
934 | zone east of the international date-line switches to being west of it), the |
935 | return shall be invalid. Passing an invalid time-zone as \a zone will also |
936 | produce an invalid result, as shall dates that start outside the range |
937 | representable by QDateTime. |
938 | |
939 | \sa endOfDay() |
940 | */ |
941 | QDateTime QDate::startOfDay(const QTimeZone &zone) const |
942 | { |
943 | if (!inDateTimeRange(jd, side: DaySide::Start) || !zone.isValid()) |
944 | return QDateTime(); |
945 | |
946 | QDateTime when(*this, QTime(0, 0), zone, |
947 | QDateTime::TransitionResolution::RelativeToBefore); |
948 | if (Q_UNLIKELY(!when.isValid() || when.date() != *this)) { |
949 | #if QT_CONFIG(timezone) |
950 | // The start of the day must have fallen in a spring-forward's gap; find the spring-forward: |
951 | if (zone.timeSpec() == Qt::TimeZone && zone.hasTransitions()) { |
952 | QTimeZone::OffsetData tran |
953 | // There's unlikely to be another transition before noon tomorrow. |
954 | // However, the whole of today may have been skipped ! |
955 | = zone.previousTransition(beforeDateTime: QDateTime(addDays(days: 1), QTime(12, 0), zone)); |
956 | const QDateTime &at = tran.atUtc.toTimeZone(toZone: zone); |
957 | if (at.isValid() && at.date() == *this) |
958 | return at; |
959 | } |
960 | #endif |
961 | |
962 | when = toEarliest(day: *this, zone); |
963 | } |
964 | |
965 | return when; |
966 | } |
967 | |
968 | /*! |
969 | \overload |
970 | \since 6.5 |
971 | */ |
972 | QDateTime QDate::startOfDay() const |
973 | { |
974 | return startOfDay(zone: QTimeZone::LocalTime); |
975 | } |
976 | |
977 | #if QT_DEPRECATED_SINCE(6, 9) |
978 | /*! |
979 | \overload |
980 | \since 5.14 |
981 | \deprecated [6.9] Use \c{startOfDay(const QTimeZone &)} instead. |
982 | |
983 | Returns the start-moment of the day. |
984 | |
985 | When a day starts depends on a how time is described: each day starts and |
986 | ends earlier for those with higher offsets from UTC and later for those with |
987 | lower offsets from UTC. The time representation to use can be specified |
988 | either by a \a spec and \a offsetSeconds (ignored unless \a spec is |
989 | Qt::OffsetSeconds) or by a time zone. |
990 | |
991 | Usually, the start of the day is midnight, 00:00: however, if a local time |
992 | transition causes the given date to skip over that midnight (e.g. a DST |
993 | spring-forward skipping over the first hour of the day day), the actual |
994 | earliest time in the day is returned. |
995 | |
996 | When \a spec is Qt::OffsetFromUTC, \a offsetSeconds gives an implied zone's |
997 | offset from UTC. As UTC and such zones have no transitions, the start of the |
998 | day is QTime(0, 0) in these cases. |
999 | |
1000 | In the rare case of a date that was entirely skipped (this happens when a |
1001 | zone east of the international date-line switches to being west of it), the |
1002 | return shall be invalid. Passing Qt::TimeZone as \a spec (instead of passing |
1003 | a QTimeZone) will also produce an invalid result, as shall dates that start |
1004 | outside the range representable by QDateTime. |
1005 | */ |
1006 | QDateTime QDate::startOfDay(Qt::TimeSpec spec, int offsetSeconds) const |
1007 | { |
1008 | QTimeZone zone = asTimeZone(spec, offset: offsetSeconds, warner: "QDate::startOfDay" ); |
1009 | // If spec was Qt::TimeZone, zone's is Qt::LocalTime. |
1010 | return zone.timeSpec() == spec ? startOfDay(zone) : QDateTime(); |
1011 | } |
1012 | #endif // 6.9 deprecation |
1013 | |
1014 | static QDateTime toLatest(QDate day, const QTimeZone &zone) |
1015 | { |
1016 | Q_ASSERT(!zone.isUtcOrFixedOffset()); |
1017 | // And the day ends in a gap. First find a moment not in that gap: |
1018 | const auto moment = [=](QTime time) { |
1019 | return QDateTime(day, time, zone, QDateTime::TransitionResolution::Reject); |
1020 | }; |
1021 | // Longest routine time-zone transition is 2 hours: |
1022 | QDateTime when = moment(QTime(21, 59, 59, 999)); |
1023 | if (!when.isValid()) { |
1024 | // Noon should be safe ... |
1025 | when = moment(QTime(12, 0)); |
1026 | if (!when.isValid()) { |
1027 | // ... unless it's a 24-hour jump (moving the date-line) |
1028 | when = moment(QTime(0, 0)); |
1029 | if (!when.isValid()) |
1030 | return QDateTime(); |
1031 | } |
1032 | } |
1033 | int high = 24 * 60; |
1034 | int low = when.time().msecsSinceStartOfDay() / 60000; |
1035 | // Binary chop to the right minute |
1036 | while (high > low + 1) { |
1037 | const int mid = (high + low) / 2; |
1038 | const QDateTime probe = QDateTime(day, QTime(mid / 60, mid % 60, 59, 999), zone, |
1039 | QDateTime::TransitionResolution::PreferAfter); |
1040 | if (probe.isValid() && probe.date() == day) { |
1041 | low = mid; |
1042 | when = probe; |
1043 | } else { |
1044 | high = mid; |
1045 | } |
1046 | } |
1047 | // Transitions out of local solar mean time, and the few international |
1048 | // date-line crossings before that (Alaska, Philippines), may have happened |
1049 | // between minute boundaries. Don't try to fix milliseconds. |
1050 | if (QDateTime p = moment(when.time().addSecs(secs: 1)); Q_UNLIKELY(p.isValid() && p.date() == day)) { |
1051 | high *= 60; |
1052 | low *= 60; |
1053 | while (high > low + 1) { |
1054 | const int mid = (high + low) / 2; |
1055 | const int min = mid / 60; |
1056 | const QDateTime probe = moment(QTime(min / 60, min % 60, mid % 60, 999)); |
1057 | if (probe.isValid() && probe.date() == day) { |
1058 | low = mid; |
1059 | when = probe; |
1060 | } else { |
1061 | high = mid; |
1062 | } |
1063 | } |
1064 | } |
1065 | return when.isValid() ? when : QDateTime(); |
1066 | } |
1067 | |
1068 | /*! |
1069 | \since 5.14 |
1070 | |
1071 | Returns the end-moment of the day. |
1072 | |
1073 | When a day ends depends on a how time is described: each day starts and ends |
1074 | earlier for those in time-zones further west and later for those in |
1075 | time-zones further east. The time representation to use can be specified by |
1076 | an optional time \a zone. The default time representation is the system's |
1077 | local time. |
1078 | |
1079 | Usually, the end of the day is one millisecond before the midnight, 24:00: |
1080 | however, if a time-zone transition causes the given date to skip over that |
1081 | moment (e.g. a DST spring-forward skipping over 23:00 and the following |
1082 | hour), the actual latest time in the day is returned. This can only arise |
1083 | when the time representation is a time-zone or local time. |
1084 | |
1085 | When \a zone has a timeSpec() of Qt::OffsetFromUTC or Qt::UTC, the time |
1086 | representation has no transitions so the end of the day is QTime(23, 59, 59, |
1087 | 999). |
1088 | |
1089 | In the rare case of a date that was entirely skipped (this happens when a |
1090 | zone east of the international date-line switches to being west of it), the |
1091 | return shall be invalid. Passing an invalid time-zone as \a zone will also |
1092 | produce an invalid result, as shall dates that end outside the range |
1093 | representable by QDateTime. |
1094 | |
1095 | \sa startOfDay() |
1096 | */ |
1097 | QDateTime QDate::endOfDay(const QTimeZone &zone) const |
1098 | { |
1099 | if (!inDateTimeRange(jd, side: DaySide::End) || !zone.isValid()) |
1100 | return QDateTime(); |
1101 | |
1102 | QDateTime when(*this, QTime(23, 59, 59, 999), zone, |
1103 | QDateTime::TransitionResolution::RelativeToAfter); |
1104 | if (Q_UNLIKELY(!when.isValid() || when.date() != *this)) { |
1105 | #if QT_CONFIG(timezone) |
1106 | // The end of the day must have fallen in a spring-forward's gap; find the spring-forward: |
1107 | if (zone.timeSpec() == Qt::TimeZone && zone.hasTransitions()) { |
1108 | QTimeZone::OffsetData tran |
1109 | // It's unlikely there's been another transition since yesterday noon. |
1110 | // However, the whole of today may have been skipped ! |
1111 | = zone.nextTransition(afterDateTime: QDateTime(addDays(days: -1), QTime(12, 0), zone)); |
1112 | const QDateTime &at = tran.atUtc.toTimeZone(toZone: zone); |
1113 | if (at.isValid() && at.date() == *this) |
1114 | return at; |
1115 | } |
1116 | #endif |
1117 | |
1118 | when = toLatest(day: *this, zone); |
1119 | } |
1120 | return when; |
1121 | } |
1122 | |
1123 | /*! |
1124 | \overload |
1125 | \since 6.5 |
1126 | */ |
1127 | QDateTime QDate::endOfDay() const |
1128 | { |
1129 | return endOfDay(zone: QTimeZone::LocalTime); |
1130 | } |
1131 | |
1132 | #if QT_DEPRECATED_SINCE(6, 9) |
1133 | /*! |
1134 | \overload |
1135 | \since 5.14 |
1136 | \deprecated [6.9] Use \c{endOfDay(const QTimeZone &) instead. |
1137 | |
1138 | Returns the end-moment of the day. |
1139 | |
1140 | When a day ends depends on a how time is described: each day starts and ends |
1141 | earlier for those with higher offsets from UTC and later for those with |
1142 | lower offsets from UTC. The time representation to use can be specified |
1143 | either by a \a spec and \a offsetSeconds (ignored unless \a spec is |
1144 | Qt::OffsetSeconds) or by a time zone. |
1145 | |
1146 | Usually, the end of the day is one millisecond before the midnight, 24:00: |
1147 | however, if a local time transition causes the given date to skip over that |
1148 | moment (e.g. a DST spring-forward skipping over 23:00 and the following |
1149 | hour), the actual latest time in the day is returned. |
1150 | |
1151 | When \a spec is Qt::OffsetFromUTC, \a offsetSeconds gives the implied zone's |
1152 | offset from UTC. As UTC and such zones have no transitions, the end of the |
1153 | day is QTime(23, 59, 59, 999) in these cases. |
1154 | |
1155 | In the rare case of a date that was entirely skipped (this happens when a |
1156 | zone east of the international date-line switches to being west of it), the |
1157 | return shall be invalid. Passing Qt::TimeZone as \a spec (instead of passing |
1158 | a QTimeZone) will also produce an invalid result, as shall dates that end |
1159 | outside the range representable by QDateTime. |
1160 | */ |
1161 | QDateTime QDate::endOfDay(Qt::TimeSpec spec, int offsetSeconds) const |
1162 | { |
1163 | QTimeZone zone = asTimeZone(spec, offset: offsetSeconds, warner: "QDate::endOfDay" ); |
1164 | // If spec was Qt::TimeZone, zone's is Qt::LocalTime. |
1165 | return endOfDay(zone); |
1166 | } |
1167 | #endif // 6.9 deprecation |
1168 | |
1169 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
1170 | |
1171 | static QString toStringTextDate(QDate date) |
1172 | { |
1173 | if (date.isValid()) { |
1174 | QCalendar cal; // Always Gregorian |
1175 | const auto parts = cal.partsFromDate(date); |
1176 | if (parts.isValid()) { |
1177 | const QLatin1Char sp(' '); |
1178 | return QLocale::c().dayName(cal.dayOfWeek(date), format: QLocale::ShortFormat) + sp |
1179 | + cal.monthName(locale: QLocale::c(), month: parts.month, year: parts.year, format: QLocale::ShortFormat) |
1180 | // Documented to use 4-digit year |
1181 | + sp + QString::asprintf(format: "%d %04d" , parts.day, parts.year); |
1182 | } |
1183 | } |
1184 | return QString(); |
1185 | } |
1186 | |
1187 | static QString toStringIsoDate(QDate date) |
1188 | { |
1189 | const auto parts = QCalendar().partsFromDate(date); |
1190 | if (parts.isValid() && parts.year >= 0 && parts.year <= 9999) |
1191 | return QString::asprintf(format: "%04d-%02d-%02d" , parts.year, parts.month, parts.day); |
1192 | return QString(); |
1193 | } |
1194 | |
1195 | /*! |
1196 | \overload |
1197 | |
1198 | Returns the date as a string. The \a format parameter determines the format |
1199 | of the string. |
1200 | |
1201 | If the \a format is Qt::TextDate, the string is formatted in the default |
1202 | way. The day and month names will be in English. An example of this |
1203 | formatting is "Sat May 20 1995". For localized formatting, see |
1204 | \l{QLocale::toString()}. |
1205 | |
1206 | If the \a format is Qt::ISODate, the string format corresponds |
1207 | to the ISO 8601 extended specification for representations of |
1208 | dates and times, taking the form yyyy-MM-dd, where yyyy is the |
1209 | year, MM is the month of the year (between 01 and 12), and dd is |
1210 | the day of the month between 01 and 31. |
1211 | |
1212 | If the \a format is Qt::RFC2822Date, the string is formatted in |
1213 | an \l{RFC 2822} compatible way. An example of this formatting is |
1214 | "20 May 1995". |
1215 | |
1216 | If the date is invalid, an empty string will be returned. |
1217 | |
1218 | \warning The Qt::ISODate format is only valid for years in the |
1219 | range 0 to 9999. |
1220 | |
1221 | \sa fromString(), QLocale::toString() |
1222 | */ |
1223 | QString QDate::toString(Qt::DateFormat format) const |
1224 | { |
1225 | if (!isValid()) |
1226 | return QString(); |
1227 | |
1228 | switch (format) { |
1229 | case Qt::RFC2822Date: |
1230 | return QLocale::c().toString(date: *this, format: u"dd MMM yyyy" ); |
1231 | default: |
1232 | case Qt::TextDate: |
1233 | return toStringTextDate(date: *this); |
1234 | case Qt::ISODate: |
1235 | case Qt::ISODateWithMs: |
1236 | // No calendar dependence |
1237 | return toStringIsoDate(date: *this); |
1238 | } |
1239 | } |
1240 | |
1241 | /*! |
1242 | \fn QString QDate::toString(const QString &format, QCalendar cal) const |
1243 | \fn QString QDate::toString(QStringView format, QCalendar cal) const |
1244 | \since 5.14 |
1245 | |
1246 | Returns the date as a string. The \a format parameter determines the format |
1247 | of the result string. If \a cal is supplied, it determines the calendar used |
1248 | to represent the date; it defaults to Gregorian. Prior to Qt 5.14, there was |
1249 | no \a cal parameter and the Gregorian calendar was always used. |
1250 | |
1251 | These expressions may be used in the \a format parameter: |
1252 | |
1253 | \table |
1254 | \header \li Expression \li Output |
1255 | \row \li d \li The day as a number without a leading zero (1 to 31) |
1256 | \row \li dd \li The day as a number with a leading zero (01 to 31) |
1257 | \row \li ddd \li The abbreviated day name ('Mon' to 'Sun'). |
1258 | \row \li dddd \li The long day name ('Monday' to 'Sunday'). |
1259 | \row \li M \li The month as a number without a leading zero (1 to 12) |
1260 | \row \li MM \li The month as a number with a leading zero (01 to 12) |
1261 | \row \li MMM \li The abbreviated month name ('Jan' to 'Dec'). |
1262 | \row \li MMMM \li The long month name ('January' to 'December'). |
1263 | \row \li yy \li The year as a two digit number (00 to 99) |
1264 | \row \li yyyy \li The year as a four digit number. If the year is negative, |
1265 | a minus sign is prepended, making five characters. |
1266 | \endtable |
1267 | |
1268 | Any sequence of characters enclosed in single quotes will be included |
1269 | verbatim in the output string (stripped of the quotes), even if it contains |
1270 | formatting characters. Two consecutive single quotes ("''") are replaced by |
1271 | a single quote in the output. All other characters in the format string are |
1272 | included verbatim in the output string. |
1273 | |
1274 | Formats without separators (e.g. "ddMM") are supported but must be used with |
1275 | care, as the resulting strings aren't always reliably readable (e.g. if "dM" |
1276 | produces "212" it could mean either the 2nd of December or the 21st of |
1277 | February). |
1278 | |
1279 | Example format strings (assuming that the QDate is the 20 July |
1280 | 1969): |
1281 | |
1282 | \table |
1283 | \header \li Format \li Result |
1284 | \row \li dd.MM.yyyy \li 20.07.1969 |
1285 | \row \li ddd MMMM d yy \li Sun July 20 69 |
1286 | \row \li 'The day is' dddd \li The day is Sunday |
1287 | \endtable |
1288 | |
1289 | If the datetime is invalid, an empty string will be returned. |
1290 | |
1291 | \note Day and month names are given in English (C locale). To get localized |
1292 | month and day names, use QLocale::system().toString(). |
1293 | |
1294 | \note If a format character is repeated more times than the longest |
1295 | expression in the table above using it, this part of the format will be read |
1296 | as several expressions with no separator between them; the longest above, |
1297 | possibly repeated as many times as there are copies of it, ending with a |
1298 | residue that may be a shorter expression. Thus \c{'MMMMMMMMMM'} for a date |
1299 | in May will contribute \c{"MayMay05"} to the output. |
1300 | |
1301 | \sa fromString(), QDateTime::toString(), QTime::toString(), QLocale::toString() |
1302 | */ |
1303 | QString QDate::toString(QStringView format, QCalendar cal) const |
1304 | { |
1305 | return QLocale::c().toString(date: *this, format, cal); |
1306 | } |
1307 | |
1308 | // Out-of-line no-calendar overloads, since QCalendar is a non-trivial type |
1309 | /*! |
1310 | \overload |
1311 | \since 5.10 |
1312 | */ |
1313 | QString QDate::toString(QStringView format) const |
1314 | { |
1315 | return QLocale::c().toString(date: *this, format, cal: QCalendar()); |
1316 | } |
1317 | |
1318 | /*! |
1319 | \overload |
1320 | \since 4.6 |
1321 | */ |
1322 | QString QDate::toString(const QString &format) const |
1323 | { |
1324 | return QLocale::c().toString(date: *this, format: qToStringViewIgnoringNull(s: format), cal: QCalendar()); |
1325 | } |
1326 | #endif // datestring |
1327 | |
1328 | /*! |
1329 | \since 4.2 |
1330 | |
1331 | Sets this to represent the date, in the Gregorian calendar, with the given |
1332 | \a year, \a month and \a day numbers. Returns true if the resulting date is |
1333 | valid, otherwise it sets this to represent an invalid date and returns |
1334 | false. |
1335 | |
1336 | \sa isValid(), QCalendar::dateFromParts() |
1337 | */ |
1338 | bool QDate::setDate(int year, int month, int day) |
1339 | { |
1340 | const auto maybe = QGregorianCalendar::julianFromParts(year, month, day); |
1341 | jd = maybe.value_or(u: nullJd()); |
1342 | return bool(maybe); |
1343 | } |
1344 | |
1345 | /*! |
1346 | \since 5.14 |
1347 | |
1348 | Sets this to represent the date, in the given calendar \a cal, with the |
1349 | given \a year, \a month and \a day numbers. Returns true if the resulting |
1350 | date is valid, otherwise it sets this to represent an invalid date and |
1351 | returns false. |
1352 | |
1353 | \sa isValid(), QCalendar::dateFromParts() |
1354 | */ |
1355 | |
1356 | bool QDate::setDate(int year, int month, int day, QCalendar cal) |
1357 | { |
1358 | *this = QDate(year, month, day, cal); |
1359 | return isValid(); |
1360 | } |
1361 | |
1362 | /*! |
1363 | \since 4.5 |
1364 | |
1365 | Extracts the date's year, month, and day, and assigns them to |
1366 | *\a year, *\a month, and *\a day. The pointers may be null. |
1367 | |
1368 | Returns 0 if the date is invalid. |
1369 | |
1370 | \note In Qt versions prior to 5.7, this function is marked as non-\c{const}. |
1371 | |
1372 | \sa year(), month(), day(), isValid(), QCalendar::partsFromDate() |
1373 | */ |
1374 | void QDate::getDate(int *year, int *month, int *day) const |
1375 | { |
1376 | QCalendar::YearMonthDay parts; // invalid by default |
1377 | if (isValid()) |
1378 | parts = QGregorianCalendar::partsFromJulian(jd); |
1379 | |
1380 | const bool ok = parts.isValid(); |
1381 | if (year) |
1382 | *year = ok ? parts.year : 0; |
1383 | if (month) |
1384 | *month = ok ? parts.month : 0; |
1385 | if (day) |
1386 | *day = ok ? parts.day : 0; |
1387 | } |
1388 | |
1389 | /*! |
1390 | Returns a QDate object containing a date \a ndays later than the |
1391 | date of this object (or earlier if \a ndays is negative). |
1392 | |
1393 | Returns a null date if the current date is invalid or the new date is |
1394 | out of range. |
1395 | |
1396 | \sa addMonths(), addYears(), daysTo() |
1397 | */ |
1398 | |
1399 | QDate QDate::addDays(qint64 ndays) const |
1400 | { |
1401 | if (isNull()) |
1402 | return QDate(); |
1403 | |
1404 | if (qint64 r; Q_UNLIKELY(qAddOverflow(jd, ndays, &r))) |
1405 | return QDate(); |
1406 | else |
1407 | return fromJulianDay(jd_: r); |
1408 | } |
1409 | |
1410 | /*! |
1411 | \fn QDate QDate::addDuration(std::chrono::days ndays) const |
1412 | |
1413 | \since 6.4 |
1414 | |
1415 | Returns a QDate object containing a date \a ndays later than the |
1416 | date of this object (or earlier if \a ndays is negative). |
1417 | |
1418 | Returns a null date if the current date is invalid or the new date is |
1419 | out of range. |
1420 | |
1421 | \note Adding durations expressed in \c{std::chrono::months} or |
1422 | \c{std::chrono::years} does not yield the same result obtained by using |
1423 | addMonths() or addYears(). The former are fixed durations, calculated in |
1424 | relation to the solar year; the latter use the Gregorian calendar definitions |
1425 | of months/years. |
1426 | |
1427 | \note This function requires C++20. |
1428 | |
1429 | \sa addMonths(), addYears(), daysTo() |
1430 | */ |
1431 | |
1432 | /*! |
1433 | Returns a QDate object containing a date \a nmonths later than the |
1434 | date of this object (or earlier if \a nmonths is negative). |
1435 | |
1436 | Uses \a cal as calendar, if supplied, else the Gregorian calendar. |
1437 | |
1438 | \note If the ending day/month combination does not exist in the resulting |
1439 | month/year, this function will return a date that is the latest valid date |
1440 | in the selected month. |
1441 | |
1442 | \sa addDays(), addYears() |
1443 | */ |
1444 | |
1445 | QDate QDate::addMonths(int nmonths, QCalendar cal) const |
1446 | { |
1447 | if (!isValid()) |
1448 | return QDate(); |
1449 | |
1450 | if (nmonths == 0) |
1451 | return *this; |
1452 | |
1453 | auto parts = cal.partsFromDate(date: *this); |
1454 | |
1455 | if (!parts.isValid()) |
1456 | return QDate(); |
1457 | Q_ASSERT(parts.year || cal.hasYearZero()); |
1458 | |
1459 | parts.month += nmonths; |
1460 | while (parts.month <= 0) { |
1461 | if (--parts.year || cal.hasYearZero()) |
1462 | parts.month += cal.monthsInYear(year: parts.year); |
1463 | } |
1464 | int count = cal.monthsInYear(year: parts.year); |
1465 | while (parts.month > count) { |
1466 | parts.month -= count; |
1467 | count = (++parts.year || cal.hasYearZero()) ? cal.monthsInYear(year: parts.year) : 0; |
1468 | } |
1469 | |
1470 | return fixedDate(parts, cal); |
1471 | } |
1472 | |
1473 | /*! |
1474 | \overload |
1475 | */ |
1476 | |
1477 | QDate QDate::addMonths(int nmonths) const |
1478 | { |
1479 | if (isNull()) |
1480 | return QDate(); |
1481 | |
1482 | if (nmonths == 0) |
1483 | return *this; |
1484 | |
1485 | auto parts = QGregorianCalendar::partsFromJulian(jd); |
1486 | |
1487 | if (!parts.isValid()) |
1488 | return QDate(); |
1489 | Q_ASSERT(parts.year); |
1490 | |
1491 | parts.month += nmonths; |
1492 | while (parts.month <= 0) { |
1493 | if (--parts.year) // skip over year 0 |
1494 | parts.month += 12; |
1495 | } |
1496 | while (parts.month > 12) { |
1497 | parts.month -= 12; |
1498 | if (!++parts.year) // skip over year 0 |
1499 | ++parts.year; |
1500 | } |
1501 | |
1502 | return fixedDate(parts); |
1503 | } |
1504 | |
1505 | /*! |
1506 | Returns a QDate object containing a date \a nyears later than the |
1507 | date of this object (or earlier if \a nyears is negative). |
1508 | |
1509 | Uses \a cal as calendar, if supplied, else the Gregorian calendar. |
1510 | |
1511 | \note If the ending day/month combination does not exist in the resulting |
1512 | year (e.g., for the Gregorian calendar, if the date was Feb 29 and the final |
1513 | year is not a leap year), this function will return a date that is the |
1514 | latest valid date in the given month (in the example, Feb 28). |
1515 | |
1516 | \sa addDays(), addMonths() |
1517 | */ |
1518 | |
1519 | QDate QDate::addYears(int nyears, QCalendar cal) const |
1520 | { |
1521 | if (!isValid()) |
1522 | return QDate(); |
1523 | |
1524 | auto parts = cal.partsFromDate(date: *this); |
1525 | if (!parts.isValid()) |
1526 | return QDate(); |
1527 | |
1528 | int old_y = parts.year; |
1529 | parts.year += nyears; |
1530 | |
1531 | // If we just crossed (or hit) a missing year zero, adjust year by ±1: |
1532 | if (!cal.hasYearZero() && ((old_y > 0) != (parts.year > 0) || !parts.year)) |
1533 | parts.year += nyears > 0 ? +1 : -1; |
1534 | |
1535 | return fixedDate(parts, cal); |
1536 | } |
1537 | |
1538 | /*! |
1539 | \overload |
1540 | */ |
1541 | |
1542 | QDate QDate::addYears(int nyears) const |
1543 | { |
1544 | if (isNull()) |
1545 | return QDate(); |
1546 | |
1547 | auto parts = QGregorianCalendar::partsFromJulian(jd); |
1548 | if (!parts.isValid()) |
1549 | return QDate(); |
1550 | |
1551 | int old_y = parts.year; |
1552 | parts.year += nyears; |
1553 | |
1554 | // If we just crossed (or hit) a missing year zero, adjust year by ±1: |
1555 | if ((old_y > 0) != (parts.year > 0) || !parts.year) |
1556 | parts.year += nyears > 0 ? +1 : -1; |
1557 | |
1558 | return fixedDate(parts); |
1559 | } |
1560 | |
1561 | /*! |
1562 | Returns the number of days from this date to \a d (which is |
1563 | negative if \a d is earlier than this date). |
1564 | |
1565 | Returns 0 if either date is invalid. |
1566 | |
1567 | Example: |
1568 | \snippet code/src_corelib_time_qdatetime.cpp 0 |
1569 | |
1570 | \sa addDays() |
1571 | */ |
1572 | |
1573 | qint64 QDate::daysTo(QDate d) const |
1574 | { |
1575 | if (isNull() || d.isNull()) |
1576 | return 0; |
1577 | |
1578 | // Due to limits on minJd() and maxJd() we know this will never overflow |
1579 | return d.jd - jd; |
1580 | } |
1581 | |
1582 | |
1583 | /*! |
1584 | \fn bool QDate::operator==(const QDate &lhs, const QDate &rhs) |
1585 | |
1586 | Returns \c true if \a lhs and \a rhs represent the same day, otherwise |
1587 | \c false. |
1588 | */ |
1589 | |
1590 | /*! |
1591 | \fn bool QDate::operator!=(const QDate &lhs, const QDate &rhs) |
1592 | |
1593 | Returns \c true if \a lhs and \a rhs represent distinct days; otherwise |
1594 | returns \c false. |
1595 | |
1596 | \sa operator==() |
1597 | */ |
1598 | |
1599 | /*! |
1600 | \fn bool QDate::operator<(const QDate &lhs, const QDate &rhs) |
1601 | |
1602 | Returns \c true if \a lhs is earlier than \a rhs; otherwise returns \c false. |
1603 | */ |
1604 | |
1605 | /*! |
1606 | \fn bool QDate::operator<=(const QDate &lhs, const QDate &rhs) |
1607 | |
1608 | Returns \c true if \a lhs is earlier than or equal to \a rhs; |
1609 | otherwise returns \c false. |
1610 | */ |
1611 | |
1612 | /*! |
1613 | \fn bool QDate::operator>(const QDate &lhs, const QDate &rhs) |
1614 | |
1615 | Returns \c true if \a lhs is later than \a rhs; otherwise returns \c false. |
1616 | */ |
1617 | |
1618 | /*! |
1619 | \fn bool QDate::operator>=(const QDate &lhs, const QDate &rhs) |
1620 | |
1621 | Returns \c true if \a lhs is later than or equal to \a rhs; |
1622 | otherwise returns \c false. |
1623 | */ |
1624 | |
1625 | /*! |
1626 | \fn QDate::currentDate() |
1627 | Returns the system clock's current date. |
1628 | |
1629 | \sa QTime::currentTime(), QDateTime::currentDateTime() |
1630 | */ |
1631 | |
1632 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
1633 | |
1634 | /*! |
1635 | \fn QDate QDate::fromString(const QString &string, Qt::DateFormat format) |
1636 | |
1637 | Returns the QDate represented by the \a string, using the |
1638 | \a format given, or an invalid date if the string cannot be |
1639 | parsed. |
1640 | |
1641 | Note for Qt::TextDate: only English month names (e.g. "Jan" in short form or |
1642 | "January" in long form) are recognized. |
1643 | |
1644 | \sa toString(), QLocale::toDate() |
1645 | */ |
1646 | |
1647 | /*! |
1648 | \overload |
1649 | \since 6.0 |
1650 | */ |
1651 | QDate QDate::fromString(QStringView string, Qt::DateFormat format) |
1652 | { |
1653 | if (string.isEmpty()) |
1654 | return QDate(); |
1655 | |
1656 | switch (format) { |
1657 | case Qt::RFC2822Date: |
1658 | return rfcDateImpl(s: string).date; |
1659 | default: |
1660 | case Qt::TextDate: { |
1661 | // Documented as "ddd MMM d yyyy" |
1662 | QVarLengthArray<QStringView, 4> parts; |
1663 | auto tokens = string.tokenize(needle: u' ', flags: Qt::SkipEmptyParts); |
1664 | auto it = tokens.begin(); |
1665 | for (int i = 0; i < 4 && it != tokens.end(); ++i, ++it) |
1666 | parts.emplace_back(args: *it); |
1667 | |
1668 | if (parts.size() != 4 || it != tokens.end()) |
1669 | return QDate(); |
1670 | |
1671 | bool ok = false; |
1672 | int year = parts.at(idx: 3).toInt(ok: &ok); |
1673 | int day = ok ? parts.at(idx: 2).toInt(ok: &ok) : 0; |
1674 | if (!ok || !day) |
1675 | return QDate(); |
1676 | |
1677 | const int month = fromShortMonthName(monthName: parts.at(idx: 1)); |
1678 | if (month == -1) // Month name matches no English or localised name. |
1679 | return QDate(); |
1680 | |
1681 | return QDate(year, month, day); |
1682 | } |
1683 | case Qt::ISODate: |
1684 | // Semi-strict parsing, must be long enough and have punctuators as separators |
1685 | if (string.size() >= 10 && string[4].isPunct() && string[7].isPunct() |
1686 | && (string.size() == 10 || !string[10].isDigit())) { |
1687 | const ParsedInt year = readInt(text: string.first(n: 4)); |
1688 | const ParsedInt month = readInt(text: string.sliced(pos: 5, n: 2)); |
1689 | const ParsedInt day = readInt(text: string.sliced(pos: 8, n: 2)); |
1690 | if (year.ok() && year.result > 0 && year.result <= 9999 && month.ok() && day.ok()) |
1691 | return QDate(year.result, month.result, day.result); |
1692 | } |
1693 | break; |
1694 | } |
1695 | return QDate(); |
1696 | } |
1697 | |
1698 | /*! |
1699 | \fn QDate QDate::fromString(const QString &string, const QString &format, int baseYear, QCalendar cal) |
1700 | |
1701 | Returns the QDate represented by the \a string, using the \a |
1702 | format given, or an invalid date if the string cannot be parsed. |
1703 | |
1704 | Uses \a cal as calendar if supplied, else the Gregorian calendar. Ranges of |
1705 | values in the format descriptions below are for the latter; they may be |
1706 | different for other calendars. |
1707 | |
1708 | These expressions may be used for the format: |
1709 | |
1710 | \table |
1711 | \header \li Expression \li Output |
1712 | \row \li d \li The day as a number without a leading zero (1 to 31) |
1713 | \row \li dd \li The day as a number with a leading zero (01 to 31) |
1714 | \row \li ddd \li The abbreviated day name ('Mon' to 'Sun'). |
1715 | \row \li dddd \li The long day name ('Monday' to 'Sunday'). |
1716 | \row \li M \li The month as a number without a leading zero (1 to 12) |
1717 | \row \li MM \li The month as a number with a leading zero (01 to 12) |
1718 | \row \li MMM \li The abbreviated month name ('Jan' to 'Dec'). |
1719 | \row \li MMMM \li The long month name ('January' to 'December'). |
1720 | \row \li yy \li The year as a two digit number (00 to 99) |
1721 | \row \li yyyy \li The year as a four digit number, possibly plus a leading |
1722 | minus sign for negative years. |
1723 | \endtable |
1724 | |
1725 | \note Day and month names must be given in English (C locale). If localized |
1726 | month and day names are to be recognized, use QLocale::system().toDate(). |
1727 | |
1728 | All other input characters will be treated as text. Any non-empty sequence |
1729 | of characters enclosed in single quotes will also be treated (stripped of |
1730 | the quotes) as text and not be interpreted as expressions. For example: |
1731 | |
1732 | \snippet code/src_corelib_time_qdatetime.cpp 1 |
1733 | |
1734 | If the format is not satisfied, an invalid QDate is returned. The |
1735 | expressions that don't expect leading zeroes (d, M) will be |
1736 | greedy. This means that they will use two digits even if this |
1737 | will put them outside the accepted range of values and leaves too |
1738 | few digits for other sections. For example, the following format |
1739 | string could have meant January 30 but the M will grab two |
1740 | digits, resulting in an invalid date: |
1741 | |
1742 | \snippet code/src_corelib_time_qdatetime.cpp 2 |
1743 | |
1744 | For any field that is not represented in the format the following |
1745 | defaults are used: |
1746 | |
1747 | \table |
1748 | \header \li Field \li Default value |
1749 | \row \li Year \li \a baseYear (or 1900) |
1750 | \row \li Month \li 1 (January) |
1751 | \row \li Day \li 1 |
1752 | \endtable |
1753 | |
1754 | When \a format only specifies the last two digits of a year, the 100 years |
1755 | starting at \a baseYear are the candidates first considered. Prior to 6.7 |
1756 | there was no \a baseYear parameter and 1900 was always used. This is the |
1757 | default for \a baseYear, selecting a year from then to 1999. Passing 1976 as |
1758 | \a baseYear will select a year from 1976 through 2075, for example. When the |
1759 | format also includes month, day (of month) and day-of-week, these suffice to |
1760 | imply the century. In such a case, a matching date is selected in the |
1761 | nearest century to the one indicated by \a baseYear, prefering later over |
1762 | earlier. See \l QCalendar::matchCenturyToWeekday() and \l {Date ambiguities} |
1763 | for further details, |
1764 | |
1765 | The following examples demonstrate the default values: |
1766 | |
1767 | \snippet code/src_corelib_time_qdatetime.cpp 3 |
1768 | |
1769 | \note If a format character is repeated more times than the longest |
1770 | expression in the table above using it, this part of the format will be read |
1771 | as several expressions with no separator between them; the longest above, |
1772 | possibly repeated as many times as there are copies of it, ending with a |
1773 | residue that may be a shorter expression. Thus \c{'MMMMMMMMMM'} would match |
1774 | \c{"MayMay05"} and set the month to May. Likewise, \c{'MMMMMM'} would match |
1775 | \c{"May08"} and find it inconsistent, leading to an invalid date. |
1776 | |
1777 | \section2 Date ambiguities |
1778 | |
1779 | Different cultures use different formats for dates and, as a result, users |
1780 | may mix up the order in which date fields should be given. For example, |
1781 | \c{"Wed 28-Nov-01"} might mean either 2028 November 1st or the 28th of |
1782 | November, 2001 (each of which happens to be a Wednesday). Using format |
1783 | \c{"ddd yy-MMM-dd"} it shall be interpreted the first way, using \c{"ddd |
1784 | dd-MMM-yy"} the second. However, which the user meant may depend on the way |
1785 | the user normally writes dates, rather than the format the code was |
1786 | expecting. |
1787 | |
1788 | The example considered above mixed up day of the month and a two-digit year. |
1789 | Similar confusion can arise over interchanging the month and day of the |
1790 | month, when both are given as numbers. In these cases, including a day of |
1791 | the week field in the date format can provide some redundancy, that may help |
1792 | to catch errors of this kind. However, as in the example above, this is not |
1793 | always effective: the interchange of two fields (or their meanings) may |
1794 | produce dates with the same day of the week. |
1795 | |
1796 | Including a day of the week in the format can also resolve the century of a |
1797 | date specified using only the last two digits of its year. Unfortunately, |
1798 | when combined with a date in which the user (or other source of data) has |
1799 | mixed up two of the fields, this resolution can lead to finding a date which |
1800 | does match the format's reading but isn't the one intended by its author. |
1801 | Likewise, if the user simply gets the day of the week wrong, in an otherwise |
1802 | correct date, this can lead a date in a different century. In each case, |
1803 | finding a date in a different century can turn a wrongly-input date into a |
1804 | wildly different one. |
1805 | |
1806 | The best way to avoid date ambiguities is to use four-digit years and months |
1807 | specified by name (whether full or abbreviated), ideally collected via user |
1808 | interface idioms that make abundantly clear to the user which part of the |
1809 | date they are selecting. Including a day of the week can also help by |
1810 | providing the means to check consistency of the data. Where data comes from |
1811 | the user, using a format supplied by a locale selected by the user, it is |
1812 | best to use a long format as short formats are more likely to use two-digit |
1813 | years. Of course, it is not always possible to control the format - data may |
1814 | come from a source you do not control, for example. |
1815 | |
1816 | As a result of these possible sources of confusion, particularly when you |
1817 | cannot be sure an unambiguous format is in use, it is important to check |
1818 | that the result of reading a string as a date is not just valid but |
1819 | reasonable for the purpose for which it was supplied. If the result is |
1820 | outside some range of reasonable values, it may be worth getting the user to |
1821 | confirm their date selection, showing the date read from the string in a |
1822 | long format that does include month name and four-digit year, to make it |
1823 | easier for them to recognize any errors. |
1824 | |
1825 | \sa toString(), QDateTime::fromString(), QTime::fromString(), |
1826 | QLocale::toDate() |
1827 | */ |
1828 | |
1829 | /*! |
1830 | \fn QDate QDate::fromString(QStringView string, QStringView format, QCalendar cal) |
1831 | \overload |
1832 | \since 6.0 |
1833 | */ |
1834 | |
1835 | /*! |
1836 | \overload |
1837 | \since 6.0 |
1838 | */ |
1839 | QDate QDate::fromString(const QString &string, QStringView format, int baseYear, QCalendar cal) |
1840 | { |
1841 | QDate date; |
1842 | #if QT_CONFIG(datetimeparser) |
1843 | QDateTimeParser dt(QMetaType::QDate, QDateTimeParser::FromString, cal); |
1844 | dt.setDefaultLocale(QLocale::c()); |
1845 | if (dt.parseFormat(format)) |
1846 | dt.fromString(text: string, date: &date, time: nullptr, baseYear); |
1847 | #else |
1848 | Q_UNUSED(string); |
1849 | Q_UNUSED(format); |
1850 | Q_UNUSED(baseYear); |
1851 | Q_UNUSED(cal); |
1852 | #endif |
1853 | return date; |
1854 | } |
1855 | |
1856 | /*! |
1857 | \fn QDate QDate::fromString(const QString &string, const QString &format, QCalendar cal) |
1858 | \overload |
1859 | \since 5.14 |
1860 | */ |
1861 | |
1862 | /*! |
1863 | \fn QDate QDate::fromString(const QString &string, QStringView format, QCalendar cal) |
1864 | \overload |
1865 | \since 6.0 |
1866 | */ |
1867 | |
1868 | /*! |
1869 | \fn QDate QDate::fromString(QStringView string, QStringView format, int baseYear, QCalendar cal) |
1870 | \overload |
1871 | \since 6.7 |
1872 | */ |
1873 | |
1874 | /*! |
1875 | \fn QDate QDate::fromString(QStringView string, QStringView format, int baseYear) |
1876 | \overload |
1877 | \since 6.7 |
1878 | |
1879 | Uses a default-constructed QCalendar. |
1880 | */ |
1881 | |
1882 | /*! |
1883 | \overload |
1884 | \since 6.7 |
1885 | |
1886 | Uses a default-constructed QCalendar. |
1887 | */ |
1888 | QDate QDate::fromString(const QString &string, QStringView format, int baseYear) |
1889 | { |
1890 | return fromString(string, format, baseYear, cal: QCalendar()); |
1891 | } |
1892 | |
1893 | /*! |
1894 | \fn QDate QDate::fromString(const QString &string, const QString &format, int baseYear) |
1895 | \overload |
1896 | \since 6.7 |
1897 | |
1898 | Uses a default-constructed QCalendar. |
1899 | */ |
1900 | #endif // datestring |
1901 | |
1902 | /*! |
1903 | \overload |
1904 | |
1905 | Returns \c true if the specified date (\a year, \a month, and \a day) is |
1906 | valid in the Gregorian calendar; otherwise returns \c false. |
1907 | |
1908 | Example: |
1909 | \snippet code/src_corelib_time_qdatetime.cpp 4 |
1910 | |
1911 | \sa isNull(), setDate(), QCalendar::isDateValid() |
1912 | */ |
1913 | |
1914 | bool QDate::isValid(int year, int month, int day) |
1915 | { |
1916 | return QGregorianCalendar::validParts(year, month, day); |
1917 | } |
1918 | |
1919 | /*! |
1920 | \fn bool QDate::isLeapYear(int year) |
1921 | |
1922 | Returns \c true if the specified \a year is a leap year in the Gregorian |
1923 | calendar; otherwise returns \c false. |
1924 | |
1925 | \sa QCalendar::isLeapYear() |
1926 | */ |
1927 | |
1928 | bool QDate::isLeapYear(int y) |
1929 | { |
1930 | return QGregorianCalendar::leapTest(year: y); |
1931 | } |
1932 | |
1933 | /*! \fn static QDate QDate::fromJulianDay(qint64 jd) |
1934 | |
1935 | Converts the Julian day \a jd to a QDate. |
1936 | |
1937 | \sa toJulianDay() |
1938 | */ |
1939 | |
1940 | /*! \fn int QDate::toJulianDay() const |
1941 | |
1942 | Converts the date to a Julian day. |
1943 | |
1944 | \sa fromJulianDay() |
1945 | */ |
1946 | |
1947 | /***************************************************************************** |
1948 | QTime member functions |
1949 | *****************************************************************************/ |
1950 | |
1951 | /*! |
1952 | \class QTime |
1953 | \inmodule QtCore |
1954 | \reentrant |
1955 | |
1956 | \brief The QTime class provides clock time functions. |
1957 | |
1958 | \compares strong |
1959 | |
1960 | A QTime object contains a clock time, which it can express as the numbers of |
1961 | hours, minutes, seconds, and milliseconds since midnight. It provides |
1962 | functions for comparing times and for manipulating a time by adding a number |
1963 | of milliseconds. QTime objects should be passed by value rather than by |
1964 | reference to const; they simply package \c int. |
1965 | |
1966 | QTime uses the 24-hour clock format; it has no concept of AM/PM. |
1967 | Unlike QDateTime, QTime knows nothing about time zones or |
1968 | daylight-saving time (DST). |
1969 | |
1970 | A QTime object is typically created either by giving the number of hours, |
1971 | minutes, seconds, and milliseconds explicitly, or by using the static |
1972 | function currentTime(), which creates a QTime object that represents the |
1973 | system's local time. |
1974 | |
1975 | The hour(), minute(), second(), and msec() functions provide |
1976 | access to the number of hours, minutes, seconds, and milliseconds |
1977 | of the time. The same information is provided in textual format by |
1978 | the toString() function. |
1979 | |
1980 | The addSecs() and addMSecs() functions provide the time a given |
1981 | number of seconds or milliseconds later than a given time. |
1982 | Correspondingly, the number of seconds or milliseconds |
1983 | between two times can be found using secsTo() or msecsTo(). |
1984 | |
1985 | QTime provides a full set of operators to compare two QTime |
1986 | objects; an earlier time is considered smaller than a later one; |
1987 | if A.msecsTo(B) is positive, then A < B. |
1988 | |
1989 | QTime objects can also be created from a text representation using |
1990 | fromString() and converted to a string representation using toString(). All |
1991 | conversion to and from string formats is done using the C locale. For |
1992 | localized conversions, see QLocale. |
1993 | |
1994 | \sa QDate, QDateTime |
1995 | */ |
1996 | |
1997 | /*! |
1998 | \fn QTime::QTime() |
1999 | |
2000 | Constructs a null time object. For a null time, isNull() returns \c true and |
2001 | isValid() returns \c false. If you need a zero time, use QTime(0, 0). For |
2002 | the start of a day, see QDate::startOfDay(). |
2003 | |
2004 | \sa isNull(), isValid() |
2005 | */ |
2006 | |
2007 | /*! |
2008 | Constructs a time with hour \a h, minute \a m, seconds \a s and |
2009 | milliseconds \a ms. |
2010 | |
2011 | \a h must be in the range 0 to 23, \a m and \a s must be in the |
2012 | range 0 to 59, and \a ms must be in the range 0 to 999. |
2013 | |
2014 | \sa isValid() |
2015 | */ |
2016 | |
2017 | QTime::QTime(int h, int m, int s, int ms) |
2018 | { |
2019 | setHMS(h, m, s, ms); |
2020 | } |
2021 | |
2022 | |
2023 | /*! |
2024 | \fn bool QTime::isNull() const |
2025 | |
2026 | Returns \c true if the time is null (i.e., the QTime object was |
2027 | constructed using the default constructor); otherwise returns |
2028 | false. A null time is also an invalid time. |
2029 | |
2030 | \sa isValid() |
2031 | */ |
2032 | |
2033 | /*! |
2034 | Returns \c true if the time is valid; otherwise returns \c false. For example, |
2035 | the time 23:30:55.746 is valid, but 24:12:30 is invalid. |
2036 | |
2037 | \sa isNull() |
2038 | */ |
2039 | |
2040 | bool QTime::isValid() const |
2041 | { |
2042 | return mds > NullTime && mds < MSECS_PER_DAY; |
2043 | } |
2044 | |
2045 | |
2046 | /*! |
2047 | Returns the hour part (0 to 23) of the time. |
2048 | |
2049 | Returns -1 if the time is invalid. |
2050 | |
2051 | \sa minute(), second(), msec() |
2052 | */ |
2053 | |
2054 | int QTime::hour() const |
2055 | { |
2056 | if (!isValid()) |
2057 | return -1; |
2058 | |
2059 | return ds() / MSECS_PER_HOUR; |
2060 | } |
2061 | |
2062 | /*! |
2063 | Returns the minute part (0 to 59) of the time. |
2064 | |
2065 | Returns -1 if the time is invalid. |
2066 | |
2067 | \sa hour(), second(), msec() |
2068 | */ |
2069 | |
2070 | int QTime::minute() const |
2071 | { |
2072 | if (!isValid()) |
2073 | return -1; |
2074 | |
2075 | return (ds() % MSECS_PER_HOUR) / MSECS_PER_MIN; |
2076 | } |
2077 | |
2078 | /*! |
2079 | Returns the second part (0 to 59) of the time. |
2080 | |
2081 | Returns -1 if the time is invalid. |
2082 | |
2083 | \sa hour(), minute(), msec() |
2084 | */ |
2085 | |
2086 | int QTime::second() const |
2087 | { |
2088 | if (!isValid()) |
2089 | return -1; |
2090 | |
2091 | return (ds() / MSECS_PER_SEC) % SECS_PER_MIN; |
2092 | } |
2093 | |
2094 | /*! |
2095 | Returns the millisecond part (0 to 999) of the time. |
2096 | |
2097 | Returns -1 if the time is invalid. |
2098 | |
2099 | \sa hour(), minute(), second() |
2100 | */ |
2101 | |
2102 | int QTime::msec() const |
2103 | { |
2104 | if (!isValid()) |
2105 | return -1; |
2106 | |
2107 | return ds() % MSECS_PER_SEC; |
2108 | } |
2109 | |
2110 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
2111 | /*! |
2112 | \overload |
2113 | |
2114 | Returns the time as a string. The \a format parameter determines |
2115 | the format of the string. |
2116 | |
2117 | If \a format is Qt::TextDate, the string format is HH:mm:ss; |
2118 | e.g. 1 second before midnight would be "23:59:59". |
2119 | |
2120 | If \a format is Qt::ISODate, the string format corresponds to the |
2121 | ISO 8601 extended specification for representations of dates, |
2122 | represented by HH:mm:ss. To include milliseconds in the ISO 8601 |
2123 | date, use the \a format Qt::ISODateWithMs, which corresponds to |
2124 | HH:mm:ss.zzz. |
2125 | |
2126 | If the \a format is Qt::RFC2822Date, the string is formatted in |
2127 | an \l{RFC 2822} compatible way. An example of this formatting is |
2128 | "23:59:20". |
2129 | |
2130 | If the time is invalid, an empty string will be returned. |
2131 | |
2132 | \sa fromString(), QDate::toString(), QDateTime::toString(), QLocale::toString() |
2133 | */ |
2134 | |
2135 | QString QTime::toString(Qt::DateFormat format) const |
2136 | { |
2137 | if (!isValid()) |
2138 | return QString(); |
2139 | |
2140 | switch (format) { |
2141 | case Qt::ISODateWithMs: |
2142 | return QString::asprintf(format: "%02d:%02d:%02d.%03d" , hour(), minute(), second(), msec()); |
2143 | case Qt::RFC2822Date: |
2144 | case Qt::ISODate: |
2145 | case Qt::TextDate: |
2146 | default: |
2147 | return QString::asprintf(format: "%02d:%02d:%02d" , hour(), minute(), second()); |
2148 | } |
2149 | } |
2150 | |
2151 | /*! |
2152 | \fn QString QTime::toString(const QString &format) const |
2153 | \fn QString QTime::toString(QStringView format) const |
2154 | |
2155 | Returns a string representing the time. |
2156 | |
2157 | The \a format parameter determines the format of the result string. If the |
2158 | time is invalid, an empty string will be returned. |
2159 | |
2160 | These expressions may be used: |
2161 | |
2162 | \table |
2163 | \header \li Expression \li Output |
2164 | \row \li h |
2165 | \li The hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) |
2166 | \row \li hh |
2167 | \li The hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) |
2168 | \row \li H |
2169 | \li The hour without a leading zero (0 to 23, even with AM/PM display) |
2170 | \row \li HH |
2171 | \li The hour with a leading zero (00 to 23, even with AM/PM display) |
2172 | \row \li m \li The minute without a leading zero (0 to 59) |
2173 | \row \li mm \li The minute with a leading zero (00 to 59) |
2174 | \row \li s \li The whole second, without any leading zero (0 to 59) |
2175 | \row \li ss \li The whole second, with a leading zero where applicable (00 to 59) |
2176 | \row \li z or zz |
2177 | \li The fractional part of the second, to go after a decimal point, |
2178 | without trailing zeroes. Thus \c{"s.z"} reports the seconds to full |
2179 | available (millisecond) precision without trailing zeroes (0 to |
2180 | 999). For example, \c{"s.z"} would produce \c{"0.25"} for a time a |
2181 | quarter second into a minute. |
2182 | \row \li zzz |
2183 | \li The fractional part of the second, to millisecond precision, |
2184 | including trailing zeroes where applicable (000 to 999). For |
2185 | example, \c{"ss.zzz"} would produce \c{"00.250"} for a time a |
2186 | quarter second into a minute. |
2187 | \row \li AP or A |
2188 | \li Use AM/PM display. \c A/AP will be replaced by 'AM' or 'PM'. In |
2189 | localized forms (only relevant to \l{QLocale::toString()}), the |
2190 | locale-appropriate text is converted to upper-case. |
2191 | \row \li ap or a |
2192 | \li Use am/pm display. \c a/ap will be replaced by 'am' or 'pm'. In |
2193 | localized forms (only relevant to \l{QLocale::toString()}), the |
2194 | locale-appropriate text is converted to lower-case. |
2195 | \row \li aP or Ap |
2196 | \li Use AM/PM display (since 6.3). \c aP/Ap will be replaced by 'AM' or |
2197 | 'PM'. In localized forms (only relevant to |
2198 | \l{QLocale::toString()}), the locale-appropriate text (returned by |
2199 | \l{QLocale::amText()} or \l{QLocale::pmText()}) is used without |
2200 | change of case. |
2201 | \row \li t |
2202 | \li The timezone abbreviation (for example "CEST"). Note that time zone |
2203 | abbreviations are not unique. In particular, \l fromString() cannot |
2204 | parse this. |
2205 | \row \li tt |
2206 | \li The timezone's offset from UTC with no colon between the hours and |
2207 | minutes (for example "+0200"). |
2208 | \row \li ttt |
2209 | \li The timezone's offset from UTC with a colon between the hours and |
2210 | minutes (for example "+02:00"). |
2211 | \row \li tttt |
2212 | \li The timezone name (for example "Europe/Berlin"). Note that this |
2213 | gives no indication of whether the datetime was in daylight-saving |
2214 | time or standard time, which may lead to ambiguity if the datetime |
2215 | falls in an hour repeated by a transition between the two. The name |
2216 | used is the one provided by \l QTimeZone::displayName() with the \l |
2217 | QTimeZone::LongName type. This may depend on the operating system |
2218 | in use. |
2219 | \endtable |
2220 | |
2221 | \note To get localized forms of AM or PM (the \c{AP}, \c{ap}, \c{A}, \c{a}, |
2222 | \c{aP} or \c{Ap} formats) or of time zone representations (the \c{t} |
2223 | formats), use QLocale::system().toString(). |
2224 | |
2225 | When the timezone cannot be determined or no suitable representation of it |
2226 | is available, the \c{t} forms to represent it may be skipped. See \l |
2227 | QTimeZone::displayName() for details of when it returns an empty string. |
2228 | |
2229 | Any non-empty sequence of characters enclosed in single quotes will be |
2230 | included verbatim in the output string (stripped of the quotes), even if it |
2231 | contains formatting characters. Two consecutive single quotes ("''") are |
2232 | replaced by a single quote in the output. All other characters in the format |
2233 | string are included verbatim in the output string. |
2234 | |
2235 | Formats without separators (e.g. "ddMM") are supported but must be used with |
2236 | care, as the resulting strings aren't always reliably readable (e.g. if "dM" |
2237 | produces "212" it could mean either the 2nd of December or the 21st of |
2238 | February). |
2239 | |
2240 | Example format strings (assuming that the QTime is 14:13:09.042) |
2241 | |
2242 | \table |
2243 | \header \li Format \li Result |
2244 | \row \li hh:mm:ss.zzz \li 14:13:09.042 |
2245 | \row \li h:m:s ap \li 2:13:9 pm |
2246 | \row \li H:m:s a \li 14:13:9 pm |
2247 | \endtable |
2248 | |
2249 | \note If a format character is repeated more times than the longest |
2250 | expression in the table above using it, this part of the format will be read |
2251 | as several expressions with no separator between them; the longest above, |
2252 | possibly repeated as many times as there are copies of it, ending with a |
2253 | residue that may be a shorter expression. Thus \c{'HHHHH'} for the time |
2254 | 08:00 will contribute \c{"08088"} to the output. |
2255 | |
2256 | \sa fromString(), QDate::toString(), QDateTime::toString(), QLocale::toString() |
2257 | */ |
2258 | // ### Qt 7 The 't' format specifiers should be specific to QDateTime (compare fromString). |
2259 | QString QTime::toString(QStringView format) const |
2260 | { |
2261 | return QLocale::c().toString(time: *this, format); |
2262 | } |
2263 | #endif // datestring |
2264 | |
2265 | /*! |
2266 | Sets the time to hour \a h, minute \a m, seconds \a s and |
2267 | milliseconds \a ms. |
2268 | |
2269 | \a h must be in the range 0 to 23, \a m and \a s must be in the |
2270 | range 0 to 59, and \a ms must be in the range 0 to 999. |
2271 | Returns \c true if the set time is valid; otherwise returns \c false. |
2272 | |
2273 | \sa isValid() |
2274 | */ |
2275 | |
2276 | bool QTime::setHMS(int h, int m, int s, int ms) |
2277 | { |
2278 | if (!isValid(h,m,s,ms)) { |
2279 | mds = NullTime; // make this invalid |
2280 | return false; |
2281 | } |
2282 | mds = ((h * MINS_PER_HOUR + m) * SECS_PER_MIN + s) * MSECS_PER_SEC + ms; |
2283 | Q_ASSERT(mds >= 0 && mds < MSECS_PER_DAY); |
2284 | return true; |
2285 | } |
2286 | |
2287 | /*! |
2288 | Returns a QTime object containing a time \a s seconds later |
2289 | than the time of this object (or earlier if \a s is negative). |
2290 | |
2291 | Note that the time will wrap if it passes midnight. |
2292 | |
2293 | Returns a null time if this time is invalid. |
2294 | |
2295 | Example: |
2296 | |
2297 | \snippet code/src_corelib_time_qdatetime.cpp 5 |
2298 | |
2299 | \sa addMSecs(), secsTo(), QDateTime::addSecs() |
2300 | */ |
2301 | |
2302 | QTime QTime::addSecs(int s) const |
2303 | { |
2304 | s %= SECS_PER_DAY; |
2305 | return addMSecs(ms: s * MSECS_PER_SEC); |
2306 | } |
2307 | |
2308 | /*! |
2309 | Returns the number of seconds from this time to \a t. |
2310 | If \a t is earlier than this time, the number of seconds returned |
2311 | is negative. |
2312 | |
2313 | Because QTime measures time within a day and there are 86400 |
2314 | seconds in a day, the result is always between -86400 and 86400. |
2315 | |
2316 | secsTo() does not take into account any milliseconds. |
2317 | |
2318 | Returns 0 if either time is invalid. |
2319 | |
2320 | \sa addSecs(), QDateTime::secsTo() |
2321 | */ |
2322 | |
2323 | int QTime::secsTo(QTime t) const |
2324 | { |
2325 | if (!isValid() || !t.isValid()) |
2326 | return 0; |
2327 | |
2328 | // Truncate milliseconds as we do not want to consider them. |
2329 | int ourSeconds = ds() / MSECS_PER_SEC; |
2330 | int theirSeconds = t.ds() / MSECS_PER_SEC; |
2331 | return theirSeconds - ourSeconds; |
2332 | } |
2333 | |
2334 | /*! |
2335 | Returns a QTime object containing a time \a ms milliseconds later |
2336 | than the time of this object (or earlier if \a ms is negative). |
2337 | |
2338 | Note that the time will wrap if it passes midnight. See addSecs() |
2339 | for an example. |
2340 | |
2341 | Returns a null time if this time is invalid. |
2342 | |
2343 | \sa addSecs(), msecsTo(), QDateTime::addMSecs() |
2344 | */ |
2345 | |
2346 | QTime QTime::addMSecs(int ms) const |
2347 | { |
2348 | QTime t; |
2349 | if (isValid()) |
2350 | t.mds = QRoundingDown::qMod<MSECS_PER_DAY>(a: ds() + ms); |
2351 | return t; |
2352 | } |
2353 | |
2354 | /*! |
2355 | Returns the number of milliseconds from this time to \a t. |
2356 | If \a t is earlier than this time, the number of milliseconds returned |
2357 | is negative. |
2358 | |
2359 | Because QTime measures time within a day and there are 86400 |
2360 | seconds in a day, the result is always between -86400000 and |
2361 | 86400000 ms. |
2362 | |
2363 | Returns 0 if either time is invalid. |
2364 | |
2365 | \sa secsTo(), addMSecs(), QDateTime::msecsTo() |
2366 | */ |
2367 | |
2368 | int QTime::msecsTo(QTime t) const |
2369 | { |
2370 | if (!isValid() || !t.isValid()) |
2371 | return 0; |
2372 | return t.ds() - ds(); |
2373 | } |
2374 | |
2375 | |
2376 | /*! |
2377 | \fn bool QTime::operator==(const QTime &lhs, const QTime &rhs) |
2378 | |
2379 | Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false. |
2380 | */ |
2381 | |
2382 | /*! |
2383 | \fn bool QTime::operator!=(const QTime &lhs, const QTime &rhs) |
2384 | |
2385 | Returns \c true if \a lhs is different from \a rhs; otherwise returns \c false. |
2386 | */ |
2387 | |
2388 | /*! |
2389 | \fn bool QTime::operator<(const QTime &lhs, const QTime &rhs) |
2390 | |
2391 | Returns \c true if \a lhs is earlier than \a rhs; otherwise returns \c false. |
2392 | */ |
2393 | |
2394 | /*! |
2395 | \fn bool QTime::operator<=(const QTime &lhs, const QTime &rhs) |
2396 | |
2397 | Returns \c true if \a lhs is earlier than or equal to \a rhs; |
2398 | otherwise returns \c false. |
2399 | */ |
2400 | |
2401 | /*! |
2402 | \fn bool QTime::operator>(const QTime &lhs, const QTime &rhs) |
2403 | |
2404 | Returns \c true if \a lhs is later than \a rhs; otherwise returns \c false. |
2405 | */ |
2406 | |
2407 | /*! |
2408 | \fn bool QTime::operator>=(const QTime &lhs, const QTime &rhs) |
2409 | |
2410 | Returns \c true if \a lhs is later than or equal to \a rhs; |
2411 | otherwise returns \c false. |
2412 | */ |
2413 | |
2414 | /*! |
2415 | \fn QTime QTime::fromMSecsSinceStartOfDay(int msecs) |
2416 | |
2417 | Returns a new QTime instance with the time set to the number of \a msecs |
2418 | since the start of the day, i.e. since 00:00:00. |
2419 | |
2420 | If \a msecs falls outside the valid range an invalid QTime will be returned. |
2421 | |
2422 | \sa msecsSinceStartOfDay() |
2423 | */ |
2424 | |
2425 | /*! |
2426 | \fn int QTime::msecsSinceStartOfDay() const |
2427 | |
2428 | Returns the number of msecs since the start of the day, i.e. since 00:00:00. |
2429 | |
2430 | \sa fromMSecsSinceStartOfDay() |
2431 | */ |
2432 | |
2433 | /*! |
2434 | \fn QTime::currentTime() |
2435 | |
2436 | Returns the current time as reported by the system clock. |
2437 | |
2438 | Note that the accuracy depends on the accuracy of the underlying |
2439 | operating system; not all systems provide 1-millisecond accuracy. |
2440 | |
2441 | Furthermore, currentTime() only increases within each day; it shall drop by |
2442 | 24 hours each time midnight passes; and, beside this, changes in it may not |
2443 | correspond to elapsed time, if a daylight-saving transition intervenes. |
2444 | |
2445 | \sa QDateTime::currentDateTime(), QDateTime::currentDateTimeUtc() |
2446 | */ |
2447 | |
2448 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
2449 | |
2450 | static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *isMidnight24) |
2451 | { |
2452 | Q_ASSERT(format == Qt::TextDate || format == Qt::ISODate || format == Qt::ISODateWithMs); |
2453 | if (isMidnight24) |
2454 | *isMidnight24 = false; |
2455 | // Match /\d\d(:\d\d(:\d\d)?)?([,.]\d+)?/ as "HH[:mm[:ss]][.zzz]" |
2456 | // The fractional part, if present, is in the same units as the field it follows. |
2457 | // TextDate restricts fractional parts to the seconds field. |
2458 | |
2459 | QStringView tail; |
2460 | const qsizetype dot = string.indexOf(c: u'.'), comma = string.indexOf(c: u','); |
2461 | if (dot != -1) { |
2462 | tail = string.sliced(pos: dot + 1); |
2463 | if (tail.indexOf(c: u'.') != -1) // Forbid second dot: |
2464 | return QTime(); |
2465 | string = string.first(n: dot); |
2466 | } else if (comma != -1) { |
2467 | tail = string.sliced(pos: comma + 1); |
2468 | string = string.first(n: comma); |
2469 | } |
2470 | if (tail.indexOf(c: u',') != -1) // Forbid comma after first dot-or-comma: |
2471 | return QTime(); |
2472 | |
2473 | const ParsedInt frac = readInt(text: tail); |
2474 | // There must be *some* digits in a fractional part; and it must be all digits: |
2475 | if (tail.isEmpty() ? dot != -1 || comma != -1 : !frac.ok()) |
2476 | return QTime(); |
2477 | Q_ASSERT(frac.ok() ^ tail.isEmpty()); |
2478 | double fraction = frac.ok() ? frac.result * std::pow(x: 0.1, y: tail.size()) : 0.0; |
2479 | |
2480 | const qsizetype size = string.size(); |
2481 | if (size < 2 || size > 8) |
2482 | return QTime(); |
2483 | |
2484 | ParsedInt hour = readInt(text: string.first(n: 2)); |
2485 | if (!hour.ok() || hour.result > (format == Qt::TextDate ? 23 : 24)) |
2486 | return QTime(); |
2487 | |
2488 | ParsedInt minute{}; |
2489 | if (string.size() > 2) { |
2490 | if (string[2] == u':' && string.size() > 4) |
2491 | minute = readInt(text: string.sliced(pos: 3, n: 2)); |
2492 | if (!minute.ok() || minute.result >= MINS_PER_HOUR) |
2493 | return QTime(); |
2494 | } else if (format == Qt::TextDate) { // Requires minutes |
2495 | return QTime(); |
2496 | } else if (frac.ok()) { |
2497 | Q_ASSERT(!(fraction < 0.0) && fraction < 1.0); |
2498 | fraction *= MINS_PER_HOUR; |
2499 | minute.result = qulonglong(fraction); |
2500 | fraction -= minute.result; |
2501 | } |
2502 | |
2503 | ParsedInt second{}; |
2504 | if (string.size() > 5) { |
2505 | if (string[5] == u':' && string.size() == 8) |
2506 | second = readInt(text: string.sliced(pos: 6, n: 2)); |
2507 | if (!second.ok() || second.result >= SECS_PER_MIN) |
2508 | return QTime(); |
2509 | } else if (frac.ok()) { |
2510 | if (format == Qt::TextDate) // Doesn't allow fraction of minutes |
2511 | return QTime(); |
2512 | Q_ASSERT(!(fraction < 0.0) && fraction < 1.0); |
2513 | fraction *= SECS_PER_MIN; |
2514 | second.result = qulonglong(fraction); |
2515 | fraction -= second.result; |
2516 | } |
2517 | |
2518 | Q_ASSERT(!(fraction < 0.0) && fraction < 1.0); |
2519 | // Round millis to nearest (unlike minutes and seconds, rounded down): |
2520 | int msec = frac.ok() ? qRound(d: MSECS_PER_SEC * fraction) : 0; |
2521 | // But handle overflow gracefully: |
2522 | if (msec == MSECS_PER_SEC) { |
2523 | // If we can (when data were otherwise valid) validly propagate overflow |
2524 | // into other fields, do so: |
2525 | if (isMidnight24 || hour.result < 23 || minute.result < 59 || second.result < 59) { |
2526 | msec = 0; |
2527 | if (++second.result == SECS_PER_MIN) { |
2528 | second.result = 0; |
2529 | if (++minute.result == MINS_PER_HOUR) { |
2530 | minute.result = 0; |
2531 | ++hour.result; |
2532 | // May need to propagate further via isMidnight24, see below |
2533 | } |
2534 | } |
2535 | } else { |
2536 | // QTime::fromString() or Qt::TextDate: rounding up would cause |
2537 | // 23:59:59.999... to become invalid; clip to 999 ms instead: |
2538 | msec = MSECS_PER_SEC - 1; |
2539 | } |
2540 | } |
2541 | |
2542 | // For ISO date format, 24:0:0 means 0:0:0 on the next day: |
2543 | if (hour.result == 24 && minute.result == 0 && second.result == 0 && msec == 0) { |
2544 | Q_ASSERT(format != Qt::TextDate); // It clipped hour at 23, above. |
2545 | if (isMidnight24) |
2546 | *isMidnight24 = true; |
2547 | hour.result = 0; |
2548 | } |
2549 | |
2550 | return QTime(hour.result, minute.result, second.result, msec); |
2551 | } |
2552 | |
2553 | /*! |
2554 | \fn QTime QTime::fromString(const QString &string, Qt::DateFormat format) |
2555 | |
2556 | Returns the time represented in the \a string as a QTime using the |
2557 | \a format given, or an invalid time if this is not possible. |
2558 | |
2559 | \sa toString(), QLocale::toTime() |
2560 | */ |
2561 | |
2562 | /*! |
2563 | \overload |
2564 | \since 6.0 |
2565 | */ |
2566 | QTime QTime::fromString(QStringView string, Qt::DateFormat format) |
2567 | { |
2568 | if (string.isEmpty()) |
2569 | return QTime(); |
2570 | |
2571 | switch (format) { |
2572 | case Qt::RFC2822Date: |
2573 | return rfcDateImpl(s: string).time; |
2574 | case Qt::ISODate: |
2575 | case Qt::ISODateWithMs: |
2576 | case Qt::TextDate: |
2577 | default: |
2578 | return fromIsoTimeString(string, format, isMidnight24: nullptr); |
2579 | } |
2580 | } |
2581 | |
2582 | /*! |
2583 | \fn QTime QTime::fromString(const QString &string, const QString &format) |
2584 | |
2585 | Returns the QTime represented by the \a string, using the \a |
2586 | format given, or an invalid time if the string cannot be parsed. |
2587 | |
2588 | These expressions may be used for the format: |
2589 | |
2590 | \table |
2591 | \header \li Expression \li Output |
2592 | \row \li h |
2593 | \li The hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) |
2594 | \row \li hh |
2595 | \li The hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) |
2596 | \row \li H |
2597 | \li The hour without a leading zero (0 to 23, even with AM/PM display) |
2598 | \row \li HH |
2599 | \li The hour with a leading zero (00 to 23, even with AM/PM display) |
2600 | \row \li m \li The minute without a leading zero (0 to 59) |
2601 | \row \li mm \li The minute with a leading zero (00 to 59) |
2602 | \row \li s \li The whole second, without any leading zero (0 to 59) |
2603 | \row \li ss \li The whole second, with a leading zero where applicable (00 to 59) |
2604 | \row \li z or zz |
2605 | \li The fractional part of the second, as would usually follow a |
2606 | decimal point, without requiring trailing zeroes (0 to 999). Thus |
2607 | \c{"s.z"} matches the seconds with up to three digits of fractional |
2608 | part supplying millisecond precision, without needing trailing |
2609 | zeroes. For example, \c{"s.z"} would recognize either \c{"00.250"} |
2610 | or \c{"0.25"} as representing a time a quarter second into its |
2611 | minute. |
2612 | \row \li zzz |
2613 | \li Three digit fractional part of the second, to millisecond |
2614 | precision, including trailing zeroes where applicable (000 to 999). |
2615 | For example, \c{"ss.zzz"} would reject \c{"0.25"} but recognize |
2616 | \c{"00.250"} as representing a time a quarter second into its |
2617 | minute. |
2618 | \row \li AP, A, ap, a, aP or Ap |
2619 | \li Either 'AM' indicating a time before 12:00 or 'PM' for later times, |
2620 | matched case-insensitively. |
2621 | \endtable |
2622 | |
2623 | All other input characters will be treated as text. Any non-empty sequence |
2624 | of characters enclosed in single quotes will also be treated (stripped of |
2625 | the quotes) as text and not be interpreted as expressions. |
2626 | |
2627 | \snippet code/src_corelib_time_qdatetime.cpp 6 |
2628 | |
2629 | If the format is not satisfied, an invalid QTime is returned. |
2630 | Expressions that do not expect leading zeroes to be given (h, m, s |
2631 | and z) are greedy. This means that they will use two digits (or three, for z) even if |
2632 | this puts them outside the range of accepted values and leaves too |
2633 | few digits for other sections. For example, the following string |
2634 | could have meant 00:07:10, but the m will grab two digits, resulting |
2635 | in an invalid time: |
2636 | |
2637 | \snippet code/src_corelib_time_qdatetime.cpp 7 |
2638 | |
2639 | Any field that is not represented in the format will be set to zero. |
2640 | For example: |
2641 | |
2642 | \snippet code/src_corelib_time_qdatetime.cpp 8 |
2643 | |
2644 | \note If localized forms of am or pm (the AP, ap, Ap, aP, A or a formats) |
2645 | are to be recognized, use QLocale::system().toTime(). |
2646 | |
2647 | \note If a format character is repeated more times than the longest |
2648 | expression in the table above using it, this part of the format will be read |
2649 | as several expressions with no separator between them; the longest above, |
2650 | possibly repeated as many times as there are copies of it, ending with a |
2651 | residue that may be a shorter expression. Thus \c{'HHHHH'} would match |
2652 | \c{"08088"} or \c{"080808"} and set the hour to 8; if the time string |
2653 | contained "070809" it would "match" but produce an inconsistent result, |
2654 | leading to an invalid time. |
2655 | |
2656 | \sa toString(), QDateTime::fromString(), QDate::fromString(), |
2657 | QLocale::toTime(), QLocale::toDateTime() |
2658 | */ |
2659 | |
2660 | /*! |
2661 | \fn QTime QTime::fromString(QStringView string, QStringView format) |
2662 | \overload |
2663 | \since 6.0 |
2664 | */ |
2665 | |
2666 | /*! |
2667 | \overload |
2668 | \since 6.0 |
2669 | */ |
2670 | QTime QTime::fromString(const QString &string, QStringView format) |
2671 | { |
2672 | QTime time; |
2673 | #if QT_CONFIG(datetimeparser) |
2674 | QDateTimeParser dt(QMetaType::QTime, QDateTimeParser::FromString, QCalendar()); |
2675 | dt.setDefaultLocale(QLocale::c()); |
2676 | if (dt.parseFormat(format)) |
2677 | dt.fromString(text: string, date: nullptr, time: &time); |
2678 | #else |
2679 | Q_UNUSED(string); |
2680 | Q_UNUSED(format); |
2681 | #endif |
2682 | return time; |
2683 | } |
2684 | #endif // datestring |
2685 | |
2686 | |
2687 | /*! |
2688 | \overload |
2689 | |
2690 | Returns \c true if the specified time is valid; otherwise returns |
2691 | false. |
2692 | |
2693 | The time is valid if \a h is in the range 0 to 23, \a m and |
2694 | \a s are in the range 0 to 59, and \a ms is in the range 0 to 999. |
2695 | |
2696 | Example: |
2697 | |
2698 | \snippet code/src_corelib_time_qdatetime.cpp 9 |
2699 | */ |
2700 | |
2701 | bool QTime::isValid(int h, int m, int s, int ms) |
2702 | { |
2703 | return (uint(h) < 24 && uint(m) < MINS_PER_HOUR && uint(s) < SECS_PER_MIN |
2704 | && uint(ms) < MSECS_PER_SEC); |
2705 | } |
2706 | |
2707 | /***************************************************************************** |
2708 | QDateTime static helper functions |
2709 | *****************************************************************************/ |
2710 | |
2711 | // get the types from QDateTime (through QDateTimePrivate) |
2712 | typedef QDateTimePrivate::QDateTimeShortData ShortData; |
2713 | typedef QDateTimePrivate::QDateTimeData QDateTimeData; |
2714 | |
2715 | // Converts milliseconds since the start of 1970 into a date and/or time: |
2716 | static qint64 msecsToJulianDay(qint64 msecs) |
2717 | { |
2718 | return JULIAN_DAY_FOR_EPOCH + QRoundingDown::qDiv<MSECS_PER_DAY>(a: msecs); |
2719 | } |
2720 | |
2721 | static QDate msecsToDate(qint64 msecs) |
2722 | { |
2723 | return QDate::fromJulianDay(jd_: msecsToJulianDay(msecs)); |
2724 | } |
2725 | |
2726 | static QTime msecsToTime(qint64 msecs) |
2727 | { |
2728 | return QTime::fromMSecsSinceStartOfDay(msecs: QRoundingDown::qMod<MSECS_PER_DAY>(a: msecs)); |
2729 | } |
2730 | |
2731 | // True if combining days with millis overflows; otherwise, stores result in *sumMillis |
2732 | // The inputs should not have opposite signs. |
2733 | static inline bool daysAndMillisOverflow(qint64 days, qint64 millisInDay, qint64 *sumMillis) |
2734 | { |
2735 | return qMulOverflow(v1: days, std::integral_constant<qint64, MSECS_PER_DAY>(), r: sumMillis) |
2736 | || qAddOverflow(v1: *sumMillis, v2: millisInDay, r: sumMillis); |
2737 | } |
2738 | |
2739 | // Converts a date/time value into msecs |
2740 | static qint64 timeToMSecs(QDate date, QTime time) |
2741 | { |
2742 | qint64 days = date.toJulianDay() - JULIAN_DAY_FOR_EPOCH; |
2743 | qint64 msecs, dayms = time.msecsSinceStartOfDay(); |
2744 | if (days < 0 && dayms > 0) { |
2745 | ++days; |
2746 | dayms -= MSECS_PER_DAY; |
2747 | } |
2748 | if (daysAndMillisOverflow(days, millisInDay: dayms, sumMillis: &msecs)) { |
2749 | using Bound = std::numeric_limits<qint64>; |
2750 | return days < 0 ? Bound::min() : Bound::max(); |
2751 | } |
2752 | return msecs; |
2753 | } |
2754 | |
2755 | /*! |
2756 | \internal |
2757 | Tests whether system functions can handle a given time. |
2758 | |
2759 | The range of milliseconds for which the time_t-based functions work depends |
2760 | somewhat on platform (see computeSystemMillisRange() for details). This |
2761 | function tests whether the UTC time \a millis milliseconds from the epoch is |
2762 | in the supported range. |
2763 | |
2764 | To test a local time, pass an upper bound on the magnitude of time-zone |
2765 | correction potentially needed as \a slack: in this case the range is |
2766 | extended by this many milliseconds at each end (where applicable). The |
2767 | function then returns true precisely if \a millis is within this (possibly) |
2768 | widened range. This doesn't guarantee that the time_t functions can handle |
2769 | the time, so check their returns to be sure. Values for which the function |
2770 | returns false should be assumed unrepresentable. |
2771 | */ |
2772 | static inline bool millisInSystemRange(qint64 millis, qint64 slack = 0) |
2773 | { |
2774 | static const auto bounds = QLocalTime::computeSystemMillisRange(); |
2775 | return (bounds.minClip || millis >= bounds.min - slack) |
2776 | && (bounds.maxClip || millis <= bounds.max + slack); |
2777 | } |
2778 | |
2779 | /*! |
2780 | \internal |
2781 | Returns a year, in the system range, with the same day-of-week pattern |
2782 | |
2783 | Returns the number of a year, in the range supported by system time_t |
2784 | functions, that starts and ends on the same days of the week as \a year. |
2785 | This implies it is a leap year precisely if \a year is. If year is before |
2786 | the epoch, a year early in the supported range is used; otherwise, one late |
2787 | in that range. For a leap year, this may be as much as 26 years years from |
2788 | the range's relevant end; for normal years at most a decade from the end. |
2789 | |
2790 | This ensures that any DST rules based on, e.g., the last Sunday in a |
2791 | particular month will select the same date in the returned year as they |
2792 | would if applied to \a year. Of course, the zone's rules may be different in |
2793 | \a year than in the selected year, but it's hard to do better. |
2794 | */ |
2795 | static int systemTimeYearMatching(int year) |
2796 | { |
2797 | #if defined(Q_OS_WIN) || defined(Q_OS_WASM)// They don't support times before the epoch |
2798 | static constexpr int forLeapEarly[] = { 1984, 1996, 1980, 1992, 1976, 1988, 1972 }; |
2799 | static constexpr int regularEarly[] = { 1978, 1973, 1974, 1975, 1970, 1971, 1977 }; |
2800 | #else // First year fully in 32-bit time_t range is 1902 |
2801 | static constexpr int forLeapEarly[] = { 1928, 1912, 1924, 1908, 1920, 1904, 1916 }; |
2802 | static constexpr int regularEarly[] = { 1905, 1906, 1907, 1902, 1903, 1909, 1910 }; |
2803 | #endif |
2804 | static constexpr int forLeapLate[] = { 2012, 2024, 2036, 2020, 2032, 2016, 2028 }; |
2805 | static constexpr int regularLate[] = { 2034, 2035, 2030, 2031, 2037, 2027, 2033 }; |
2806 | const int dow = QGregorianCalendar::yearStartWeekDay(year); |
2807 | Q_ASSERT(dow == QDate(year, 1, 1).dayOfWeek()); |
2808 | const int res = (QGregorianCalendar::leapTest(year) |
2809 | ? (year < 1970 ? forLeapEarly : forLeapLate) |
2810 | : (year < 1970 ? regularEarly : regularLate))[dow == 7 ? 0 : dow]; |
2811 | Q_ASSERT(QDate(res, 1, 1).dayOfWeek() == dow); |
2812 | Q_ASSERT(QDate(res, 12, 31).dayOfWeek() == QDate(year, 12, 31).dayOfWeek()); |
2813 | return res; |
2814 | } |
2815 | |
2816 | // Sets up d and status to represent local time at the given UTC msecs since epoch: |
2817 | QDateTimePrivate::ZoneState QDateTimePrivate::expressUtcAsLocal(qint64 utcMSecs) |
2818 | { |
2819 | ZoneState result{utcMSecs}; |
2820 | // Within the time_t supported range, localtime() can handle it: |
2821 | if (millisInSystemRange(millis: utcMSecs)) { |
2822 | result = QLocalTime::utcToLocal(utcMillis: utcMSecs); |
2823 | if (result.valid) |
2824 | return result; |
2825 | } |
2826 | |
2827 | // Docs state any LocalTime after 2038-01-18 *will* have any DST applied. |
2828 | // When this falls outside the supported range, we need to fake it. |
2829 | #if QT_CONFIG(timezone) // Use the system time-zone. |
2830 | if (const auto sys = QTimeZone::systemTimeZone(); sys.isValid()) { |
2831 | result.offset = sys.d->offsetFromUtc(atMSecsSinceEpoch: utcMSecs); |
2832 | if (qAddOverflow(v1: utcMSecs, v2: result.offset * MSECS_PER_SEC, r: &result.when)) |
2833 | return result; |
2834 | result.dst = sys.d->isDaylightTime(atMSecsSinceEpoch: utcMSecs) ? DaylightTime : StandardTime; |
2835 | result.valid = true; |
2836 | return result; |
2837 | } |
2838 | #endif // timezone |
2839 | |
2840 | // Kludge |
2841 | // Do the conversion in a year with the same days of the week, so DST |
2842 | // dates might be right, and adjust by the number of days that was off: |
2843 | const qint64 jd = msecsToJulianDay(msecs: utcMSecs); |
2844 | const auto ymd = QGregorianCalendar::partsFromJulian(jd); |
2845 | qint64 diffMillis, fakeUtc; |
2846 | const auto fakeJd = QGregorianCalendar::julianFromParts(year: systemTimeYearMatching(year: ymd.year), |
2847 | month: ymd.month, day: ymd.day); |
2848 | if (Q_UNLIKELY(!fakeJd |
2849 | || qMulOverflow(jd - *fakeJd, std::integral_constant<qint64, MSECS_PER_DAY>(), |
2850 | &diffMillis) |
2851 | || qSubOverflow(utcMSecs, diffMillis, &fakeUtc))) { |
2852 | return result; |
2853 | } |
2854 | |
2855 | result = QLocalTime::utcToLocal(utcMillis: fakeUtc); |
2856 | // Now correct result.when for the use of the fake date: |
2857 | if (!result.valid || qAddOverflow(v1: result.when, v2: diffMillis, r: &result.when)) { |
2858 | // If utcToLocal() failed, its return has the fake when; restore utcMSecs. |
2859 | // Fail on overflow, but preserve offset and DST-ness. |
2860 | result.when = utcMSecs; |
2861 | result.valid = false; |
2862 | } |
2863 | return result; |
2864 | } |
2865 | |
2866 | static auto millisToWithinRange(qint64 millis) |
2867 | { |
2868 | struct R { |
2869 | qint64 shifted = 0; |
2870 | bool good = false; |
2871 | } result; |
2872 | qint64 jd = msecsToJulianDay(msecs: millis); |
2873 | auto ymd = QGregorianCalendar::partsFromJulian(jd); |
2874 | const auto fakeJd = QGregorianCalendar::julianFromParts(year: systemTimeYearMatching(year: ymd.year), |
2875 | month: ymd.month, day: ymd.day); |
2876 | result.good = fakeJd && !daysAndMillisOverflow(days: *fakeJd - jd, millisInDay: millis, sumMillis: &result.shifted); |
2877 | return result; |
2878 | } |
2879 | |
2880 | /*! |
2881 | \internal |
2882 | \enum QDateTimePrivate::TransitionOption |
2883 | |
2884 | This enumeration is used to resolve datetime combinations which fall in \l |
2885 | {Timezone transitions}. The transition is described as a "gap" if there are |
2886 | time representations skipped over by the zone, as is common in the "spring |
2887 | forward" transitions in many zones on entering daylight-saving time. The |
2888 | transition is described as a "fold" if there are time representations |
2889 | repeated in the zone, as in a "fall back" transition out of daylight-saving |
2890 | time. |
2891 | |
2892 | When the options specified do not determine a resolution for a datetime, it |
2893 | is marked invalid. |
2894 | |
2895 | The prepared option sets above are in fact composed from low-level atomic |
2896 | options. For each of gap and fold you can chose between two candidate times, |
2897 | one before or after the transition, based on the time requested; or you can |
2898 | pick the moment of transition, or the start or end of the transition |
2899 | interval. For a gap, the start and end of the interval are the moment of the |
2900 | transition, but for a repeated interval the start of the first pass is the |
2901 | start of the transition interval, the end of the second pass is the end of |
2902 | the transition interval and the moment of the transition itself is both the |
2903 | end of the first pass and the start of the second. |
2904 | |
2905 | \value GapUseBefore For a time in a gap, use a time before the transition, |
2906 | as if stepping back from a later time. |
2907 | \value GapUseAfter For a time in a gap, use a time after the transition, as |
2908 | if stepping forward from an earlier time. |
2909 | \value FoldUseBefore For a repeated time, use the first candidate, which is |
2910 | before the transition. |
2911 | \value FoldUseAfter For a repeated time, use the second candidate, which is |
2912 | after the transition. |
2913 | \value FlipForReverseDst For "reversed" DST, this reverses the preceding |
2914 | four options (see below). |
2915 | |
2916 | The last has no effect unless the "daylight-saving" time side of the |
2917 | transition is known to have a lower offset from UTC than the standard time |
2918 | side. (This is the "reversed" DST case of \l {Timezone transitions}.) In |
2919 | that case, if other options would select a time after the transition, a time |
2920 | before is used instead, and vice versa. This effectively turns a preference |
2921 | for the side with lower offset into a preference for the side that is |
2922 | officially standard time, even if it has higher offset; and conversely a |
2923 | preference for higher offset into a preference for daylight-saving time, |
2924 | even if it has a lower offset. This option has no effect on a resolution |
2925 | that selects the moment of transition or the start or end of the transition |
2926 | interval. |
2927 | |
2928 | The result of combining more than one of the \c GapUse* options is |
2929 | undefined; likewise for the \c FoldUse*. Each of QDateTime's |
2930 | TransitionResolution values, aside from Reject, maps to a combination that |
2931 | incorporates one from each of these sets. |
2932 | */ |
2933 | |
2934 | constexpr static QDateTimePrivate::TransitionOptions |
2935 | toTransitionOptions(QDateTime::TransitionResolution res) |
2936 | { |
2937 | switch (res) { |
2938 | case QDateTime::TransitionResolution::RelativeToBefore: |
2939 | return QDateTimePrivate::GapUseAfter | QDateTimePrivate::FoldUseBefore; |
2940 | case QDateTime::TransitionResolution::RelativeToAfter: |
2941 | return QDateTimePrivate::GapUseBefore | QDateTimePrivate::FoldUseAfter; |
2942 | case QDateTime::TransitionResolution::PreferBefore: |
2943 | return QDateTimePrivate::GapUseBefore | QDateTimePrivate::FoldUseBefore; |
2944 | case QDateTime::TransitionResolution::PreferAfter: |
2945 | return QDateTimePrivate::GapUseAfter | QDateTimePrivate::FoldUseAfter; |
2946 | case QDateTime::TransitionResolution::PreferStandard: |
2947 | return QDateTimePrivate::GapUseBefore |
2948 | | QDateTimePrivate::FoldUseAfter |
2949 | | QDateTimePrivate::FlipForReverseDst; |
2950 | case QDateTime::TransitionResolution::PreferDaylightSaving: |
2951 | return QDateTimePrivate::GapUseAfter |
2952 | | QDateTimePrivate::FoldUseBefore |
2953 | | QDateTimePrivate::FlipForReverseDst; |
2954 | case QDateTime::TransitionResolution::Reject: break; |
2955 | } |
2956 | return {}; |
2957 | } |
2958 | |
2959 | constexpr static QDateTimePrivate::TransitionOptions |
2960 | toTransitionOptions(QDateTimePrivate::DaylightStatus dst) |
2961 | { |
2962 | return toTransitionOptions(res: dst == QDateTimePrivate::DaylightTime |
2963 | ? QDateTime::TransitionResolution::PreferDaylightSaving |
2964 | : QDateTime::TransitionResolution::PreferStandard); |
2965 | } |
2966 | |
2967 | QString QDateTimePrivate::localNameAtMillis(qint64 millis, DaylightStatus dst) |
2968 | { |
2969 | const QDateTimePrivate::TransitionOptions resolve = toTransitionOptions(dst); |
2970 | QString abbreviation; |
2971 | if (millisInSystemRange(millis, slack: MSECS_PER_DAY)) { |
2972 | abbreviation = QLocalTime::localTimeAbbbreviationAt(local: millis, resolve); |
2973 | if (!abbreviation.isEmpty()) |
2974 | return abbreviation; |
2975 | } |
2976 | |
2977 | // Otherwise, outside the system range. |
2978 | #if QT_CONFIG(timezone) |
2979 | // Use the system zone: |
2980 | const auto sys = QTimeZone::systemTimeZone(); |
2981 | if (sys.isValid()) { |
2982 | ZoneState state = zoneStateAtMillis(zone: sys, millis, resolve); |
2983 | if (state.valid) |
2984 | return sys.d->abbreviation(atMSecsSinceEpoch: state.when - state.offset * MSECS_PER_SEC); |
2985 | } |
2986 | #endif // timezone |
2987 | |
2988 | // Kludge |
2989 | // Use a time in the system range with the same day-of-week pattern to its year: |
2990 | auto fake = millisToWithinRange(millis); |
2991 | if (Q_LIKELY(fake.good)) |
2992 | return QLocalTime::localTimeAbbbreviationAt(local: fake.shifted, resolve); |
2993 | |
2994 | // Overflow, apparently. |
2995 | return {}; |
2996 | } |
2997 | |
2998 | // Determine the offset from UTC at the given local time as millis. |
2999 | QDateTimePrivate::ZoneState QDateTimePrivate::localStateAtMillis( |
3000 | qint64 millis, QDateTimePrivate::TransitionOptions resolve) |
3001 | { |
3002 | // First, if millis is within a day of the viable range, try mktime() in |
3003 | // case it does fall in the range and gets useful information: |
3004 | if (millisInSystemRange(millis, slack: MSECS_PER_DAY)) { |
3005 | auto result = QLocalTime::mapLocalTime(local: millis, resolve); |
3006 | if (result.valid) |
3007 | return result; |
3008 | } |
3009 | |
3010 | // Otherwise, outside the system range. |
3011 | #if QT_CONFIG(timezone) |
3012 | // Use the system zone: |
3013 | const auto sys = QTimeZone::systemTimeZone(); |
3014 | if (sys.isValid()) |
3015 | return zoneStateAtMillis(zone: sys, millis, resolve); |
3016 | #endif // timezone |
3017 | |
3018 | // Kludge |
3019 | // Use a time in the system range with the same day-of-week pattern to its year: |
3020 | auto fake = millisToWithinRange(millis); |
3021 | if (Q_LIKELY(fake.good)) { |
3022 | auto result = QLocalTime::mapLocalTime(local: fake.shifted, resolve); |
3023 | if (result.valid) { |
3024 | qint64 adjusted; |
3025 | if (Q_UNLIKELY(qAddOverflow(result.when, millis - fake.shifted, &adjusted))) { |
3026 | using Bound = std::numeric_limits<qint64>; |
3027 | adjusted = millis < fake.shifted ? Bound::min() : Bound::max(); |
3028 | } |
3029 | result.when = adjusted; |
3030 | } else { |
3031 | result.when = millis; |
3032 | } |
3033 | return result; |
3034 | } |
3035 | // Overflow, apparently. |
3036 | return {millis}; |
3037 | } |
3038 | |
3039 | #if QT_CONFIG(timezone) |
3040 | // For a TimeZone and a time expressed in zone msecs encoding, compute the |
3041 | // actual DST-ness and offset, adjusting the time if needed to escape a |
3042 | // spring-forward. |
3043 | QDateTimePrivate::ZoneState QDateTimePrivate::zoneStateAtMillis( |
3044 | const QTimeZone &zone, qint64 millis, QDateTimePrivate::TransitionOptions resolve) |
3045 | { |
3046 | Q_ASSERT(zone.isValid()); |
3047 | Q_ASSERT(zone.timeSpec() == Qt::TimeZone); |
3048 | return zone.d->stateAtZoneTime(forLocalMSecs: millis, resolve); |
3049 | } |
3050 | #endif // timezone |
3051 | |
3052 | static inline QDateTimePrivate::ZoneState stateAtMillis(const QTimeZone &zone, qint64 millis, |
3053 | QDateTimePrivate::TransitionOptions resolve) |
3054 | { |
3055 | if (zone.timeSpec() == Qt::LocalTime) |
3056 | return QDateTimePrivate::localStateAtMillis(millis, resolve); |
3057 | #if QT_CONFIG(timezone) |
3058 | if (zone.timeSpec() == Qt::TimeZone && zone.isValid()) |
3059 | return QDateTimePrivate::zoneStateAtMillis(zone, millis, resolve); |
3060 | #endif |
3061 | return {millis}; |
3062 | } |
3063 | |
3064 | static inline bool specCanBeSmall(Qt::TimeSpec spec) |
3065 | { |
3066 | return spec == Qt::LocalTime || spec == Qt::UTC; |
3067 | } |
3068 | |
3069 | static inline bool msecsCanBeSmall(qint64 msecs) |
3070 | { |
3071 | if constexpr (!QDateTimeData::CanBeSmall) |
3072 | return false; |
3073 | |
3074 | ShortData sd; |
3075 | sd.msecs = qintptr(msecs); |
3076 | return sd.msecs == msecs; |
3077 | } |
3078 | |
3079 | static constexpr inline |
3080 | QDateTimePrivate::StatusFlags mergeSpec(QDateTimePrivate::StatusFlags status, Qt::TimeSpec spec) |
3081 | { |
3082 | status &= ~QDateTimePrivate::TimeSpecMask; |
3083 | status |= QDateTimePrivate::StatusFlags::fromInt(i: int(spec) << QDateTimePrivate::TimeSpecShift); |
3084 | return status; |
3085 | } |
3086 | |
3087 | static constexpr inline Qt::TimeSpec (QDateTimePrivate::StatusFlags status) |
3088 | { |
3089 | return Qt::TimeSpec((status & QDateTimePrivate::TimeSpecMask).toInt() >> QDateTimePrivate::TimeSpecShift); |
3090 | } |
3091 | |
3092 | // Set the Daylight Status if LocalTime set via msecs |
3093 | static constexpr inline QDateTimePrivate::StatusFlags |
3094 | mergeDaylightStatus(QDateTimePrivate::StatusFlags sf, QDateTimePrivate::DaylightStatus status) |
3095 | { |
3096 | sf &= ~QDateTimePrivate::DaylightMask; |
3097 | if (status == QDateTimePrivate::DaylightTime) { |
3098 | sf |= QDateTimePrivate::SetToDaylightTime; |
3099 | } else if (status == QDateTimePrivate::StandardTime) { |
3100 | sf |= QDateTimePrivate::SetToStandardTime; |
3101 | } |
3102 | return sf; |
3103 | } |
3104 | |
3105 | // Get the DST Status if LocalTime set via msecs |
3106 | static constexpr inline |
3107 | QDateTimePrivate::DaylightStatus (QDateTimePrivate::StatusFlags status) |
3108 | { |
3109 | if (status.testFlag(flag: QDateTimePrivate::SetToDaylightTime)) |
3110 | return QDateTimePrivate::DaylightTime; |
3111 | if (status.testFlag(flag: QDateTimePrivate::SetToStandardTime)) |
3112 | return QDateTimePrivate::StandardTime; |
3113 | return QDateTimePrivate::UnknownDaylightTime; |
3114 | } |
3115 | |
3116 | static inline qint64 getMSecs(const QDateTimeData &d) |
3117 | { |
3118 | if (d.isShort()) { |
3119 | // same as, but producing better code |
3120 | //return d.data.msecs; |
3121 | return qintptr(d.d) >> 8; |
3122 | } |
3123 | return d->m_msecs; |
3124 | } |
3125 | |
3126 | static inline QDateTimePrivate::StatusFlags getStatus(const QDateTimeData &d) |
3127 | { |
3128 | if (d.isShort()) { |
3129 | // same as, but producing better code |
3130 | //return StatusFlag(d.data.status); |
3131 | return QDateTimePrivate::StatusFlag(qintptr(d.d) & 0xFF); |
3132 | } |
3133 | return d->m_status; |
3134 | } |
3135 | |
3136 | static inline Qt::TimeSpec getSpec(const QDateTimeData &d) |
3137 | { |
3138 | return extractSpec(status: getStatus(d)); |
3139 | } |
3140 | |
3141 | /* True if we *can cheaply determine* that a and b use the same offset. |
3142 | If they use different offsets or it would be expensive to find out, false. |
3143 | Calls to toMSecsSinceEpoch() are expensive, for these purposes. |
3144 | See QDateTime's comparison operators. |
3145 | */ |
3146 | static inline bool usesSameOffset(const QDateTimeData &a, const QDateTimeData &b) |
3147 | { |
3148 | const auto status = getStatus(d: a); |
3149 | if (status != getStatus(d: b)) |
3150 | return false; |
3151 | // Status includes DST-ness, so we now know they match in it. |
3152 | |
3153 | switch (extractSpec(status)) { |
3154 | case Qt::LocalTime: |
3155 | case Qt::UTC: |
3156 | return true; |
3157 | |
3158 | case Qt::TimeZone: |
3159 | /* TimeZone always determines its offset during construction of the |
3160 | private data. Even if we're in different zones, what matters is the |
3161 | offset actually in effect at the specific time. (DST can cause things |
3162 | with the same time-zone to use different offsets, but we already |
3163 | checked their DSTs match.) */ |
3164 | case Qt::OffsetFromUTC: // always knows its offset, which is all that matters. |
3165 | Q_ASSERT(!a.isShort() && !b.isShort()); |
3166 | return a->m_offsetFromUtc == b->m_offsetFromUtc; |
3167 | } |
3168 | Q_UNREACHABLE_RETURN(false); |
3169 | } |
3170 | |
3171 | // Refresh the LocalTime or TimeZone validity and offset |
3172 | static void refreshZonedDateTime(QDateTimeData &d, const QTimeZone &zone, |
3173 | QDateTimePrivate::TransitionOptions resolve) |
3174 | { |
3175 | Q_ASSERT(zone.timeSpec() == Qt::TimeZone || zone.timeSpec() == Qt::LocalTime); |
3176 | auto status = getStatus(d); |
3177 | Q_ASSERT(extractSpec(status) == zone.timeSpec()); |
3178 | int offsetFromUtc = 0; |
3179 | /* Callers are: |
3180 | * QDTP::create(), where d is too new to be shared yet |
3181 | * reviseTimeZone(), which detach()es if not short before calling this |
3182 | * checkValidDateTime(), always follows a setDateTime() that detach()ed if not short |
3183 | |
3184 | So we can assume d is not shared. We only need to detach() if we convert |
3185 | from short to pimpled to accommodate an oversize msecs, which can only be |
3186 | needed in the unlikely event we revise it. |
3187 | */ |
3188 | |
3189 | // If not valid date and time then is invalid |
3190 | if (!status.testFlags(flags: QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime)) { |
3191 | status.setFlag(flag: QDateTimePrivate::ValidDateTime, on: false); |
3192 | } else { |
3193 | // We have a valid date and time and a Qt::LocalTime or Qt::TimeZone |
3194 | // that might fall into a "missing" DST transition hour. |
3195 | qint64 msecs = getMSecs(d); |
3196 | QDateTimePrivate::ZoneState state = stateAtMillis(zone, millis: msecs, resolve); |
3197 | Q_ASSERT(!state.valid || (state.offset >= -SECS_PER_DAY && state.offset <= SECS_PER_DAY)); |
3198 | if (state.dst == QDateTimePrivate::UnknownDaylightTime) { // Overflow |
3199 | status.setFlag(flag: QDateTimePrivate::ValidDateTime, on: false); |
3200 | } else if (state.valid) { |
3201 | status = mergeDaylightStatus(sf: status, status: state.dst); |
3202 | offsetFromUtc = state.offset; |
3203 | status.setFlag(flag: QDateTimePrivate::ValidDateTime, on: true); |
3204 | if (Q_UNLIKELY(msecs != state.when)) { |
3205 | // Update msecs to the resolution: |
3206 | if (status.testFlag(flag: QDateTimePrivate::ShortData)) { |
3207 | if (msecsCanBeSmall(msecs: state.when)) { |
3208 | d.data.msecs = qintptr(state.when); |
3209 | } else { |
3210 | // Convert to long-form so we can hold the revised msecs: |
3211 | status.setFlag(flag: QDateTimePrivate::ShortData, on: false); |
3212 | d.detach(); |
3213 | } |
3214 | } |
3215 | if (!status.testFlag(flag: QDateTimePrivate::ShortData)) |
3216 | d->m_msecs = state.when; |
3217 | } |
3218 | } else { |
3219 | status.setFlag(flag: QDateTimePrivate::ValidDateTime, on: false); |
3220 | } |
3221 | } |
3222 | |
3223 | if (status.testFlag(flag: QDateTimePrivate::ShortData)) { |
3224 | d.data.status = status.toInt(); |
3225 | } else { |
3226 | d->m_status = status; |
3227 | d->m_offsetFromUtc = offsetFromUtc; |
3228 | } |
3229 | } |
3230 | |
3231 | // Check the UTC / offsetFromUTC validity |
3232 | static void refreshSimpleDateTime(QDateTimeData &d) |
3233 | { |
3234 | auto status = getStatus(d); |
3235 | Q_ASSERT(QTimeZone::isUtcOrFixedOffset(extractSpec(status))); |
3236 | status.setFlag(flag: QDateTimePrivate::ValidDateTime, |
3237 | on: status.testFlags(flags: QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime)); |
3238 | |
3239 | if (status.testFlag(flag: QDateTimePrivate::ShortData)) |
3240 | d.data.status = status.toInt(); |
3241 | else |
3242 | d->m_status = status; |
3243 | } |
3244 | |
3245 | // Clean up and set status after assorted set-up or reworking: |
3246 | static void checkValidDateTime(QDateTimeData &d, QDateTime::TransitionResolution resolve) |
3247 | { |
3248 | auto spec = extractSpec(status: getStatus(d)); |
3249 | switch (spec) { |
3250 | case Qt::OffsetFromUTC: |
3251 | case Qt::UTC: |
3252 | // for these, a valid date and a valid time imply a valid QDateTime |
3253 | refreshSimpleDateTime(d); |
3254 | break; |
3255 | case Qt::TimeZone: |
3256 | case Qt::LocalTime: |
3257 | // For these, we need to check whether (the zone is valid and) the time |
3258 | // is valid for the zone. Expensive, but we have no other option. |
3259 | refreshZonedDateTime(d, zone: d.timeZone(), resolve: toTransitionOptions(res: resolve)); |
3260 | break; |
3261 | } |
3262 | } |
3263 | |
3264 | static void reviseTimeZone(QDateTimeData &d, const QTimeZone &zone, |
3265 | QDateTime::TransitionResolution resolve) |
3266 | { |
3267 | Qt::TimeSpec spec = zone.timeSpec(); |
3268 | auto status = mergeSpec(status: getStatus(d), spec); |
3269 | bool reuse = d.isShort(); |
3270 | int offset = 0; |
3271 | |
3272 | switch (spec) { |
3273 | case Qt::UTC: |
3274 | Q_ASSERT(zone.fixedSecondsAheadOfUtc() == 0); |
3275 | break; |
3276 | case Qt::OffsetFromUTC: |
3277 | reuse = false; |
3278 | offset = zone.fixedSecondsAheadOfUtc(); |
3279 | Q_ASSERT(offset); |
3280 | break; |
3281 | case Qt::TimeZone: |
3282 | reuse = false; |
3283 | break; |
3284 | case Qt::LocalTime: |
3285 | break; |
3286 | } |
3287 | |
3288 | status &= ~(QDateTimePrivate::ValidDateTime | QDateTimePrivate::DaylightMask); |
3289 | if (reuse) { |
3290 | d.data.status = status.toInt(); |
3291 | } else { |
3292 | d.detach(); |
3293 | d->m_status = status & ~QDateTimePrivate::ShortData; |
3294 | d->m_offsetFromUtc = offset; |
3295 | #if QT_CONFIG(timezone) |
3296 | if (spec == Qt::TimeZone) |
3297 | d->m_timeZone = zone; |
3298 | #endif // timezone |
3299 | } |
3300 | |
3301 | if (QTimeZone::isUtcOrFixedOffset(spec)) |
3302 | refreshSimpleDateTime(d); |
3303 | else |
3304 | refreshZonedDateTime(d, zone, resolve: toTransitionOptions(res: resolve)); |
3305 | } |
3306 | |
3307 | static void setDateTime(QDateTimeData &d, QDate date, QTime time) |
3308 | { |
3309 | // If the date is valid and the time is not we set time to 00:00:00 |
3310 | if (!time.isValid() && date.isValid()) |
3311 | time = QTime::fromMSecsSinceStartOfDay(msecs: 0); |
3312 | |
3313 | QDateTimePrivate::StatusFlags newStatus = { }; |
3314 | |
3315 | // Set date value and status |
3316 | qint64 days = 0; |
3317 | if (date.isValid()) { |
3318 | days = date.toJulianDay() - JULIAN_DAY_FOR_EPOCH; |
3319 | newStatus = QDateTimePrivate::ValidDate; |
3320 | } |
3321 | |
3322 | // Set time value and status |
3323 | int ds = 0; |
3324 | if (time.isValid()) { |
3325 | ds = time.msecsSinceStartOfDay(); |
3326 | newStatus |= QDateTimePrivate::ValidTime; |
3327 | } |
3328 | Q_ASSERT(ds < MSECS_PER_DAY); |
3329 | // Only the later parts of the very first day are representable - its start |
3330 | // would overflow - so get ds the same side of 0 as days: |
3331 | if (days < 0 && ds > 0) { |
3332 | days++; |
3333 | ds -= MSECS_PER_DAY; |
3334 | } |
3335 | |
3336 | // Check in representable range: |
3337 | qint64 msecs = 0; |
3338 | if (daysAndMillisOverflow(days, millisInDay: qint64(ds), sumMillis: &msecs)) { |
3339 | newStatus = QDateTimePrivate::StatusFlags{}; |
3340 | msecs = 0; |
3341 | } |
3342 | if (d.isShort()) { |
3343 | // let's see if we can keep this short |
3344 | if (msecsCanBeSmall(msecs)) { |
3345 | // yes, we can |
3346 | d.data.msecs = qintptr(msecs); |
3347 | d.data.status &= ~(QDateTimePrivate::ValidityMask | QDateTimePrivate::DaylightMask).toInt(); |
3348 | d.data.status |= newStatus.toInt(); |
3349 | } else { |
3350 | // nope... |
3351 | d.detach(); |
3352 | } |
3353 | } |
3354 | if (!d.isShort()) { |
3355 | d.detach(); |
3356 | d->m_msecs = msecs; |
3357 | d->m_status &= ~(QDateTimePrivate::ValidityMask | QDateTimePrivate::DaylightMask); |
3358 | d->m_status |= newStatus; |
3359 | } |
3360 | } |
3361 | |
3362 | static std::pair<QDate, QTime> getDateTime(const QDateTimeData &d) |
3363 | { |
3364 | auto status = getStatus(d); |
3365 | const qint64 msecs = getMSecs(d); |
3366 | const auto dayMilli = QRoundingDown::qDivMod<MSECS_PER_DAY>(a: msecs); |
3367 | return { status.testFlag(flag: QDateTimePrivate::ValidDate) |
3368 | ? QDate::fromJulianDay(jd_: JULIAN_DAY_FOR_EPOCH + dayMilli.quotient) |
3369 | : QDate(), |
3370 | status.testFlag(flag: QDateTimePrivate::ValidTime) |
3371 | ? QTime::fromMSecsSinceStartOfDay(msecs: dayMilli.remainder) |
3372 | : QTime() }; |
3373 | } |
3374 | |
3375 | /***************************************************************************** |
3376 | QDateTime::Data member functions |
3377 | *****************************************************************************/ |
3378 | |
3379 | inline QDateTime::Data::Data() noexcept |
3380 | { |
3381 | // default-constructed data has a special exception: |
3382 | // it can be small even if CanBeSmall == false |
3383 | // (optimization so we don't allocate memory in the default constructor) |
3384 | quintptr value = mergeSpec(status: QDateTimePrivate::ShortData, spec: Qt::LocalTime).toInt(); |
3385 | d = reinterpret_cast<QDateTimePrivate *>(value); |
3386 | } |
3387 | |
3388 | inline QDateTime::Data::Data(const QTimeZone &zone) |
3389 | { |
3390 | Qt::TimeSpec spec = zone.timeSpec(); |
3391 | if (CanBeSmall && Q_LIKELY(specCanBeSmall(spec))) { |
3392 | quintptr value = mergeSpec(status: QDateTimePrivate::ShortData, spec).toInt(); |
3393 | d = reinterpret_cast<QDateTimePrivate *>(value); |
3394 | Q_ASSERT(isShort()); |
3395 | } else { |
3396 | // the structure is too small, we need to detach |
3397 | d = new QDateTimePrivate; |
3398 | d->ref.ref(); |
3399 | d->m_status = mergeSpec(status: {}, spec); |
3400 | if (spec == Qt::OffsetFromUTC) |
3401 | d->m_offsetFromUtc = zone.fixedSecondsAheadOfUtc(); |
3402 | else if (spec == Qt::TimeZone) |
3403 | d->m_timeZone = zone; |
3404 | Q_ASSERT(!isShort()); |
3405 | } |
3406 | } |
3407 | |
3408 | inline QDateTime::Data::Data(const Data &other) noexcept |
3409 | : data(other.data) |
3410 | { |
3411 | if (!isShort()) { |
3412 | // check if we could shrink |
3413 | if (specCanBeSmall(spec: extractSpec(status: d->m_status)) && msecsCanBeSmall(msecs: d->m_msecs)) { |
3414 | ShortData sd; |
3415 | sd.msecs = qintptr(d->m_msecs); |
3416 | sd.status = (d->m_status | QDateTimePrivate::ShortData).toInt(); |
3417 | data = sd; |
3418 | } else { |
3419 | // no, have to keep it big |
3420 | d->ref.ref(); |
3421 | } |
3422 | } |
3423 | } |
3424 | |
3425 | inline QDateTime::Data::Data(Data &&other) noexcept |
3426 | : data(other.data) |
3427 | { |
3428 | // reset the other to a short state |
3429 | Data dummy; |
3430 | Q_ASSERT(dummy.isShort()); |
3431 | other.data = dummy.data; |
3432 | } |
3433 | |
3434 | inline QDateTime::Data &QDateTime::Data::operator=(const Data &other) noexcept |
3435 | { |
3436 | if (isShort() ? data == other.data : d == other.d) |
3437 | return *this; |
3438 | |
3439 | auto x = d; |
3440 | d = other.d; |
3441 | if (!other.isShort()) { |
3442 | // check if we could shrink |
3443 | if (specCanBeSmall(spec: extractSpec(status: other.d->m_status)) && msecsCanBeSmall(msecs: other.d->m_msecs)) { |
3444 | ShortData sd; |
3445 | sd.msecs = qintptr(other.d->m_msecs); |
3446 | sd.status = (other.d->m_status | QDateTimePrivate::ShortData).toInt(); |
3447 | data = sd; |
3448 | } else { |
3449 | // no, have to keep it big |
3450 | other.d->ref.ref(); |
3451 | } |
3452 | } |
3453 | |
3454 | if (!(quintptr(x) & QDateTimePrivate::ShortData) && !x->ref.deref()) |
3455 | delete x; |
3456 | return *this; |
3457 | } |
3458 | |
3459 | inline QDateTime::Data::~Data() |
3460 | { |
3461 | if (!isShort() && !d->ref.deref()) |
3462 | delete d; |
3463 | } |
3464 | |
3465 | inline bool QDateTime::Data::isShort() const |
3466 | { |
3467 | bool b = quintptr(d) & QDateTimePrivate::ShortData; |
3468 | |
3469 | // sanity check: |
3470 | Q_ASSERT(b || !d->m_status.testFlag(QDateTimePrivate::ShortData)); |
3471 | |
3472 | // even if CanBeSmall = false, we have short data for a default-constructed |
3473 | // QDateTime object. But it's unlikely. |
3474 | if constexpr (CanBeSmall) |
3475 | return Q_LIKELY(b); |
3476 | return Q_UNLIKELY(b); |
3477 | } |
3478 | |
3479 | inline void QDateTime::Data::detach() |
3480 | { |
3481 | QDateTimePrivate *x; |
3482 | bool wasShort = isShort(); |
3483 | if (wasShort) { |
3484 | // force enlarging |
3485 | x = new QDateTimePrivate; |
3486 | x->m_status = QDateTimePrivate::StatusFlags::fromInt(i: data.status) & ~QDateTimePrivate::ShortData; |
3487 | x->m_msecs = data.msecs; |
3488 | } else { |
3489 | if (d->ref.loadRelaxed() == 1) |
3490 | return; |
3491 | |
3492 | x = new QDateTimePrivate(*d); |
3493 | } |
3494 | |
3495 | x->ref.storeRelaxed(newValue: 1); |
3496 | if (!wasShort && !d->ref.deref()) |
3497 | delete d; |
3498 | d = x; |
3499 | } |
3500 | |
3501 | void QDateTime::Data::invalidate() |
3502 | { |
3503 | if (isShort()) { |
3504 | data.status &= ~int(QDateTimePrivate::ValidityMask); |
3505 | } else { |
3506 | detach(); |
3507 | d->m_status &= ~QDateTimePrivate::ValidityMask; |
3508 | } |
3509 | } |
3510 | |
3511 | QTimeZone QDateTime::Data::timeZone() const |
3512 | { |
3513 | switch (getSpec(d: *this)) { |
3514 | case Qt::UTC: |
3515 | return QTimeZone::UTC; |
3516 | case Qt::OffsetFromUTC: |
3517 | return QTimeZone::fromSecondsAheadOfUtc(offset: d->m_offsetFromUtc); |
3518 | case Qt::TimeZone: |
3519 | #if QT_CONFIG(timezone) |
3520 | if (d->m_timeZone.isValid()) |
3521 | return d->m_timeZone; |
3522 | #endif |
3523 | break; |
3524 | case Qt::LocalTime: |
3525 | return QTimeZone::LocalTime; |
3526 | } |
3527 | return QTimeZone(); |
3528 | } |
3529 | |
3530 | inline const QDateTimePrivate *QDateTime::Data::operator->() const |
3531 | { |
3532 | Q_ASSERT(!isShort()); |
3533 | return d; |
3534 | } |
3535 | |
3536 | inline QDateTimePrivate *QDateTime::Data::operator->() |
3537 | { |
3538 | // should we attempt to detach here? |
3539 | Q_ASSERT(!isShort()); |
3540 | Q_ASSERT(d->ref.loadRelaxed() == 1); |
3541 | return d; |
3542 | } |
3543 | |
3544 | /***************************************************************************** |
3545 | QDateTimePrivate member functions |
3546 | *****************************************************************************/ |
3547 | |
3548 | Q_NEVER_INLINE |
3549 | QDateTime::Data QDateTimePrivate::create(QDate toDate, QTime toTime, const QTimeZone &zone, |
3550 | QDateTime::TransitionResolution resolve) |
3551 | { |
3552 | QDateTime::Data result(zone); |
3553 | setDateTime(d&: result, date: toDate, time: toTime); |
3554 | if (zone.isUtcOrFixedOffset()) |
3555 | refreshSimpleDateTime(d&: result); |
3556 | else |
3557 | refreshZonedDateTime(d&: result, zone, resolve: toTransitionOptions(res: resolve)); |
3558 | return result; |
3559 | } |
3560 | |
3561 | /***************************************************************************** |
3562 | QDateTime member functions |
3563 | *****************************************************************************/ |
3564 | |
3565 | /*! |
3566 | \class QDateTime |
3567 | \inmodule QtCore |
3568 | \ingroup shared |
3569 | \reentrant |
3570 | \brief The QDateTime class provides date and time functions. |
3571 | |
3572 | \compares weak |
3573 | |
3574 | A QDateTime object encodes a calendar date and a clock time (a "datetime") |
3575 | in accordance with a time representation. It combines features of the QDate |
3576 | and QTime classes. It can read the current datetime from the system |
3577 | clock. It provides functions for comparing datetimes and for manipulating a |
3578 | datetime by adding a number of seconds, days, months, or years. |
3579 | |
3580 | QDateTime can describe datetimes with respect to \l{Qt::LocalTime}{local |
3581 | time}, to \l{Qt::UTC}{UTC}, to a specified \l{Qt::OffsetFromUTC}{offset from |
3582 | UTC} or to a specified \l{Qt::TimeZone}{time zone}. Each of these time |
3583 | representations can be encapsulated in a suitable instance of the QTimeZone |
3584 | class. For example, a time zone of "Europe/Berlin" will apply the |
3585 | daylight-saving rules as used in Germany. In contrast, a fixed offset from |
3586 | UTC of +3600 seconds is one hour ahead of UTC (usually written in ISO |
3587 | standard notation as "UTC+01:00"), with no daylight-saving |
3588 | complications. When using either local time or a specified time zone, |
3589 | time-zone transitions (see \l {Timezone transitions}{below}) are taken into |
3590 | account. A QDateTime's timeSpec() will tell you which of the four types of |
3591 | time representation is in use; its timeRepresentation() provides a full |
3592 | description of that time representation, as a QTimeZone. |
3593 | |
3594 | A QDateTime object is typically created either by giving a date and time |
3595 | explicitly in the constructor, or by using a static function such as |
3596 | currentDateTime() or fromMSecsSinceEpoch(). The date and time can be changed |
3597 | with setDate() and setTime(). A datetime can also be set using the |
3598 | setMSecsSinceEpoch() function that takes the time, in milliseconds, since |
3599 | the start, in UTC, of the year 1970. The fromString() function returns a |
3600 | QDateTime, given a string and a date format used to interpret the date |
3601 | within the string. |
3602 | |
3603 | QDateTime::currentDateTime() returns a QDateTime that expresses the current |
3604 | date and time with respect to a specific time representation, such as local |
3605 | time (its default). QDateTime::currentDateTimeUtc() returns a QDateTime that |
3606 | expresses the current date and time with respect to UTC; it is equivalent to |
3607 | \c {QDateTime::currentDateTime(QTimeZone::UTC)}. |
3608 | |
3609 | The date() and time() functions provide access to the date and |
3610 | time parts of the datetime. The same information is provided in |
3611 | textual format by the toString() function. |
3612 | |
3613 | QDateTime provides a full set of operators to compare two |
3614 | QDateTime objects, where smaller means earlier and larger means |
3615 | later. |
3616 | |
3617 | You can increment (or decrement) a datetime by a given number of |
3618 | milliseconds using addMSecs(), seconds using addSecs(), or days using |
3619 | addDays(). Similarly, you can use addMonths() and addYears(). The daysTo() |
3620 | function returns the number of days between two datetimes, secsTo() returns |
3621 | the number of seconds between two datetimes, and msecsTo() returns the |
3622 | number of milliseconds between two datetimes. These operations are aware of |
3623 | daylight-saving time (DST) and other time-zone transitions, where |
3624 | applicable. |
3625 | |
3626 | Use toTimeZone() to re-express a datetime in terms of a different time |
3627 | representation. By passing a lightweight QTimeZone that represents local |
3628 | time, UTC or a fixed offset from UTC, you can convert the datetime to use |
3629 | the corresponding time representation; or you can pass a full time zone |
3630 | (whose \l {QTimeZone::timeSpec()}{timeSpec()} is \c {Qt::TimeZone}) to use |
3631 | that instead. |
3632 | |
3633 | \section1 Remarks |
3634 | |
3635 | \note QDateTime does not account for leap seconds. |
3636 | |
3637 | \note All conversion to and from string formats is done using the C locale. |
3638 | For localized conversions, see QLocale. |
3639 | |
3640 | \note There is no year 0 in the Gregorian calendar. Dates in that year are |
3641 | considered invalid. The year -1 is the year "1 before Christ" or "1 before |
3642 | common era." The day before 1 January 1 CE is 31 December 1 BCE. |
3643 | |
3644 | \note Using local time (the default) or a specified time zone implies a need |
3645 | to resolve any issues around \l {Timezone transitions}{transitions}. As a |
3646 | result, operations on such QDateTime instances (notably including |
3647 | constructing them) may be more expensive than the equivalent when using UTC |
3648 | or a fixed offset from it. |
3649 | |
3650 | \section2 Range of Valid Dates |
3651 | |
3652 | The range of values that QDateTime can represent is dependent on the |
3653 | internal storage implementation. QDateTime is currently stored in a qint64 |
3654 | as a serial msecs value encoding the date and time. This restricts the date |
3655 | range to about ±292 million years, compared to the QDate range of ±2 billion |
3656 | years. Care must be taken when creating a QDateTime with extreme values that |
3657 | you do not overflow the storage. The exact range of supported values varies |
3658 | depending on the time representation used. |
3659 | |
3660 | \section2 Use of Timezones |
3661 | |
3662 | QDateTime uses the system's time zone information to determine the current |
3663 | local time zone and its offset from UTC. If the system is not configured |
3664 | correctly or not up-to-date, QDateTime will give wrong results. |
3665 | |
3666 | QDateTime likewise uses system-provided information to determine the offsets |
3667 | of other timezones from UTC. If this information is incomplete or out of |
3668 | date, QDateTime will give wrong results. See the QTimeZone documentation for |
3669 | more details. |
3670 | |
3671 | On modern Unix systems, this means QDateTime usually has accurate |
3672 | information about historical transitions (including DST, see below) whenever |
3673 | possible. On Windows, where the system doesn't support historical timezone |
3674 | data, historical accuracy is not maintained with respect to timezone |
3675 | transitions, notably including DST. However, building Qt with the ICU |
3676 | library will equip QTimeZone with the same timezone database as is used on |
3677 | Unix. |
3678 | |
3679 | \section2 Timezone transitions |
3680 | |
3681 | QDateTime takes into account timezone transitions, both the transitions |
3682 | between Standard Time and Daylight-Saving Time (DST) and the transitions |
3683 | that arise when a zone changes its standard offset. For example, if the |
3684 | transition is at 2am and the clock goes forward to 3am, then there is a |
3685 | "missing" hour from 02:00:00 to 02:59:59.999. Such a transition is known as |
3686 | a "spring forward" and the times skipped over have no meaning. When a |
3687 | transition goes the other way, known as a "fall back", a time interval is |
3688 | repeated, first in the old zone (usually DST), then in the new zone (usually |
3689 | Standard Time), so times in this interval are ambiguous. |
3690 | |
3691 | Some zones use "reversed" DST, using standard time in summer and |
3692 | daylight-saving time (with a lowered offset) in winter. For such zones, the |
3693 | spring forward still happens in spring and skips an hour, but is a |
3694 | transition \e{out of} daylight-saving time, while the fall back still |
3695 | repeats an autumn hour but is a transition \e to daylight-saving time. |
3696 | |
3697 | When converting from a UTC time (or a time at fixed offset from UTC), there |
3698 | is always an unambiguous valid result in any timezone. However, when |
3699 | combining a date and time to make a datetime, expressed with respect to |
3700 | local time or a specific time-zone, the nominal result may fall in a |
3701 | transition, making it either invalid or ambiguous. Methods where this |
3702 | situation may arise take a \c resolve parameter: this is always ignored if |
3703 | the requested datetime is valid and unambiguous. See \l TransitionResolution |
3704 | for the options it lets you control. Prior to Qt 6.7, the equivalent of its |
3705 | \l LegacyBehavior was selected. |
3706 | |
3707 | For a spring forward's skipped interval, interpreting the requested time |
3708 | with either offset yields an actual time at which the other offset was in |
3709 | use; so passing \c TransitionResolution::RelativeToBefore for \c resolve |
3710 | will actually result in a time after the transition, that would have had the |
3711 | requested representation had the transition not happened. Likewise, \c |
3712 | TransitionResolution::RelativeToAfter for \c resolve results in a time |
3713 | before the transition, that would have had the requested representation, had |
3714 | the transition happened earlier. |
3715 | |
3716 | When QDateTime performs arithmetic, as with addDay() or addSecs(), it takes |
3717 | care to produce a valid result. For example, on a day when there is a spring |
3718 | forward from 02:00 to 03:00, adding one second to 01:59:59 will get |
3719 | 03:00:00. Adding one day to 02:30 on the preceding day will get 03:30 on the |
3720 | day of the transition, while subtracting one day, by calling \c{addDay(-1)}, |
3721 | to 02:30 on the following day will get 01:30 on the day of the transition. |
3722 | While addSecs() will deliver a time offset by the given number of seconds, |
3723 | addDays() adjusts the date and only adjusts time if it would otherwise get |
3724 | an invalid result. Applying \c{addDays(1)} to 03:00 on the day before the |
3725 | spring-forward will simply get 03:00 on the day of the transition, even |
3726 | though the latter is only 23 hours after the former; but \c{addSecs(24 * 60 |
3727 | * 60)} will get 04:00 on the day of the transition, since that's 24 hours |
3728 | later. Typical transitions make some days 23 or 25 hours long. |
3729 | |
3730 | For datetimes that the system \c time_t can represent (from 1901-12-14 to |
3731 | 2038-01-18 on systems with 32-bit \c time_t; for the full range QDateTime |
3732 | can represent if the type is 64-bit), the standard system APIs are used to |
3733 | determine local time's offset from UTC. For datetimes not handled by these |
3734 | system APIs (potentially including some within the \c time_t range), |
3735 | QTimeZone::systemTimeZone() is used, if available, or a best effort is made |
3736 | to estimate. In any case, the offset information used depends on the system |
3737 | and may be incomplete or, for past times, historically |
3738 | inaccurate. Furthermore, for future dates, the local time zone's offsets and |
3739 | DST rules may change before that date comes around. |
3740 | |
3741 | \section3 Whole day transitions |
3742 | |
3743 | A small number of zones have skipped or repeated entire days as part of |
3744 | moving The International Date Line across themselves. For these, daysTo() |
3745 | will be unaware of the duplication or gap, simply using the difference in |
3746 | calendar date; in contrast, msecsTo() and secsTo() know the true time |
3747 | interval. Likewise, addMSecs() and addSecs() correspond directly to elapsed |
3748 | time, where addDays(), addMonths() and addYears() follow the nominal |
3749 | calendar, aside from where landing in a gap or duplication requires |
3750 | resolving an ambiguity or invalidity due to a duplication or omission. |
3751 | |
3752 | \note Days "lost" during a change of calendar, such as from Julian to |
3753 | Gregorian, do not affect QDateTime. Although the two calendars describe |
3754 | dates differently, the successive days across the change are described by |
3755 | consecutive QDate instances, each one day later than the previous, as |
3756 | described by either calendar or by their toJulianDay() values. In contrast, |
3757 | a zone skipping or duplicating a day is changing its description of \e time, |
3758 | not date, for all that it does so by a whole 24 hours. |
3759 | |
3760 | \section2 Offsets From UTC |
3761 | |
3762 | Offsets from UTC are measured in seconds east of Greenwich. The moment |
3763 | described by a particular date and time, such as noon on a particular day, |
3764 | depends on the time representation used. Those with a higher offset from UTC |
3765 | describe an earlier moment, and those with a lower offset a later moment, by |
3766 | any given combination of date and time. |
3767 | |
3768 | There is no explicit size restriction on an offset from UTC, but there is an |
3769 | implicit limit imposed when using the toString() and fromString() methods |
3770 | which use a ±hh:mm format, effectively limiting the range to ± 99 hours and |
3771 | 59 minutes and whole minutes only. Note that currently no time zone has an |
3772 | offset outside the range of ±14 hours and all known offsets are multiples of |
3773 | five minutes. Historical time zones have a wider range and may have offsets |
3774 | including seconds; these last cannot be faithfully represented in strings. |
3775 | |
3776 | \sa QDate, QTime, QDateTimeEdit, QTimeZone |
3777 | */ |
3778 | |
3779 | /*! |
3780 | \since 5.14 |
3781 | \enum QDateTime::YearRange |
3782 | |
3783 | This enumerated type describes the range of years (in the Gregorian |
3784 | calendar) representable by QDateTime: |
3785 | |
3786 | \value First The later parts of this year are representable |
3787 | \value Last The earlier parts of this year are representable |
3788 | |
3789 | All dates strictly between these two years are also representable. |
3790 | Note, however, that the Gregorian Calendar has no year zero. |
3791 | |
3792 | \note QDate can describe dates in a wider range of years. For most |
3793 | purposes, this makes little difference, as the range of years that QDateTime |
3794 | can support reaches 292 million years either side of 1970. |
3795 | |
3796 | \sa isValid(), QDate |
3797 | */ |
3798 | |
3799 | /*! |
3800 | \since 6.7 |
3801 | \enum QDateTime::TransitionResolution |
3802 | |
3803 | This enumeration is used to resolve datetime combinations which fall in \l |
3804 | {Timezone transitions}. |
3805 | |
3806 | When constructing a datetime, specified in terms of local time or a |
3807 | time-zone that has daylight-saving time, or revising one with setDate(), |
3808 | setTime() or setTimeZone(), the given parameters may imply a time |
3809 | representation that either has no meaning or has two meanings in the |
3810 | zone. Such time representations are described as being in the transition. In |
3811 | either case, we can simply return an invalid datetime, to indicate that the |
3812 | operation is ill-defined. In the ambiguous case, we can alternatively select |
3813 | one of the two times that could be meant. When there is no meaning, we can |
3814 | select a time either side of it that might plausibly have been meant. For |
3815 | example, when advancing from an earlier time, we can select the time after |
3816 | the transition that is actually the specified amount of time after the |
3817 | earlier time in question. The options specified here configure how such |
3818 | selection is performed. |
3819 | |
3820 | \value Reject |
3821 | Treat any time in a transition as invalid. Either it really is, or it |
3822 | is ambiguous. |
3823 | \value RelativeToBefore |
3824 | Selects a time as if stepping forward from a time before the |
3825 | transition. This interprets the requested time using the offset in |
3826 | effect before the transition and, if necessary, converts the result |
3827 | to the offset in effect at the resulting time. |
3828 | \value RelativeToAfter |
3829 | Select a time as if stepping backward from a time after the |
3830 | transition. This interprets the requested time using the offset in |
3831 | effect after the transition and, if necessary, converts the result to |
3832 | the offset in effect at the resulting time. |
3833 | \value PreferBefore |
3834 | Selects a time before the transition, |
3835 | \value PreferAfter |
3836 | Selects a time after the transition. |
3837 | \value PreferStandard |
3838 | Selects a time on the standard time side of the transition. |
3839 | \value PreferDaylightSaving |
3840 | Selects a time on the daylight-saving-time side of the transition. |
3841 | \value LegacyBehavior |
3842 | An alias for RelativeToBefore, which is used as default for |
3843 | TransitionResolution parameters, as this most closely matches the |
3844 | behavior prior to Qt 6.7. |
3845 | |
3846 | For \l addDays(), \l addMonths() or \l addYears(), the behavior is and |
3847 | (mostly) was to use \c RelativeToBefore if adding a positive adjustment and \c |
3848 | RelativeToAfter if adding a negative adjustment. |
3849 | |
3850 | \note In time zones where daylight-saving increases the offset from UTC in |
3851 | summer (known as "positive DST"), PreferStandard is an alias for |
3852 | RelativeToAfter and PreferDaylightSaving for RelativeToBefore. In time zones |
3853 | where the daylight-saving mechanism is a decrease in offset from UTC in |
3854 | winter (known as "negative DST"), the reverse applies, provided the |
3855 | operating system reports - as it does on most platforms - whether a datetime |
3856 | is in DST or standard time. For some platforms, where transition times are |
3857 | unavailable even for Qt::TimeZone datetimes, QTimeZone is obliged to presume |
3858 | that the side with lower offset from UTC is standard time, effectively |
3859 | assuming positive DST. |
3860 | |
3861 | The following tables illustrate how a QDateTime constructor resolves a |
3862 | request for 02:30 on a day when local time has a transition between 02:00 |
3863 | and 03:00, with a nominal standard time LST and daylight-saving time LDT on |
3864 | the two sides, in the various possible cases. The transition type may be to |
3865 | skip an hour or repeat it. The type of transition and value of a parameter |
3866 | \c resolve determine which actual time on the given date is selected. First, |
3867 | the common case of positive daylight-saving, where: |
3868 | |
3869 | \table |
3870 | \header \li Before \li 02:00--03:00 \li After \li \c resolve \li selected |
3871 | \row \li LST \li skip \li LDT \li RelativeToBefore \li 03:30 LDT |
3872 | \row \li LST \li skip \li LDT \li RelativeToAfter \li 01:30 LST |
3873 | \row \li LST \li skip \li LDT \li PreferBefore \li 01:30 LST |
3874 | \row \li LST \li skip \li LDT \li PreferAfter \li 03:30 LDT |
3875 | \row \li LST \li skip \li LDT \li PreferStandard \li 01:30 LST |
3876 | \row \li LST \li skip \li LDT \li PreferDaylightSaving \li 03:30 LDT |
3877 | \row \li LDT \li repeat \li LST \li RelativeToBefore \li 02:30 LDT |
3878 | \row \li LDT \li repeat \li LST \li RelativeToAfter \li 02:30 LST |
3879 | \row \li LDT \li repeat \li LST \li PreferBefore \li 02:30 LDT |
3880 | \row \li LDT \li repeat \li LST \li PreferAfter \li 02:30 LST |
3881 | \row \li LDT \li repeat \li LST \li PreferStandard \li 02:30 LST |
3882 | \row \li LDT \li repeat \li LST \li PreferDaylightSaving \li 02:30 LDT |
3883 | \endtable |
3884 | |
3885 | Second, the case for negative daylight-saving, using LDT in winter and |
3886 | skipping an hour to transition to LST in summer, then repeating an hour at |
3887 | the transition back to winter: |
3888 | |
3889 | \table |
3890 | \row \li LDT \li skip \li LST \li RelativeToBefore \li 03:30 LST |
3891 | \row \li LDT \li skip \li LST \li RelativeToAfter \li 01:30 LDT |
3892 | \row \li LDT \li skip \li LST \li PreferBefore \li 01:30 LDT |
3893 | \row \li LDT \li skip \li LST \li PreferAfter \li 03:30 LST |
3894 | \row \li LDT \li skip \li LST \li PreferStandard \li 03:30 LST |
3895 | \row \li LDT \li skip \li LST \li PreferDaylightSaving \li 01:30 LDT |
3896 | \row \li LST \li repeat \li LDT \li RelativeToBefore \li 02:30 LST |
3897 | \row \li LST \li repeat \li LDT \li RelativeToAfter \li 02:30 LDT |
3898 | \row \li LST \li repeat \li LDT \li PreferBefore \li 02:30 LST |
3899 | \row \li LST \li repeat \li LDT \li PreferAfter \li 02:30 LDT |
3900 | \row \li LST \li repeat \li LDT \li PreferStandard \li 02:30 LST |
3901 | \row \li LST \li repeat \li LDT \li PreferDaylightSaving \li 02:30 LDT |
3902 | \endtable |
3903 | |
3904 | Reject can be used to prompt relevant QDateTime APIs to return an invalid |
3905 | datetime object so that your code can deal with transitions for itself, for |
3906 | example by alerting a user to the fact that the datetime they have selected |
3907 | is in a transition interval, to offer them the opportunity to resolve a |
3908 | conflict or ambiguity. Code using this may well find the other options above |
3909 | useful to determine relevant information to use in its own (or the user's) |
3910 | resolution. If the start or end of the transition, or the moment of the |
3911 | transition itself, is the right resolution, QTimeZone's transition APIs can |
3912 | be used to obtain that information. You can determine whether the transition |
3913 | is a repeated or skipped interval by using \l secsTo() to measure the actual |
3914 | time between noon on the previous and following days. The result will be |
3915 | less than 48 hours for a skipped interval (such as a spring-forward) and |
3916 | more than 48 hours for a repeated interval (such as a fall-back). |
3917 | |
3918 | \note When a resolution other than Reject is specified, a valid QDateTime |
3919 | object is returned, if possible. If the requested date-time falls in a gap, |
3920 | the returned date-time will not have the time() requested - or, in some |
3921 | cases, the date(), if a whole day was skipped. You can thus detect when a |
3922 | gap is hit by comparing date() and time() to what was requested. |
3923 | |
3924 | \section2 Relation to other datetime software |
3925 | |
3926 | The Python programming language's datetime APIs have a \c fold parameter |
3927 | that corresponds to \c RelativeToBefore (\c{fold = True}) and \c |
3928 | RelativeToAfter (\c{fold = False}). |
3929 | |
3930 | The \c Temporal proposal to replace JavaScript's \c Date offers four options |
3931 | for how to resolve a transition, as value for a \c disambiguation |
3932 | parameter. Its \c{'reject'} raises an exception, which roughly corresponds |
3933 | to \c Reject producing an invalid result. Its \c{'earlier'} and \c{'later'} |
3934 | options correspond to \c PreferBefore and \c PreferAfter. Its |
3935 | \c{'compatible'} option corresponds to \c RelativeToBefore (and Python's |
3936 | \c{fold = True}). |
3937 | |
3938 | \sa {Timezone transitions}, QDateTime::TransitionResolution |
3939 | */ |
3940 | |
3941 | /*! |
3942 | Constructs a null datetime, nominally using local time. |
3943 | |
3944 | A null datetime is invalid, since its date and time are invalid. |
3945 | |
3946 | \sa isValid(), setMSecsSinceEpoch(), setDate(), setTime(), setTimeZone() |
3947 | */ |
3948 | QDateTime::QDateTime() noexcept |
3949 | { |
3950 | #if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) || defined(QT_BOOTSTRAPPED) || QT_POINTER_SIZE == 8 |
3951 | static_assert(sizeof(ShortData) == sizeof(qint64)); |
3952 | static_assert(sizeof(Data) == sizeof(qint64)); |
3953 | #endif |
3954 | static_assert(sizeof(ShortData) >= sizeof(void*), "oops, Data::swap() is broken!" ); |
3955 | } |
3956 | |
3957 | #if QT_DEPRECATED_SINCE(6, 9) |
3958 | /*! |
3959 | \deprecated [6.9] Use \c{QDateTime(date, time)} or \c{QDateTime(date, time, QTimeZone::fromSecondsAheadOfUtc(offsetSeconds))}. |
3960 | |
3961 | Constructs a datetime with the given \a date and \a time, using the time |
3962 | representation implied by \a spec and \a offsetSeconds seconds. |
3963 | |
3964 | If \a date is valid and \a time is not, the time will be set to midnight. |
3965 | |
3966 | If \a spec is not Qt::OffsetFromUTC then \a offsetSeconds will be |
3967 | ignored. If \a spec is Qt::OffsetFromUTC and \a offsetSeconds is 0 then the |
3968 | timeSpec() will be set to Qt::UTC, i.e. an offset of 0 seconds. |
3969 | |
3970 | If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime, |
3971 | i.e. the current system time zone. To create a Qt::TimeZone datetime |
3972 | use the correct constructor. |
3973 | |
3974 | If \a date lies outside the range of dates representable by QDateTime, the |
3975 | result is invalid. If \a spec is Qt::LocalTime and the system's time-zone |
3976 | skipped over the given date and time, the result is invalid. |
3977 | */ |
3978 | QDateTime::QDateTime(QDate date, QTime time, Qt::TimeSpec spec, int offsetSeconds) |
3979 | : d(QDateTimePrivate::create(toDate: date, toTime: time, zone: asTimeZone(spec, offset: offsetSeconds, warner: "QDateTime" ), |
3980 | resolve: TransitionResolution::LegacyBehavior)) |
3981 | { |
3982 | } |
3983 | #endif // 6.9 deprecation |
3984 | |
3985 | /*! |
3986 | \since 5.2 |
3987 | |
3988 | Constructs a datetime with the given \a date and \a time, using the time |
3989 | representation described by \a timeZone. |
3990 | |
3991 | If \a date is valid and \a time is not, the time will be set to midnight. |
3992 | If \a timeZone is invalid then the datetime will be invalid. If \a date and |
3993 | \a time describe a moment close to a transition for \a timeZone, \a resolve |
3994 | controls how that situation is resolved. |
3995 | |
3996 | //! [pre-resolve-note] |
3997 | \note Prior to Qt 6.7, the version of this function lacked the \a resolve |
3998 | parameter so had no way to resolve the ambiguities related to transitions. |
3999 | //! [pre-resolve-note] |
4000 | */ |
4001 | |
4002 | QDateTime::QDateTime(QDate date, QTime time, const QTimeZone &timeZone, TransitionResolution resolve) |
4003 | : d(QDateTimePrivate::create(toDate: date, toTime: time, zone: timeZone, resolve)) |
4004 | { |
4005 | } |
4006 | |
4007 | /*! |
4008 | \since 6.5 |
4009 | \overload |
4010 | |
4011 | Constructs a datetime with the given \a date and \a time, using local time. |
4012 | |
4013 | If \a date is valid and \a time is not, midnight will be used as the |
4014 | time. If \a date and \a time describe a moment close to a transition for |
4015 | local time, \a resolve controls how that situation is resolved. |
4016 | |
4017 | \include qdatetime.cpp pre-resolve-note |
4018 | */ |
4019 | |
4020 | QDateTime::QDateTime(QDate date, QTime time, TransitionResolution resolve) |
4021 | : d(QDateTimePrivate::create(toDate: date, toTime: time, zone: QTimeZone::LocalTime, resolve)) |
4022 | { |
4023 | } |
4024 | |
4025 | /*! |
4026 | Constructs a copy of the \a other datetime. |
4027 | */ |
4028 | QDateTime::QDateTime(const QDateTime &other) noexcept |
4029 | : d(other.d) |
4030 | { |
4031 | } |
4032 | |
4033 | /*! |
4034 | \since 5.8 |
4035 | Moves the content of the temporary \a other datetime to this object and |
4036 | leaves \a other in an unspecified (but proper) state. |
4037 | */ |
4038 | QDateTime::QDateTime(QDateTime &&other) noexcept |
4039 | : d(std::move(other.d)) |
4040 | { |
4041 | } |
4042 | |
4043 | /*! |
4044 | Destroys the datetime. |
4045 | */ |
4046 | QDateTime::~QDateTime() |
4047 | { |
4048 | } |
4049 | |
4050 | /*! |
4051 | Copies the \a other datetime into this and returns this copy. |
4052 | */ |
4053 | |
4054 | QDateTime &QDateTime::operator=(const QDateTime &other) noexcept |
4055 | { |
4056 | d = other.d; |
4057 | return *this; |
4058 | } |
4059 | /*! |
4060 | \fn void QDateTime::swap(QDateTime &other) |
4061 | \since 5.0 |
4062 | |
4063 | Swaps this datetime with \a other. This operation is very fast |
4064 | and never fails. |
4065 | */ |
4066 | |
4067 | /*! |
4068 | Returns \c true if both the date and the time are null; otherwise |
4069 | returns \c false. A null datetime is invalid. |
4070 | |
4071 | \sa QDate::isNull(), QTime::isNull(), isValid() |
4072 | */ |
4073 | |
4074 | bool QDateTime::isNull() const |
4075 | { |
4076 | // If date or time is invalid, we don't set datetime valid. |
4077 | return !getStatus(d).testAnyFlag(flag: QDateTimePrivate::ValidityMask); |
4078 | } |
4079 | |
4080 | /*! |
4081 | Returns \c true if this datetime represents a definite moment, otherwise \c false. |
4082 | |
4083 | A datetime is valid if both its date and its time are valid and the time |
4084 | representation used gives a valid meaning to their combination. When the |
4085 | time representation is a specific time-zone or local time, there may be |
4086 | times on some dates that the zone skips in its representation, as when a |
4087 | daylight-saving transition skips an hour (typically during a night in |
4088 | spring). For example, if DST ends at 2am with the clock advancing to 3am, |
4089 | then datetimes from 02:00:00 to 02:59:59.999 on that day are invalid. |
4090 | |
4091 | \sa QDateTime::YearRange, QDate::isValid(), QTime::isValid() |
4092 | */ |
4093 | |
4094 | bool QDateTime::isValid() const |
4095 | { |
4096 | return getStatus(d).testFlag(flag: QDateTimePrivate::ValidDateTime); |
4097 | } |
4098 | |
4099 | /*! |
4100 | Returns the date part of the datetime. |
4101 | |
4102 | \sa setDate(), time(), timeRepresentation() |
4103 | */ |
4104 | |
4105 | QDate QDateTime::date() const |
4106 | { |
4107 | return getStatus(d).testFlag(flag: QDateTimePrivate::ValidDate) ? msecsToDate(msecs: getMSecs(d)) : QDate(); |
4108 | } |
4109 | |
4110 | /*! |
4111 | Returns the time part of the datetime. |
4112 | |
4113 | \sa setTime(), date(), timeRepresentation() |
4114 | */ |
4115 | |
4116 | QTime QDateTime::time() const |
4117 | { |
4118 | return getStatus(d).testFlag(flag: QDateTimePrivate::ValidTime) ? msecsToTime(msecs: getMSecs(d)) : QTime(); |
4119 | } |
4120 | |
4121 | /*! |
4122 | Returns the time specification of the datetime. |
4123 | |
4124 | This classifies its time representation as local time, UTC, a fixed offset |
4125 | from UTC (without indicating the offset) or a time zone (without giving the |
4126 | details of that time zone). Equivalent to |
4127 | \c{timeRepresentation().timeSpec()}. |
4128 | |
4129 | \sa setTimeSpec(), timeRepresentation(), date(), time() |
4130 | */ |
4131 | |
4132 | Qt::TimeSpec QDateTime::timeSpec() const |
4133 | { |
4134 | return getSpec(d); |
4135 | } |
4136 | |
4137 | /*! |
4138 | \since 6.5 |
4139 | Returns a QTimeZone identifying how this datetime represents time. |
4140 | |
4141 | The timeSpec() of the returned QTimeZone will coincide with that of this |
4142 | datetime; if it is not Qt::TimeZone then the returned QTimeZone is a time |
4143 | representation. When their timeSpec() is Qt::OffsetFromUTC, the returned |
4144 | QTimeZone's fixedSecondsAheadOfUtc() supplies the offset. When timeSpec() |
4145 | is Qt::TimeZone, the QTimeZone object itself is the full representation of |
4146 | that time zone. |
4147 | |
4148 | \sa timeZone(), setTimeZone(), QTimeZone::asBackendZone() |
4149 | */ |
4150 | |
4151 | QTimeZone QDateTime::timeRepresentation() const |
4152 | { |
4153 | return d.timeZone(); |
4154 | } |
4155 | |
4156 | #if QT_CONFIG(timezone) |
4157 | /*! |
4158 | \since 5.2 |
4159 | |
4160 | Returns the time zone of the datetime. |
4161 | |
4162 | The result is the same as \c{timeRepresentation().asBackendZone()}. In all |
4163 | cases, the result's \l {QTimeZone::timeSpec()}{timeSpec()} is Qt::TimeZone. |
4164 | |
4165 | When timeSpec() is Qt::LocalTime, the result will describe local time at the |
4166 | time this method was called. It will not reflect subsequent changes to the |
4167 | system time zone, even when the QDateTime from which it was obtained does. |
4168 | |
4169 | \sa timeRepresentation(), setTimeZone(), Qt::TimeSpec, QTimeZone::asBackendZone() |
4170 | */ |
4171 | |
4172 | QTimeZone QDateTime::timeZone() const |
4173 | { |
4174 | return d.timeZone().asBackendZone(); |
4175 | } |
4176 | #endif // timezone |
4177 | |
4178 | /*! |
4179 | \since 5.2 |
4180 | |
4181 | Returns this datetime's Offset From UTC in seconds. |
4182 | |
4183 | The result depends on timeSpec(): |
4184 | \list |
4185 | \li \c Qt::UTC The offset is 0. |
4186 | \li \c Qt::OffsetFromUTC The offset is the value originally set. |
4187 | \li \c Qt::LocalTime The local time's offset from UTC is returned. |
4188 | \li \c Qt::TimeZone The offset used by the time-zone is returned. |
4189 | \endlist |
4190 | |
4191 | For the last two, the offset at this date and time will be returned, taking |
4192 | account of Daylight-Saving Offset. The offset is the difference between the |
4193 | local time or time in the given time-zone and UTC time; it is positive in |
4194 | time-zones ahead of UTC (East of The Prime Meridian), negative for those |
4195 | behind UTC (West of The Prime Meridian). |
4196 | |
4197 | \sa setOffsetFromUtc() |
4198 | */ |
4199 | |
4200 | int QDateTime::offsetFromUtc() const |
4201 | { |
4202 | const auto status = getStatus(d); |
4203 | if (!status.testFlags(flags: QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime)) |
4204 | return 0; |
4205 | // But allow invalid date-time (e.g. gap's resolution) to report its offset. |
4206 | if (!d.isShort()) |
4207 | return d->m_offsetFromUtc; |
4208 | |
4209 | auto spec = extractSpec(status); |
4210 | if (spec == Qt::LocalTime) { |
4211 | // We didn't cache the value, so we need to calculate it: |
4212 | const auto resolve = toTransitionOptions(dst: extractDaylightStatus(status)); |
4213 | return QDateTimePrivate::localStateAtMillis(millis: getMSecs(d), resolve).offset; |
4214 | } |
4215 | |
4216 | Q_ASSERT(spec == Qt::UTC); |
4217 | return 0; |
4218 | } |
4219 | |
4220 | /*! |
4221 | \since 5.2 |
4222 | |
4223 | Returns the Time Zone Abbreviation for this datetime. |
4224 | |
4225 | The returned string depends on timeSpec(): |
4226 | |
4227 | \list |
4228 | \li For Qt::UTC it is "UTC". |
4229 | \li For Qt::OffsetFromUTC it will be in the format "UTC±00:00". |
4230 | \li For Qt::LocalTime, the host system is queried. |
4231 | \li For Qt::TimeZone, the associated QTimeZone object is queried. |
4232 | \endlist |
4233 | |
4234 | \note The abbreviation is not guaranteed to be unique, i.e. different time |
4235 | zones may have the same abbreviation. For Qt::LocalTime and Qt::TimeZone, |
4236 | when returned by the host system, the abbreviation may be localized. |
4237 | |
4238 | \sa timeSpec(), QTimeZone::abbreviation() |
4239 | */ |
4240 | |
4241 | QString QDateTime::timeZoneAbbreviation() const |
4242 | { |
4243 | if (!isValid()) |
4244 | return QString(); |
4245 | |
4246 | switch (getSpec(d)) { |
4247 | case Qt::UTC: |
4248 | return "UTC"_L1 ; |
4249 | case Qt::OffsetFromUTC: |
4250 | return "UTC"_L1 + toOffsetString(format: Qt::ISODate, offset: d->m_offsetFromUtc); |
4251 | case Qt::TimeZone: |
4252 | #if !QT_CONFIG(timezone) |
4253 | break; |
4254 | #else |
4255 | Q_ASSERT(d->m_timeZone.isValid()); |
4256 | return d->m_timeZone.abbreviation(atDateTime: *this); |
4257 | #endif // timezone |
4258 | case Qt::LocalTime: |
4259 | return QDateTimePrivate::localNameAtMillis(millis: getMSecs(d), |
4260 | dst: extractDaylightStatus(status: getStatus(d))); |
4261 | } |
4262 | return QString(); |
4263 | } |
4264 | |
4265 | /*! |
4266 | \since 5.2 |
4267 | |
4268 | Returns if this datetime falls in Daylight-Saving Time. |
4269 | |
4270 | If the Qt::TimeSpec is not Qt::LocalTime or Qt::TimeZone then will always |
4271 | return false. |
4272 | |
4273 | \sa timeSpec() |
4274 | */ |
4275 | |
4276 | bool QDateTime::isDaylightTime() const |
4277 | { |
4278 | if (!isValid()) |
4279 | return false; |
4280 | |
4281 | switch (getSpec(d)) { |
4282 | case Qt::UTC: |
4283 | case Qt::OffsetFromUTC: |
4284 | return false; |
4285 | case Qt::TimeZone: |
4286 | #if !QT_CONFIG(timezone) |
4287 | break; |
4288 | #else |
4289 | Q_ASSERT(d->m_timeZone.isValid()); |
4290 | if (auto dst = extractDaylightStatus(status: getStatus(d)); |
4291 | dst != QDateTimePrivate::UnknownDaylightTime) { |
4292 | return dst == QDateTimePrivate::DaylightTime; |
4293 | } |
4294 | return d->m_timeZone.d->isDaylightTime(atMSecsSinceEpoch: toMSecsSinceEpoch()); |
4295 | #endif // timezone |
4296 | case Qt::LocalTime: { |
4297 | auto dst = extractDaylightStatus(status: getStatus(d)); |
4298 | if (dst == QDateTimePrivate::UnknownDaylightTime) { |
4299 | dst = QDateTimePrivate::localStateAtMillis( |
4300 | millis: getMSecs(d), resolve: toTransitionOptions(res: TransitionResolution::LegacyBehavior)).dst; |
4301 | } |
4302 | return dst == QDateTimePrivate::DaylightTime; |
4303 | } |
4304 | } |
4305 | return false; |
4306 | } |
4307 | |
4308 | /*! |
4309 | Sets the date part of this datetime to \a date. |
4310 | |
4311 | If no time is set yet, it is set to midnight. If \a date is invalid, this |
4312 | QDateTime becomes invalid. |
4313 | |
4314 | If \a date and time() describe a moment close to a transition for this |
4315 | datetime's time representation, \a resolve controls how that situation is |
4316 | resolved. |
4317 | |
4318 | \include qdatetime.cpp pre-resolve-note |
4319 | |
4320 | \sa date(), setTime(), setTimeZone() |
4321 | */ |
4322 | |
4323 | void QDateTime::setDate(QDate date, TransitionResolution resolve) |
4324 | { |
4325 | setDateTime(d, date, time: time()); |
4326 | checkValidDateTime(d, resolve); |
4327 | } |
4328 | |
4329 | /*! |
4330 | Sets the time part of this datetime to \a time. If \a time is not valid, |
4331 | this function sets it to midnight. Therefore, it's possible to clear any |
4332 | set time in a QDateTime by setting it to a default QTime: |
4333 | |
4334 | \code |
4335 | QDateTime dt = QDateTime::currentDateTime(); |
4336 | dt.setTime(QTime()); |
4337 | \endcode |
4338 | |
4339 | If date() and \a time describe a moment close to a transition for this |
4340 | datetime's time representation, \a resolve controls how that situation is |
4341 | resolved. |
4342 | |
4343 | \include qdatetime.cpp pre-resolve-note |
4344 | |
4345 | \sa time(), setDate(), setTimeZone() |
4346 | */ |
4347 | |
4348 | void QDateTime::setTime(QTime time, TransitionResolution resolve) |
4349 | { |
4350 | setDateTime(d, date: date(), time); |
4351 | checkValidDateTime(d, resolve); |
4352 | } |
4353 | |
4354 | #if QT_DEPRECATED_SINCE(6, 9) |
4355 | /*! |
4356 | \deprecated [6.9] Use setTimeZone() instead |
4357 | |
4358 | Sets the time specification used in this datetime to \a spec. |
4359 | The datetime may refer to a different point in time. |
4360 | |
4361 | If \a spec is Qt::OffsetFromUTC then the timeSpec() will be set |
4362 | to Qt::UTC, i.e. an effective offset of 0. |
4363 | |
4364 | If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime, |
4365 | i.e. the current system time zone. |
4366 | |
4367 | Example: |
4368 | \snippet code/src_corelib_time_qdatetime.cpp 19 |
4369 | |
4370 | \sa setTimeZone(), timeSpec(), toTimeSpec(), setDate(), setTime() |
4371 | */ |
4372 | |
4373 | void QDateTime::setTimeSpec(Qt::TimeSpec spec) |
4374 | { |
4375 | reviseTimeZone(d, zone: asTimeZone(spec, offset: 0, warner: "QDateTime::setTimeSpec" ), |
4376 | resolve: TransitionResolution::LegacyBehavior); |
4377 | } |
4378 | |
4379 | /*! |
4380 | \since 5.2 |
4381 | \deprecated [6.9] Use setTimeZone(QTimeZone::fromSecondsAheadOfUtc(offsetSeconds)) instead |
4382 | |
4383 | Sets the timeSpec() to Qt::OffsetFromUTC and the offset to \a offsetSeconds. |
4384 | The datetime may refer to a different point in time. |
4385 | |
4386 | The maximum and minimum offset is 14 positive or negative hours. If |
4387 | \a offsetSeconds is larger or smaller than that, then the result is |
4388 | undefined. |
4389 | |
4390 | If \a offsetSeconds is 0 then the timeSpec() will be set to Qt::UTC. |
4391 | |
4392 | \sa setTimeZone(), isValid(), offsetFromUtc(), toOffsetFromUtc() |
4393 | */ |
4394 | |
4395 | void QDateTime::setOffsetFromUtc(int offsetSeconds) |
4396 | { |
4397 | reviseTimeZone(d, zone: QTimeZone::fromSecondsAheadOfUtc(offset: offsetSeconds), |
4398 | resolve: TransitionResolution::Reject); |
4399 | } |
4400 | #endif // 6.9 deprecations |
4401 | |
4402 | /*! |
4403 | \since 5.2 |
4404 | |
4405 | Sets the time zone used in this datetime to \a toZone. |
4406 | |
4407 | The datetime may refer to a different point in time. It uses the time |
4408 | representation of \a toZone, which may change the meaning of its unchanged |
4409 | date() and time(). |
4410 | |
4411 | If \a toZone is invalid then the datetime will be invalid. Otherwise, this |
4412 | datetime's timeSpec() after the call will match \c{toZone.timeSpec()}. |
4413 | |
4414 | If date() and time() describe a moment close to a transition for \a toZone, |
4415 | \a resolve controls how that situation is resolved. |
4416 | |
4417 | \include qdatetime.cpp pre-resolve-note |
4418 | |
4419 | \sa timeRepresentation(), timeZone(), Qt::TimeSpec |
4420 | */ |
4421 | |
4422 | void QDateTime::setTimeZone(const QTimeZone &toZone, TransitionResolution resolve) |
4423 | { |
4424 | reviseTimeZone(d, zone: toZone, resolve); |
4425 | } |
4426 | |
4427 | /*! |
4428 | \since 4.7 |
4429 | |
4430 | Returns the datetime as a number of milliseconds after the start, in UTC, of |
4431 | the year 1970. |
4432 | |
4433 | On systems that do not support time zones, this function will |
4434 | behave as if local time were Qt::UTC. |
4435 | |
4436 | The behavior for this function is undefined if the datetime stored in |
4437 | this object is not valid. However, for all valid dates, this function |
4438 | returns a unique value. |
4439 | |
4440 | \sa toSecsSinceEpoch(), setMSecsSinceEpoch(), fromMSecsSinceEpoch() |
4441 | */ |
4442 | qint64 QDateTime::toMSecsSinceEpoch() const |
4443 | { |
4444 | // Note: QDateTimeParser relies on this producing a useful result, even when |
4445 | // !isValid(), at least when the invalidity is a time in a fall-back (that |
4446 | // we'll have adjusted to lie outside it, but marked invalid because it's |
4447 | // not what was asked for). Other things may be doing similar. But that's |
4448 | // only relevant when we got enough data for resolution to find it invalid. |
4449 | const auto status = getStatus(d); |
4450 | if (!status.testFlags(flags: QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime)) |
4451 | return 0; |
4452 | |
4453 | switch (extractSpec(status)) { |
4454 | case Qt::UTC: |
4455 | return getMSecs(d); |
4456 | |
4457 | case Qt::OffsetFromUTC: |
4458 | Q_ASSERT(!d.isShort()); |
4459 | return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC; |
4460 | |
4461 | case Qt::LocalTime: |
4462 | if (status.testFlag(flag: QDateTimePrivate::ShortData)) { |
4463 | // Short form has nowhere to cache the offset, so recompute. |
4464 | const auto resolve = toTransitionOptions(dst: extractDaylightStatus(status: getStatus(d))); |
4465 | const auto state = QDateTimePrivate::localStateAtMillis(millis: getMSecs(d), resolve); |
4466 | return state.when - state.offset * MSECS_PER_SEC; |
4467 | } |
4468 | // Use the offset saved by refreshZonedDateTime() on creation. |
4469 | return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC; |
4470 | |
4471 | case Qt::TimeZone: |
4472 | Q_ASSERT(!d.isShort()); |
4473 | #if QT_CONFIG(timezone) |
4474 | // Use offset refreshZonedDateTime() saved on creation: |
4475 | if (d->m_timeZone.isValid()) |
4476 | return d->m_msecs - d->m_offsetFromUtc * MSECS_PER_SEC; |
4477 | #endif |
4478 | return 0; |
4479 | } |
4480 | Q_UNREACHABLE_RETURN(0); |
4481 | } |
4482 | |
4483 | /*! |
4484 | \since 5.8 |
4485 | |
4486 | Returns the datetime as a number of seconds after the start, in UTC, of the |
4487 | year 1970. |
4488 | |
4489 | On systems that do not support time zones, this function will |
4490 | behave as if local time were Qt::UTC. |
4491 | |
4492 | The behavior for this function is undefined if the datetime stored in |
4493 | this object is not valid. However, for all valid dates, this function |
4494 | returns a unique value. |
4495 | |
4496 | \sa toMSecsSinceEpoch(), fromSecsSinceEpoch(), setSecsSinceEpoch() |
4497 | */ |
4498 | qint64 QDateTime::toSecsSinceEpoch() const |
4499 | { |
4500 | return toMSecsSinceEpoch() / MSECS_PER_SEC; |
4501 | } |
4502 | |
4503 | /*! |
4504 | \since 4.7 |
4505 | |
4506 | Sets the datetime to represent a moment a given number, \a msecs, of |
4507 | milliseconds after the start, in UTC, of the year 1970. |
4508 | |
4509 | On systems that do not support time zones, this function will |
4510 | behave as if local time were Qt::UTC. |
4511 | |
4512 | Note that passing the minimum of \c qint64 |
4513 | (\c{std::numeric_limits<qint64>::min()}) to \a msecs will result in |
4514 | undefined behavior. |
4515 | |
4516 | \sa setSecsSinceEpoch(), toMSecsSinceEpoch(), fromMSecsSinceEpoch() |
4517 | */ |
4518 | void QDateTime::setMSecsSinceEpoch(qint64 msecs) |
4519 | { |
4520 | auto status = getStatus(d); |
4521 | const auto spec = extractSpec(status); |
4522 | Q_ASSERT(specCanBeSmall(spec) || !d.isShort()); |
4523 | QDateTimePrivate::ZoneState state(msecs); |
4524 | |
4525 | status &= ~QDateTimePrivate::ValidityMask; |
4526 | if (QTimeZone::isUtcOrFixedOffset(spec)) { |
4527 | if (spec == Qt::OffsetFromUTC) |
4528 | state.offset = d->m_offsetFromUtc; |
4529 | if (!state.offset || !qAddOverflow(v1: msecs, v2: state.offset * MSECS_PER_SEC, r: &state.when)) |
4530 | status |= QDateTimePrivate::ValidityMask; |
4531 | } else if (spec == Qt::LocalTime) { |
4532 | state = QDateTimePrivate::expressUtcAsLocal(utcMSecs: msecs); |
4533 | if (state.valid) |
4534 | status = mergeDaylightStatus(sf: status | QDateTimePrivate::ValidityMask, status: state.dst); |
4535 | #if QT_CONFIG(timezone) |
4536 | } else if (spec == Qt::TimeZone && (d.detach(), d->m_timeZone.isValid())) { |
4537 | const auto data = d->m_timeZone.d->data(forMSecsSinceEpoch: msecs); |
4538 | if (Q_LIKELY(data.offsetFromUtc != QTimeZonePrivate::invalidSeconds())) { |
4539 | state.offset = data.offsetFromUtc; |
4540 | Q_ASSERT(state.offset >= -SECS_PER_DAY && state.offset <= SECS_PER_DAY); |
4541 | if (!state.offset |
4542 | || !Q_UNLIKELY(qAddOverflow(msecs, state.offset * MSECS_PER_SEC, &state.when))) { |
4543 | d->m_status = mergeDaylightStatus(sf: status | QDateTimePrivate::ValidityMask, |
4544 | status: data.daylightTimeOffset |
4545 | ? QDateTimePrivate::DaylightTime |
4546 | : QDateTimePrivate::StandardTime); |
4547 | d->m_msecs = state.when; |
4548 | d->m_offsetFromUtc = state.offset; |
4549 | return; |
4550 | } // else: zone can't represent this UTC time |
4551 | } // else: zone unable to represent given UTC time (should only happen on overflow). |
4552 | #endif // timezone |
4553 | } |
4554 | Q_ASSERT(!status.testFlag(QDateTimePrivate::ValidDateTime) |
4555 | || (state.offset >= -SECS_PER_DAY && state.offset <= SECS_PER_DAY)); |
4556 | |
4557 | if (msecsCanBeSmall(msecs: state.when) && d.isShort()) { |
4558 | // we can keep short |
4559 | d.data.msecs = qintptr(state.when); |
4560 | d.data.status = status.toInt(); |
4561 | } else { |
4562 | d.detach(); |
4563 | d->m_status = status & ~QDateTimePrivate::ShortData; |
4564 | d->m_msecs = state.when; |
4565 | d->m_offsetFromUtc = state.offset; |
4566 | } |
4567 | } |
4568 | |
4569 | /*! |
4570 | \since 5.8 |
4571 | |
4572 | Sets the datetime to represent a moment a given number, \a secs, of seconds |
4573 | after the start, in UTC, of the year 1970. |
4574 | |
4575 | On systems that do not support time zones, this function will |
4576 | behave as if local time were Qt::UTC. |
4577 | |
4578 | \sa setMSecsSinceEpoch(), toSecsSinceEpoch(), fromSecsSinceEpoch() |
4579 | */ |
4580 | void QDateTime::setSecsSinceEpoch(qint64 secs) |
4581 | { |
4582 | qint64 msecs; |
4583 | if (!qMulOverflow(v1: secs, std::integral_constant<qint64, MSECS_PER_SEC>(), r: &msecs)) |
4584 | setMSecsSinceEpoch(msecs); |
4585 | else |
4586 | d.invalidate(); |
4587 | } |
4588 | |
4589 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
4590 | /*! |
4591 | \overload |
4592 | |
4593 | Returns the datetime as a string in the \a format given. |
4594 | |
4595 | If the \a format is Qt::TextDate, the string is formatted in the default |
4596 | way. The day and month names will be in English. An example of this |
4597 | formatting is "Wed May 20 03:40:13 1998". For localized formatting, see |
4598 | \l{QLocale::toString()}. |
4599 | |
4600 | If the \a format is Qt::ISODate, the string format corresponds to the ISO |
4601 | 8601 extended specification for representations of dates and times, taking |
4602 | the form yyyy-MM-ddTHH:mm:ss[Z|±HH:mm], depending on the timeSpec() of the |
4603 | QDateTime. If the timeSpec() is Qt::UTC, Z will be appended to the string; |
4604 | if the timeSpec() is Qt::OffsetFromUTC, the offset in hours and minutes from |
4605 | UTC will be appended to the string. To include milliseconds in the ISO 8601 |
4606 | date, use the \a format Qt::ISODateWithMs, which corresponds to |
4607 | yyyy-MM-ddTHH:mm:ss.zzz[Z|±HH:mm]. |
4608 | |
4609 | If the \a format is Qt::RFC2822Date, the string is formatted |
4610 | following \l{RFC 2822}. |
4611 | |
4612 | If the datetime is invalid, an empty string will be returned. |
4613 | |
4614 | \warning The Qt::ISODate format is only valid for years in the |
4615 | range 0 to 9999. |
4616 | |
4617 | \sa fromString(), QDate::toString(), QTime::toString(), |
4618 | QLocale::toString() |
4619 | */ |
4620 | QString QDateTime::toString(Qt::DateFormat format) const |
4621 | { |
4622 | QString buf; |
4623 | if (!isValid()) |
4624 | return buf; |
4625 | |
4626 | switch (format) { |
4627 | case Qt::RFC2822Date: |
4628 | buf = QLocale::c().toString(dateTime: *this, format: u"dd MMM yyyy hh:mm:ss " ); |
4629 | buf += toOffsetString(format: Qt::TextDate, offset: offsetFromUtc()); |
4630 | return buf; |
4631 | default: |
4632 | case Qt::TextDate: { |
4633 | const std::pair<QDate, QTime> p = getDateTime(d); |
4634 | buf = toStringTextDate(date: p.first); |
4635 | // Insert time between date's day and year: |
4636 | buf.insert(i: buf.lastIndexOf(c: u' '), |
4637 | s: u' ' + p.second.toString(format: Qt::TextDate)); |
4638 | // Append zone/offset indicator, as appropriate: |
4639 | switch (timeSpec()) { |
4640 | case Qt::LocalTime: |
4641 | break; |
4642 | #if QT_CONFIG(timezone) |
4643 | case Qt::TimeZone: |
4644 | buf += u' ' + d->m_timeZone.displayName( |
4645 | atDateTime: *this, nameType: QTimeZone::OffsetName, locale: QLocale::c()); |
4646 | break; |
4647 | #endif |
4648 | default: |
4649 | #if 0 // ### Qt 7 GMT: use UTC instead, see qnamespace.qdoc documentation |
4650 | buf += " UTC"_L1 ; |
4651 | #else |
4652 | buf += " GMT"_L1 ; |
4653 | #endif |
4654 | if (getSpec(d) == Qt::OffsetFromUTC) |
4655 | buf += toOffsetString(format: Qt::TextDate, offset: offsetFromUtc()); |
4656 | } |
4657 | return buf; |
4658 | } |
4659 | case Qt::ISODate: |
4660 | case Qt::ISODateWithMs: { |
4661 | const std::pair<QDate, QTime> p = getDateTime(d); |
4662 | buf = toStringIsoDate(date: p.first); |
4663 | if (buf.isEmpty()) |
4664 | return QString(); // failed to convert |
4665 | buf += u'T' + p.second.toString(format); |
4666 | switch (getSpec(d)) { |
4667 | case Qt::UTC: |
4668 | buf += u'Z'; |
4669 | break; |
4670 | case Qt::OffsetFromUTC: |
4671 | case Qt::TimeZone: |
4672 | buf += toOffsetString(format: Qt::ISODate, offset: offsetFromUtc()); |
4673 | break; |
4674 | default: |
4675 | break; |
4676 | } |
4677 | return buf; |
4678 | } |
4679 | } |
4680 | } |
4681 | |
4682 | /*! |
4683 | \fn QString QDateTime::toString(const QString &format, QCalendar cal) const |
4684 | \fn QString QDateTime::toString(QStringView format, QCalendar cal) const |
4685 | \since 5.14 |
4686 | |
4687 | Returns the datetime as a string. The \a format parameter determines the |
4688 | format of the result string. If \a cal is supplied, it determines the |
4689 | calendar used to represent the date; it defaults to Gregorian. Prior to Qt |
4690 | 5.14, there was no \a cal parameter and the Gregorian calendar was always |
4691 | used. See QTime::toString() and QDate::toString() for the supported |
4692 | specifiers for time and date, respectively, in the \a format parameter. |
4693 | |
4694 | Any sequence of characters enclosed in single quotes will be included |
4695 | verbatim in the output string (stripped of the quotes), even if it contains |
4696 | formatting characters. Two consecutive single quotes ("''") are replaced by |
4697 | a single quote in the output. All other characters in the format string are |
4698 | included verbatim in the output string. |
4699 | |
4700 | Formats without separators (e.g. "ddMM") are supported but must be used with |
4701 | care, as the resulting strings aren't always reliably readable (e.g. if "dM" |
4702 | produces "212" it could mean either the 2nd of December or the 21st of |
4703 | February). |
4704 | |
4705 | Example format strings (assumed that the QDateTime is 21 May 2001 |
4706 | 14:13:09.120): |
4707 | |
4708 | \table |
4709 | \header \li Format \li Result |
4710 | \row \li dd.MM.yyyy \li 21.05.2001 |
4711 | \row \li ddd MMMM d yy \li Tue May 21 01 |
4712 | \row \li hh:mm:ss.zzz \li 14:13:09.120 |
4713 | \row \li hh:mm:ss.z \li 14:13:09.12 |
4714 | \row \li h:m:s ap \li 2:13:9 pm |
4715 | \endtable |
4716 | |
4717 | If the datetime is invalid, an empty string will be returned. |
4718 | |
4719 | \note Day and month names as well as AM/PM indicators are given in English |
4720 | (C locale). To get localized month and day names and localized forms of |
4721 | AM/PM, use QLocale::system().toDateTime(). |
4722 | |
4723 | \sa fromString(), QDate::toString(), QTime::toString(), QLocale::toString() |
4724 | */ |
4725 | QString QDateTime::toString(QStringView format, QCalendar cal) const |
4726 | { |
4727 | return QLocale::c().toString(dateTime: *this, format, cal); |
4728 | } |
4729 | |
4730 | // Out-of-line no-calendar overloads, since QCalendar is a non-trivial type |
4731 | /*! |
4732 | \overload |
4733 | \since 5.10 |
4734 | */ |
4735 | QString QDateTime::toString(QStringView format) const |
4736 | { |
4737 | return QLocale::c().toString(dateTime: *this, format, cal: QCalendar()); |
4738 | } |
4739 | |
4740 | /*! |
4741 | \overload |
4742 | \since 4.6 |
4743 | */ |
4744 | QString QDateTime::toString(const QString &format) const |
4745 | { |
4746 | return QLocale::c().toString(dateTime: *this, format: qToStringViewIgnoringNull(s: format), cal: QCalendar()); |
4747 | } |
4748 | #endif // datestring |
4749 | |
4750 | static inline void massageAdjustedDateTime(QDateTimeData &d, QDate date, QTime time, bool forward) |
4751 | { |
4752 | const QDateTimePrivate::TransitionOptions resolve = toTransitionOptions( |
4753 | res: forward ? QDateTime::TransitionResolution::RelativeToBefore |
4754 | : QDateTime::TransitionResolution::RelativeToAfter); |
4755 | auto status = getStatus(d); |
4756 | Q_ASSERT(status.testFlags(QDateTimePrivate::ValidDate | QDateTimePrivate::ValidTime |
4757 | | QDateTimePrivate::ValidDateTime)); |
4758 | auto spec = extractSpec(status); |
4759 | if (QTimeZone::isUtcOrFixedOffset(spec)) { |
4760 | setDateTime(d, date, time); |
4761 | refreshSimpleDateTime(d); |
4762 | return; |
4763 | } |
4764 | qint64 local = timeToMSecs(date, time); |
4765 | const QDateTimePrivate::ZoneState state = stateAtMillis(zone: d.timeZone(), millis: local, resolve); |
4766 | Q_ASSERT(state.valid || state.dst == QDateTimePrivate::UnknownDaylightTime); |
4767 | if (state.dst == QDateTimePrivate::UnknownDaylightTime) |
4768 | status.setFlag(flag: QDateTimePrivate::ValidDateTime, on: false); |
4769 | else |
4770 | status = mergeDaylightStatus(sf: status | QDateTimePrivate::ValidDateTime, status: state.dst); |
4771 | |
4772 | if (status & QDateTimePrivate::ShortData) { |
4773 | d.data.msecs = state.when; |
4774 | d.data.status = status.toInt(); |
4775 | } else { |
4776 | d.detach(); |
4777 | d->m_status = status; |
4778 | if (state.valid) { |
4779 | d->m_msecs = state.when; |
4780 | d->m_offsetFromUtc = state.offset; |
4781 | } |
4782 | } |
4783 | } |
4784 | |
4785 | /*! |
4786 | Returns a QDateTime object containing a datetime \a ndays days |
4787 | later than the datetime of this object (or earlier if \a ndays is |
4788 | negative). |
4789 | |
4790 | If the timeSpec() is Qt::LocalTime or Qt::TimeZone and the resulting date |
4791 | and time fall in the Standard Time to Daylight-Saving Time transition hour |
4792 | then the result will be just beyond this gap, in the direction of change. |
4793 | If the transition is at 2am and the clock goes forward to 3am, the result of |
4794 | aiming between 2am and 3am will be adjusted to fall before 2am (if \c{ndays |
4795 | < 0}) or after 3am (otherwise). |
4796 | |
4797 | \sa daysTo(), addMonths(), addYears(), addSecs(), {Timezone transitions} |
4798 | */ |
4799 | |
4800 | QDateTime QDateTime::addDays(qint64 ndays) const |
4801 | { |
4802 | if (isNull()) |
4803 | return QDateTime(); |
4804 | |
4805 | QDateTime dt(*this); |
4806 | std::pair<QDate, QTime> p = getDateTime(d); |
4807 | massageAdjustedDateTime(d&: dt.d, date: p.first.addDays(ndays), time: p.second, forward: ndays >= 0); |
4808 | return dt; |
4809 | } |
4810 | |
4811 | /*! |
4812 | Returns a QDateTime object containing a datetime \a nmonths months |
4813 | later than the datetime of this object (or earlier if \a nmonths |
4814 | is negative). |
4815 | |
4816 | If the timeSpec() is Qt::LocalTime or Qt::TimeZone and the resulting date |
4817 | and time fall in the Standard Time to Daylight-Saving Time transition hour |
4818 | then the result will be just beyond this gap, in the direction of change. |
4819 | If the transition is at 2am and the clock goes forward to 3am, the result of |
4820 | aiming between 2am and 3am will be adjusted to fall before 2am (if |
4821 | \c{nmonths < 0}) or after 3am (otherwise). |
4822 | |
4823 | \sa daysTo(), addDays(), addYears(), addSecs(), {Timezone transitions} |
4824 | */ |
4825 | |
4826 | QDateTime QDateTime::addMonths(int nmonths) const |
4827 | { |
4828 | if (isNull()) |
4829 | return QDateTime(); |
4830 | |
4831 | QDateTime dt(*this); |
4832 | std::pair<QDate, QTime> p = getDateTime(d); |
4833 | massageAdjustedDateTime(d&: dt.d, date: p.first.addMonths(nmonths), time: p.second, forward: nmonths >= 0); |
4834 | return dt; |
4835 | } |
4836 | |
4837 | /*! |
4838 | Returns a QDateTime object containing a datetime \a nyears years |
4839 | later than the datetime of this object (or earlier if \a nyears is |
4840 | negative). |
4841 | |
4842 | If the timeSpec() is Qt::LocalTime or Qt::TimeZone and the resulting date |
4843 | and time fall in the Standard Time to Daylight-Saving Time transition hour |
4844 | then the result will be just beyond this gap, in the direction of change. |
4845 | If the transition is at 2am and the clock goes forward to 3am, the result of |
4846 | aiming between 2am and 3am will be adjusted to fall before 2am (if \c{nyears |
4847 | < 0}) or after 3am (otherwise). |
4848 | |
4849 | \sa daysTo(), addDays(), addMonths(), addSecs(), {Timezone transitions} |
4850 | */ |
4851 | |
4852 | QDateTime QDateTime::addYears(int nyears) const |
4853 | { |
4854 | if (isNull()) |
4855 | return QDateTime(); |
4856 | |
4857 | QDateTime dt(*this); |
4858 | std::pair<QDate, QTime> p = getDateTime(d); |
4859 | massageAdjustedDateTime(d&: dt.d, date: p.first.addYears(nyears), time: p.second, forward: nyears >= 0); |
4860 | return dt; |
4861 | } |
4862 | |
4863 | /*! |
4864 | Returns a QDateTime object containing a datetime \a s seconds |
4865 | later than the datetime of this object (or earlier if \a s is |
4866 | negative). |
4867 | |
4868 | If this datetime is invalid, an invalid datetime will be returned. |
4869 | |
4870 | \sa addMSecs(), secsTo(), addDays(), addMonths(), addYears() |
4871 | */ |
4872 | |
4873 | QDateTime QDateTime::addSecs(qint64 s) const |
4874 | { |
4875 | qint64 msecs; |
4876 | if (qMulOverflow(v1: s, std::integral_constant<qint64, MSECS_PER_SEC>(), r: &msecs)) |
4877 | return QDateTime(); |
4878 | return addMSecs(msecs); |
4879 | } |
4880 | |
4881 | /*! |
4882 | Returns a QDateTime object containing a datetime \a msecs milliseconds |
4883 | later than the datetime of this object (or earlier if \a msecs is |
4884 | negative). |
4885 | |
4886 | If this datetime is invalid, an invalid datetime will be returned. |
4887 | |
4888 | \sa addSecs(), msecsTo(), addDays(), addMonths(), addYears() |
4889 | */ |
4890 | QDateTime QDateTime::addMSecs(qint64 msecs) const |
4891 | { |
4892 | if (!isValid()) |
4893 | return QDateTime(); |
4894 | |
4895 | QDateTime dt(*this); |
4896 | switch (getSpec(d)) { |
4897 | case Qt::LocalTime: |
4898 | case Qt::TimeZone: |
4899 | // Convert to real UTC first in case this crosses a DST transition: |
4900 | if (!qAddOverflow(v1: toMSecsSinceEpoch(), v2: msecs, r: &msecs)) |
4901 | dt.setMSecsSinceEpoch(msecs); |
4902 | else |
4903 | dt.d.invalidate(); |
4904 | break; |
4905 | case Qt::UTC: |
4906 | case Qt::OffsetFromUTC: |
4907 | // No need to convert, just add on |
4908 | if (qAddOverflow(v1: getMSecs(d), v2: msecs, r: &msecs)) { |
4909 | dt.d.invalidate(); |
4910 | } else if (d.isShort() && msecsCanBeSmall(msecs)) { |
4911 | dt.d.data.msecs = qintptr(msecs); |
4912 | } else { |
4913 | dt.d.detach(); |
4914 | dt.d->m_msecs = msecs; |
4915 | } |
4916 | break; |
4917 | } |
4918 | return dt; |
4919 | } |
4920 | |
4921 | /*! |
4922 | \fn QDateTime QDateTime::addDuration(std::chrono::milliseconds msecs) const |
4923 | |
4924 | \since 6.4 |
4925 | |
4926 | Returns a QDateTime object containing a datetime \a msecs milliseconds |
4927 | later than the datetime of this object (or earlier if \a msecs is |
4928 | negative). |
4929 | |
4930 | If this datetime is invalid, an invalid datetime will be returned. |
4931 | |
4932 | \note Adding durations expressed in \c{std::chrono::months} or |
4933 | \c{std::chrono::years} does not yield the same result obtained by using |
4934 | addMonths() or addYears(). The former are fixed durations, calculated in |
4935 | relation to the solar year; the latter use the Gregorian calendar definitions |
4936 | of months/years. |
4937 | |
4938 | \sa addMSecs(), msecsTo(), addDays(), addMonths(), addYears() |
4939 | */ |
4940 | |
4941 | /*! |
4942 | Returns the number of days from this datetime to the \a other |
4943 | datetime. The number of days is counted as the number of times |
4944 | midnight is reached between this datetime to the \a other |
4945 | datetime. This means that a 10 minute difference from 23:55 to |
4946 | 0:05 the next day counts as one day. |
4947 | |
4948 | If the \a other datetime is earlier than this datetime, |
4949 | the value returned is negative. |
4950 | |
4951 | Example: |
4952 | \snippet code/src_corelib_time_qdatetime.cpp 15 |
4953 | |
4954 | \sa addDays(), secsTo(), msecsTo() |
4955 | */ |
4956 | |
4957 | qint64 QDateTime::daysTo(const QDateTime &other) const |
4958 | { |
4959 | return date().daysTo(d: other.date()); |
4960 | } |
4961 | |
4962 | /*! |
4963 | Returns the number of seconds from this datetime to the \a other |
4964 | datetime. If the \a other datetime is earlier than this datetime, |
4965 | the value returned is negative. |
4966 | |
4967 | Before performing the comparison, the two datetimes are converted |
4968 | to Qt::UTC to ensure that the result is correct if daylight-saving |
4969 | (DST) applies to one of the two datetimes but not the other. |
4970 | |
4971 | Returns 0 if either datetime is invalid. |
4972 | |
4973 | Example: |
4974 | \snippet code/src_corelib_time_qdatetime.cpp 11 |
4975 | |
4976 | \sa addSecs(), daysTo(), QTime::secsTo() |
4977 | */ |
4978 | |
4979 | qint64 QDateTime::secsTo(const QDateTime &other) const |
4980 | { |
4981 | return msecsTo(other) / MSECS_PER_SEC; |
4982 | } |
4983 | |
4984 | /*! |
4985 | Returns the number of milliseconds from this datetime to the \a other |
4986 | datetime. If the \a other datetime is earlier than this datetime, |
4987 | the value returned is negative. |
4988 | |
4989 | Before performing the comparison, the two datetimes are converted |
4990 | to Qt::UTC to ensure that the result is correct if daylight-saving |
4991 | (DST) applies to one of the two datetimes and but not the other. |
4992 | |
4993 | Returns 0 if either datetime is invalid. |
4994 | |
4995 | \sa addMSecs(), daysTo(), QTime::msecsTo() |
4996 | */ |
4997 | |
4998 | qint64 QDateTime::msecsTo(const QDateTime &other) const |
4999 | { |
5000 | if (!isValid() || !other.isValid()) |
5001 | return 0; |
5002 | |
5003 | return other.toMSecsSinceEpoch() - toMSecsSinceEpoch(); |
5004 | } |
5005 | |
5006 | /*! |
5007 | \fn std::chrono::milliseconds QDateTime::operator-(const QDateTime &lhs, const QDateTime &rhs) |
5008 | \since 6.4 |
5009 | |
5010 | Returns the number of milliseconds between \a lhs and \a rhs. |
5011 | If \a lhs is earlier than \a rhs, the result will be negative. |
5012 | |
5013 | Returns 0 if either datetime is invalid. |
5014 | |
5015 | \sa msecsTo() |
5016 | */ |
5017 | |
5018 | /*! |
5019 | \fn QDateTime QDateTime::operator+(const QDateTime &dateTime, std::chrono::milliseconds duration) |
5020 | \fn QDateTime QDateTime::operator+(std::chrono::milliseconds duration, const QDateTime &dateTime) |
5021 | |
5022 | \since 6.4 |
5023 | |
5024 | Returns a QDateTime object containing a datetime \a duration milliseconds |
5025 | later than \a dateTime (or earlier if \a duration is negative). |
5026 | |
5027 | If \a dateTime is invalid, an invalid datetime will be returned. |
5028 | |
5029 | \sa addMSecs() |
5030 | */ |
5031 | |
5032 | /*! |
5033 | \fn QDateTime &QDateTime::operator+=(std::chrono::milliseconds duration) |
5034 | \since 6.4 |
5035 | |
5036 | Modifies this datetime object by adding the given \a duration. |
5037 | The updated object will be later if \a duration is positive, |
5038 | or earlier if it is negative. |
5039 | |
5040 | If this datetime is invalid, this function has no effect. |
5041 | |
5042 | Returns a reference to this datetime object. |
5043 | |
5044 | \sa addMSecs() |
5045 | */ |
5046 | |
5047 | /*! |
5048 | \fn QDateTime QDateTime::operator-(const QDateTime &dateTime, std::chrono::milliseconds duration) |
5049 | |
5050 | \since 6.4 |
5051 | |
5052 | Returns a QDateTime object containing a datetime \a duration milliseconds |
5053 | earlier than \a dateTime (or later if \a duration is negative). |
5054 | |
5055 | If \a dateTime is invalid, an invalid datetime will be returned. |
5056 | |
5057 | \sa addMSecs() |
5058 | */ |
5059 | |
5060 | /*! |
5061 | \fn QDateTime &QDateTime::operator-=(std::chrono::milliseconds duration) |
5062 | \since 6.4 |
5063 | |
5064 | Modifies this datetime object by subtracting the given \a duration. |
5065 | The updated object will be earlier if \a duration is positive, |
5066 | or later if it is negative. |
5067 | |
5068 | If this datetime is invalid, this function has no effect. |
5069 | |
5070 | Returns a reference to this datetime object. |
5071 | |
5072 | \sa addMSecs |
5073 | */ |
5074 | |
5075 | #if QT_DEPRECATED_SINCE(6, 9) |
5076 | /*! |
5077 | \deprecated [6.9] Use \l toTimeZone() instead. |
5078 | |
5079 | Returns a copy of this datetime converted to the given time \a spec. |
5080 | |
5081 | The result represents the same moment in time as, and is equal to, this datetime. |
5082 | |
5083 | If \a spec is Qt::OffsetFromUTC then it is set to Qt::UTC. To set to a fixed |
5084 | offset from UTC, use toTimeZone() or toOffsetFromUtc(). |
5085 | |
5086 | If \a spec is Qt::TimeZone then it is set to Qt::LocalTime, i.e. the local |
5087 | Time Zone. To set a specified time-zone, use toTimeZone(). |
5088 | |
5089 | Example: |
5090 | \snippet code/src_corelib_time_qdatetime.cpp 16 |
5091 | |
5092 | \sa setTimeSpec(), timeSpec(), toTimeZone() |
5093 | */ |
5094 | |
5095 | QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const |
5096 | { |
5097 | return toTimeZone(toZone: asTimeZone(spec, offset: 0, warner: "toTimeSpec" )); |
5098 | } |
5099 | #endif // 6.9 deprecation |
5100 | |
5101 | /*! |
5102 | \since 5.2 |
5103 | |
5104 | Returns a copy of this datetime converted to a spec of Qt::OffsetFromUTC |
5105 | with the given \a offsetSeconds. Equivalent to |
5106 | \c{toTimeZone(QTimeZone::fromSecondsAheadOfUtc(offsetSeconds))}. |
5107 | |
5108 | If the \a offsetSeconds equals 0 then a UTC datetime will be returned. |
5109 | |
5110 | The result represents the same moment in time as, and is equal to, this datetime. |
5111 | |
5112 | \sa setOffsetFromUtc(), offsetFromUtc(), toTimeZone() |
5113 | */ |
5114 | |
5115 | QDateTime QDateTime::toOffsetFromUtc(int offsetSeconds) const |
5116 | { |
5117 | return toTimeZone(toZone: QTimeZone::fromSecondsAheadOfUtc(offset: offsetSeconds)); |
5118 | } |
5119 | |
5120 | /*! |
5121 | Returns a copy of this datetime converted to local time. |
5122 | |
5123 | The result represents the same moment in time as, and is equal to, this datetime. |
5124 | |
5125 | Example: |
5126 | |
5127 | \snippet code/src_corelib_time_qdatetime.cpp 17 |
5128 | |
5129 | \sa toTimeZone(), toUTC(), toOffsetFromUtc() |
5130 | */ |
5131 | QDateTime QDateTime::toLocalTime() const |
5132 | { |
5133 | return toTimeZone(toZone: QTimeZone::LocalTime); |
5134 | } |
5135 | |
5136 | /*! |
5137 | Returns a copy of this datetime converted to UTC. |
5138 | |
5139 | The result represents the same moment in time as, and is equal to, this datetime. |
5140 | |
5141 | Example: |
5142 | |
5143 | \snippet code/src_corelib_time_qdatetime.cpp 18 |
5144 | |
5145 | \sa toTimeZone(), toLocalTime(), toOffsetFromUtc() |
5146 | */ |
5147 | QDateTime QDateTime::toUTC() const |
5148 | { |
5149 | return toTimeZone(toZone: QTimeZone::UTC); |
5150 | } |
5151 | |
5152 | /*! |
5153 | \since 5.2 |
5154 | |
5155 | Returns a copy of this datetime converted to the given \a timeZone. |
5156 | |
5157 | The result represents the same moment in time as, and is equal to, this datetime. |
5158 | |
5159 | The result describes the moment in time in terms of \a timeZone's time |
5160 | representation. For example: |
5161 | |
5162 | \snippet code/src_corelib_time_qdatetime.cpp 23 |
5163 | |
5164 | If \a timeZone is invalid then the datetime will be invalid. Otherwise the |
5165 | returned datetime's timeSpec() will match \c{timeZone.timeSpec()}. |
5166 | |
5167 | \sa timeRepresentation(), toLocalTime(), toUTC(), toOffsetFromUtc() |
5168 | */ |
5169 | |
5170 | QDateTime QDateTime::toTimeZone(const QTimeZone &timeZone) const |
5171 | { |
5172 | if (timeRepresentation() == timeZone) |
5173 | return *this; |
5174 | |
5175 | if (!isValid()) { |
5176 | QDateTime ret = *this; |
5177 | ret.setTimeZone(toZone: timeZone); |
5178 | return ret; |
5179 | } |
5180 | |
5181 | return fromMSecsSinceEpoch(msecs: toMSecsSinceEpoch(), timeZone); |
5182 | } |
5183 | |
5184 | /*! |
5185 | \internal |
5186 | Returns \c true if this datetime is equal to the \a other datetime; |
5187 | otherwise returns \c false. |
5188 | |
5189 | \sa precedes(), operator==() |
5190 | */ |
5191 | |
5192 | bool QDateTime::equals(const QDateTime &other) const |
5193 | { |
5194 | if (!isValid()) |
5195 | return !other.isValid(); |
5196 | if (!other.isValid()) |
5197 | return false; |
5198 | |
5199 | if (usesSameOffset(a: d, b: other.d)) |
5200 | return getMSecs(d) == getMSecs(d: other.d); |
5201 | |
5202 | // Convert to UTC and compare |
5203 | return toMSecsSinceEpoch() == other.toMSecsSinceEpoch(); |
5204 | } |
5205 | |
5206 | /*! |
5207 | \fn bool QDateTime::operator==(const QDateTime &lhs, const QDateTime &rhs) |
5208 | |
5209 | Returns \c true if \a lhs represents the same moment in time as \a rhs; |
5210 | otherwise returns \c false. |
5211 | |
5212 | //! [datetime-order-details] |
5213 | Two datetimes using different time representations can have different |
5214 | offsets from UTC. In this case, they may compare equivalent even if their \l |
5215 | date() and \l time() differ, if that difference matches the difference in |
5216 | UTC offset. If their \c date() and \c time() coincide, the one with higher |
5217 | offset from UTC is less (earlier) than the one with lower offset. As a |
5218 | result, datetimes are only weakly ordered. |
5219 | |
5220 | Since 5.14, all invalid datetimes are equivalent and less than all valid |
5221 | datetimes. |
5222 | //! [datetime-order-details] |
5223 | |
5224 | \sa operator!=(), operator<(), operator<=(), operator>(), operator>=() |
5225 | */ |
5226 | |
5227 | /*! |
5228 | \fn bool QDateTime::operator!=(const QDateTime &lhs, const QDateTime &rhs) |
5229 | |
5230 | Returns \c true if \a lhs is different from \a rhs; otherwise returns \c |
5231 | false. |
5232 | |
5233 | \include qdatetime.cpp datetime-order-details |
5234 | |
5235 | \sa operator==() |
5236 | */ |
5237 | |
5238 | Qt::weak_ordering compareThreeWay(const QDateTime &lhs, const QDateTime &rhs) |
5239 | { |
5240 | if (!lhs.isValid()) |
5241 | return rhs.isValid() ? Qt::weak_ordering::less : Qt::weak_ordering::equivalent; |
5242 | |
5243 | if (!rhs.isValid()) |
5244 | return Qt::weak_ordering::greater; // we know that lhs is valid here |
5245 | |
5246 | if (usesSameOffset(a: lhs.d, b: rhs.d)) |
5247 | return Qt::compareThreeWay(lhs: getMSecs(d: lhs.d), rhs: getMSecs(d: rhs.d)); |
5248 | |
5249 | // Convert to UTC and compare |
5250 | return Qt::compareThreeWay(lhs: lhs.toMSecsSinceEpoch(), rhs: rhs.toMSecsSinceEpoch()); |
5251 | } |
5252 | |
5253 | /*! |
5254 | \fn bool QDateTime::operator<(const QDateTime &lhs, const QDateTime &rhs) |
5255 | |
5256 | Returns \c true if \a lhs is earlier than \a rhs; |
5257 | otherwise returns \c false. |
5258 | |
5259 | \include qdatetime.cpp datetime-order-details |
5260 | |
5261 | \sa operator==() |
5262 | */ |
5263 | |
5264 | /*! |
5265 | \fn bool QDateTime::operator<=(const QDateTime &lhs, const QDateTime &rhs) |
5266 | |
5267 | Returns \c true if \a lhs is earlier than or equal to \a rhs; otherwise |
5268 | returns \c false. |
5269 | |
5270 | \include qdatetime.cpp datetime-order-details |
5271 | |
5272 | \sa operator==() |
5273 | */ |
5274 | |
5275 | /*! |
5276 | \fn bool QDateTime::operator>(const QDateTime &lhs, const QDateTime &rhs) |
5277 | |
5278 | Returns \c true if \a lhs is later than \a rhs; otherwise returns \c false. |
5279 | |
5280 | \include qdatetime.cpp datetime-order-details |
5281 | |
5282 | \sa operator==() |
5283 | */ |
5284 | |
5285 | /*! |
5286 | \fn bool QDateTime::operator>=(const QDateTime &lhs, const QDateTime &rhs) |
5287 | |
5288 | Returns \c true if \a lhs is later than or equal to \a rhs; |
5289 | otherwise returns \c false. |
5290 | |
5291 | \include qdatetime.cpp datetime-order-details |
5292 | |
5293 | \sa operator==() |
5294 | */ |
5295 | |
5296 | /*! |
5297 | \since 6.5 |
5298 | \fn QDateTime QDateTime::currentDateTime(const QTimeZone &zone) |
5299 | |
5300 | Returns the system clock's current datetime, using the time representation |
5301 | described by \a zone. If \a zone is omitted, local time is used. |
5302 | |
5303 | \sa currentDateTimeUtc(), QDate::currentDate(), QTime::currentTime(), toTimeSpec() |
5304 | */ |
5305 | |
5306 | /*! |
5307 | \overload |
5308 | \since 0.90 |
5309 | */ |
5310 | QDateTime QDateTime::currentDateTime() |
5311 | { |
5312 | return currentDateTime(zone: QTimeZone::LocalTime); |
5313 | } |
5314 | |
5315 | /*! |
5316 | \fn QDateTime QDateTime::currentDateTimeUtc() |
5317 | \since 4.7 |
5318 | Returns the system clock's current datetime, expressed in terms of UTC. |
5319 | |
5320 | Equivalent to \c{currentDateTime(QTimeZone::UTC)}. |
5321 | |
5322 | \sa currentDateTime(), QDate::currentDate(), QTime::currentTime(), toTimeSpec() |
5323 | */ |
5324 | |
5325 | QDateTime QDateTime::currentDateTimeUtc() |
5326 | { |
5327 | return currentDateTime(zone: QTimeZone::UTC); |
5328 | } |
5329 | |
5330 | /*! |
5331 | \fn qint64 QDateTime::currentMSecsSinceEpoch() |
5332 | \since 4.7 |
5333 | |
5334 | Returns the current number of milliseconds since the start, in UTC, of the year 1970. |
5335 | |
5336 | This number is like the POSIX time_t variable, but expressed in milliseconds |
5337 | instead of seconds. |
5338 | |
5339 | \sa currentDateTime(), currentDateTimeUtc(), toTimeSpec() |
5340 | */ |
5341 | |
5342 | /*! |
5343 | \fn qint64 QDateTime::currentSecsSinceEpoch() |
5344 | \since 5.8 |
5345 | |
5346 | Returns the number of seconds since the start, in UTC, of the year 1970. |
5347 | |
5348 | This number is like the POSIX time_t variable. |
5349 | |
5350 | \sa currentMSecsSinceEpoch() |
5351 | */ |
5352 | |
5353 | /*! |
5354 | \fn template <typename Clock, typename Duration> QDateTime QDateTime::fromStdTimePoint(const std::chrono::time_point<Clock, Duration> &time) |
5355 | \since 6.4 |
5356 | |
5357 | Constructs a datetime representing the same point in time as \a time, |
5358 | using Qt::UTC as its time representation. |
5359 | |
5360 | The clock of \a time must be compatible with |
5361 | \c{std::chrono::system_clock}; in particular, a conversion |
5362 | supported by \c{std::chrono::clock_cast} must exist. After the |
5363 | conversion, the duration type of the result must be convertible to |
5364 | \c{std::chrono::milliseconds}. |
5365 | |
5366 | If this is not the case, the caller must perform the necessary |
5367 | clock conversion towards \c{std::chrono::system_clock} and the |
5368 | necessary conversion of the duration type |
5369 | (cast/round/floor/ceil/...) so that the input to this function |
5370 | satisfies the constraints above. |
5371 | |
5372 | \note This function requires C++20. |
5373 | |
5374 | \sa toStdSysMilliseconds(), fromMSecsSinceEpoch() |
5375 | */ |
5376 | |
5377 | /*! |
5378 | \since 6.4 |
5379 | \overload |
5380 | |
5381 | Constructs a datetime representing the same point in time as \a time, |
5382 | using Qt::UTC as its time representation. |
5383 | */ |
5384 | QDateTime QDateTime::fromStdTimePoint( |
5385 | std::chrono::time_point< |
5386 | std::chrono::system_clock, |
5387 | std::chrono::milliseconds |
5388 | > time) |
5389 | { |
5390 | return fromMSecsSinceEpoch(msecs: time.time_since_epoch().count(), timeZone: QTimeZone::UTC); |
5391 | } |
5392 | |
5393 | /*! |
5394 | \fn QDateTime QDateTime::fromStdTimePoint(const std::chrono::local_time<std::chrono::milliseconds> &time) |
5395 | \since 6.4 |
5396 | |
5397 | Constructs a datetime whose date and time are the number of milliseconds |
5398 | represented by \a time, counted since 1970-01-01T00:00:00.000 in local |
5399 | time (Qt::LocalTime). |
5400 | |
5401 | \note This function requires C++20. |
5402 | |
5403 | \sa toStdSysMilliseconds(), fromMSecsSinceEpoch() |
5404 | */ |
5405 | |
5406 | /*! |
5407 | \fn QDateTime QDateTime::fromStdLocalTime(const std::chrono::local_time<std::chrono::milliseconds> &time) |
5408 | \since 6.4 |
5409 | |
5410 | Constructs a datetime whose date and time are the number of milliseconds |
5411 | represented by \a time, counted since 1970-01-01T00:00:00.000 in local |
5412 | time (Qt::LocalTime). |
5413 | |
5414 | \note This function requires C++20. |
5415 | |
5416 | \sa toStdSysMilliseconds(), fromMSecsSinceEpoch() |
5417 | */ |
5418 | |
5419 | /*! |
5420 | \fn QDateTime QDateTime::fromStdZonedTime(const std::chrono::zoned_time<std::chrono::milliseconds, const std::chrono::time_zone *> &time); |
5421 | \since 6.4 |
5422 | |
5423 | Constructs a datetime representing the same point in time as \a time. |
5424 | The result will be expressed in \a{time}'s time zone. |
5425 | |
5426 | \note This function requires C++20. |
5427 | |
5428 | \sa QTimeZone |
5429 | |
5430 | \sa toStdSysMilliseconds(), fromMSecsSinceEpoch() |
5431 | */ |
5432 | |
5433 | /*! |
5434 | \fn std::chrono::sys_time<std::chrono::milliseconds> QDateTime::toStdSysMilliseconds() const |
5435 | \since 6.4 |
5436 | |
5437 | Converts this datetime object to the equivalent time point expressed in |
5438 | milliseconds, using \c{std::chrono::system_clock} as a clock. |
5439 | |
5440 | \note This function requires C++20. |
5441 | |
5442 | \sa fromStdTimePoint(), toMSecsSinceEpoch() |
5443 | */ |
5444 | |
5445 | /*! |
5446 | \fn std::chrono::sys_seconds QDateTime::toStdSysSeconds() const |
5447 | \since 6.4 |
5448 | |
5449 | Converts this datetime object to the equivalent time point expressed in |
5450 | seconds, using \c{std::chrono::system_clock} as a clock. |
5451 | |
5452 | \note This function requires C++20. |
5453 | |
5454 | \sa fromStdTimePoint(), toSecsSinceEpoch() |
5455 | */ |
5456 | |
5457 | #if defined(Q_OS_WIN) |
5458 | static inline uint msecsFromDecomposed(int hour, int minute, int sec, int msec = 0) |
5459 | { |
5460 | return MSECS_PER_HOUR * hour + MSECS_PER_MIN * minute + MSECS_PER_SEC * sec + msec; |
5461 | } |
5462 | |
5463 | QDate QDate::currentDate() |
5464 | { |
5465 | SYSTEMTIME st = {}; |
5466 | GetLocalTime(&st); |
5467 | return QDate(st.wYear, st.wMonth, st.wDay); |
5468 | } |
5469 | |
5470 | QTime QTime::currentTime() |
5471 | { |
5472 | QTime ct; |
5473 | SYSTEMTIME st = {}; |
5474 | GetLocalTime(&st); |
5475 | ct.setHMS(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); |
5476 | return ct; |
5477 | } |
5478 | |
5479 | QDateTime QDateTime::currentDateTime(const QTimeZone &zone) |
5480 | { |
5481 | // We can get local time or "system" time (which is UTC); otherwise, we must |
5482 | // convert, which is most efficiently done from UTC. |
5483 | const Qt::TimeSpec spec = zone.timeSpec(); |
5484 | SYSTEMTIME st = {}; |
5485 | // GetSystemTime()'s page links to its partner page for GetLocalTime(). |
5486 | // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtime |
5487 | (spec == Qt::LocalTime ? GetLocalTime : GetSystemTime)(&st); |
5488 | QDate d(st.wYear, st.wMonth, st.wDay); |
5489 | QTime t(msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds)); |
5490 | if (spec == Qt::LocalTime) |
5491 | return QDateTime(d, t); |
5492 | QDateTime utc(d, t, QTimeZone::UTC); |
5493 | return spec == Qt::UTC ? utc : utc.toTimeZone(zone); |
5494 | } |
5495 | |
5496 | qint64 QDateTime::currentMSecsSinceEpoch() noexcept |
5497 | { |
5498 | SYSTEMTIME st = {}; |
5499 | GetSystemTime(&st); |
5500 | const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay)); |
5501 | |
5502 | return msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds) + |
5503 | daysAfterEpoch * MSECS_PER_DAY; |
5504 | } |
5505 | |
5506 | qint64 QDateTime::currentSecsSinceEpoch() noexcept |
5507 | { |
5508 | SYSTEMTIME st = {}; |
5509 | GetSystemTime(&st); |
5510 | const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay)); |
5511 | |
5512 | return st.wHour * SECS_PER_HOUR + st.wMinute * SECS_PER_MIN + st.wSecond + |
5513 | daysAfterEpoch * SECS_PER_DAY; |
5514 | } |
5515 | |
5516 | #elif defined(Q_OS_UNIX) // Assume POSIX-compliant |
5517 | QDate QDate::currentDate() |
5518 | { |
5519 | return QDateTime::currentDateTime().date(); |
5520 | } |
5521 | |
5522 | QTime QTime::currentTime() |
5523 | { |
5524 | return QDateTime::currentDateTime().time(); |
5525 | } |
5526 | |
5527 | QDateTime QDateTime::currentDateTime(const QTimeZone &zone) |
5528 | { |
5529 | return fromMSecsSinceEpoch(msecs: currentMSecsSinceEpoch(), timeZone: zone); |
5530 | } |
5531 | |
5532 | qint64 QDateTime::currentMSecsSinceEpoch() noexcept |
5533 | { |
5534 | struct timespec when; |
5535 | if (clock_gettime(CLOCK_REALTIME, tp: &when) == 0) // should always succeed |
5536 | return when.tv_sec * MSECS_PER_SEC + (when.tv_nsec + 500'000) / 1'000'000; |
5537 | Q_UNREACHABLE_RETURN(0); |
5538 | } |
5539 | |
5540 | qint64 QDateTime::currentSecsSinceEpoch() noexcept |
5541 | { |
5542 | struct timespec when; |
5543 | if (clock_gettime(CLOCK_REALTIME, tp: &when) == 0) // should always succeed |
5544 | return when.tv_sec; |
5545 | Q_UNREACHABLE_RETURN(0); |
5546 | } |
5547 | #else |
5548 | #error "What system is this?" |
5549 | #endif |
5550 | |
5551 | #if QT_DEPRECATED_SINCE(6, 9) |
5552 | /*! |
5553 | \since 5.2 |
5554 | \overload |
5555 | \deprecated [6.9] Pass a \l QTimeZone instead, or omit \a spec and \a offsetSeconds. |
5556 | |
5557 | Returns a datetime representing a moment the given number \a msecs of |
5558 | milliseconds after the start, in UTC, of the year 1970, described as |
5559 | specified by \a spec and \a offsetSeconds. |
5560 | |
5561 | Note that there are possible values for \a msecs that lie outside the valid |
5562 | range of QDateTime, both negative and positive. The behavior of this |
5563 | function is undefined for those values. |
5564 | |
5565 | If the \a spec is not Qt::OffsetFromUTC then the \a offsetSeconds will be |
5566 | ignored. If the \a spec is Qt::OffsetFromUTC and the \a offsetSeconds is 0 |
5567 | then Qt::UTC will be used as the \a spec, since UTC has zero offset. |
5568 | |
5569 | If \a spec is Qt::TimeZone then Qt::LocalTime will be used in its place, |
5570 | equivalent to using the current system time zone (but differently |
5571 | represented). |
5572 | |
5573 | \sa fromSecsSinceEpoch(), toMSecsSinceEpoch(), setMSecsSinceEpoch() |
5574 | */ |
5575 | QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec, int offsetSeconds) |
5576 | { |
5577 | return fromMSecsSinceEpoch(msecs, |
5578 | timeZone: asTimeZone(spec, offset: offsetSeconds, warner: "QDateTime::fromMSecsSinceEpoch" )); |
5579 | } |
5580 | |
5581 | /*! |
5582 | \since 5.8 |
5583 | \overload |
5584 | \deprecated [6.9] Pass a \l QTimeZone instead, or omit \a spec and \a offsetSeconds. |
5585 | |
5586 | Returns a datetime representing a moment the given number \a secs of seconds |
5587 | after the start, in UTC, of the year 1970, described as specified by \a spec |
5588 | and \a offsetSeconds. |
5589 | |
5590 | Note that there are possible values for \a secs that lie outside the valid |
5591 | range of QDateTime, both negative and positive. The behavior of this |
5592 | function is undefined for those values. |
5593 | |
5594 | If the \a spec is not Qt::OffsetFromUTC then the \a offsetSeconds will be |
5595 | ignored. If the \a spec is Qt::OffsetFromUTC and the \a offsetSeconds is 0 |
5596 | then Qt::UTC will be used as the \a spec, since UTC has zero offset. |
5597 | |
5598 | If \a spec is Qt::TimeZone then Qt::LocalTime will be used in its place, |
5599 | equivalent to using the current system time zone (but differently |
5600 | represented). |
5601 | |
5602 | \sa fromMSecsSinceEpoch(), toSecsSinceEpoch(), setSecsSinceEpoch() |
5603 | */ |
5604 | QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec, int offsetSeconds) |
5605 | { |
5606 | return fromSecsSinceEpoch(secs, |
5607 | timeZone: asTimeZone(spec, offset: offsetSeconds, warner: "QDateTime::fromSecsSinceEpoch" )); |
5608 | } |
5609 | #endif // 6.9 deprecations |
5610 | |
5611 | /*! |
5612 | \since 5.2 |
5613 | |
5614 | Returns a datetime representing a moment the given number \a msecs of |
5615 | milliseconds after the start, in UTC, of the year 1970, described as |
5616 | specified by \a timeZone. The default time representation is local time. |
5617 | |
5618 | Note that there are possible values for \a msecs that lie outside the valid |
5619 | range of QDateTime, both negative and positive. The behavior of this |
5620 | function is undefined for those values. |
5621 | |
5622 | \sa fromSecsSinceEpoch(), toMSecsSinceEpoch(), setMSecsSinceEpoch() |
5623 | */ |
5624 | QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone) |
5625 | { |
5626 | QDateTime dt; |
5627 | reviseTimeZone(d&: dt.d, zone: timeZone, resolve: TransitionResolution::Reject); |
5628 | if (timeZone.isValid()) |
5629 | dt.setMSecsSinceEpoch(msecs); |
5630 | return dt; |
5631 | } |
5632 | |
5633 | /*! |
5634 | \overload |
5635 | */ |
5636 | QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs) |
5637 | { |
5638 | return fromMSecsSinceEpoch(msecs, timeZone: QTimeZone::LocalTime); |
5639 | } |
5640 | |
5641 | /*! |
5642 | \since 5.8 |
5643 | |
5644 | Returns a datetime representing a moment the given number \a secs of seconds |
5645 | after the start, in UTC, of the year 1970, described as specified by \a |
5646 | timeZone. The default time representation is local time. |
5647 | |
5648 | Note that there are possible values for \a secs that lie outside the valid |
5649 | range of QDateTime, both negative and positive. The behavior of this |
5650 | function is undefined for those values. |
5651 | |
5652 | \sa fromMSecsSinceEpoch(), toSecsSinceEpoch(), setSecsSinceEpoch() |
5653 | */ |
5654 | QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, const QTimeZone &timeZone) |
5655 | { |
5656 | QDateTime dt; |
5657 | reviseTimeZone(d&: dt.d, zone: timeZone, resolve: TransitionResolution::Reject); |
5658 | if (timeZone.isValid()) |
5659 | dt.setSecsSinceEpoch(secs); |
5660 | return dt; |
5661 | } |
5662 | |
5663 | /*! |
5664 | \overload |
5665 | */ |
5666 | QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs) |
5667 | { |
5668 | return fromSecsSinceEpoch(secs, timeZone: QTimeZone::LocalTime); |
5669 | } |
5670 | |
5671 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
5672 | |
5673 | /*! |
5674 | \fn QDateTime QDateTime::fromString(const QString &string, Qt::DateFormat format) |
5675 | |
5676 | Returns the QDateTime represented by the \a string, using the |
5677 | \a format given, or an invalid datetime if this is not possible. |
5678 | |
5679 | Note for Qt::TextDate: only English short month names (e.g. "Jan" in short |
5680 | form or "January" in long form) are recognized. |
5681 | |
5682 | \sa toString(), QLocale::toDateTime() |
5683 | */ |
5684 | |
5685 | /*! |
5686 | \overload |
5687 | \since 6.0 |
5688 | */ |
5689 | QDateTime QDateTime::fromString(QStringView string, Qt::DateFormat format) |
5690 | { |
5691 | if (string.isEmpty()) |
5692 | return QDateTime(); |
5693 | |
5694 | switch (format) { |
5695 | case Qt::RFC2822Date: { |
5696 | const ParsedRfcDateTime rfc = rfcDateImpl(s: string); |
5697 | |
5698 | if (!rfc.date.isValid() || !rfc.time.isValid()) |
5699 | return QDateTime(); |
5700 | |
5701 | QDateTime dateTime(rfc.date, rfc.time, QTimeZone::UTC); |
5702 | dateTime.setTimeZone(toZone: QTimeZone::fromSecondsAheadOfUtc(offset: rfc.utcOffset)); |
5703 | return dateTime; |
5704 | } |
5705 | case Qt::ISODate: |
5706 | case Qt::ISODateWithMs: { |
5707 | const int size = string.size(); |
5708 | if (size < 10) |
5709 | return QDateTime(); |
5710 | |
5711 | QDate date = QDate::fromString(string: string.first(n: 10), format: Qt::ISODate); |
5712 | if (!date.isValid()) |
5713 | return QDateTime(); |
5714 | if (size == 10) |
5715 | return date.startOfDay(); |
5716 | |
5717 | QTimeZone zone = QTimeZone::LocalTime; |
5718 | QStringView isoString = string.sliced(pos: 10); // trim "yyyy-MM-dd" |
5719 | |
5720 | // Must be left with T (or space) and at least one digit for the hour: |
5721 | if (isoString.size() < 2 |
5722 | || !(isoString.startsWith(c: u'T', cs: Qt::CaseInsensitive) |
5723 | // RFC 3339 (section 5.6) allows a space here. (It actually |
5724 | // allows any separator one considers more readable, merely |
5725 | // giving space as an example - but let's not go wild !) |
5726 | || isoString.startsWith(c: u' '))) { |
5727 | return QDateTime(); |
5728 | } |
5729 | isoString = isoString.sliced(pos: 1); // trim 'T' (or space) |
5730 | |
5731 | // Check end of string for Time Zone definition, either Z for UTC or ±HH:mm for Offset |
5732 | if (isoString.endsWith(c: u'Z', cs: Qt::CaseInsensitive)) { |
5733 | zone = QTimeZone::UTC; |
5734 | isoString.chop(n: 1); // trim 'Z' |
5735 | } else { |
5736 | // the loop below is faster but functionally equal to: |
5737 | // const int signIndex = isoString.indexOf(QRegulargExpression(QStringLiteral("[+-]"))); |
5738 | int signIndex = isoString.size() - 1; |
5739 | Q_ASSERT(signIndex >= 0); |
5740 | bool found = false; |
5741 | do { |
5742 | QChar character(isoString[signIndex]); |
5743 | found = character == u'+' || character == u'-'; |
5744 | } while (!found && --signIndex >= 0); |
5745 | |
5746 | if (found) { |
5747 | bool ok; |
5748 | int offset = fromOffsetString(offsetString: isoString.sliced(pos: signIndex), valid: &ok); |
5749 | if (!ok) |
5750 | return QDateTime(); |
5751 | isoString = isoString.first(n: signIndex); |
5752 | zone = QTimeZone::fromSecondsAheadOfUtc(offset); |
5753 | } |
5754 | } |
5755 | |
5756 | // Might be end of day (24:00, including variants), which QTime considers invalid. |
5757 | // ISO 8601 (section 4.2.3) says that 24:00 is equivalent to 00:00 the next day. |
5758 | bool isMidnight24 = false; |
5759 | QTime time = fromIsoTimeString(string: isoString, format, isMidnight24: &isMidnight24); |
5760 | if (!time.isValid()) |
5761 | return QDateTime(); |
5762 | if (isMidnight24) // time is 0:0, but we want the start of next day: |
5763 | return date.addDays(ndays: 1).startOfDay(zone); |
5764 | return QDateTime(date, time, zone); |
5765 | } |
5766 | case Qt::TextDate: { |
5767 | QVarLengthArray<QStringView, 6> parts; |
5768 | |
5769 | auto tokens = string.tokenize(needle: u' ', flags: Qt::SkipEmptyParts); |
5770 | auto it = tokens.begin(); |
5771 | for (int i = 0; i < 6 && it != tokens.end(); ++i, ++it) |
5772 | parts.emplace_back(args: *it); |
5773 | |
5774 | // Documented as "ddd MMM d HH:mm:ss yyyy" with optional offset-suffix; |
5775 | // and allow time either before or after year. |
5776 | if (parts.size() < 5 || it != tokens.end()) |
5777 | return QDateTime(); |
5778 | |
5779 | // Year and time can be in either order. |
5780 | // Guess which by looking for ':' in the time |
5781 | int yearPart = 3; |
5782 | int timePart = 3; |
5783 | if (parts.at(idx: 3).contains(c: u':')) |
5784 | yearPart = 4; |
5785 | else if (parts.at(idx: 4).contains(c: u':')) |
5786 | timePart = 4; |
5787 | else |
5788 | return QDateTime(); |
5789 | |
5790 | bool ok = false; |
5791 | int day = parts.at(idx: 2).toInt(ok: &ok); |
5792 | int year = ok ? parts.at(idx: yearPart).toInt(ok: &ok) : 0; |
5793 | int month = fromShortMonthName(monthName: parts.at(idx: 1)); |
5794 | if (!ok || year == 0 || day == 0 || month < 1) |
5795 | return QDateTime(); |
5796 | |
5797 | const QDate date(year, month, day); |
5798 | if (!date.isValid()) |
5799 | return QDateTime(); |
5800 | |
5801 | const QTime time = fromIsoTimeString(string: parts.at(idx: timePart), format, isMidnight24: nullptr); |
5802 | if (!time.isValid()) |
5803 | return QDateTime(); |
5804 | |
5805 | if (parts.size() == 5) |
5806 | return QDateTime(date, time); |
5807 | |
5808 | QStringView tz = parts.at(idx: 5); |
5809 | if (tz.startsWith(s: "UTC"_L1 ) |
5810 | // GMT has long been deprecated as an alias for UTC. |
5811 | || tz.startsWith(s: "GMT"_L1 , cs: Qt::CaseInsensitive)) { |
5812 | tz = tz.sliced(pos: 3); |
5813 | if (tz.isEmpty()) |
5814 | return QDateTime(date, time, QTimeZone::UTC); |
5815 | |
5816 | int offset = fromOffsetString(offsetString: tz, valid: &ok); |
5817 | return ok ? QDateTime(date, time, QTimeZone::fromSecondsAheadOfUtc(offset)) |
5818 | : QDateTime(); |
5819 | } |
5820 | return QDateTime(); |
5821 | } |
5822 | } |
5823 | |
5824 | return QDateTime(); |
5825 | } |
5826 | |
5827 | /*! |
5828 | \fn QDateTime QDateTime::fromString(const QString &string, const QString &format, int baseYear, QCalendar cal) |
5829 | |
5830 | Returns the QDateTime represented by the \a string, using the \a |
5831 | format given, or an invalid datetime if the string cannot be parsed. |
5832 | |
5833 | Uses the calendar \a cal if supplied, else Gregorian. |
5834 | |
5835 | \include qlocale.cpp base-year-for-two-digit |
5836 | |
5837 | In addition to the expressions, recognized in the format string to represent |
5838 | parts of the date and time, by QDate::fromString() and QTime::fromString(), |
5839 | this method supports: |
5840 | |
5841 | \table |
5842 | \header \li Expression \li Output |
5843 | \row \li t |
5844 | \li the timezone (offset, name, "Z" or offset with "UTC" prefix) |
5845 | \row \li tt |
5846 | \li the timezone in offset format with no colon between hours and |
5847 | minutes (for example "+0200") |
5848 | \row \li ttt |
5849 | \li the timezone in offset format with a colon between hours and |
5850 | minutes (for example "+02:00") |
5851 | \row \li tttt |
5852 | \li the timezone name (for example "Europe/Berlin"). The name |
5853 | recognized are those known to \l QTimeZone, which may depend on the |
5854 | operating system in use. |
5855 | \endtable |
5856 | |
5857 | If no 't' format specifier is present, the system's local time-zone is used. |
5858 | For the defaults of all other fields, see QDate::fromString() and QTime::fromString(). |
5859 | |
5860 | For example: |
5861 | |
5862 | \snippet code/src_corelib_time_qdatetime.cpp 14 |
5863 | |
5864 | All other input characters will be treated as text. Any non-empty sequence |
5865 | of characters enclosed in single quotes will also be treated (stripped of |
5866 | the quotes) as text and not be interpreted as expressions. |
5867 | |
5868 | \snippet code/src_corelib_time_qdatetime.cpp 12 |
5869 | |
5870 | If the format is not satisfied, an invalid QDateTime is returned. If the |
5871 | format is satisfied but \a string represents an invalid datetime (e.g. in a |
5872 | gap skipped by a time-zone transition), an valid QDateTime is returned, that |
5873 | represents a near-by datetime that is valid. |
5874 | |
5875 | The expressions that don't have leading zeroes (d, M, h, m, s, z) will be |
5876 | greedy. This means that they will use two digits (or three, for z) even if this will |
5877 | put them outside the range and/or leave too few digits for other |
5878 | sections. |
5879 | |
5880 | \snippet code/src_corelib_time_qdatetime.cpp 13 |
5881 | |
5882 | This could have meant 1 January 00:30.00 but the M will grab |
5883 | two digits. |
5884 | |
5885 | Incorrectly specified fields of the \a string will cause an invalid |
5886 | QDateTime to be returned. Only datetimes between the local time start of |
5887 | year 100 and end of year 9999 are supported. Note that datetimes near the |
5888 | ends of this range in other time-zones, notably including UTC, may fall |
5889 | outside the range (and thus be treated as invalid) depending on local time |
5890 | zone. |
5891 | |
5892 | \note Day and month names as well as AM/PM indicators must be given in |
5893 | English (C locale). If localized month and day names or localized forms of |
5894 | AM/PM are to be recognized, use QLocale::system().toDateTime(). |
5895 | |
5896 | \note If a format character is repeated more times than the longest |
5897 | expression in the table above using it, this part of the format will be read |
5898 | as several expressions with no separator between them; the longest above, |
5899 | possibly repeated as many times as there are copies of it, ending with a |
5900 | residue that may be a shorter expression. Thus \c{'tttttt'} would match |
5901 | \c{"Europe/BerlinEurope/Berlin"} and set the zone to Berlin time; if the |
5902 | datetime string contained "Europe/BerlinZ" it would "match" but produce an |
5903 | inconsistent result, leading to an invalid datetime. |
5904 | |
5905 | \sa toString(), QDate::fromString(), QTime::fromString(), |
5906 | QLocale::toDateTime() |
5907 | */ |
5908 | |
5909 | /*! |
5910 | \fn QDateTime QDateTime::fromString(QStringView string, QStringView format, QCalendar cal) |
5911 | \overload |
5912 | \since 6.0 |
5913 | */ |
5914 | |
5915 | /*! |
5916 | \overload |
5917 | \since 6.0 |
5918 | */ |
5919 | QDateTime QDateTime::fromString(const QString &string, QStringView format, int baseYear, |
5920 | QCalendar cal) |
5921 | { |
5922 | #if QT_CONFIG(datetimeparser) |
5923 | QDateTime datetime; |
5924 | |
5925 | QDateTimeParser dt(QMetaType::QDateTime, QDateTimeParser::FromString, cal); |
5926 | dt.setDefaultLocale(QLocale::c()); |
5927 | if (dt.parseFormat(format) && (dt.fromString(text: string, datetime: &datetime, baseYear) |
5928 | || !datetime.isValid())) { |
5929 | return datetime; |
5930 | } |
5931 | #else |
5932 | Q_UNUSED(string); |
5933 | Q_UNUSED(format); |
5934 | Q_UNUSED(baseYear); |
5935 | Q_UNUSED(cal); |
5936 | #endif |
5937 | return QDateTime(); |
5938 | } |
5939 | |
5940 | /*! |
5941 | \fn QDateTime QDateTime::fromString(const QString &string, const QString &format, QCalendar cal) |
5942 | \overload |
5943 | \since 5.14 |
5944 | */ |
5945 | |
5946 | /*! |
5947 | \fn QDateTime QDateTime::fromString(const QString &string, QStringView format, QCalendar cal) |
5948 | \overload |
5949 | \since 6.0 |
5950 | */ |
5951 | |
5952 | /*! |
5953 | \fn QDateTime QDateTime::fromString(QStringView string, QStringView format, int baseYear, QCalendar cal) |
5954 | \overload |
5955 | \since 6.7 |
5956 | */ |
5957 | |
5958 | /*! |
5959 | \fn QDateTime QDateTime::fromString(QStringView string, QStringView format, int baseYear) |
5960 | \overload |
5961 | \since 6.7 |
5962 | |
5963 | Uses a default-constructed QCalendar. |
5964 | */ |
5965 | |
5966 | /*! |
5967 | \overload |
5968 | \since 6.7 |
5969 | |
5970 | Uses a default-constructed QCalendar. |
5971 | */ |
5972 | QDateTime QDateTime::fromString(const QString &string, QStringView format, int baseYear) |
5973 | { |
5974 | return fromString(string, format, baseYear, cal: QCalendar()); |
5975 | } |
5976 | |
5977 | /*! |
5978 | \fn QDateTime QDateTime::fromString(const QString &string, const QString &format, int baseYear) |
5979 | \overload |
5980 | \since 6.7 |
5981 | |
5982 | Uses a default-constructed QCalendar. |
5983 | */ |
5984 | #endif // datestring |
5985 | |
5986 | /***************************************************************************** |
5987 | Date/time stream functions |
5988 | *****************************************************************************/ |
5989 | |
5990 | #ifndef QT_NO_DATASTREAM |
5991 | /*! |
5992 | \relates QDate |
5993 | |
5994 | Writes the \a date to stream \a out. |
5995 | |
5996 | \sa {Serializing Qt Data Types} |
5997 | */ |
5998 | |
5999 | QDataStream &operator<<(QDataStream &out, QDate date) |
6000 | { |
6001 | if (out.version() < QDataStream::Qt_5_0) |
6002 | return out << quint32(date.jd); |
6003 | else |
6004 | return out << date.jd; |
6005 | } |
6006 | |
6007 | /*! |
6008 | \relates QDate |
6009 | |
6010 | Reads a date from stream \a in into the \a date. |
6011 | |
6012 | \sa {Serializing Qt Data Types} |
6013 | */ |
6014 | |
6015 | QDataStream &operator>>(QDataStream &in, QDate &date) |
6016 | { |
6017 | if (in.version() < QDataStream::Qt_5_0) { |
6018 | quint32 jd; |
6019 | in >> jd; |
6020 | // Older versions consider 0 an invalid jd. |
6021 | date.jd = (jd != 0 ? jd : QDate::nullJd()); |
6022 | } else { |
6023 | in >> date.jd; |
6024 | } |
6025 | |
6026 | return in; |
6027 | } |
6028 | |
6029 | /*! |
6030 | \relates QTime |
6031 | |
6032 | Writes \a time to stream \a out. |
6033 | |
6034 | \sa {Serializing Qt Data Types} |
6035 | */ |
6036 | |
6037 | QDataStream &operator<<(QDataStream &out, QTime time) |
6038 | { |
6039 | if (out.version() >= QDataStream::Qt_4_0) { |
6040 | return out << quint32(time.mds); |
6041 | } else { |
6042 | // Qt3 had no support for reading -1, QTime() was valid and serialized as 0 |
6043 | return out << quint32(time.isNull() ? 0 : time.mds); |
6044 | } |
6045 | } |
6046 | |
6047 | /*! |
6048 | \relates QTime |
6049 | |
6050 | Reads a time from stream \a in into the given \a time. |
6051 | |
6052 | \sa {Serializing Qt Data Types} |
6053 | */ |
6054 | |
6055 | QDataStream &operator>>(QDataStream &in, QTime &time) |
6056 | { |
6057 | quint32 ds; |
6058 | in >> ds; |
6059 | if (in.version() >= QDataStream::Qt_4_0) { |
6060 | time.mds = int(ds); |
6061 | } else { |
6062 | // Qt3 would write 0 for a null time |
6063 | time.mds = (ds == 0) ? QTime::NullTime : int(ds); |
6064 | } |
6065 | return in; |
6066 | } |
6067 | |
6068 | /*! |
6069 | \relates QDateTime |
6070 | |
6071 | Writes \a dateTime to the \a out stream. |
6072 | |
6073 | \sa {Serializing Qt Data Types} |
6074 | */ |
6075 | QDataStream &operator<<(QDataStream &out, const QDateTime &dateTime) |
6076 | { |
6077 | std::pair<QDate, QTime> dateAndTime; |
6078 | |
6079 | // TODO: new version, route spec and details via QTimeZone |
6080 | if (out.version() >= QDataStream::Qt_5_2) { |
6081 | |
6082 | // In 5.2 we switched to using Qt::TimeSpec and added offset and zone support |
6083 | dateAndTime = getDateTime(d: dateTime.d); |
6084 | out << dateAndTime << qint8(dateTime.timeSpec()); |
6085 | if (dateTime.timeSpec() == Qt::OffsetFromUTC) |
6086 | out << qint32(dateTime.offsetFromUtc()); |
6087 | #if QT_CONFIG(timezone) |
6088 | else if (dateTime.timeSpec() == Qt::TimeZone) |
6089 | out << dateTime.timeZone(); |
6090 | #endif // timezone |
6091 | |
6092 | } else if (out.version() == QDataStream::Qt_5_0) { |
6093 | |
6094 | // In Qt 5.0 we incorrectly serialised all datetimes as UTC. |
6095 | // This approach is wrong and should not be used again; it breaks |
6096 | // the guarantee that a deserialised local datetime is the same time |
6097 | // of day, regardless of which timezone it was serialised in. |
6098 | dateAndTime = getDateTime(d: (dateTime.isValid() ? dateTime.toUTC() : dateTime).d); |
6099 | out << dateAndTime << qint8(dateTime.timeSpec()); |
6100 | |
6101 | } else if (out.version() >= QDataStream::Qt_4_0) { |
6102 | |
6103 | // From 4.0 to 5.1 (except 5.0) we used QDateTimePrivate::Spec |
6104 | dateAndTime = getDateTime(d: dateTime.d); |
6105 | out << dateAndTime; |
6106 | switch (dateTime.timeSpec()) { |
6107 | case Qt::UTC: |
6108 | out << (qint8)QDateTimePrivate::UTC; |
6109 | break; |
6110 | case Qt::OffsetFromUTC: |
6111 | out << (qint8)QDateTimePrivate::OffsetFromUTC; |
6112 | break; |
6113 | case Qt::TimeZone: |
6114 | out << (qint8)QDateTimePrivate::TimeZone; |
6115 | break; |
6116 | case Qt::LocalTime: |
6117 | out << (qint8)QDateTimePrivate::LocalUnknown; |
6118 | break; |
6119 | } |
6120 | |
6121 | } else { // version < QDataStream::Qt_4_0 |
6122 | |
6123 | // Before 4.0 there was no TimeSpec, only Qt::LocalTime was supported |
6124 | dateAndTime = getDateTime(d: dateTime.d); |
6125 | out << dateAndTime; |
6126 | |
6127 | } |
6128 | |
6129 | return out; |
6130 | } |
6131 | |
6132 | /*! |
6133 | \relates QDateTime |
6134 | |
6135 | Reads a datetime from the stream \a in into \a dateTime. |
6136 | |
6137 | \sa {Serializing Qt Data Types} |
6138 | */ |
6139 | |
6140 | QDataStream &operator>>(QDataStream &in, QDateTime &dateTime) |
6141 | { |
6142 | QDate dt; |
6143 | QTime tm; |
6144 | qint8 ts = 0; |
6145 | QTimeZone zone(QTimeZone::LocalTime); |
6146 | |
6147 | if (in.version() >= QDataStream::Qt_5_2) { |
6148 | |
6149 | // In 5.2 we switched to using Qt::TimeSpec and added offset and zone support |
6150 | in >> dt >> tm >> ts; |
6151 | switch (static_cast<Qt::TimeSpec>(ts)) { |
6152 | case Qt::UTC: |
6153 | zone = QTimeZone::UTC; |
6154 | break; |
6155 | case Qt::OffsetFromUTC: { |
6156 | qint32 offset = 0; |
6157 | in >> offset; |
6158 | zone = QTimeZone::fromSecondsAheadOfUtc(offset); |
6159 | break; |
6160 | } |
6161 | case Qt::LocalTime: |
6162 | break; |
6163 | case Qt::TimeZone: |
6164 | in >> zone; |
6165 | break; |
6166 | } |
6167 | // Note: no way to resolve transition ambiguity, when relevant; use default. |
6168 | dateTime = QDateTime(dt, tm, zone); |
6169 | |
6170 | } else if (in.version() == QDataStream::Qt_5_0) { |
6171 | |
6172 | // In Qt 5.0 we incorrectly serialised all datetimes as UTC |
6173 | in >> dt >> tm >> ts; |
6174 | dateTime = QDateTime(dt, tm, QTimeZone::UTC); |
6175 | if (static_cast<Qt::TimeSpec>(ts) == Qt::LocalTime) |
6176 | dateTime = dateTime.toTimeZone(timeZone: zone); |
6177 | |
6178 | } else if (in.version() >= QDataStream::Qt_4_0) { |
6179 | |
6180 | // From 4.0 to 5.1 (except 5.0) we used QDateTimePrivate::Spec |
6181 | in >> dt >> tm >> ts; |
6182 | switch (static_cast<QDateTimePrivate::Spec>(ts)) { |
6183 | case QDateTimePrivate::OffsetFromUTC: // No offset was stored, so treat as UTC. |
6184 | case QDateTimePrivate::UTC: |
6185 | zone = QTimeZone::UTC; |
6186 | break; |
6187 | case QDateTimePrivate::TimeZone: // No zone was stored, so treat as LocalTime: |
6188 | case QDateTimePrivate::LocalUnknown: |
6189 | case QDateTimePrivate::LocalStandard: |
6190 | case QDateTimePrivate::LocalDST: |
6191 | break; |
6192 | } |
6193 | dateTime = QDateTime(dt, tm, zone); |
6194 | |
6195 | } else { // version < QDataStream::Qt_4_0 |
6196 | |
6197 | // Before 4.0 there was no TimeSpec, only Qt::LocalTime was supported |
6198 | in >> dt >> tm; |
6199 | dateTime = QDateTime(dt, tm); |
6200 | |
6201 | } |
6202 | |
6203 | return in; |
6204 | } |
6205 | #endif // QT_NO_DATASTREAM |
6206 | |
6207 | /***************************************************************************** |
6208 | Date / Time Debug Streams |
6209 | *****************************************************************************/ |
6210 | |
6211 | #if !defined(QT_NO_DEBUG_STREAM) && QT_CONFIG(datestring) |
6212 | QDebug operator<<(QDebug dbg, QDate date) |
6213 | { |
6214 | QDebugStateSaver saver(dbg); |
6215 | dbg.nospace() << "QDate(" ; |
6216 | if (date.isValid()) |
6217 | // QTBUG-91070, ISODate only supports years in the range 0-9999 |
6218 | if (int y = date.year(); y > 0 && y <= 9999) |
6219 | dbg.nospace() << date.toString(format: Qt::ISODate); |
6220 | else |
6221 | dbg.nospace() << date.toString(format: Qt::TextDate); |
6222 | else |
6223 | dbg.nospace() << "Invalid" ; |
6224 | dbg.nospace() << ')'; |
6225 | return dbg; |
6226 | } |
6227 | |
6228 | QDebug operator<<(QDebug dbg, QTime time) |
6229 | { |
6230 | QDebugStateSaver saver(dbg); |
6231 | dbg.nospace() << "QTime(" ; |
6232 | if (time.isValid()) |
6233 | dbg.nospace() << time.toString(format: u"HH:mm:ss.zzz" ); |
6234 | else |
6235 | dbg.nospace() << "Invalid" ; |
6236 | dbg.nospace() << ')'; |
6237 | return dbg; |
6238 | } |
6239 | |
6240 | QDebug operator<<(QDebug dbg, const QDateTime &date) |
6241 | { |
6242 | QDebugStateSaver saver(dbg); |
6243 | dbg.nospace() << "QDateTime(" ; |
6244 | if (date.isValid()) { |
6245 | const Qt::TimeSpec ts = date.timeSpec(); |
6246 | dbg.noquote() << date.toString(format: u"yyyy-MM-dd HH:mm:ss.zzz t" ) |
6247 | << ' ' << ts; |
6248 | switch (ts) { |
6249 | case Qt::UTC: |
6250 | break; |
6251 | case Qt::OffsetFromUTC: |
6252 | dbg.space() << date.offsetFromUtc() << 's'; |
6253 | break; |
6254 | case Qt::TimeZone: |
6255 | #if QT_CONFIG(timezone) |
6256 | dbg.space() << date.timeZone().id(); |
6257 | #endif // timezone |
6258 | break; |
6259 | case Qt::LocalTime: |
6260 | break; |
6261 | } |
6262 | } else { |
6263 | dbg.nospace() << "Invalid" ; |
6264 | } |
6265 | return dbg.nospace() << ')'; |
6266 | } |
6267 | #endif // debug_stream && datestring |
6268 | |
6269 | /*! \fn size_t qHash(const QDateTime &key, size_t seed = 0) |
6270 | \relates QHash |
6271 | \since 5.0 |
6272 | |
6273 | Returns the hash value for the \a key, using \a seed to seed the calculation. |
6274 | */ |
6275 | size_t qHash(const QDateTime &key, size_t seed) |
6276 | { |
6277 | // Use to toMSecsSinceEpoch instead of individual qHash functions for |
6278 | // QDate/QTime/spec/offset because QDateTime::operator== converts both arguments |
6279 | // to the same timezone. If we don't, qHash would return different hashes for |
6280 | // two QDateTimes that are equivalent once converted to the same timezone. |
6281 | return key.isValid() ? qHash(key: key.toMSecsSinceEpoch(), seed) : seed; |
6282 | } |
6283 | |
6284 | /*! \fn size_t qHash(QDate key, size_t seed = 0) |
6285 | \relates QHash |
6286 | \since 5.0 |
6287 | |
6288 | Returns the hash value for the \a key, using \a seed to seed the calculation. |
6289 | */ |
6290 | size_t qHash(QDate key, size_t seed) noexcept |
6291 | { |
6292 | return qHash(key: key.toJulianDay(), seed); |
6293 | } |
6294 | |
6295 | /*! \fn size_t qHash(QTime key, size_t seed = 0) |
6296 | \relates QHash |
6297 | \since 5.0 |
6298 | |
6299 | Returns the hash value for the \a key, using \a seed to seed the calculation. |
6300 | */ |
6301 | size_t qHash(QTime key, size_t seed) noexcept |
6302 | { |
6303 | return qHash(key: key.msecsSinceStartOfDay(), seed); |
6304 | } |
6305 | |
6306 | QT_END_NAMESPACE |
6307 | |