1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2020 The Qt Company Ltd. |
4 | ** Copyright (C) 2016 Intel Corporation. |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the QtCore module of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:LGPL$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU Lesser General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
22 | ** packaging of this file. Please review the following information to |
23 | ** ensure the GNU Lesser General Public License version 3 requirements |
24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
25 | ** |
26 | ** GNU General Public License Usage |
27 | ** Alternatively, this file may be used under the terms of the GNU |
28 | ** General Public License version 2.0 or (at your option) the GNU General |
29 | ** Public license version 3 or any later version approved by the KDE Free |
30 | ** Qt Foundation. The licenses are as published by the Free Software |
31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
32 | ** included in the packaging of this file. Please review the following |
33 | ** information to ensure the GNU General Public License requirements will |
34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
35 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
36 | ** |
37 | ** $QT_END_LICENSE$ |
38 | ** |
39 | ****************************************************************************/ |
40 | |
41 | #include "qplatformdefs.h" |
42 | #include "qdatetime.h" |
43 | |
44 | #include "qcalendar.h" |
45 | #include "qdatastream.h" |
46 | #include "qdebug.h" |
47 | #include "qset.h" |
48 | #include "qlocale.h" |
49 | |
50 | #include "private/qdatetime_p.h" |
51 | #if QT_CONFIG(datetimeparser) |
52 | #include "private/qdatetimeparser_p.h" |
53 | #endif |
54 | #ifdef Q_OS_DARWIN |
55 | #include "private/qcore_mac_p.h" |
56 | #endif |
57 | #include "private/qgregoriancalendar_p.h" |
58 | #include "private/qnumeric_p.h" |
59 | #include "private/qstringiterator_p.h" |
60 | #if QT_CONFIG(timezone) |
61 | #include "private/qtimezoneprivate_p.h" |
62 | #endif |
63 | |
64 | #include <cmath> |
65 | #ifdef Q_OS_WIN |
66 | # include <qt_windows.h> |
67 | #endif |
68 | #include <time.h> |
69 | #ifdef Q_CC_MINGW |
70 | # include <unistd.h> // Define _POSIX_THREAD_SAFE_FUNCTIONS to obtain localtime_r() |
71 | #endif |
72 | |
73 | QT_BEGIN_NAMESPACE |
74 | |
75 | /***************************************************************************** |
76 | Date/Time Constants |
77 | *****************************************************************************/ |
78 | |
79 | enum { |
80 | SECS_PER_DAY = 86400, |
81 | MSECS_PER_DAY = 86400000, |
82 | SECS_PER_HOUR = 3600, |
83 | MSECS_PER_HOUR = 3600000, |
84 | SECS_PER_MIN = 60, |
85 | MSECS_PER_MIN = 60000, |
86 | TIME_T_MAX = 2145916799, // int maximum 2037-12-31T23:59:59 UTC |
87 | JULIAN_DAY_FOR_EPOCH = 2440588 // result of julianDayFromDate(1970, 1, 1) |
88 | }; |
89 | |
90 | /***************************************************************************** |
91 | QDate static helper functions |
92 | *****************************************************************************/ |
93 | |
94 | static inline QDate fixedDate(QCalendar::YearMonthDay &&parts, QCalendar cal) |
95 | { |
96 | if ((parts.year < 0 && !cal.isProleptic()) || (parts.year == 0 && !cal.hasYearZero())) |
97 | return QDate(); |
98 | |
99 | parts.day = qMin(parts.day, cal.daysInMonth(parts.month, parts.year)); |
100 | return cal.dateFromParts(parts); |
101 | } |
102 | |
103 | static inline QDate fixedDate(QCalendar::YearMonthDay &&parts) |
104 | { |
105 | if (parts.year) { |
106 | parts.day = qMin(parts.day, QGregorianCalendar::monthLength(parts.month, parts.year)); |
107 | qint64 jd; |
108 | if (QGregorianCalendar::julianFromParts(parts.year, parts.month, parts.day, &jd)) |
109 | return QDate::fromJulianDay(jd); |
110 | } |
111 | return QDate(); |
112 | } |
113 | |
114 | /***************************************************************************** |
115 | Date/Time formatting helper functions |
116 | *****************************************************************************/ |
117 | |
118 | #if QT_CONFIG(textdate) |
119 | static const char qt_shortMonthNames[][4] = { |
120 | "Jan" , "Feb" , "Mar" , "Apr" , "May" , "Jun" , |
121 | "Jul" , "Aug" , "Sep" , "Oct" , "Nov" , "Dec" |
122 | }; |
123 | |
124 | static int fromShortMonthName(QStringView monthName) |
125 | { |
126 | for (unsigned int i = 0; i < sizeof(qt_shortMonthNames) / sizeof(qt_shortMonthNames[0]); ++i) { |
127 | if (monthName == QLatin1String(qt_shortMonthNames[i], 3)) |
128 | return i + 1; |
129 | } |
130 | return -1; |
131 | } |
132 | #endif // textdate |
133 | |
134 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
135 | struct ParsedRfcDateTime { |
136 | QDate date; |
137 | QTime time; |
138 | int utcOffset; |
139 | }; |
140 | |
141 | static int shortDayFromName(QStringView name) |
142 | { |
143 | const char16_t shortDayNames[] = u"MonTueWedThuFriSatSun" ; |
144 | for (int i = 0; i < 7; i++) { |
145 | if (name == QStringView(shortDayNames + 3 * i, 3)) |
146 | return i + 1; |
147 | } |
148 | return 0; |
149 | } |
150 | |
151 | static ParsedRfcDateTime rfcDateImpl(QStringView s) |
152 | { |
153 | // Matches "[ddd,] dd MMM yyyy[ hh:mm[:ss]] [±hhmm]" - correct RFC 822, 2822, 5322 format - |
154 | // or "ddd MMM dd[ hh:mm:ss] yyyy [±hhmm]" - permissive RFC 850, 1036 (read only) |
155 | ParsedRfcDateTime result; |
156 | |
157 | auto words = QStringView{s}.split(u' ', Qt::SkipEmptyParts); |
158 | if (words.size() < 3 || words.size() > 6) |
159 | return result; |
160 | const QChar colon(u':'); |
161 | bool ok = true; |
162 | QDate date; |
163 | |
164 | const auto isShortName = [](QStringView name) { |
165 | return (name.length() == 3 && name[0].isUpper() |
166 | && name[1].isLower() && name[2].isLower()); |
167 | }; |
168 | |
169 | /* Reject entirely (return) if the string is malformed; however, if the date |
170 | * is merely invalid, (break, so as to) go on to parsing of the time. |
171 | */ |
172 | int yearIndex; |
173 | do { // "loop" so that we can use break on merely invalid, but "right shape" date. |
174 | QStringView dayName; |
175 | bool rfcX22 = true; |
176 | if (words.at(0).endsWith(u',')) { |
177 | dayName = words.takeFirst().chopped(1); |
178 | } else if (!words.at(0)[0].isDigit()) { |
179 | dayName = words.takeFirst(); |
180 | rfcX22 = false; |
181 | } // else: dayName is not specified (so we can only be RFC *22) |
182 | if (words.size() < 3 || words.size() > 5) |
183 | return result; |
184 | |
185 | // Don't break before setting yearIndex. |
186 | int dayIndex, monthIndex; |
187 | if (rfcX22) { |
188 | // dd MMM yyyy [hh:mm[:ss]] [±hhmm] |
189 | dayIndex = 0; |
190 | monthIndex = 1; |
191 | yearIndex = 2; |
192 | } else { |
193 | // MMM dd[ hh:mm:ss] yyyy [±hhmm] |
194 | dayIndex = 1; |
195 | monthIndex = 0; |
196 | yearIndex = words.size() > 3 && words.at(2).contains(colon) ? 3 : 2; |
197 | } |
198 | |
199 | int dayOfWeek = 0; |
200 | if (!dayName.isEmpty()) { |
201 | if (!isShortName(dayName)) |
202 | return result; |
203 | dayOfWeek = shortDayFromName(dayName); |
204 | if (!dayOfWeek) |
205 | break; |
206 | } |
207 | |
208 | const int day = words.at(dayIndex).toInt(&ok); |
209 | if (!ok) |
210 | return result; |
211 | const int year = words.at(yearIndex).toInt(&ok); |
212 | if (!ok) |
213 | return result; |
214 | const QStringView monthName = words.at(monthIndex); |
215 | if (!isShortName(monthName)) |
216 | return result; |
217 | int month = fromShortMonthName(monthName); |
218 | if (month < 0) |
219 | break; |
220 | |
221 | date = QDate(year, month, day); |
222 | if (dayOfWeek && date.dayOfWeek() != dayOfWeek) |
223 | date = QDate(); |
224 | } while (false); |
225 | words.remove(yearIndex); |
226 | words.remove(0, 2); // month and day-of-month, in some order |
227 | |
228 | // Time: [hh:mm[:ss]] |
229 | QTime time; |
230 | if (words.size() && words.at(0).contains(colon)) { |
231 | const QStringView when = words.takeFirst(); |
232 | if (when.size() < 5 || when[2] != colon |
233 | || (when.size() == 8 ? when[5] != colon : when.size() > 5)) { |
234 | return result; |
235 | } |
236 | const int hour = when.first(2).toInt(&ok); |
237 | if (!ok) |
238 | return result; |
239 | const int minute = when.sliced(3, 2).toInt(&ok); |
240 | if (!ok) |
241 | return result; |
242 | const auto secs = when.size() == 8 ? when.last(2).toInt(&ok) : 0; |
243 | if (!ok) |
244 | return result; |
245 | time = QTime(hour, minute, secs); |
246 | } |
247 | |
248 | // Offset: [±hh[mm]] |
249 | int offset = 0; |
250 | if (words.size()) { |
251 | const QStringView zone = words.takeFirst(); |
252 | if (words.size() || !(zone.size() == 3 || zone.size() == 5)) |
253 | return result; |
254 | bool negate = false; |
255 | if (zone[0] == u'-') |
256 | negate = true; |
257 | else if (zone[0] != u'+') |
258 | return result; |
259 | const int hour = zone.sliced(1, 2).toInt(&ok); |
260 | if (!ok) |
261 | return result; |
262 | const auto minute = zone.size() == 5 ? zone.last(2).toInt(&ok) : 0; |
263 | if (!ok) |
264 | return result; |
265 | offset = (hour * 60 + minute) * 60; |
266 | if (negate) |
267 | offset = -offset; |
268 | } |
269 | |
270 | result.date = date; |
271 | result.time = time; |
272 | result.utcOffset = offset; |
273 | return result; |
274 | } |
275 | #endif // datestring |
276 | |
277 | // Return offset in [+-]HH:mm format |
278 | static QString toOffsetString(Qt::DateFormat format, int offset) |
279 | { |
280 | return QString::asprintf("%c%02d%s%02d" , |
281 | offset >= 0 ? '+' : '-', |
282 | qAbs(offset) / SECS_PER_HOUR, |
283 | // Qt::ISODate puts : between the hours and minutes, but Qt:TextDate does not: |
284 | format == Qt::TextDate ? "" : ":" , |
285 | (qAbs(offset) / 60) % 60); |
286 | } |
287 | |
288 | #if QT_CONFIG(datestring) |
289 | // Parse offset in [+-]HH[[:]mm] format |
290 | static int fromOffsetString(QStringView offsetString, bool *valid) noexcept |
291 | { |
292 | *valid = false; |
293 | |
294 | const int size = offsetString.size(); |
295 | if (size < 2 || size > 6) |
296 | return 0; |
297 | |
298 | // sign will be +1 for a positive and -1 for a negative offset |
299 | int sign; |
300 | |
301 | // First char must be + or - |
302 | const QChar signChar = offsetString[0]; |
303 | if (signChar == u'+') |
304 | sign = 1; |
305 | else if (signChar == u'-') |
306 | sign = -1; |
307 | else |
308 | return 0; |
309 | |
310 | // Split the hour and minute parts |
311 | const QStringView time = offsetString.sliced(1); |
312 | qsizetype hhLen = time.indexOf(u':'); |
313 | qsizetype mmIndex; |
314 | if (hhLen == -1) |
315 | mmIndex = hhLen = 2; // [+-]HHmm or [+-]HH format |
316 | else |
317 | mmIndex = hhLen + 1; |
318 | |
319 | const QStringView hhRef = time.first(qMin(hhLen, time.size())); |
320 | bool ok = false; |
321 | const int hour = hhRef.toInt(&ok); |
322 | if (!ok) |
323 | return 0; |
324 | |
325 | const QStringView mmRef = time.sliced(qMin(mmIndex, time.size())); |
326 | const int minute = mmRef.isEmpty() ? 0 : mmRef.toInt(&ok); |
327 | if (!ok || minute < 0 || minute > 59) |
328 | return 0; |
329 | |
330 | *valid = true; |
331 | return sign * ((hour * 60) + minute) * 60; |
332 | } |
333 | #endif // datestring |
334 | |
335 | /***************************************************************************** |
336 | QDate member functions |
337 | *****************************************************************************/ |
338 | |
339 | /*! |
340 | \class QDate |
341 | \inmodule QtCore |
342 | \reentrant |
343 | \brief The QDate class provides date functions. |
344 | |
345 | A QDate object represents a particular day, regardless of calendar, locale |
346 | or other settings used when creating it or supplied by the system. It can |
347 | report the year, month and day of the month that represent the day with |
348 | respect to the proleptic Gregorian calendar or any calendar supplied as a |
349 | QCalendar object. QDate objects should be passed by value rather than by |
350 | reference to const; they simply package \c qint64. |
351 | |
352 | A QDate object is typically created by giving the year, month, and day |
353 | numbers explicitly. Note that QDate interprets year numbers less than 100 as |
354 | presented, i.e., as years 1 through 99, without adding any offset. The |
355 | static function currentDate() creates a QDate object containing the date |
356 | read from the system clock. An explicit date can also be set using |
357 | setDate(). The fromString() function returns a QDate given a string and a |
358 | date format which is used to interpret the date within the string. |
359 | |
360 | The year(), month(), and day() functions provide access to the year, month, |
361 | and day numbers. When more than one of these values is needed, it is more |
362 | efficient to call QCalendar::partsFromDate(), to save repeating (potentially |
363 | expensive) calendrical calculations. |
364 | |
365 | Also, dayOfWeek() and dayOfYear() functions are provided. The same |
366 | information is provided in textual format by toString(). QLocale can map the |
367 | day numbers to names, QCalendar can map month numbers to names. |
368 | |
369 | QDate provides a full set of operators to compare two QDate |
370 | objects where smaller means earlier, and larger means later. |
371 | |
372 | You can increment (or decrement) a date by a given number of days |
373 | using addDays(). Similarly you can use addMonths() and addYears(). |
374 | The daysTo() function returns the number of days between two |
375 | dates. |
376 | |
377 | The daysInMonth() and daysInYear() functions return how many days there are |
378 | in this date's month and year, respectively. The isLeapYear() function |
379 | indicates whether a date is in a leap year. QCalendar can also supply this |
380 | information, in some cases more conveniently. |
381 | |
382 | \section1 Remarks |
383 | |
384 | \note All conversion to and from string formats is done using the C locale. |
385 | For localized conversions, see QLocale. |
386 | |
387 | In the Gregorian calendar, there is no year 0. Dates in that year are |
388 | considered invalid. The year -1 is the year "1 before Christ" or "1 before |
389 | common era." The day before 1 January 1 CE, QDate(1, 1, 1), is 31 December |
390 | 1 BCE, QDate(-1, 12, 31). Various other calendars behave similarly; see |
391 | QCalendar::hasYearZero(). |
392 | |
393 | \section2 Range of Valid Dates |
394 | |
395 | Dates are stored internally as a Julian Day number, an integer count of |
396 | every day in a contiguous range, with 24 November 4714 BCE in the Gregorian |
397 | calendar being Julian Day 0 (1 January 4713 BCE in the Julian calendar). |
398 | As well as being an efficient and accurate way of storing an absolute date, |
399 | it is suitable for converting a date into other calendar systems such as |
400 | Hebrew, Islamic or Chinese. The Julian Day number can be obtained using |
401 | QDate::toJulianDay() and can be set using QDate::fromJulianDay(). |
402 | |
403 | The range of Julian Day numbers that QDate can represent is, for technical |
404 | reasons, limited to between -784350574879 and 784354017364, which means from |
405 | before 2 billion BCE to after 2 billion CE. This is more than seven times as |
406 | wide as the range of dates a QDateTime can represent. |
407 | |
408 | \sa QTime, QDateTime, QCalendar, QDateTime::YearRange, QDateEdit, QDateTimeEdit, QCalendarWidget |
409 | */ |
410 | |
411 | /*! |
412 | \fn QDate::QDate() |
413 | |
414 | Constructs a null date. Null dates are invalid. |
415 | |
416 | \sa isNull(), isValid() |
417 | */ |
418 | |
419 | /*! |
420 | Constructs a date with year \a y, month \a m and day \a d. |
421 | |
422 | The date is understood in terms of the Gregorian calendar. If the specified |
423 | date is invalid, the date is not set and isValid() returns \c false. |
424 | |
425 | \warning Years 1 to 99 are interpreted as is. Year 0 is invalid. |
426 | |
427 | \sa isValid(), QCalendar::dateFromParts() |
428 | */ |
429 | |
430 | QDate::QDate(int y, int m, int d) |
431 | { |
432 | if (!QGregorianCalendar::julianFromParts(y, m, d, &jd)) |
433 | jd = nullJd(); |
434 | } |
435 | |
436 | QDate::QDate(int y, int m, int d, QCalendar cal) |
437 | { |
438 | *this = cal.dateFromParts(y, m, d); |
439 | } |
440 | |
441 | /*! |
442 | \fn bool QDate::isNull() const |
443 | |
444 | Returns \c true if the date is null; otherwise returns \c false. A null |
445 | date is invalid. |
446 | |
447 | \note The behavior of this function is equivalent to isValid(). |
448 | |
449 | \sa isValid() |
450 | */ |
451 | |
452 | /*! |
453 | \fn bool QDate::isValid() const |
454 | |
455 | Returns \c true if this date is valid; otherwise returns \c false. |
456 | |
457 | \sa isNull(), QCalendar::isDateValid() |
458 | */ |
459 | |
460 | /*! |
461 | Returns the year of this date. |
462 | |
463 | Uses \a cal as calendar, if supplied, else the Gregorian calendar. |
464 | |
465 | Returns 0 if the date is invalid. For some calendars, dates before their |
466 | first year may all be invalid. |
467 | |
468 | If using a calendar which has a year 0, check using isValid() if the return |
469 | is 0. Such calendars use negative year numbers in the obvious way, with |
470 | year 1 preceded by year 0, in turn preceded by year -1 and so on. |
471 | |
472 | Some calendars, despite having no year 0, have a conventional numbering of |
473 | the years before their first year, counting backwards from 1. For example, |
474 | in the proleptic Gregorian calendar, successive years before 1 CE (the first |
475 | year) are identified as 1 BCE, 2 BCE, 3 BCE and so on. For such calendars, |
476 | negative year numbers are used to indicate these years before year 1, with |
477 | -1 indicating the year before 1. |
478 | |
479 | \sa month(), day(), QCalendar::hasYearZero(), QCalendar::isProleptic(), QCalendar::partsFromDate() |
480 | */ |
481 | |
482 | int QDate::year(QCalendar cal) const |
483 | { |
484 | if (isValid()) { |
485 | const auto parts = cal.partsFromDate(*this); |
486 | if (parts.isValid()) |
487 | return parts.year; |
488 | } |
489 | return 0; |
490 | } |
491 | |
492 | /*! |
493 | \overload |
494 | */ |
495 | |
496 | int QDate::year() const |
497 | { |
498 | if (isValid()) { |
499 | const auto parts = QGregorianCalendar::partsFromJulian(jd); |
500 | if (parts.isValid()) |
501 | return parts.year; |
502 | } |
503 | return 0; |
504 | } |
505 | |
506 | /*! |
507 | Returns the month-number for the date. |
508 | |
509 | Numbers the months of the year starting with 1 for the first. Uses \a cal |
510 | as calendar if supplied, else the Gregorian calendar, for which the month |
511 | numbering is as follows: |
512 | |
513 | \list |
514 | \li 1 = "January" |
515 | \li 2 = "February" |
516 | \li 3 = "March" |
517 | \li 4 = "April" |
518 | \li 5 = "May" |
519 | \li 6 = "June" |
520 | \li 7 = "July" |
521 | \li 8 = "August" |
522 | \li 9 = "September" |
523 | \li 10 = "October" |
524 | \li 11 = "November" |
525 | \li 12 = "December" |
526 | \endlist |
527 | |
528 | Returns 0 if the date is invalid. Note that some calendars may have more |
529 | than 12 months in some years. |
530 | |
531 | \sa year(), day(), QCalendar::partsFromDate() |
532 | */ |
533 | |
534 | int QDate::month(QCalendar cal) const |
535 | { |
536 | if (isValid()) { |
537 | const auto parts = cal.partsFromDate(*this); |
538 | if (parts.isValid()) |
539 | return parts.month; |
540 | } |
541 | return 0; |
542 | } |
543 | |
544 | /*! |
545 | \overload |
546 | */ |
547 | |
548 | int QDate::month() const |
549 | { |
550 | if (isValid()) { |
551 | const auto parts = QGregorianCalendar::partsFromJulian(jd); |
552 | if (parts.isValid()) |
553 | return parts.month; |
554 | } |
555 | return 0; |
556 | } |
557 | |
558 | /*! |
559 | Returns the day of the month for this date. |
560 | |
561 | Uses \a cal as calendar if supplied, else the Gregorian calendar (for which |
562 | the return ranges from 1 to 31). Returns 0 if the date is invalid. |
563 | |
564 | \sa year(), month(), dayOfWeek(), QCalendar::partsFromDate() |
565 | */ |
566 | |
567 | int QDate::day(QCalendar cal) const |
568 | { |
569 | if (isValid()) { |
570 | const auto parts = cal.partsFromDate(*this); |
571 | if (parts.isValid()) |
572 | return parts.day; |
573 | } |
574 | return 0; |
575 | } |
576 | |
577 | /*! |
578 | \overload |
579 | */ |
580 | |
581 | int QDate::day() const |
582 | { |
583 | if (isValid()) { |
584 | const auto parts = QGregorianCalendar::partsFromJulian(jd); |
585 | if (parts.isValid()) |
586 | return parts.day; |
587 | } |
588 | return 0; |
589 | } |
590 | |
591 | /*! |
592 | Returns the weekday (1 = Monday to 7 = Sunday) for this date. |
593 | |
594 | Uses \a cal as calendar if supplied, else the Gregorian calendar. Returns 0 |
595 | if the date is invalid. Some calendars may give special meaning |
596 | (e.g. intercallary days) to values greater than 7. |
597 | |
598 | \sa day(), dayOfYear(), QCalendar::dayOfWeek(), Qt::DayOfWeek |
599 | */ |
600 | |
601 | int QDate::dayOfWeek(QCalendar cal) const |
602 | { |
603 | if (isNull()) |
604 | return 0; |
605 | |
606 | return cal.dayOfWeek(*this); |
607 | } |
608 | |
609 | /*! |
610 | \overload |
611 | */ |
612 | |
613 | int QDate::dayOfWeek() const |
614 | { |
615 | return isValid() ? QGregorianCalendar::weekDayOfJulian(jd) : 0; |
616 | } |
617 | |
618 | /*! |
619 | Returns the day of the year (1 for the first day) for this date. |
620 | |
621 | Uses \a cal as calendar if supplied, else the Gregorian calendar. |
622 | Returns 0 if either the date or the first day of its year is invalid. |
623 | |
624 | \sa day(), dayOfWeek(), QCalendar::daysInYear() |
625 | */ |
626 | |
627 | int QDate::dayOfYear(QCalendar cal) const |
628 | { |
629 | if (isValid()) { |
630 | QDate firstDay = cal.dateFromParts(year(cal), 1, 1); |
631 | if (firstDay.isValid()) |
632 | return firstDay.daysTo(*this) + 1; |
633 | } |
634 | return 0; |
635 | } |
636 | |
637 | /*! |
638 | \overload |
639 | */ |
640 | |
641 | int QDate::dayOfYear() const |
642 | { |
643 | if (isValid()) { |
644 | qint64 first; |
645 | if (QGregorianCalendar::julianFromParts(year(), 1, 1, &first)) |
646 | return jd - first + 1; |
647 | } |
648 | return 0; |
649 | } |
650 | |
651 | /*! |
652 | Returns the number of days in the month for this date. |
653 | |
654 | Uses \a cal as calendar if supplied, else the Gregorian calendar (for which |
655 | the result ranges from 28 to 31). Returns 0 if the date is invalid. |
656 | |
657 | \sa day(), daysInYear(), QCalendar::daysInMonth(), |
658 | QCalendar::maximumDaysInMonth(), QCalendar::minimumDaysInMonth() |
659 | */ |
660 | |
661 | int QDate::daysInMonth(QCalendar cal) const |
662 | { |
663 | if (isValid()) { |
664 | const auto parts = cal.partsFromDate(*this); |
665 | if (parts.isValid()) |
666 | return cal.daysInMonth(parts.month, parts.year); |
667 | } |
668 | return 0; |
669 | } |
670 | |
671 | /*! |
672 | \overload |
673 | */ |
674 | |
675 | int QDate::daysInMonth() const |
676 | { |
677 | if (isValid()) { |
678 | const auto parts = QGregorianCalendar::partsFromJulian(jd); |
679 | if (parts.isValid()) |
680 | return QGregorianCalendar::monthLength(parts.month, parts.year); |
681 | } |
682 | return 0; |
683 | } |
684 | |
685 | /*! |
686 | Returns the number of days in the year for this date. |
687 | |
688 | Uses \a cal as calendar if supplied, else the Gregorian calendar (for which |
689 | the result is 365 or 366). Returns 0 if the date is invalid. |
690 | |
691 | \sa day(), daysInMonth(), QCalendar::daysInYear(), QCalendar::maximumMonthsInYear() |
692 | */ |
693 | |
694 | int QDate::daysInYear(QCalendar cal) const |
695 | { |
696 | if (isNull()) |
697 | return 0; |
698 | |
699 | return cal.daysInYear(year(cal)); |
700 | } |
701 | |
702 | /*! |
703 | \overload |
704 | */ |
705 | |
706 | int QDate::daysInYear() const |
707 | { |
708 | return isValid() ? QGregorianCalendar::leapTest(year()) ? 366 : 365 : 0; |
709 | } |
710 | |
711 | /*! |
712 | Returns the ISO 8601 week number (1 to 53). |
713 | |
714 | Returns 0 if the date is invalid. Otherwise, returns the week number for the |
715 | date. If \a yearNumber is not \nullptr (its default), stores the year as |
716 | *\a{yearNumber}. |
717 | |
718 | In accordance with ISO 8601, each week falls in the year to which most of |
719 | its days belong, in the Gregorian calendar. As ISO 8601's week starts on |
720 | Monday, this is the year in which the week's Thursday falls. Most years have |
721 | 52 weeks, but some have 53. |
722 | |
723 | \note *\a{yearNumber} is not always the same as year(). For example, 1 |
724 | January 2000 has week number 52 in the year 1999, and 31 December |
725 | 2002 has week number 1 in the year 2003. |
726 | |
727 | \sa isValid() |
728 | */ |
729 | |
730 | int QDate::weekNumber(int *yearNumber) const |
731 | { |
732 | if (!isValid()) |
733 | return 0; |
734 | |
735 | // This could be replaced by use of QIso8601Calendar, once we implement it. |
736 | // The Thursday of the same week determines our answer: |
737 | QDate thursday(addDays(4 - dayOfWeek())); |
738 | int year = thursday.year(); |
739 | // Week n's Thurs's DOY has 1 <= DOY - 7*(n-1) < 8, so 0 <= DOY + 6 - 7*n < 7: |
740 | int week = (thursday.dayOfYear() + 6) / 7; |
741 | |
742 | if (yearNumber) |
743 | *yearNumber = year; |
744 | return week; |
745 | } |
746 | |
747 | static bool inDateTimeRange(qint64 jd, bool start) |
748 | { |
749 | using Bounds = std::numeric_limits<qint64>; |
750 | if (jd < Bounds::min() + JULIAN_DAY_FOR_EPOCH) |
751 | return false; |
752 | jd -= JULIAN_DAY_FOR_EPOCH; |
753 | const qint64 maxDay = Bounds::max() / MSECS_PER_DAY; |
754 | const qint64 minDay = Bounds::min() / MSECS_PER_DAY - 1; |
755 | // (Divisions rounded towards zero, as MSECS_PER_DAY has factors other than two.) |
756 | // Range includes start of last day and end of first: |
757 | if (start) |
758 | return jd > minDay && jd <= maxDay; |
759 | return jd >= minDay && jd < maxDay; |
760 | } |
761 | |
762 | static QDateTime toEarliest(QDate day, const QDateTime &form) |
763 | { |
764 | const Qt::TimeSpec spec = form.timeSpec(); |
765 | const int offset = (spec == Qt::OffsetFromUTC) ? form.offsetFromUtc() : 0; |
766 | #if QT_CONFIG(timezone) |
767 | QTimeZone zone; |
768 | if (spec == Qt::TimeZone) |
769 | zone = form.timeZone(); |
770 | #endif |
771 | auto moment = [=](QTime time) { |
772 | switch (spec) { |
773 | case Qt::OffsetFromUTC: return QDateTime(day, time, spec, offset); |
774 | #if QT_CONFIG(timezone) |
775 | case Qt::TimeZone: return QDateTime(day, time, zone); |
776 | #endif |
777 | default: return QDateTime(day, time, spec); |
778 | } |
779 | }; |
780 | // Longest routine time-zone transition is 2 hours: |
781 | QDateTime when = moment(QTime(2, 0)); |
782 | if (!when.isValid()) { |
783 | // Noon should be safe ... |
784 | when = moment(QTime(12, 0)); |
785 | if (!when.isValid()) { |
786 | // ... unless it's a 24-hour jump (moving the date-line) |
787 | when = moment(QTime(23, 59, 59, 999)); |
788 | if (!when.isValid()) |
789 | return QDateTime(); |
790 | } |
791 | } |
792 | int high = when.time().msecsSinceStartOfDay() / 60000; |
793 | int low = 0; |
794 | // Binary chop to the right minute |
795 | while (high > low + 1) { |
796 | int mid = (high + low) / 2; |
797 | QDateTime probe = moment(QTime(mid / 60, mid % 60)); |
798 | if (probe.isValid() && probe.date() == day) { |
799 | high = mid; |
800 | when = probe; |
801 | } else { |
802 | low = mid; |
803 | } |
804 | } |
805 | return when; |
806 | } |
807 | |
808 | /*! |
809 | \since 5.14 |
810 | \fn QDateTime QDate::startOfDay(Qt::TimeSpec spec, int offsetSeconds) const |
811 | \fn QDateTime QDate::startOfDay(const QTimeZone &zone) const |
812 | |
813 | Returns the start-moment of the day. Usually, this shall be midnight at the |
814 | start of the day: however, if a time-zone transition causes the given date |
815 | to skip over that midnight (e.g. a DST spring-forward skipping from the end |
816 | of the previous day to 01:00 of the new day), the actual earliest time in |
817 | the day is returned. This can only arise when the start-moment is specified |
818 | in terms of a time-zone (by passing its QTimeZone as \a zone) or in terms of |
819 | local time (by passing Qt::LocalTime as \a spec; this is its default). |
820 | |
821 | The \a offsetSeconds is ignored unless \a spec is Qt::OffsetFromUTC, when it |
822 | gives the implied zone's offset from UTC. As UTC and such zones have no |
823 | transitions, the start of the day is QTime(0, 0) in these cases. |
824 | |
825 | In the rare case of a date that was entirely skipped (this happens when a |
826 | zone east of the international date-line switches to being west of it), the |
827 | return shall be invalid. Passing Qt::TimeZone as \a spec (instead of |
828 | passing a QTimeZone) or passing an invalid time-zone as \a zone will also |
829 | produce an invalid result, as shall dates that start outside the range |
830 | representable by QDateTime. |
831 | |
832 | \sa endOfDay() |
833 | */ |
834 | QDateTime QDate::startOfDay(Qt::TimeSpec spec, int offsetSeconds) const |
835 | { |
836 | if (!inDateTimeRange(jd, true)) |
837 | return QDateTime(); |
838 | |
839 | switch (spec) { |
840 | case Qt::TimeZone: // should pass a QTimeZone instead of Qt::TimeZone |
841 | qWarning() << "Called QDate::startOfDay(Qt::TimeZone) on" << *this; |
842 | return QDateTime(); |
843 | case Qt::OffsetFromUTC: |
844 | case Qt::UTC: |
845 | return QDateTime(*this, QTime(0, 0), spec, offsetSeconds); |
846 | |
847 | case Qt::LocalTime: |
848 | if (offsetSeconds) |
849 | qWarning("Ignoring offset (%d seconds) passed with Qt::LocalTime" , offsetSeconds); |
850 | break; |
851 | } |
852 | QDateTime when(*this, QTime(0, 0), spec); |
853 | if (!when.isValid()) |
854 | when = toEarliest(*this, when); |
855 | |
856 | return when.isValid() ? when : QDateTime(); |
857 | } |
858 | |
859 | #if QT_CONFIG(timezone) |
860 | /*! |
861 | \overload |
862 | \since 5.14 |
863 | */ |
864 | QDateTime QDate::startOfDay(const QTimeZone &zone) const |
865 | { |
866 | if (!inDateTimeRange(jd, true) || !zone.isValid()) |
867 | return QDateTime(); |
868 | |
869 | QDateTime when(*this, QTime(0, 0), zone); |
870 | if (when.isValid()) |
871 | return when; |
872 | |
873 | // The start of the day must have fallen in a spring-forward's gap; find the spring-forward: |
874 | if (zone.hasTransitions()) { |
875 | QTimeZone::OffsetData tran |
876 | // There's unlikely to be another transition before noon tomorrow. |
877 | // However, the whole of today may have been skipped ! |
878 | = zone.previousTransition(QDateTime(addDays(1), QTime(12, 0), zone)); |
879 | const QDateTime &at = tran.atUtc.toTimeZone(zone); |
880 | if (at.isValid() && at.date() == *this) |
881 | return at; |
882 | } |
883 | |
884 | when = toEarliest(*this, when); |
885 | return when.isValid() ? when : QDateTime(); |
886 | } |
887 | #endif // timezone |
888 | |
889 | static QDateTime toLatest(QDate day, const QDateTime &form) |
890 | { |
891 | const Qt::TimeSpec spec = form.timeSpec(); |
892 | const int offset = (spec == Qt::OffsetFromUTC) ? form.offsetFromUtc() : 0; |
893 | #if QT_CONFIG(timezone) |
894 | QTimeZone zone; |
895 | if (spec == Qt::TimeZone) |
896 | zone = form.timeZone(); |
897 | #endif |
898 | auto moment = [=](QTime time) { |
899 | switch (spec) { |
900 | case Qt::OffsetFromUTC: return QDateTime(day, time, spec, offset); |
901 | #if QT_CONFIG(timezone) |
902 | case Qt::TimeZone: return QDateTime(day, time, zone); |
903 | #endif |
904 | default: return QDateTime(day, time, spec); |
905 | } |
906 | }; |
907 | // Longest routine time-zone transition is 2 hours: |
908 | QDateTime when = moment(QTime(21, 59, 59, 999)); |
909 | if (!when.isValid()) { |
910 | // Noon should be safe ... |
911 | when = moment(QTime(12, 0)); |
912 | if (!when.isValid()) { |
913 | // ... unless it's a 24-hour jump (moving the date-line) |
914 | when = moment(QTime(0, 0)); |
915 | if (!when.isValid()) |
916 | return QDateTime(); |
917 | } |
918 | } |
919 | int high = 24 * 60; |
920 | int low = when.time().msecsSinceStartOfDay() / 60000; |
921 | // Binary chop to the right minute |
922 | while (high > low + 1) { |
923 | int mid = (high + low) / 2; |
924 | QDateTime probe = moment(QTime(mid / 60, mid % 60, 59, 999)); |
925 | if (probe.isValid() && probe.date() == day) { |
926 | low = mid; |
927 | when = probe; |
928 | } else { |
929 | high = mid; |
930 | } |
931 | } |
932 | return when; |
933 | } |
934 | |
935 | /*! |
936 | \since 5.14 |
937 | \fn QDateTime QDate::endOfDay(Qt::TimeSpec spec, int offsetSeconds) const |
938 | \fn QDateTime QDate::endOfDay(const QTimeZone &zone) const |
939 | |
940 | Returns the end-moment of the day. Usually, this is one millisecond before |
941 | the midnight at the end of the day: however, if a time-zone transition |
942 | causes the given date to skip over that midnight (e.g. a DST spring-forward |
943 | skipping from just before 23:00 to the start of the next day), the actual |
944 | latest time in the day is returned. This can only arise when the |
945 | start-moment is specified in terms of a time-zone (by passing its QTimeZone |
946 | as \a zone) or in terms of local time (by passing Qt::LocalTime as \a spec; |
947 | this is its default). |
948 | |
949 | The \a offsetSeconds is ignored unless \a spec is Qt::OffsetFromUTC, when it |
950 | gives the implied zone's offset from UTC. As UTC and such zones have no |
951 | transitions, the end of the day is QTime(23, 59, 59, 999) in these cases. |
952 | |
953 | In the rare case of a date that was entirely skipped (this happens when a |
954 | zone east of the international date-line switches to being west of it), the |
955 | return shall be invalid. Passing Qt::TimeZone as \a spec (instead of |
956 | passing a QTimeZone) will also produce an invalid result, as shall dates |
957 | that end outside the range representable by QDateTime. |
958 | |
959 | \sa startOfDay() |
960 | */ |
961 | QDateTime QDate::endOfDay(Qt::TimeSpec spec, int offsetSeconds) const |
962 | { |
963 | if (!inDateTimeRange(jd, false)) |
964 | return QDateTime(); |
965 | |
966 | switch (spec) { |
967 | case Qt::TimeZone: // should pass a QTimeZone instead of Qt::TimeZone |
968 | qWarning() << "Called QDate::endOfDay(Qt::TimeZone) on" << *this; |
969 | return QDateTime(); |
970 | case Qt::UTC: |
971 | case Qt::OffsetFromUTC: |
972 | return QDateTime(*this, QTime(23, 59, 59, 999), spec, offsetSeconds); |
973 | |
974 | case Qt::LocalTime: |
975 | if (offsetSeconds) |
976 | qWarning("Ignoring offset (%d seconds) passed with Qt::LocalTime" , offsetSeconds); |
977 | break; |
978 | } |
979 | QDateTime when(*this, QTime(23, 59, 59, 999), spec); |
980 | if (!when.isValid()) |
981 | when = toLatest(*this, when); |
982 | return when.isValid() ? when : QDateTime(); |
983 | } |
984 | |
985 | #if QT_CONFIG(timezone) |
986 | /*! |
987 | \overload |
988 | \since 5.14 |
989 | */ |
990 | QDateTime QDate::endOfDay(const QTimeZone &zone) const |
991 | { |
992 | if (!inDateTimeRange(jd, false) || !zone.isValid()) |
993 | return QDateTime(); |
994 | |
995 | QDateTime when(*this, QTime(23, 59, 59, 999), zone); |
996 | if (when.isValid()) |
997 | return when; |
998 | |
999 | // The end of the day must have fallen in a spring-forward's gap; find the spring-forward: |
1000 | if (zone.hasTransitions()) { |
1001 | QTimeZone::OffsetData tran |
1002 | // It's unlikely there's been another transition since yesterday noon. |
1003 | // However, the whole of today may have been skipped ! |
1004 | = zone.nextTransition(QDateTime(addDays(-1), QTime(12, 0), zone)); |
1005 | const QDateTime &at = tran.atUtc.toTimeZone(zone); |
1006 | if (at.isValid() && at.date() == *this) |
1007 | return at; |
1008 | } |
1009 | |
1010 | when = toLatest(*this, when); |
1011 | return when.isValid() ? when : QDateTime(); |
1012 | } |
1013 | #endif // timezone |
1014 | |
1015 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
1016 | |
1017 | static QString toStringTextDate(QDate date) |
1018 | { |
1019 | if (date.isValid()) { |
1020 | QCalendar cal; // Always Gregorian |
1021 | const auto parts = cal.partsFromDate(date); |
1022 | if (parts.isValid()) { |
1023 | const QLatin1Char sp(' '); |
1024 | return QLocale::c().dayName(cal.dayOfWeek(date), QLocale::ShortFormat) + sp |
1025 | + cal.monthName(QLocale::c(), parts.month, parts.year, QLocale::ShortFormat) |
1026 | // Documented to use 4-digit year |
1027 | + sp + QString::asprintf("%d %04d" , parts.day, parts.year); |
1028 | } |
1029 | } |
1030 | return QString(); |
1031 | } |
1032 | |
1033 | static QString toStringIsoDate(QDate date) |
1034 | { |
1035 | const auto parts = QCalendar().partsFromDate(date); |
1036 | if (parts.isValid() && parts.year >= 0 && parts.year <= 9999) |
1037 | return QString::asprintf("%04d-%02d-%02d" , parts.year, parts.month, parts.day); |
1038 | return QString(); |
1039 | } |
1040 | |
1041 | /*! |
1042 | \overload |
1043 | |
1044 | Returns the date as a string. The \a format parameter determines the format |
1045 | of the string. |
1046 | |
1047 | If the \a format is Qt::TextDate, the string is formatted in the default |
1048 | way. The day and month names will be in English. An example of this |
1049 | formatting is "Sat May 20 1995". For localized formatting, see |
1050 | \l{QLocale::toString()}. |
1051 | |
1052 | If the \a format is Qt::ISODate, the string format corresponds |
1053 | to the ISO 8601 extended specification for representations of |
1054 | dates and times, taking the form yyyy-MM-dd, where yyyy is the |
1055 | year, MM is the month of the year (between 01 and 12), and dd is |
1056 | the day of the month between 01 and 31. |
1057 | |
1058 | If the \a format is Qt::RFC2822Date, the string is formatted in |
1059 | an \l{RFC 2822} compatible way. An example of this formatting is |
1060 | "20 May 1995". |
1061 | |
1062 | If the date is invalid, an empty string will be returned. |
1063 | |
1064 | \warning The Qt::ISODate format is only valid for years in the |
1065 | range 0 to 9999. |
1066 | |
1067 | \sa fromString(), QLocale::toString() |
1068 | */ |
1069 | QString QDate::toString(Qt::DateFormat format) const |
1070 | { |
1071 | if (!isValid()) |
1072 | return QString(); |
1073 | |
1074 | switch (format) { |
1075 | case Qt::RFC2822Date: |
1076 | return QLocale::c().toString(*this, u"dd MMM yyyy" ); |
1077 | default: |
1078 | case Qt::TextDate: |
1079 | return toStringTextDate(*this); |
1080 | case Qt::ISODate: |
1081 | case Qt::ISODateWithMs: |
1082 | // No calendar dependence |
1083 | return toStringIsoDate(*this); |
1084 | } |
1085 | } |
1086 | |
1087 | /*! |
1088 | \fn QString QDate::toString(const QString &format, QCalendar cal) const |
1089 | \fn QString QDate::toString(QStringView format, QCalendar cal) const |
1090 | |
1091 | Returns the date as a string. The \a format parameter determines the format |
1092 | of the result string. If \a cal is supplied, it determines the calendar used |
1093 | to represent the date; it defaults to Gregorian. |
1094 | |
1095 | These expressions may be used: |
1096 | |
1097 | \table |
1098 | \header \li Expression \li Output |
1099 | \row \li d \li The day as a number without a leading zero (1 to 31) |
1100 | \row \li dd \li The day as a number with a leading zero (01 to 31) |
1101 | \row \li ddd \li The abbreviated day name ('Mon' to 'Sun'). |
1102 | \row \li dddd \li The long day name ('Monday' to 'Sunday'). |
1103 | \row \li M \li The month as a number without a leading zero (1 to 12) |
1104 | \row \li MM \li The month as a number with a leading zero (01 to 12) |
1105 | \row \li MMM \li The abbreviated month name ('Jan' to 'Dec'). |
1106 | \row \li MMMM \li The long month name ('January' to 'December'). |
1107 | \row \li yy \li The year as a two digit number (00 to 99) |
1108 | \row \li yyyy \li The year as a four digit number. If the year is negative, |
1109 | a minus sign is prepended, making five characters. |
1110 | \endtable |
1111 | |
1112 | Any sequence of characters enclosed in single quotes will be included |
1113 | verbatim in the output string (stripped of the quotes), even if it contains |
1114 | formatting characters. Two consecutive single quotes ("''") are replaced by |
1115 | a single quote in the output. All other characters in the format string are |
1116 | included verbatim in the output string. |
1117 | |
1118 | Formats without separators (e.g. "ddMM") are supported but must be used with |
1119 | care, as the resulting strings aren't always reliably readable (e.g. if "dM" |
1120 | produces "212" it could mean either the 2nd of December or the 21st of |
1121 | February). |
1122 | |
1123 | Example format strings (assuming that the QDate is the 20 July |
1124 | 1969): |
1125 | |
1126 | \table |
1127 | \header \li Format \li Result |
1128 | \row \li dd.MM.yyyy \li 20.07.1969 |
1129 | \row \li ddd MMMM d yy \li Sun July 20 69 |
1130 | \row \li 'The day is' dddd \li The day is Sunday |
1131 | \endtable |
1132 | |
1133 | If the datetime is invalid, an empty string will be returned. |
1134 | |
1135 | \note Day and month names are given in English (C locale). |
1136 | If localized month and day names are desired, use |
1137 | QLocale::system().toString(). |
1138 | |
1139 | \sa fromString(), QDateTime::toString(), QTime::toString(), QLocale::toString() |
1140 | |
1141 | */ |
1142 | QString QDate::toString(QStringView format, QCalendar cal) const |
1143 | { |
1144 | return QLocale::c().toString(*this, format, cal); |
1145 | } |
1146 | #endif // datestring |
1147 | |
1148 | /*! |
1149 | \since 4.2 |
1150 | |
1151 | Sets this to represent the date, in the Gregorian calendar, with the given |
1152 | \a year, \a month and \a day numbers. Returns true if the resulting date is |
1153 | valid, otherwise it sets this to represent an invalid date and returns |
1154 | false. |
1155 | |
1156 | \sa isValid(), QCalendar::dateFromParts() |
1157 | */ |
1158 | bool QDate::setDate(int year, int month, int day) |
1159 | { |
1160 | if (QGregorianCalendar::julianFromParts(year, month, day, &jd)) |
1161 | return true; |
1162 | |
1163 | jd = nullJd(); |
1164 | return false; |
1165 | } |
1166 | |
1167 | /*! |
1168 | \since 5.14 |
1169 | |
1170 | Sets this to represent the date, in the given calendar \a cal, with the |
1171 | given \a year, \a month and \a day numbers. Returns true if the resulting |
1172 | date is valid, otherwise it sets this to represent an invalid date and |
1173 | returns false. |
1174 | |
1175 | \sa isValid(), QCalendar::dateFromParts() |
1176 | */ |
1177 | |
1178 | bool QDate::setDate(int year, int month, int day, QCalendar cal) |
1179 | { |
1180 | *this = QDate(year, month, day, cal); |
1181 | return isValid(); |
1182 | } |
1183 | |
1184 | /*! |
1185 | \since 4.5 |
1186 | |
1187 | Extracts the date's year, month, and day, and assigns them to |
1188 | *\a year, *\a month, and *\a day. The pointers may be null. |
1189 | |
1190 | Returns 0 if the date is invalid. |
1191 | |
1192 | \note In Qt versions prior to 5.7, this function is marked as non-\c{const}. |
1193 | |
1194 | \sa year(), month(), day(), isValid(), QCalendar::partsFromDate() |
1195 | */ |
1196 | void QDate::getDate(int *year, int *month, int *day) const |
1197 | { |
1198 | QCalendar::YearMonthDay parts; // invalid by default |
1199 | if (isValid()) |
1200 | parts = QGregorianCalendar::partsFromJulian(jd); |
1201 | |
1202 | const bool ok = parts.isValid(); |
1203 | if (year) |
1204 | *year = ok ? parts.year : 0; |
1205 | if (month) |
1206 | *month = ok ? parts.month : 0; |
1207 | if (day) |
1208 | *day = ok ? parts.day : 0; |
1209 | } |
1210 | |
1211 | /*! |
1212 | Returns a QDate object containing a date \a ndays later than the |
1213 | date of this object (or earlier if \a ndays is negative). |
1214 | |
1215 | Returns a null date if the current date is invalid or the new date is |
1216 | out of range. |
1217 | |
1218 | \sa addMonths(), addYears(), daysTo() |
1219 | */ |
1220 | |
1221 | QDate QDate::addDays(qint64 ndays) const |
1222 | { |
1223 | if (isNull()) |
1224 | return QDate(); |
1225 | |
1226 | // Due to limits on minJd() and maxJd() we know that any overflow |
1227 | // will be invalid and caught by fromJulianDay(). |
1228 | return fromJulianDay(jd + ndays); |
1229 | } |
1230 | |
1231 | /*! |
1232 | Returns a QDate object containing a date \a nmonths later than the |
1233 | date of this object (or earlier if \a nmonths is negative). |
1234 | |
1235 | Uses \a cal as calendar, if supplied, else the Gregorian calendar. |
1236 | |
1237 | \note If the ending day/month combination does not exist in the resulting |
1238 | month/year, this function will return a date that is the latest valid date |
1239 | in the selected month. |
1240 | |
1241 | \sa addDays(), addYears() |
1242 | */ |
1243 | |
1244 | QDate QDate::addMonths(int nmonths, QCalendar cal) const |
1245 | { |
1246 | if (!isValid()) |
1247 | return QDate(); |
1248 | |
1249 | if (nmonths == 0) |
1250 | return *this; |
1251 | |
1252 | auto parts = cal.partsFromDate(*this); |
1253 | |
1254 | if (!parts.isValid()) |
1255 | return QDate(); |
1256 | Q_ASSERT(parts.year || cal.hasYearZero()); |
1257 | |
1258 | parts.month += nmonths; |
1259 | while (parts.month <= 0) { |
1260 | if (--parts.year || cal.hasYearZero()) |
1261 | parts.month += cal.monthsInYear(parts.year); |
1262 | } |
1263 | int count = cal.monthsInYear(parts.year); |
1264 | while (parts.month > count) { |
1265 | parts.month -= count; |
1266 | count = (++parts.year || cal.hasYearZero()) ? cal.monthsInYear(parts.year) : 0; |
1267 | } |
1268 | |
1269 | return fixedDate(std::move(parts), cal); |
1270 | } |
1271 | |
1272 | /*! |
1273 | \overload |
1274 | */ |
1275 | |
1276 | QDate QDate::addMonths(int nmonths) const |
1277 | { |
1278 | if (isNull()) |
1279 | return QDate(); |
1280 | |
1281 | if (nmonths == 0) |
1282 | return *this; |
1283 | |
1284 | auto parts = QGregorianCalendar::partsFromJulian(jd); |
1285 | |
1286 | if (!parts.isValid()) |
1287 | return QDate(); |
1288 | Q_ASSERT(parts.year); |
1289 | |
1290 | parts.month += nmonths; |
1291 | while (parts.month <= 0) { |
1292 | if (--parts.year) // skip over year 0 |
1293 | parts.month += 12; |
1294 | } |
1295 | while (parts.month > 12) { |
1296 | parts.month -= 12; |
1297 | if (!++parts.year) // skip over year 0 |
1298 | ++parts.year; |
1299 | } |
1300 | |
1301 | return fixedDate(std::move(parts)); |
1302 | } |
1303 | |
1304 | /*! |
1305 | Returns a QDate object containing a date \a nyears later than the |
1306 | date of this object (or earlier if \a nyears is negative). |
1307 | |
1308 | Uses \a cal as calendar, if supplied, else the Gregorian calendar. |
1309 | |
1310 | \note If the ending day/month combination does not exist in the resulting |
1311 | year (e.g., for the Gregorian calendar, if the date was Feb 29 and the final |
1312 | year is not a leap year), this function will return a date that is the |
1313 | latest valid date in the given month (in the example, Feb 28). |
1314 | |
1315 | \sa addDays(), addMonths() |
1316 | */ |
1317 | |
1318 | QDate QDate::addYears(int nyears, QCalendar cal) const |
1319 | { |
1320 | if (!isValid()) |
1321 | return QDate(); |
1322 | |
1323 | auto parts = cal.partsFromDate(*this); |
1324 | if (!parts.isValid()) |
1325 | return QDate(); |
1326 | |
1327 | int old_y = parts.year; |
1328 | parts.year += nyears; |
1329 | |
1330 | // If we just crossed (or hit) a missing year zero, adjust year by +/- 1: |
1331 | if (!cal.hasYearZero() && ((old_y > 0) != (parts.year > 0) || !parts.year)) |
1332 | parts.year += nyears > 0 ? +1 : -1; |
1333 | |
1334 | return fixedDate(std::move(parts), cal); |
1335 | } |
1336 | |
1337 | /*! |
1338 | \overload |
1339 | */ |
1340 | |
1341 | QDate QDate::addYears(int nyears) const |
1342 | { |
1343 | if (isNull()) |
1344 | return QDate(); |
1345 | |
1346 | auto parts = QGregorianCalendar::partsFromJulian(jd); |
1347 | if (!parts.isValid()) |
1348 | return QDate(); |
1349 | |
1350 | int old_y = parts.year; |
1351 | parts.year += nyears; |
1352 | |
1353 | // If we just crossed (or hit) a missing year zero, adjust year by +/- 1: |
1354 | if ((old_y > 0) != (parts.year > 0) || !parts.year) |
1355 | parts.year += nyears > 0 ? +1 : -1; |
1356 | |
1357 | return fixedDate(std::move(parts)); |
1358 | } |
1359 | |
1360 | /*! |
1361 | Returns the number of days from this date to \a d (which is |
1362 | negative if \a d is earlier than this date). |
1363 | |
1364 | Returns 0 if either date is invalid. |
1365 | |
1366 | Example: |
1367 | \snippet code/src_corelib_time_qdatetime.cpp 0 |
1368 | |
1369 | \sa addDays() |
1370 | */ |
1371 | |
1372 | qint64 QDate::daysTo(QDate d) const |
1373 | { |
1374 | if (isNull() || d.isNull()) |
1375 | return 0; |
1376 | |
1377 | // Due to limits on minJd() and maxJd() we know this will never overflow |
1378 | return d.jd - jd; |
1379 | } |
1380 | |
1381 | |
1382 | /*! |
1383 | \fn bool QDate::operator==(QDate d) const |
1384 | |
1385 | Returns \c true if this date and \a d represent the same day, otherwise |
1386 | \c false. |
1387 | */ |
1388 | |
1389 | /*! |
1390 | \fn bool QDate::operator!=(QDate d) const |
1391 | |
1392 | Returns \c true if this date is different from \a d; otherwise |
1393 | returns \c false. |
1394 | |
1395 | \sa operator==() |
1396 | */ |
1397 | |
1398 | /*! |
1399 | \fn bool QDate::operator<(QDate d) const |
1400 | |
1401 | Returns \c true if this date is earlier than \a d; otherwise returns |
1402 | false. |
1403 | */ |
1404 | |
1405 | /*! |
1406 | \fn bool QDate::operator<=(QDate d) const |
1407 | |
1408 | Returns \c true if this date is earlier than or equal to \a d; |
1409 | otherwise returns \c false. |
1410 | */ |
1411 | |
1412 | /*! |
1413 | \fn bool QDate::operator>(QDate d) const |
1414 | |
1415 | Returns \c true if this date is later than \a d; otherwise returns |
1416 | false. |
1417 | */ |
1418 | |
1419 | /*! |
1420 | \fn bool QDate::operator>=(QDate d) const |
1421 | |
1422 | Returns \c true if this date is later than or equal to \a d; |
1423 | otherwise returns \c false. |
1424 | */ |
1425 | |
1426 | /*! |
1427 | \fn QDate::currentDate() |
1428 | Returns the current date, as reported by the system clock. |
1429 | |
1430 | \sa QTime::currentTime(), QDateTime::currentDateTime() |
1431 | */ |
1432 | |
1433 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
1434 | namespace { |
1435 | |
1436 | struct ParsedInt { qulonglong value = 0; bool ok = false; }; |
1437 | |
1438 | /* |
1439 | /internal |
1440 | |
1441 | Read a whole number that must be the whole text. QStringView::toULongLong() |
1442 | will happily ignore spaces and accept signs; but various date formats' |
1443 | fields (e.g. all in ISO) should not. |
1444 | */ |
1445 | ParsedInt readInt(QStringView text) |
1446 | { |
1447 | ParsedInt result; |
1448 | for (QStringIterator it(text); it.hasNext();) { |
1449 | if (!QChar::isDigit(it.next())) |
1450 | return result; |
1451 | } |
1452 | result.value = text.toULongLong(&result.ok); |
1453 | return result; |
1454 | } |
1455 | |
1456 | } |
1457 | |
1458 | /*! |
1459 | \fn QDate QDate::fromString(const QString &string, Qt::DateFormat format) |
1460 | |
1461 | Returns the QDate represented by the \a string, using the |
1462 | \a format given, or an invalid date if the string cannot be |
1463 | parsed. |
1464 | |
1465 | Note for Qt::TextDate: only English month names (e.g. "Jan" in short form or |
1466 | "January" in long form) are recognized. |
1467 | |
1468 | \sa toString(), QLocale::toDate() |
1469 | */ |
1470 | |
1471 | /*! |
1472 | \overload |
1473 | \since 6.0 |
1474 | */ |
1475 | QDate QDate::fromString(QStringView string, Qt::DateFormat format) |
1476 | { |
1477 | if (string.isEmpty()) |
1478 | return QDate(); |
1479 | |
1480 | switch (format) { |
1481 | case Qt::RFC2822Date: |
1482 | return rfcDateImpl(string).date; |
1483 | default: |
1484 | case Qt::TextDate: { |
1485 | auto parts = string.split(u' ', Qt::SkipEmptyParts); |
1486 | |
1487 | if (parts.count() != 4) |
1488 | return QDate(); |
1489 | |
1490 | bool ok = false; |
1491 | int year = parts.at(3).toInt(&ok); |
1492 | int day = ok ? parts.at(2).toInt(&ok) : 0; |
1493 | if (!ok || !day) |
1494 | return QDate(); |
1495 | |
1496 | const int month = fromShortMonthName(parts.at(1)); |
1497 | if (month == -1) // Month name matches no English or localised name. |
1498 | return QDate(); |
1499 | |
1500 | return QDate(year, month, day); |
1501 | } |
1502 | case Qt::ISODate: |
1503 | // Semi-strict parsing, must be long enough and have punctuators as separators |
1504 | if (string.size() >= 10 && string.at(4).isPunct() && string.at(7).isPunct() |
1505 | && (string.size() == 10 || !string.at(10).isDigit())) { |
1506 | const ParsedInt year = readInt(string.first(4)); |
1507 | const ParsedInt month = readInt(string.sliced(5, 2)); |
1508 | const ParsedInt day = readInt(string.sliced(8, 2)); |
1509 | if (year.ok && year.value > 0 && year.value <= 9999 && month.ok && day.ok) |
1510 | return QDate(year.value, month.value, day.value); |
1511 | } |
1512 | break; |
1513 | } |
1514 | return QDate(); |
1515 | } |
1516 | |
1517 | /*! |
1518 | \fn QDate QDate::fromString(const QString &string, const QString &format, QCalendar cal) |
1519 | |
1520 | Returns the QDate represented by the \a string, using the \a |
1521 | format given, or an invalid date if the string cannot be parsed. |
1522 | |
1523 | Uses \a cal as calendar if supplied, else the Gregorian calendar. Ranges of |
1524 | values in the format descriptions below are for the latter; they may be |
1525 | different for other calendars. |
1526 | |
1527 | These expressions may be used for the format: |
1528 | |
1529 | \table |
1530 | \header \li Expression \li Output |
1531 | \row \li d \li The day as a number without a leading zero (1 to 31) |
1532 | \row \li dd \li The day as a number with a leading zero (01 to 31) |
1533 | \row \li ddd \li The abbreviated day name ('Mon' to 'Sun'). |
1534 | \row \li dddd \li The long day name ('Monday' to 'Sunday'). |
1535 | \row \li M \li The month as a number without a leading zero (1 to 12) |
1536 | \row \li MM \li The month as a number with a leading zero (01 to 12) |
1537 | \row \li MMM \li The abbreviated month name ('Jan' to 'Dec'). |
1538 | \row \li MMMM \li The long month name ('January' to 'December'). |
1539 | \row \li yy \li The year as a two digit number (00 to 99) |
1540 | \row \li yyyy \li The year as a four digit number, possibly plus a leading |
1541 | minus sign for negative years. |
1542 | \endtable |
1543 | |
1544 | \note Day and month names must be given in English (C locale). |
1545 | If localized month and day names are used, use |
1546 | QLocale::system().toDate(). |
1547 | |
1548 | All other input characters will be treated as text. Any non-empty sequence |
1549 | of characters enclosed in single quotes will also be treated (stripped of |
1550 | the quotes) as text and not be interpreted as expressions. For example: |
1551 | |
1552 | \snippet code/src_corelib_time_qdatetime.cpp 1 |
1553 | |
1554 | If the format is not satisfied, an invalid QDate is returned. The |
1555 | expressions that don't expect leading zeroes (d, M) will be |
1556 | greedy. This means that they will use two digits even if this |
1557 | will put them outside the accepted range of values and leaves too |
1558 | few digits for other sections. For example, the following format |
1559 | string could have meant January 30 but the M will grab two |
1560 | digits, resulting in an invalid date: |
1561 | |
1562 | \snippet code/src_corelib_time_qdatetime.cpp 2 |
1563 | |
1564 | For any field that is not represented in the format the following |
1565 | defaults are used: |
1566 | |
1567 | \table |
1568 | \header \li Field \li Default value |
1569 | \row \li Year \li 1900 |
1570 | \row \li Month \li 1 (January) |
1571 | \row \li Day \li 1 |
1572 | \endtable |
1573 | |
1574 | The following examples demonstrate the default values: |
1575 | |
1576 | \snippet code/src_corelib_time_qdatetime.cpp 3 |
1577 | |
1578 | \sa toString(), QDateTime::fromString(), QTime::fromString(), |
1579 | QLocale::toDate() |
1580 | */ |
1581 | |
1582 | /*! |
1583 | \fn QDate QDate::fromString(QStringView string, QStringView format, QCalendar cal) |
1584 | \overload |
1585 | \since 6.0 |
1586 | */ |
1587 | |
1588 | /*! |
1589 | \overload |
1590 | \since 6.0 |
1591 | */ |
1592 | QDate QDate::fromString(const QString &string, QStringView format, QCalendar cal) |
1593 | { |
1594 | QDate date; |
1595 | #if QT_CONFIG(datetimeparser) |
1596 | QDateTimeParser dt(QMetaType::QDate, QDateTimeParser::FromString, cal); |
1597 | dt.setDefaultLocale(QLocale::c()); |
1598 | if (dt.parseFormat(format)) |
1599 | dt.fromString(string, &date, nullptr); |
1600 | #else |
1601 | Q_UNUSED(string); |
1602 | Q_UNUSED(format); |
1603 | Q_UNUSED(cal); |
1604 | #endif |
1605 | return date; |
1606 | } |
1607 | #endif // datestring |
1608 | |
1609 | /*! |
1610 | \overload |
1611 | |
1612 | Returns \c true if the specified date (\a year, \a month, and \a day) is |
1613 | valid in the Gregorian calendar; otherwise returns \c false. |
1614 | |
1615 | Example: |
1616 | \snippet code/src_corelib_time_qdatetime.cpp 4 |
1617 | |
1618 | \sa isNull(), setDate(), QCalendar::isDateValid() |
1619 | */ |
1620 | |
1621 | bool QDate::isValid(int year, int month, int day) |
1622 | { |
1623 | return QGregorianCalendar::validParts(year, month, day); |
1624 | } |
1625 | |
1626 | /*! |
1627 | \fn bool QDate::isLeapYear(int year) |
1628 | |
1629 | Returns \c true if the specified \a year is a leap year in the Gregorian |
1630 | calendar; otherwise returns \c false. |
1631 | |
1632 | \sa QCalendar::isLeapYear() |
1633 | */ |
1634 | |
1635 | bool QDate::isLeapYear(int y) |
1636 | { |
1637 | return QGregorianCalendar::leapTest(y); |
1638 | } |
1639 | |
1640 | /*! \fn static QDate QDate::fromJulianDay(qint64 jd) |
1641 | |
1642 | Converts the Julian day \a jd to a QDate. |
1643 | |
1644 | \sa toJulianDay() |
1645 | */ |
1646 | |
1647 | /*! \fn int QDate::toJulianDay() const |
1648 | |
1649 | Converts the date to a Julian day. |
1650 | |
1651 | \sa fromJulianDay() |
1652 | */ |
1653 | |
1654 | /***************************************************************************** |
1655 | QTime member functions |
1656 | *****************************************************************************/ |
1657 | |
1658 | /*! |
1659 | \class QTime |
1660 | \inmodule QtCore |
1661 | \reentrant |
1662 | |
1663 | \brief The QTime class provides clock time functions. |
1664 | |
1665 | A QTime object contains a clock time, which it can express as the numbers of |
1666 | hours, minutes, seconds, and milliseconds since midnight. It provides |
1667 | functions for comparing times and for manipulating a time by adding a number |
1668 | of milliseconds. QTime objects should be passed by value rather than by |
1669 | reference to const; they simply package \c int. |
1670 | |
1671 | QTime uses the 24-hour clock format; it has no concept of AM/PM. |
1672 | Unlike QDateTime, QTime knows nothing about time zones or |
1673 | daylight-saving time (DST). |
1674 | |
1675 | A QTime object is typically created either by giving the number of hours, |
1676 | minutes, seconds, and milliseconds explicitly, or by using the static |
1677 | function currentTime(), which creates a QTime object that represents the |
1678 | system's local time. |
1679 | |
1680 | The hour(), minute(), second(), and msec() functions provide |
1681 | access to the number of hours, minutes, seconds, and milliseconds |
1682 | of the time. The same information is provided in textual format by |
1683 | the toString() function. |
1684 | |
1685 | The addSecs() and addMSecs() functions provide the time a given |
1686 | number of seconds or milliseconds later than a given time. |
1687 | Correspondingly, the number of seconds or milliseconds |
1688 | between two times can be found using secsTo() or msecsTo(). |
1689 | |
1690 | QTime provides a full set of operators to compare two QTime |
1691 | objects; an earlier time is considered smaller than a later one; |
1692 | if A.msecsTo(B) is positive, then A < B. |
1693 | |
1694 | QTime objects can also be created from a text representation using |
1695 | fromString() and converted to a string representation using toString(). All |
1696 | conversion to and from string formats is done using the C locale. For |
1697 | localized conversions, see QLocale. |
1698 | |
1699 | \sa QDate, QDateTime |
1700 | */ |
1701 | |
1702 | /*! |
1703 | \fn QTime::QTime() |
1704 | |
1705 | Constructs a null time object. For a null time, isNull() returns \c true and |
1706 | isValid() returns \c false. If you need a zero time, use QTime(0, 0). For |
1707 | the start of a day, see QDate::startOfDay(). |
1708 | |
1709 | \sa isNull(), isValid() |
1710 | */ |
1711 | |
1712 | /*! |
1713 | Constructs a time with hour \a h, minute \a m, seconds \a s and |
1714 | milliseconds \a ms. |
1715 | |
1716 | \a h must be in the range 0 to 23, \a m and \a s must be in the |
1717 | range 0 to 59, and \a ms must be in the range 0 to 999. |
1718 | |
1719 | \sa isValid() |
1720 | */ |
1721 | |
1722 | QTime::QTime(int h, int m, int s, int ms) |
1723 | { |
1724 | setHMS(h, m, s, ms); |
1725 | } |
1726 | |
1727 | |
1728 | /*! |
1729 | \fn bool QTime::isNull() const |
1730 | |
1731 | Returns \c true if the time is null (i.e., the QTime object was |
1732 | constructed using the default constructor); otherwise returns |
1733 | false. A null time is also an invalid time. |
1734 | |
1735 | \sa isValid() |
1736 | */ |
1737 | |
1738 | /*! |
1739 | Returns \c true if the time is valid; otherwise returns \c false. For example, |
1740 | the time 23:30:55.746 is valid, but 24:12:30 is invalid. |
1741 | |
1742 | \sa isNull() |
1743 | */ |
1744 | |
1745 | bool QTime::isValid() const |
1746 | { |
1747 | return mds > NullTime && mds < MSECS_PER_DAY; |
1748 | } |
1749 | |
1750 | |
1751 | /*! |
1752 | Returns the hour part (0 to 23) of the time. |
1753 | |
1754 | Returns -1 if the time is invalid. |
1755 | |
1756 | \sa minute(), second(), msec() |
1757 | */ |
1758 | |
1759 | int QTime::hour() const |
1760 | { |
1761 | if (!isValid()) |
1762 | return -1; |
1763 | |
1764 | return ds() / MSECS_PER_HOUR; |
1765 | } |
1766 | |
1767 | /*! |
1768 | Returns the minute part (0 to 59) of the time. |
1769 | |
1770 | Returns -1 if the time is invalid. |
1771 | |
1772 | \sa hour(), second(), msec() |
1773 | */ |
1774 | |
1775 | int QTime::minute() const |
1776 | { |
1777 | if (!isValid()) |
1778 | return -1; |
1779 | |
1780 | return (ds() % MSECS_PER_HOUR) / MSECS_PER_MIN; |
1781 | } |
1782 | |
1783 | /*! |
1784 | Returns the second part (0 to 59) of the time. |
1785 | |
1786 | Returns -1 if the time is invalid. |
1787 | |
1788 | \sa hour(), minute(), msec() |
1789 | */ |
1790 | |
1791 | int QTime::second() const |
1792 | { |
1793 | if (!isValid()) |
1794 | return -1; |
1795 | |
1796 | return (ds() / 1000)%SECS_PER_MIN; |
1797 | } |
1798 | |
1799 | /*! |
1800 | Returns the millisecond part (0 to 999) of the time. |
1801 | |
1802 | Returns -1 if the time is invalid. |
1803 | |
1804 | \sa hour(), minute(), second() |
1805 | */ |
1806 | |
1807 | int QTime::msec() const |
1808 | { |
1809 | if (!isValid()) |
1810 | return -1; |
1811 | |
1812 | return ds() % 1000; |
1813 | } |
1814 | |
1815 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
1816 | /*! |
1817 | \overload |
1818 | |
1819 | Returns the time as a string. The \a format parameter determines |
1820 | the format of the string. |
1821 | |
1822 | If \a format is Qt::TextDate, the string format is HH:mm:ss; |
1823 | e.g. 1 second before midnight would be "23:59:59". |
1824 | |
1825 | If \a format is Qt::ISODate, the string format corresponds to the |
1826 | ISO 8601 extended specification for representations of dates, |
1827 | represented by HH:mm:ss. To include milliseconds in the ISO 8601 |
1828 | date, use the \a format Qt::ISODateWithMs, which corresponds to |
1829 | HH:mm:ss.zzz. |
1830 | |
1831 | If the \a format is Qt::RFC2822Date, the string is formatted in |
1832 | an \l{RFC 2822} compatible way. An example of this formatting is |
1833 | "23:59:20". |
1834 | |
1835 | If the time is invalid, an empty string will be returned. |
1836 | |
1837 | \sa fromString(), QDate::toString(), QDateTime::toString(), QLocale::toString() |
1838 | */ |
1839 | |
1840 | QString QTime::toString(Qt::DateFormat format) const |
1841 | { |
1842 | if (!isValid()) |
1843 | return QString(); |
1844 | |
1845 | switch (format) { |
1846 | case Qt::ISODateWithMs: |
1847 | return QString::asprintf("%02d:%02d:%02d.%03d" , hour(), minute(), second(), msec()); |
1848 | case Qt::RFC2822Date: |
1849 | case Qt::ISODate: |
1850 | case Qt::TextDate: |
1851 | default: |
1852 | return QString::asprintf("%02d:%02d:%02d" , hour(), minute(), second()); |
1853 | } |
1854 | } |
1855 | |
1856 | /*! |
1857 | \fn QString QTime::toString(const QString &format) const |
1858 | \fn QString QTime::toString(QStringView format) const |
1859 | |
1860 | Returns the time as a string. The \a format parameter determines |
1861 | the format of the result string. |
1862 | |
1863 | These expressions may be used: |
1864 | |
1865 | \table |
1866 | \header \li Expression \li Output |
1867 | \row \li h |
1868 | \li The hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) |
1869 | \row \li hh |
1870 | \li The hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) |
1871 | \row \li H |
1872 | \li The hour without a leading zero (0 to 23, even with AM/PM display) |
1873 | \row \li HH |
1874 | \li The hour with a leading zero (00 to 23, even with AM/PM display) |
1875 | \row \li m \li The minute without a leading zero (0 to 59) |
1876 | \row \li mm \li The minute with a leading zero (00 to 59) |
1877 | \row \li s \li The whole second, without any leading zero (0 to 59) |
1878 | \row \li ss \li The whole second, with a leading zero where applicable (00 to 59) |
1879 | \row \li z \li The fractional part of the second, to go after a decimal |
1880 | point, without trailing zeroes (0 to 999). Thus "\c{s.z}" |
1881 | reports the seconds to full available (millisecond) precision |
1882 | without trailing zeroes. |
1883 | \row \li zzz \li The fractional part of the second, to millisecond |
1884 | precision, including trailing zeroes where applicable (000 to 999). |
1885 | \row \li AP or A |
1886 | \li Use AM/PM display. \e A/AP will be replaced by 'AM' or 'PM' |
1887 | \row \li ap or a |
1888 | \li Use am/pm display. \e a/ap will be replaced by 'am' or 'pm' |
1889 | \row \li t \li The timezone (for example "CEST") |
1890 | \endtable |
1891 | |
1892 | Any non-empty sequence of characters enclosed in single quotes will be |
1893 | included verbatim in the output string (stripped of the quotes), even if it |
1894 | contains formatting characters. Two consecutive single quotes ("''") are |
1895 | replaced by a single quote in the output. All other characters in the format |
1896 | string are included verbatim in the output string. |
1897 | |
1898 | Formats without separators (e.g. "ddMM") are supported but must be used with |
1899 | care, as the resulting strings aren't always reliably readable (e.g. if "dM" |
1900 | produces "212" it could mean either the 2nd of December or the 21st of |
1901 | February). |
1902 | |
1903 | Example format strings (assuming that the QTime is 14:13:09.042 and the system |
1904 | locale is \c{en_US}) |
1905 | |
1906 | \table |
1907 | \header \li Format \li Result |
1908 | \row \li hh:mm:ss.zzz \li 14:13:09.042 |
1909 | \row \li h:m:s ap \li 2:13:9 pm |
1910 | \row \li H:m:s a \li 14:13:9 pm |
1911 | \endtable |
1912 | |
1913 | If the time is invalid, an empty string will be returned. |
1914 | |
1915 | \note If localized forms of am or pm (the AP, ap, A or a formats) are |
1916 | desired, please use QLocale::system().toString(). |
1917 | |
1918 | \sa fromString(), QDate::toString(), QDateTime::toString(), QLocale::toString() |
1919 | */ |
1920 | QString QTime::toString(QStringView format) const |
1921 | { |
1922 | return QLocale::c().toString(*this, format); |
1923 | } |
1924 | #endif // datestring |
1925 | |
1926 | /*! |
1927 | Sets the time to hour \a h, minute \a m, seconds \a s and |
1928 | milliseconds \a ms. |
1929 | |
1930 | \a h must be in the range 0 to 23, \a m and \a s must be in the |
1931 | range 0 to 59, and \a ms must be in the range 0 to 999. |
1932 | Returns \c true if the set time is valid; otherwise returns \c false. |
1933 | |
1934 | \sa isValid() |
1935 | */ |
1936 | |
1937 | bool QTime::setHMS(int h, int m, int s, int ms) |
1938 | { |
1939 | if (!isValid(h,m,s,ms)) { |
1940 | mds = NullTime; // make this invalid |
1941 | return false; |
1942 | } |
1943 | mds = (h*SECS_PER_HOUR + m*SECS_PER_MIN + s)*1000 + ms; |
1944 | return true; |
1945 | } |
1946 | |
1947 | /*! |
1948 | Returns a QTime object containing a time \a s seconds later |
1949 | than the time of this object (or earlier if \a s is negative). |
1950 | |
1951 | Note that the time will wrap if it passes midnight. |
1952 | |
1953 | Returns a null time if this time is invalid. |
1954 | |
1955 | Example: |
1956 | |
1957 | \snippet code/src_corelib_time_qdatetime.cpp 5 |
1958 | |
1959 | \sa addMSecs(), secsTo(), QDateTime::addSecs() |
1960 | */ |
1961 | |
1962 | QTime QTime::addSecs(int s) const |
1963 | { |
1964 | s %= SECS_PER_DAY; |
1965 | return addMSecs(s * 1000); |
1966 | } |
1967 | |
1968 | /*! |
1969 | Returns the number of seconds from this time to \a t. |
1970 | If \a t is earlier than this time, the number of seconds returned |
1971 | is negative. |
1972 | |
1973 | Because QTime measures time within a day and there are 86400 |
1974 | seconds in a day, the result is always between -86400 and 86400. |
1975 | |
1976 | secsTo() does not take into account any milliseconds. |
1977 | |
1978 | Returns 0 if either time is invalid. |
1979 | |
1980 | \sa addSecs(), QDateTime::secsTo() |
1981 | */ |
1982 | |
1983 | int QTime::secsTo(QTime t) const |
1984 | { |
1985 | if (!isValid() || !t.isValid()) |
1986 | return 0; |
1987 | |
1988 | // Truncate milliseconds as we do not want to consider them. |
1989 | int ourSeconds = ds() / 1000; |
1990 | int theirSeconds = t.ds() / 1000; |
1991 | return theirSeconds - ourSeconds; |
1992 | } |
1993 | |
1994 | /*! |
1995 | Returns a QTime object containing a time \a ms milliseconds later |
1996 | than the time of this object (or earlier if \a ms is negative). |
1997 | |
1998 | Note that the time will wrap if it passes midnight. See addSecs() |
1999 | for an example. |
2000 | |
2001 | Returns a null time if this time is invalid. |
2002 | |
2003 | \sa addSecs(), msecsTo(), QDateTime::addMSecs() |
2004 | */ |
2005 | |
2006 | QTime QTime::addMSecs(int ms) const |
2007 | { |
2008 | QTime t; |
2009 | if (isValid()) { |
2010 | if (ms < 0) { |
2011 | // %,/ not well-defined for -ve, so always work with +ve. |
2012 | int negdays = (MSECS_PER_DAY - ms) / MSECS_PER_DAY; |
2013 | t.mds = (ds() + ms + negdays * MSECS_PER_DAY) % MSECS_PER_DAY; |
2014 | } else { |
2015 | t.mds = (ds() + ms) % MSECS_PER_DAY; |
2016 | } |
2017 | } |
2018 | return t; |
2019 | } |
2020 | |
2021 | /*! |
2022 | Returns the number of milliseconds from this time to \a t. |
2023 | If \a t is earlier than this time, the number of milliseconds returned |
2024 | is negative. |
2025 | |
2026 | Because QTime measures time within a day and there are 86400 |
2027 | seconds in a day, the result is always between -86400000 and |
2028 | 86400000 ms. |
2029 | |
2030 | Returns 0 if either time is invalid. |
2031 | |
2032 | \sa secsTo(), addMSecs(), QDateTime::msecsTo() |
2033 | */ |
2034 | |
2035 | int QTime::msecsTo(QTime t) const |
2036 | { |
2037 | if (!isValid() || !t.isValid()) |
2038 | return 0; |
2039 | return t.ds() - ds(); |
2040 | } |
2041 | |
2042 | |
2043 | /*! |
2044 | \fn bool QTime::operator==(QTime t) const |
2045 | |
2046 | Returns \c true if this time is equal to \a t; otherwise returns \c false. |
2047 | */ |
2048 | |
2049 | /*! |
2050 | \fn bool QTime::operator!=(QTime t) const |
2051 | |
2052 | Returns \c true if this time is different from \a t; otherwise returns \c false. |
2053 | */ |
2054 | |
2055 | /*! |
2056 | \fn bool QTime::operator<(QTime t) const |
2057 | |
2058 | Returns \c true if this time is earlier than \a t; otherwise returns \c false. |
2059 | */ |
2060 | |
2061 | /*! |
2062 | \fn bool QTime::operator<=(QTime t) const |
2063 | |
2064 | Returns \c true if this time is earlier than or equal to \a t; |
2065 | otherwise returns \c false. |
2066 | */ |
2067 | |
2068 | /*! |
2069 | \fn bool QTime::operator>(QTime t) const |
2070 | |
2071 | Returns \c true if this time is later than \a t; otherwise returns \c false. |
2072 | */ |
2073 | |
2074 | /*! |
2075 | \fn bool QTime::operator>=(QTime t) const |
2076 | |
2077 | Returns \c true if this time is later than or equal to \a t; |
2078 | otherwise returns \c false. |
2079 | */ |
2080 | |
2081 | /*! |
2082 | \fn QTime QTime::fromMSecsSinceStartOfDay(int msecs) |
2083 | |
2084 | Returns a new QTime instance with the time set to the number of \a msecs |
2085 | since the start of the day, i.e. since 00:00:00. |
2086 | |
2087 | If \a msecs falls outside the valid range an invalid QTime will be returned. |
2088 | |
2089 | \sa msecsSinceStartOfDay() |
2090 | */ |
2091 | |
2092 | /*! |
2093 | \fn int QTime::msecsSinceStartOfDay() const |
2094 | |
2095 | Returns the number of msecs since the start of the day, i.e. since 00:00:00. |
2096 | |
2097 | \sa fromMSecsSinceStartOfDay() |
2098 | */ |
2099 | |
2100 | /*! |
2101 | \fn QTime::currentTime() |
2102 | |
2103 | Returns the current time as reported by the system clock. |
2104 | |
2105 | Note that the accuracy depends on the accuracy of the underlying |
2106 | operating system; not all systems provide 1-millisecond accuracy. |
2107 | |
2108 | Furthermore, currentTime() only increases within each day; it shall drop by |
2109 | 24 hours each time midnight passes; and, beside this, changes in it may not |
2110 | correspond to elapsed time, if a daylight-saving transition intervenes. |
2111 | |
2112 | \sa QDateTime::currentDateTime(), QDateTime::currentDateTimeUtc() |
2113 | */ |
2114 | |
2115 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
2116 | |
2117 | static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *isMidnight24) |
2118 | { |
2119 | Q_ASSERT(format == Qt::TextDate || format == Qt::ISODate || format == Qt::ISODateWithMs); |
2120 | if (isMidnight24) |
2121 | *isMidnight24 = false; |
2122 | // Match /\d\d(:\d\d(:\d\d)?)?([,.]\d+)?/ as "HH[:mm[:ss]][.zzz]" |
2123 | // The fractional part, if present, is in the same units as the field it follows. |
2124 | // TextDate restricts fractional parts to the seconds field. |
2125 | |
2126 | QStringView tail; |
2127 | const int dot = string.indexOf(u'.'), comma = string.indexOf(u','); |
2128 | if (dot != -1) { |
2129 | tail = string.sliced(dot + 1); |
2130 | if (tail.indexOf(u'.') != -1) // Forbid second dot: |
2131 | return QTime(); |
2132 | string = string.first(dot); |
2133 | } else if (comma != -1) { |
2134 | tail = string.sliced(comma + 1); |
2135 | string = string.first(comma); |
2136 | } |
2137 | if (tail.indexOf(u',') != -1) // Forbid comma after first dot-or-comma: |
2138 | return QTime(); |
2139 | |
2140 | const ParsedInt frac = readInt(tail); |
2141 | // There must be *some* digits in a fractional part; and it must be all digits: |
2142 | if (tail.isEmpty() ? dot != -1 || comma != -1 : !frac.ok) |
2143 | return QTime(); |
2144 | Q_ASSERT(frac.ok ^ tail.isEmpty()); |
2145 | double fraction = frac.ok ? frac.value * std::pow(0.1, tail.size()) : 0.0; |
2146 | |
2147 | const int size = string.size(); |
2148 | if (size < 2 || size > 8) |
2149 | return QTime(); |
2150 | |
2151 | ParsedInt hour = readInt(string.first(2)); |
2152 | if (!hour.ok || hour.value > (format == Qt::TextDate ? 23 : 24)) |
2153 | return QTime(); |
2154 | |
2155 | ParsedInt minute; |
2156 | if (string.size() > 2) { |
2157 | if (string[2] == u':' && string.size() > 4) |
2158 | minute = readInt(string.sliced(3, 2)); |
2159 | if (!minute.ok || minute.value >= 60) |
2160 | return QTime(); |
2161 | } else if (format == Qt::TextDate) { // Requires minutes |
2162 | return QTime(); |
2163 | } else if (frac.ok) { |
2164 | Q_ASSERT(!(fraction < 0.0) && fraction < 1.0); |
2165 | fraction *= 60; |
2166 | minute.value = qulonglong(fraction); |
2167 | fraction -= minute.value; |
2168 | } |
2169 | |
2170 | ParsedInt second; |
2171 | if (string.size() > 5) { |
2172 | if (string[5] == u':' && string.size() == 8) |
2173 | second = readInt(string.sliced(6, 2)); |
2174 | if (!second.ok || second.value >= 60) |
2175 | return QTime(); |
2176 | } else if (frac.ok) { |
2177 | if (format == Qt::TextDate) // Doesn't allow fraction of minutes |
2178 | return QTime(); |
2179 | Q_ASSERT(!(fraction < 0.0) && fraction < 1.0); |
2180 | fraction *= 60; |
2181 | second.value = qulonglong(fraction); |
2182 | fraction -= second.value; |
2183 | } |
2184 | |
2185 | Q_ASSERT(!(fraction < 0.0) && fraction < 1.0); |
2186 | // Round millis to nearest (unlike minutes and seconds, rounded down): |
2187 | int msec = frac.ok ? qRound(1000 * fraction) : 0; |
2188 | // But handle overflow gracefully: |
2189 | if (msec == 1000) { |
2190 | // If we can (when data were otherwise valid) validly propagate overflow |
2191 | // into other fields, do so: |
2192 | if (isMidnight24 || hour.value < 23 || minute.value < 59 || second.value < 59) { |
2193 | msec = 0; |
2194 | if (++second.value == 60) { |
2195 | second.value = 0; |
2196 | if (++minute.value == 60) { |
2197 | minute.value = 0; |
2198 | ++hour.value; |
2199 | // May need to propagate further via isMidnight24, see below |
2200 | } |
2201 | } |
2202 | } else { |
2203 | // QTime::fromString() or Qt::TextDate: rounding up would cause |
2204 | // 23:59:59.999... to become invalid; clip to 999 ms instead: |
2205 | msec = 999; |
2206 | } |
2207 | } |
2208 | |
2209 | // For ISO date format, 24:0:0 means 0:0:0 on the next day: |
2210 | if (hour.value == 24 && minute.value == 0 && second.value == 0 && msec == 0) { |
2211 | Q_ASSERT(format != Qt::TextDate); // It clipped hour at 23, above. |
2212 | if (isMidnight24) |
2213 | *isMidnight24 = true; |
2214 | hour.value = 0; |
2215 | } |
2216 | |
2217 | return QTime(hour.value, minute.value, second.value, msec); |
2218 | } |
2219 | |
2220 | /*! |
2221 | \fn QTime QTime::fromString(const QString &string, Qt::DateFormat format) |
2222 | |
2223 | Returns the time represented in the \a string as a QTime using the |
2224 | \a format given, or an invalid time if this is not possible. |
2225 | |
2226 | \sa toString(), QLocale::toTime() |
2227 | */ |
2228 | |
2229 | /*! |
2230 | \overload |
2231 | \since 6.0 |
2232 | */ |
2233 | QTime QTime::fromString(QStringView string, Qt::DateFormat format) |
2234 | { |
2235 | if (string.isEmpty()) |
2236 | return QTime(); |
2237 | |
2238 | switch (format) { |
2239 | case Qt::RFC2822Date: |
2240 | return rfcDateImpl(string).time; |
2241 | case Qt::ISODate: |
2242 | case Qt::ISODateWithMs: |
2243 | case Qt::TextDate: |
2244 | default: |
2245 | return fromIsoTimeString(string, format, nullptr); |
2246 | } |
2247 | } |
2248 | |
2249 | /*! |
2250 | \fn QTime QTime::fromString(const QString &string, const QString &format) |
2251 | |
2252 | Returns the QTime represented by the \a string, using the \a |
2253 | format given, or an invalid time if the string cannot be parsed. |
2254 | |
2255 | These expressions may be used for the format: |
2256 | |
2257 | \table |
2258 | \header \li Expression \li Output |
2259 | \row \li h |
2260 | \li The hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) |
2261 | \row \li hh |
2262 | \li The hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) |
2263 | \row \li H |
2264 | \li The hour without a leading zero (0 to 23, even with AM/PM display) |
2265 | \row \li HH |
2266 | \li The hour with a leading zero (00 to 23, even with AM/PM display) |
2267 | \row \li m \li The minute without a leading zero (0 to 59) |
2268 | \row \li mm \li The minute with a leading zero (00 to 59) |
2269 | \row \li s \li The whole second, without any leading zero (0 to 59) |
2270 | \row \li ss \li The whole second, with a leading zero where applicable (00 to 59) |
2271 | \row \li z \li The fractional part of the second, to go after a decimal |
2272 | point, without trailing zeroes (0 to 999). Thus "\c{s.z}" |
2273 | reports the seconds to full available (millisecond) precision |
2274 | without trailing zeroes. |
2275 | \row \li zzz \li The fractional part of the second, to millisecond |
2276 | precision, including trailing zeroes where applicable (000 to 999). |
2277 | \row \li AP or A |
2278 | \li Interpret as an AM/PM time. \e A/AP will match 'AM' or 'PM'. |
2279 | \row \li ap or a |
2280 | \li Interpret as an am/pm time. \e a/ap will match 'am' or 'pm'. |
2281 | \endtable |
2282 | |
2283 | All other input characters will be treated as text. Any non-empty sequence |
2284 | of characters enclosed in single quotes will also be treated (stripped of |
2285 | the quotes) as text and not be interpreted as expressions. |
2286 | |
2287 | \snippet code/src_corelib_time_qdatetime.cpp 6 |
2288 | |
2289 | If the format is not satisfied, an invalid QTime is returned. |
2290 | Expressions that do not expect leading zeroes to be given (h, m, s |
2291 | and z) are greedy. This means that they will use two digits (or three, for z) even if |
2292 | this puts them outside the range of accepted values and leaves too |
2293 | few digits for other sections. For example, the following string |
2294 | could have meant 00:07:10, but the m will grab two digits, resulting |
2295 | in an invalid time: |
2296 | |
2297 | \snippet code/src_corelib_time_qdatetime.cpp 7 |
2298 | |
2299 | Any field that is not represented in the format will be set to zero. |
2300 | For example: |
2301 | |
2302 | \snippet code/src_corelib_time_qdatetime.cpp 8 |
2303 | |
2304 | \note If localized forms of am or pm (the AP, ap, A or a formats) are used, |
2305 | please use QLocale::system().toTime(). |
2306 | |
2307 | \sa toString(), QDateTime::fromString(), QDate::fromString(), |
2308 | QLocale::toTime() |
2309 | */ |
2310 | |
2311 | /*! |
2312 | \fn QTime QTime::fromString(QStringView string, QStringView format) |
2313 | \overload |
2314 | \since 6.0 |
2315 | */ |
2316 | |
2317 | /*! |
2318 | \overload |
2319 | \since 6.0 |
2320 | */ |
2321 | QTime QTime::fromString(const QString &string, QStringView format) |
2322 | { |
2323 | QTime time; |
2324 | #if QT_CONFIG(datetimeparser) |
2325 | QDateTimeParser dt(QMetaType::QTime, QDateTimeParser::FromString, QCalendar()); |
2326 | dt.setDefaultLocale(QLocale::c()); |
2327 | if (dt.parseFormat(format)) |
2328 | dt.fromString(string, nullptr, &time); |
2329 | #else |
2330 | Q_UNUSED(string); |
2331 | Q_UNUSED(format); |
2332 | #endif |
2333 | return time; |
2334 | } |
2335 | #endif // datestring |
2336 | |
2337 | |
2338 | /*! |
2339 | \overload |
2340 | |
2341 | Returns \c true if the specified time is valid; otherwise returns |
2342 | false. |
2343 | |
2344 | The time is valid if \a h is in the range 0 to 23, \a m and |
2345 | \a s are in the range 0 to 59, and \a ms is in the range 0 to 999. |
2346 | |
2347 | Example: |
2348 | |
2349 | \snippet code/src_corelib_time_qdatetime.cpp 9 |
2350 | */ |
2351 | |
2352 | bool QTime::isValid(int h, int m, int s, int ms) |
2353 | { |
2354 | return (uint)h < 24 && (uint)m < 60 && (uint)s < 60 && (uint)ms < 1000; |
2355 | } |
2356 | |
2357 | /***************************************************************************** |
2358 | QDateTime static helper functions |
2359 | *****************************************************************************/ |
2360 | |
2361 | // get the types from QDateTime (through QDateTimePrivate) |
2362 | typedef QDateTimePrivate::QDateTimeShortData ShortData; |
2363 | typedef QDateTimePrivate::QDateTimeData QDateTimeData; |
2364 | |
2365 | // Returns the platform variant of timezone, i.e. the standard time offset |
2366 | // The timezone external variable is documented as always holding the |
2367 | // Standard Time offset as seconds west of Greenwich, i.e. UTC+01:00 is -3600 |
2368 | // Note this may not be historicaly accurate. |
2369 | // Relies on tzset, mktime, or localtime having been called to populate timezone |
2370 | static int qt_timezone() |
2371 | { |
2372 | #if defined(_MSC_VER) |
2373 | long offset; |
2374 | _get_timezone(&offset); |
2375 | return offset; |
2376 | #elif defined(Q_OS_BSD4) && !defined(Q_OS_DARWIN) |
2377 | time_t clock = time(NULL); |
2378 | struct tm t; |
2379 | localtime_r(&clock, &t); |
2380 | // QTBUG-36080 Workaround for systems without the POSIX timezone |
2381 | // variable. This solution is not very efficient but fixing it is up to |
2382 | // the libc implementations. |
2383 | // |
2384 | // tm_gmtoff has some important differences compared to the timezone |
2385 | // variable: |
2386 | // - It returns the number of seconds east of UTC, and we want the |
2387 | // number of seconds west of UTC. |
2388 | // - It also takes DST into account, so we need to adjust it to always |
2389 | // get the Standard Time offset. |
2390 | return -t.tm_gmtoff + (t.tm_isdst ? (long)SECS_PER_HOUR : 0L); |
2391 | #elif defined(Q_OS_INTEGRITY) || defined(Q_OS_RTEMS) |
2392 | return 0; |
2393 | #else |
2394 | return timezone; |
2395 | #endif // Q_OS_WIN |
2396 | } |
2397 | |
2398 | // Returns the tzname, assume tzset has been called already |
2399 | static QString qt_tzname(QDateTimePrivate::DaylightStatus daylightStatus) |
2400 | { |
2401 | int isDst = (daylightStatus == QDateTimePrivate::DaylightTime) ? 1 : 0; |
2402 | #if defined(Q_CC_MSVC) |
2403 | size_t s = 0; |
2404 | char name[512]; |
2405 | if (_get_tzname(&s, name, 512, isDst)) |
2406 | return QString(); |
2407 | return QString::fromLocal8Bit(name); |
2408 | #else |
2409 | return QString::fromLocal8Bit(tzname[isDst]); |
2410 | #endif // Q_OS_WIN |
2411 | } |
2412 | |
2413 | #if QT_CONFIG(datetimeparser) |
2414 | /* |
2415 | \internal |
2416 | Implemented here to share qt_tzname() |
2417 | */ |
2418 | int QDateTimeParser::startsWithLocalTimeZone(QStringView name) |
2419 | { |
2420 | QDateTimePrivate::DaylightStatus zones[2] = { |
2421 | QDateTimePrivate::StandardTime, |
2422 | QDateTimePrivate::DaylightTime |
2423 | }; |
2424 | for (const auto z : zones) { |
2425 | QString zone(qt_tzname(z)); |
2426 | if (name.startsWith(zone)) |
2427 | return zone.size(); |
2428 | } |
2429 | return 0; |
2430 | } |
2431 | #endif // datetimeparser |
2432 | |
2433 | // Calls the platform variant of mktime for the given date, time and daylightStatus, |
2434 | // and updates the date, time, daylightStatus and abbreviation with the returned values |
2435 | // If the date falls outside the 1970 to 2037 range supported by mktime / time_t |
2436 | // then null date/time will be returned, you should adjust the date first if |
2437 | // you need a guaranteed result. |
2438 | static qint64 qt_mktime(QDate *date, QTime *time, QDateTimePrivate::DaylightStatus *daylightStatus, |
2439 | QString *abbreviation, bool *ok = nullptr) |
2440 | { |
2441 | const qint64 msec = time->msec(); |
2442 | int yy, mm, dd; |
2443 | date->getDate(&yy, &mm, &dd); |
2444 | |
2445 | // All other platforms provide standard C library time functions |
2446 | tm local; |
2447 | memset(&local, 0, sizeof(local)); // tm_[wy]day plus any non-standard fields |
2448 | local.tm_sec = time->second(); |
2449 | local.tm_min = time->minute(); |
2450 | local.tm_hour = time->hour(); |
2451 | local.tm_mday = dd; |
2452 | local.tm_mon = mm - 1; |
2453 | local.tm_year = yy - 1900; |
2454 | local.tm_isdst = daylightStatus ? int(*daylightStatus) : -1; |
2455 | |
2456 | #if defined(Q_OS_WIN) |
2457 | int hh = local.tm_hour; |
2458 | #endif // Q_OS_WIN |
2459 | time_t secsSinceEpoch = qMkTime(&local); |
2460 | if (secsSinceEpoch != time_t(-1)) { |
2461 | *date = QDate(local.tm_year + 1900, local.tm_mon + 1, local.tm_mday); |
2462 | *time = QTime(local.tm_hour, local.tm_min, local.tm_sec, msec); |
2463 | #if defined(Q_OS_WIN) |
2464 | // Windows mktime for the missing hour subtracts 1 hour from the time |
2465 | // instead of adding 1 hour. If time differs and is standard time then |
2466 | // this has happened, so add 2 hours to the time and 1 hour to the msecs |
2467 | if (local.tm_isdst == 0 && local.tm_hour != hh) { |
2468 | if (time->hour() >= 22) |
2469 | *date = date->addDays(1); |
2470 | *time = time->addSecs(2 * SECS_PER_HOUR); |
2471 | secsSinceEpoch += SECS_PER_HOUR; |
2472 | local.tm_isdst = 1; |
2473 | } |
2474 | #endif // Q_OS_WIN |
2475 | if (local.tm_isdst > 0) { |
2476 | if (daylightStatus) |
2477 | *daylightStatus = QDateTimePrivate::DaylightTime; |
2478 | if (abbreviation) |
2479 | *abbreviation = qt_tzname(QDateTimePrivate::DaylightTime); |
2480 | } else { |
2481 | if (daylightStatus) { |
2482 | *daylightStatus = (local.tm_isdst == 0 |
2483 | ? QDateTimePrivate::StandardTime |
2484 | : QDateTimePrivate::UnknownDaylightTime); |
2485 | } |
2486 | if (abbreviation) |
2487 | *abbreviation = qt_tzname(QDateTimePrivate::StandardTime); |
2488 | } |
2489 | } else if (yy == 1969 && mm == 12 && dd == 31 |
2490 | && time->second() == MSECS_PER_DAY - 1) { |
2491 | // There was, of course, a last second in 1969, at time_t(-1); we won't |
2492 | // rescue it if it's not in normalised form, and we don't know its DST |
2493 | // status (unless we did already), but let's not wantonly declare it |
2494 | // invalid. |
2495 | } else { |
2496 | *date = QDate(); |
2497 | *time = QTime(); |
2498 | if (daylightStatus) |
2499 | *daylightStatus = QDateTimePrivate::UnknownDaylightTime; |
2500 | if (abbreviation) |
2501 | *abbreviation = QString(); |
2502 | if (ok) |
2503 | *ok = false; |
2504 | return 0; |
2505 | } |
2506 | if (ok) |
2507 | *ok = true; |
2508 | |
2509 | return qint64(secsSinceEpoch) * 1000 + msec; |
2510 | } |
2511 | |
2512 | // Calls the platform variant of localtime for the given msecs, and updates |
2513 | // the date, time, and DST status with the returned values. |
2514 | static bool qt_localtime(qint64 msecsSinceEpoch, QDate *localDate, QTime *localTime, |
2515 | QDateTimePrivate::DaylightStatus *daylightStatus) |
2516 | { |
2517 | const time_t secsSinceEpoch = msecsSinceEpoch / 1000; |
2518 | const int msec = msecsSinceEpoch % 1000; |
2519 | |
2520 | tm local; |
2521 | bool valid = false; |
2522 | |
2523 | // localtime() is specified to work as if it called tzset(). |
2524 | // localtime_r() does not have this constraint, so make an explicit call. |
2525 | // The explicit call should also request the timezone info be re-parsed. |
2526 | qTzSet(); |
2527 | #if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) |
2528 | // Use the reentrant version of localtime() where available |
2529 | // as is thread-safe and doesn't use a shared static data area |
2530 | if (tm *res = localtime_r(&secsSinceEpoch, &local)) { |
2531 | Q_ASSERT(res == &local); |
2532 | valid = true; |
2533 | } |
2534 | #elif defined(Q_CC_MSVC) |
2535 | if (!_localtime64_s(&local, &secsSinceEpoch)) |
2536 | valid = true; |
2537 | #else |
2538 | // Returns shared static data which may be overwritten at any time |
2539 | // So copy the result asap |
2540 | if (tm *res = localtime(&secsSinceEpoch)) { |
2541 | local = *res; |
2542 | valid = true; |
2543 | } |
2544 | #endif |
2545 | if (valid) { |
2546 | *localDate = QDate(local.tm_year + 1900, local.tm_mon + 1, local.tm_mday); |
2547 | *localTime = QTime(local.tm_hour, local.tm_min, local.tm_sec, msec); |
2548 | if (daylightStatus) { |
2549 | if (local.tm_isdst > 0) |
2550 | *daylightStatus = QDateTimePrivate::DaylightTime; |
2551 | else if (local.tm_isdst < 0) |
2552 | *daylightStatus = QDateTimePrivate::UnknownDaylightTime; |
2553 | else |
2554 | *daylightStatus = QDateTimePrivate::StandardTime; |
2555 | } |
2556 | return true; |
2557 | } else { |
2558 | *localDate = QDate(); |
2559 | *localTime = QTime(); |
2560 | if (daylightStatus) |
2561 | *daylightStatus = QDateTimePrivate::UnknownDaylightTime; |
2562 | return false; |
2563 | } |
2564 | } |
2565 | |
2566 | // Converts an msecs value into a date and time |
2567 | static void msecsToTime(qint64 msecs, QDate *date, QTime *time) |
2568 | { |
2569 | qint64 jd = JULIAN_DAY_FOR_EPOCH; |
2570 | qint64 ds = 0; |
2571 | |
2572 | if (msecs >= MSECS_PER_DAY || msecs <= -MSECS_PER_DAY) { |
2573 | jd += msecs / MSECS_PER_DAY; |
2574 | msecs %= MSECS_PER_DAY; |
2575 | } |
2576 | |
2577 | if (msecs < 0) { |
2578 | ds = MSECS_PER_DAY - msecs - 1; |
2579 | jd -= ds / MSECS_PER_DAY; |
2580 | ds = ds % MSECS_PER_DAY; |
2581 | ds = MSECS_PER_DAY - ds - 1; |
2582 | } else { |
2583 | ds = msecs; |
2584 | } |
2585 | |
2586 | if (date) |
2587 | *date = QDate::fromJulianDay(jd); |
2588 | if (time) |
2589 | *time = QTime::fromMSecsSinceStartOfDay(ds); |
2590 | } |
2591 | |
2592 | // Converts a date/time value into msecs |
2593 | static qint64 timeToMSecs(QDate date, QTime time) |
2594 | { |
2595 | return ((date.toJulianDay() - JULIAN_DAY_FOR_EPOCH) * MSECS_PER_DAY) |
2596 | + time.msecsSinceStartOfDay(); |
2597 | } |
2598 | |
2599 | // Convert an MSecs Since Epoch into Local Time |
2600 | static bool epochMSecsToLocalTime(qint64 msecs, QDate *localDate, QTime *localTime, |
2601 | QDateTimePrivate::DaylightStatus *daylightStatus = nullptr) |
2602 | { |
2603 | if (msecs < 0) { |
2604 | // Docs state any LocalTime before 1970-01-01 will *not* have any Daylight Time applied |
2605 | // Instead just use the standard offset from UTC to convert to UTC time |
2606 | qTzSet(); |
2607 | msecsToTime(msecs - qt_timezone() * 1000, localDate, localTime); |
2608 | if (daylightStatus) |
2609 | *daylightStatus = QDateTimePrivate::StandardTime; |
2610 | return true; |
2611 | } else if (msecs > (qint64(TIME_T_MAX) * 1000)) { |
2612 | // Docs state any LocalTime after 2037-12-31 *will* have any DST applied |
2613 | // but this may fall outside the supported time_t range, so need to fake it. |
2614 | // Use existing method to fake the conversion, but this is deeply flawed as it may |
2615 | // apply the conversion from the wrong day number, e.g. if rule is last Sunday of month |
2616 | // TODO Use QTimeZone when available to apply the future rule correctly |
2617 | QDate utcDate; |
2618 | QTime utcTime; |
2619 | msecsToTime(msecs, &utcDate, &utcTime); |
2620 | int year, month, day; |
2621 | utcDate.getDate(&year, &month, &day); |
2622 | // 2037 is not a leap year, so make sure date isn't Feb 29 |
2623 | if (month == 2 && day == 29) |
2624 | --day; |
2625 | QDate fakeDate(2037, month, day); |
2626 | qint64 fakeMsecs = QDateTime(fakeDate, utcTime, Qt::UTC).toMSecsSinceEpoch(); |
2627 | bool res = qt_localtime(fakeMsecs, localDate, localTime, daylightStatus); |
2628 | *localDate = localDate->addDays(fakeDate.daysTo(utcDate)); |
2629 | return res; |
2630 | } else { |
2631 | // Falls inside time_t suported range so can use localtime |
2632 | return qt_localtime(msecs, localDate, localTime, daylightStatus); |
2633 | } |
2634 | } |
2635 | |
2636 | // Convert a LocalTime expressed in local msecs encoding and the corresponding |
2637 | // DST status into a UTC epoch msecs. Optionally populate the returned |
2638 | // values from mktime for the adjusted local date and time. |
2639 | static qint64 localMSecsToEpochMSecs(qint64 localMsecs, |
2640 | QDateTimePrivate::DaylightStatus *daylightStatus, |
2641 | QDate *localDate = nullptr, QTime *localTime = nullptr, |
2642 | QString *abbreviation = nullptr) |
2643 | { |
2644 | QDate dt; |
2645 | QTime tm; |
2646 | msecsToTime(localMsecs, &dt, &tm); |
2647 | |
2648 | const qint64 msecsMax = qint64(TIME_T_MAX) * 1000; |
2649 | |
2650 | if (localMsecs <= qint64(MSECS_PER_DAY)) { |
2651 | |
2652 | // Docs state any LocalTime before 1970-01-01 will *not* have any DST applied |
2653 | |
2654 | // First, if localMsecs is within +/- 1 day of minimum time_t try mktime in case it does |
2655 | // fall after minimum and needs proper DST conversion |
2656 | if (localMsecs >= -qint64(MSECS_PER_DAY)) { |
2657 | bool valid; |
2658 | qint64 utcMsecs = qt_mktime(&dt, &tm, daylightStatus, abbreviation, &valid); |
2659 | if (valid && utcMsecs >= 0) { |
2660 | // mktime worked and falls in valid range, so use it |
2661 | if (localDate) |
2662 | *localDate = dt; |
2663 | if (localTime) |
2664 | *localTime = tm; |
2665 | return utcMsecs; |
2666 | } |
2667 | } else { |
2668 | // If we don't call mktime then need to call tzset to get offset |
2669 | qTzSet(); |
2670 | } |
2671 | // Time is clearly before 1970-01-01 so just use standard offset to convert |
2672 | qint64 utcMsecs = localMsecs + qt_timezone() * 1000; |
2673 | if (localDate || localTime) |
2674 | msecsToTime(localMsecs, localDate, localTime); |
2675 | if (daylightStatus) |
2676 | *daylightStatus = QDateTimePrivate::StandardTime; |
2677 | if (abbreviation) |
2678 | *abbreviation = qt_tzname(QDateTimePrivate::StandardTime); |
2679 | return utcMsecs; |
2680 | |
2681 | } else if (localMsecs >= msecsMax - MSECS_PER_DAY) { |
2682 | |
2683 | // Docs state any LocalTime after 2037-12-31 *will* have any DST applied |
2684 | // but this may fall outside the supported time_t range, so need to fake it. |
2685 | |
2686 | // First, if localMsecs is within +/- 1 day of maximum time_t try mktime in case it does |
2687 | // fall before maximum and can use proper DST conversion |
2688 | if (localMsecs <= msecsMax + MSECS_PER_DAY) { |
2689 | bool valid; |
2690 | qint64 utcMsecs = qt_mktime(&dt, &tm, daylightStatus, abbreviation, &valid); |
2691 | if (valid && utcMsecs <= msecsMax) { |
2692 | // mktime worked and falls in valid range, so use it |
2693 | if (localDate) |
2694 | *localDate = dt; |
2695 | if (localTime) |
2696 | *localTime = tm; |
2697 | return utcMsecs; |
2698 | } |
2699 | } |
2700 | // Use existing method to fake the conversion, but this is deeply flawed as it may |
2701 | // apply the conversion from the wrong day number, e.g. if rule is last Sunday of month |
2702 | // TODO Use QTimeZone when available to apply the future rule correctly |
2703 | int year, month, day; |
2704 | dt.getDate(&year, &month, &day); |
2705 | // 2037 is not a leap year, so make sure date isn't Feb 29 |
2706 | if (month == 2 && day == 29) |
2707 | --day; |
2708 | QDate fakeDate(2037, month, day); |
2709 | qint64 fakeDiff = fakeDate.daysTo(dt); |
2710 | qint64 utcMsecs = qt_mktime(&fakeDate, &tm, daylightStatus, abbreviation); |
2711 | if (localDate) |
2712 | *localDate = fakeDate.addDays(fakeDiff); |
2713 | if (localTime) |
2714 | *localTime = tm; |
2715 | QDate utcDate; |
2716 | QTime utcTime; |
2717 | msecsToTime(utcMsecs, &utcDate, &utcTime); |
2718 | utcDate = utcDate.addDays(fakeDiff); |
2719 | utcMsecs = timeToMSecs(utcDate, utcTime); |
2720 | return utcMsecs; |
2721 | |
2722 | } else { |
2723 | |
2724 | // Clearly falls inside 1970-2037 suported range so can use mktime |
2725 | qint64 utcMsecs = qt_mktime(&dt, &tm, daylightStatus, abbreviation); |
2726 | if (localDate) |
2727 | *localDate = dt; |
2728 | if (localTime) |
2729 | *localTime = tm; |
2730 | return utcMsecs; |
2731 | |
2732 | } |
2733 | } |
2734 | |
2735 | static inline bool specCanBeSmall(Qt::TimeSpec spec) |
2736 | { |
2737 | return spec == Qt::LocalTime || spec == Qt::UTC; |
2738 | } |
2739 | |
2740 | static inline bool msecsCanBeSmall(qint64 msecs) |
2741 | { |
2742 | if (!QDateTimeData::CanBeSmall) |
2743 | return false; |
2744 | |
2745 | ShortData sd; |
2746 | sd.msecs = qintptr(msecs); |
2747 | return sd.msecs == msecs; |
2748 | } |
2749 | |
2750 | static constexpr inline |
2751 | QDateTimePrivate::StatusFlags mergeSpec(QDateTimePrivate::StatusFlags status, Qt::TimeSpec spec) |
2752 | { |
2753 | return QDateTimePrivate::StatusFlags((status & ~QDateTimePrivate::TimeSpecMask) | |
2754 | (int(spec) << QDateTimePrivate::TimeSpecShift)); |
2755 | } |
2756 | |
2757 | static constexpr inline Qt::TimeSpec (QDateTimePrivate::StatusFlags status) |
2758 | { |
2759 | return Qt::TimeSpec((status & QDateTimePrivate::TimeSpecMask) >> QDateTimePrivate::TimeSpecShift); |
2760 | } |
2761 | |
2762 | // Set the Daylight Status if LocalTime set via msecs |
2763 | static constexpr inline QDateTimePrivate::StatusFlags |
2764 | mergeDaylightStatus(QDateTimePrivate::StatusFlags sf, QDateTimePrivate::DaylightStatus status) |
2765 | { |
2766 | sf &= ~QDateTimePrivate::DaylightMask; |
2767 | if (status == QDateTimePrivate::DaylightTime) { |
2768 | sf |= QDateTimePrivate::SetToDaylightTime; |
2769 | } else if (status == QDateTimePrivate::StandardTime) { |
2770 | sf |= QDateTimePrivate::SetToStandardTime; |
2771 | } |
2772 | return sf; |
2773 | } |
2774 | |
2775 | // Get the DST Status if LocalTime set via msecs |
2776 | static constexpr inline |
2777 | QDateTimePrivate::DaylightStatus (QDateTimePrivate::StatusFlags status) |
2778 | { |
2779 | if (status & QDateTimePrivate::SetToDaylightTime) |
2780 | return QDateTimePrivate::DaylightTime; |
2781 | if (status & QDateTimePrivate::SetToStandardTime) |
2782 | return QDateTimePrivate::StandardTime; |
2783 | return QDateTimePrivate::UnknownDaylightTime; |
2784 | } |
2785 | |
2786 | static inline qint64 getMSecs(const QDateTimeData &d) |
2787 | { |
2788 | if (d.isShort()) { |
2789 | // same as, but producing better code |
2790 | //return d.data.msecs; |
2791 | return qintptr(d.d) >> 8; |
2792 | } |
2793 | return d->m_msecs; |
2794 | } |
2795 | |
2796 | static inline QDateTimePrivate::StatusFlags getStatus(const QDateTimeData &d) |
2797 | { |
2798 | if (d.isShort()) { |
2799 | // same as, but producing better code |
2800 | //return StatusFlag(d.data.status); |
2801 | return QDateTimePrivate::StatusFlag(qintptr(d.d) & 0xFF); |
2802 | } |
2803 | return d->m_status; |
2804 | } |
2805 | |
2806 | static inline Qt::TimeSpec getSpec(const QDateTimeData &d) |
2807 | { |
2808 | return extractSpec(getStatus(d)); |
2809 | } |
2810 | |
2811 | /* True if we *can cheaply determine* that a and b use the same offset. |
2812 | If they use different offsets or it would be expensive to find out, false. |
2813 | Calls to toMSecsSinceEpoch() are expensive, for these purposes. |
2814 | See QDateTime's comparison operators. |
2815 | */ |
2816 | static inline bool usesSameOffset(const QDateTimeData &a, const QDateTimeData &b) |
2817 | { |
2818 | const auto status = getStatus(a); |
2819 | if (status != getStatus(b)) |
2820 | return false; |
2821 | // Status includes DST-ness, so we now know they match in it. |
2822 | |
2823 | switch (extractSpec(status)) { |
2824 | case Qt::LocalTime: |
2825 | case Qt::UTC: |
2826 | return true; |
2827 | |
2828 | case Qt::TimeZone: |
2829 | /* TimeZone always determines its offset during construction of the |
2830 | private data. Even if we're in different zones, what matters is the |
2831 | offset actually in effect at the specific time. (DST can cause things |
2832 | with the same time-zone to use different offsets, but we already |
2833 | checked their DSTs match.) */ |
2834 | case Qt::OffsetFromUTC: // always knows its offset, which is all that matters. |
2835 | Q_ASSERT(!a.isShort() && !b.isShort()); |
2836 | return a->m_offsetFromUtc == b->m_offsetFromUtc; |
2837 | } |
2838 | Q_UNREACHABLE(); |
2839 | return false; |
2840 | } |
2841 | |
2842 | // Refresh the LocalTime or TimeZone validity and offset |
2843 | static void refreshZonedDateTime(QDateTimeData &d, Qt::TimeSpec spec) |
2844 | { |
2845 | Q_ASSERT(spec == Qt::TimeZone || spec == Qt::LocalTime); |
2846 | auto status = getStatus(d); |
2847 | Q_ASSERT(extractSpec(status) == spec); |
2848 | int offsetFromUtc = 0; |
2849 | |
2850 | // If not valid date and time then is invalid |
2851 | if (!(status & QDateTimePrivate::ValidDate) || !(status & QDateTimePrivate::ValidTime)) { |
2852 | status &= ~QDateTimePrivate::ValidDateTime; |
2853 | } else { |
2854 | // We have a valid date and time and a Qt::LocalTime or Qt::TimeZone that needs calculating |
2855 | // LocalTime and TimeZone might fall into a "missing" DST transition hour |
2856 | // Calling toEpochMSecs will adjust the returned date/time if it does |
2857 | const qint64 msecs = getMSecs(d); |
2858 | qint64 epochMSecs = 0; |
2859 | QDate testDate; |
2860 | QTime testTime; |
2861 | if (spec == Qt::LocalTime) { |
2862 | auto dstStatus = extractDaylightStatus(status); |
2863 | epochMSecs = localMSecsToEpochMSecs(msecs, &dstStatus, &testDate, &testTime); |
2864 | status = mergeDaylightStatus(status, dstStatus); |
2865 | #if QT_CONFIG(timezone) |
2866 | // else spec == Qt::TimeZone, so check zone is valid: |
2867 | } else if (d->m_timeZone.isValid()) { |
2868 | epochMSecs = QDateTimePrivate::zoneMSecsToEpochMSecs( |
2869 | msecs, d->m_timeZone, extractDaylightStatus(status), &testDate, &testTime); |
2870 | #endif // timezone |
2871 | } // else: testDate, testTime haven't been set, so are invalid. |
2872 | // Cache the offset to use in offsetFromUtc() &c. |
2873 | offsetFromUtc = (msecs - epochMSecs) / 1000; |
2874 | if (testDate.isValid() && testTime.isValid() |
2875 | && timeToMSecs(testDate, testTime) == msecs) { |
2876 | status |= QDateTimePrivate::ValidDateTime; |
2877 | } else { |
2878 | status &= ~QDateTimePrivate::ValidDateTime; |
2879 | } |
2880 | } |
2881 | |
2882 | if (status & QDateTimePrivate::ShortData) { |
2883 | d.data.status = status; |
2884 | } else { |
2885 | d->m_status = status; |
2886 | d->m_offsetFromUtc = offsetFromUtc; |
2887 | } |
2888 | } |
2889 | |
2890 | // Check the UTC / offsetFromUTC validity |
2891 | static void refreshSimpleDateTime(QDateTimeData &d) |
2892 | { |
2893 | auto status = getStatus(d); |
2894 | Q_ASSERT(extractSpec(status) == Qt::UTC || extractSpec(status) == Qt::OffsetFromUTC); |
2895 | if ((status & QDateTimePrivate::ValidDate) && (status & QDateTimePrivate::ValidTime)) |
2896 | status |= QDateTimePrivate::ValidDateTime; |
2897 | else |
2898 | status &= ~QDateTimePrivate::ValidDateTime; |
2899 | |
2900 | if (status & QDateTimePrivate::ShortData) |
2901 | d.data.status = status; |
2902 | else |
2903 | d->m_status = status; |
2904 | } |
2905 | |
2906 | // Clean up and set status after assorted set-up or reworking: |
2907 | static void checkValidDateTime(QDateTimeData &d) |
2908 | { |
2909 | auto status = getStatus(d); |
2910 | auto spec = extractSpec(status); |
2911 | switch (spec) { |
2912 | case Qt::OffsetFromUTC: |
2913 | case Qt::UTC: |
2914 | // for these, a valid date and a valid time imply a valid QDateTime |
2915 | refreshSimpleDateTime(d); |
2916 | break; |
2917 | case Qt::TimeZone: |
2918 | case Qt::LocalTime: |
2919 | // for these, we need to check whether the timezone is valid and whether |
2920 | // the time is valid in that timezone. Expensive, but no other option. |
2921 | refreshZonedDateTime(d, spec); |
2922 | break; |
2923 | } |
2924 | } |
2925 | |
2926 | // Caller needs to refresh after calling this |
2927 | static void setTimeSpec(QDateTimeData &d, Qt::TimeSpec spec, int offsetSeconds) |
2928 | { |
2929 | auto status = getStatus(d); |
2930 | status &= ~(QDateTimePrivate::ValidDateTime | QDateTimePrivate::DaylightMask | |
2931 | QDateTimePrivate::TimeSpecMask); |
2932 | |
2933 | switch (spec) { |
2934 | case Qt::OffsetFromUTC: |
2935 | if (offsetSeconds == 0) |
2936 | spec = Qt::UTC; |
2937 | break; |
2938 | case Qt::TimeZone: |
2939 | qWarning("Using TimeZone in setTimeSpec() is unsupported" ); // Use system time zone instead |
2940 | spec = Qt::LocalTime; |
2941 | Q_FALLTHROUGH(); |
2942 | case Qt::UTC: |
2943 | case Qt::LocalTime: |
2944 | offsetSeconds = 0; |
2945 | break; |
2946 | } |
2947 | |
2948 | status = mergeSpec(status, spec); |
2949 | if (d.isShort() && offsetSeconds == 0) { |
2950 | d.data.status = status; |
2951 | } else { |
2952 | d.detach(); |
2953 | d->m_status = status & ~QDateTimePrivate::ShortData; |
2954 | d->m_offsetFromUtc = offsetSeconds; |
2955 | #if QT_CONFIG(timezone) |
2956 | d->m_timeZone = QTimeZone(); |
2957 | #endif // timezone |
2958 | } |
2959 | } |
2960 | |
2961 | static void setDateTime(QDateTimeData &d, QDate date, QTime time) |
2962 | { |
2963 | // If the date is valid and the time is not we set time to 00:00:00 |
2964 | QTime useTime = time; |
2965 | if (!useTime.isValid() && date.isValid()) |
2966 | useTime = QTime::fromMSecsSinceStartOfDay(0); |
2967 | |
2968 | QDateTimePrivate::StatusFlags newStatus = { }; |
2969 | |
2970 | // Set date value and status |
2971 | qint64 days = 0; |
2972 | if (date.isValid()) { |
2973 | days = date.toJulianDay() - JULIAN_DAY_FOR_EPOCH; |
2974 | newStatus = QDateTimePrivate::ValidDate; |
2975 | } |
2976 | |
2977 | // Set time value and status |
2978 | int ds = 0; |
2979 | if (useTime.isValid()) { |
2980 | ds = useTime.msecsSinceStartOfDay(); |
2981 | newStatus |= QDateTimePrivate::ValidTime; |
2982 | } |
2983 | Q_ASSERT(ds < MSECS_PER_DAY); |
2984 | // Only the later parts of the very first day are representable - its start |
2985 | // would overflow - so get ds the same side of 0 as days: |
2986 | if (days < 0 && ds > 0) { |
2987 | days++; |
2988 | ds -= MSECS_PER_DAY; |
2989 | } |
2990 | |
2991 | // Check in representable range: |
2992 | qint64 msecs = 0; |
2993 | if (mul_overflow(days, std::integral_constant<qint64, MSECS_PER_DAY>(), &msecs) |
2994 | || add_overflow(msecs, qint64(ds), &msecs)) { |
2995 | newStatus = QDateTimePrivate::StatusFlags{}; |
2996 | } else if (d.isShort()) { |
2997 | // let's see if we can keep this short |
2998 | if (msecsCanBeSmall(msecs)) { |
2999 | // yes, we can |
3000 | d.data.msecs = qintptr(msecs); |
3001 | d.data.status &= ~(QDateTimePrivate::ValidityMask | QDateTimePrivate::DaylightMask); |
3002 | d.data.status |= newStatus; |
3003 | } else { |
3004 | // nope... |
3005 | d.detach(); |
3006 | } |
3007 | } |
3008 | if (!d.isShort()) { |
3009 | d.detach(); |
3010 | d->m_msecs = msecs; |
3011 | d->m_status &= ~(QDateTimePrivate::ValidityMask | QDateTimePrivate::DaylightMask); |
3012 | d->m_status |= newStatus; |
3013 | } |
3014 | } |
3015 | |
3016 | static QPair<QDate, QTime> getDateTime(const QDateTimeData &d) |
3017 | { |
3018 | QPair<QDate, QTime> result; |
3019 | qint64 msecs = getMSecs(d); |
3020 | auto status = getStatus(d); |
3021 | msecsToTime(msecs, &result.first, &result.second); |
3022 | |
3023 | if (!status.testFlag(QDateTimePrivate::ValidDate)) |
3024 | result.first = QDate(); |
3025 | |
3026 | if (!status.testFlag(QDateTimePrivate::ValidTime)) |
3027 | result.second = QTime(); |
3028 | |
3029 | return result; |
3030 | } |
3031 | |
3032 | /***************************************************************************** |
3033 | QDateTime::Data member functions |
3034 | *****************************************************************************/ |
3035 | |
3036 | inline QDateTime::Data::Data() noexcept |
3037 | { |
3038 | // default-constructed data has a special exception: |
3039 | // it can be small even if CanBeSmall == false |
3040 | // (optimization so we don't allocate memory in the default constructor) |
3041 | quintptr value = quintptr(mergeSpec(QDateTimePrivate::ShortData, Qt::LocalTime)); |
3042 | d = reinterpret_cast<QDateTimePrivate *>(value); |
3043 | } |
3044 | |
3045 | inline QDateTime::Data::Data(Qt::TimeSpec spec) |
3046 | { |
3047 | if (CanBeSmall && Q_LIKELY(specCanBeSmall(spec))) { |
3048 | d = reinterpret_cast<QDateTimePrivate *>(quintptr(mergeSpec(QDateTimePrivate::ShortData, spec))); |
3049 | } else { |
3050 | // the structure is too small, we need to detach |
3051 | d = new QDateTimePrivate; |
3052 | d->ref.ref(); |
3053 | d->m_status = mergeSpec({}, spec); |
3054 | } |
3055 | } |
3056 | |
3057 | inline QDateTime::Data::Data(const Data &other) |
3058 | : d(other.d) |
3059 | { |
3060 | if (!isShort()) { |
3061 | // check if we could shrink |
3062 | if (specCanBeSmall(extractSpec(d->m_status)) && msecsCanBeSmall(d->m_msecs)) { |
3063 | ShortData sd; |
3064 | sd.msecs = qintptr(d->m_msecs); |
3065 | sd.status = d->m_status | QDateTimePrivate::ShortData; |
3066 | data = sd; |
3067 | } else { |
3068 | // no, have to keep it big |
3069 | d->ref.ref(); |
3070 | } |
3071 | } |
3072 | } |
3073 | |
3074 | inline QDateTime::Data::Data(Data &&other) |
3075 | : d(other.d) |
3076 | { |
3077 | // reset the other to a short state |
3078 | Data dummy; |
3079 | Q_ASSERT(dummy.isShort()); |
3080 | other.d = dummy.d; |
3081 | } |
3082 | |
3083 | inline QDateTime::Data &QDateTime::Data::operator=(const Data &other) |
3084 | { |
3085 | if (d == other.d) |
3086 | return *this; |
3087 | |
3088 | auto x = d; |
3089 | d = other.d; |
3090 | if (!other.isShort()) { |
3091 | // check if we could shrink |
3092 | if (specCanBeSmall(extractSpec(other.d->m_status)) && msecsCanBeSmall(other.d->m_msecs)) { |
3093 | ShortData sd; |
3094 | sd.msecs = qintptr(other.d->m_msecs); |
3095 | sd.status = other.d->m_status | QDateTimePrivate::ShortData; |
3096 | data = sd; |
3097 | } else { |
3098 | // no, have to keep it big |
3099 | other.d->ref.ref(); |
3100 | } |
3101 | } |
3102 | |
3103 | if (!(quintptr(x) & QDateTimePrivate::ShortData) && !x->ref.deref()) |
3104 | delete x; |
3105 | return *this; |
3106 | } |
3107 | |
3108 | inline QDateTime::Data::~Data() |
3109 | { |
3110 | if (!isShort() && !d->ref.deref()) |
3111 | delete d; |
3112 | } |
3113 | |
3114 | inline bool QDateTime::Data::isShort() const |
3115 | { |
3116 | bool b = quintptr(d) & QDateTimePrivate::ShortData; |
3117 | |
3118 | // sanity check: |
3119 | Q_ASSERT(b || (d->m_status & QDateTimePrivate::ShortData) == 0); |
3120 | |
3121 | // even if CanBeSmall = false, we have short data for a default-constructed |
3122 | // QDateTime object. But it's unlikely. |
3123 | if (CanBeSmall) |
3124 | return Q_LIKELY(b); |
3125 | return Q_UNLIKELY(b); |
3126 | } |
3127 | |
3128 | inline void QDateTime::Data::detach() |
3129 | { |
3130 | QDateTimePrivate *x; |
3131 | bool wasShort = isShort(); |
3132 | if (wasShort) { |
3133 | // force enlarging |
3134 | x = new QDateTimePrivate; |
3135 | x->m_status = QDateTimePrivate::StatusFlag(data.status & ~QDateTimePrivate::ShortData); |
3136 | x->m_msecs = data.msecs; |
3137 | } else { |
3138 | if (d->ref.loadRelaxed() == 1) |
3139 | return; |
3140 | |
3141 | x = new QDateTimePrivate(*d); |
3142 | } |
3143 | |
3144 | x->ref.storeRelaxed(1); |
3145 | if (!wasShort && !d->ref.deref()) |
3146 | delete d; |
3147 | d = x; |
3148 | } |
3149 | |
3150 | inline const QDateTimePrivate *QDateTime::Data::operator->() const |
3151 | { |
3152 | Q_ASSERT(!isShort()); |
3153 | return d; |
3154 | } |
3155 | |
3156 | inline QDateTimePrivate *QDateTime::Data::operator->() |
3157 | { |
3158 | // should we attempt to detach here? |
3159 | Q_ASSERT(!isShort()); |
3160 | Q_ASSERT(d->ref.loadRelaxed() == 1); |
3161 | return d; |
3162 | } |
3163 | |
3164 | /***************************************************************************** |
3165 | QDateTimePrivate member functions |
3166 | *****************************************************************************/ |
3167 | |
3168 | Q_NEVER_INLINE |
3169 | QDateTime::Data QDateTimePrivate::create(QDate toDate, QTime toTime, Qt::TimeSpec toSpec, |
3170 | int offsetSeconds) |
3171 | { |
3172 | QDateTime::Data result(toSpec); |
3173 | setTimeSpec(result, toSpec, offsetSeconds); |
3174 | setDateTime(result, toDate, toTime); |
3175 | if (toSpec == Qt::OffsetFromUTC || toSpec == Qt::UTC) |
3176 | refreshSimpleDateTime(result); |
3177 | else |
3178 | refreshZonedDateTime(result, Qt::LocalTime); |
3179 | return result; |
3180 | } |
3181 | |
3182 | #if QT_CONFIG(timezone) |
3183 | inline QDateTime::Data QDateTimePrivate::create(QDate toDate, QTime toTime, |
3184 | const QTimeZone &toTimeZone) |
3185 | { |
3186 | QDateTime::Data result(Qt::TimeZone); |
3187 | Q_ASSERT(!result.isShort()); |
3188 | |
3189 | result.d->m_status = mergeSpec(result.d->m_status, Qt::TimeZone); |
3190 | result.d->m_timeZone = toTimeZone; |
3191 | setDateTime(result, toDate, toTime); |
3192 | refreshZonedDateTime(result, Qt::TimeZone); |
3193 | return result; |
3194 | } |
3195 | |
3196 | // Convert a TimeZone time expressed in zone msecs encoding into a UTC epoch msecs |
3197 | // DST transitions are disambiguated by hint. |
3198 | inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QTimeZone &zone, |
3199 | DaylightStatus hint, |
3200 | QDate *zoneDate, QTime *zoneTime) |
3201 | { |
3202 | Q_ASSERT(zone.isValid()); |
3203 | // Get the effective data from QTimeZone |
3204 | QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs, int(hint)); |
3205 | Q_ASSERT(zone.d->offsetFromUtc(data.atMSecsSinceEpoch) == data.offsetFromUtc); |
3206 | Q_ASSERT((zoneMSecs - data.atMSecsSinceEpoch) / 1000 == data.offsetFromUtc |
3207 | // If zoneMSecs fell in a spring-forward's gap, we get this instead: |
3208 | || (zoneMSecs - data.atMSecsSinceEpoch) / 1000 == data.standardTimeOffset |
3209 | // If it fell in a skipped day (Pacific date-line crossings), this happens: |
3210 | || (data.offsetFromUtc - (zoneMSecs - data.atMSecsSinceEpoch) / 1000) % 86400 == 0); |
3211 | // Docs state any time before 1970-01-01 will *not* have any DST applied |
3212 | // but all affected times afterwards will have DST applied. |
3213 | if (data.atMSecsSinceEpoch < 0) { |
3214 | msecsToTime(zoneMSecs, zoneDate, zoneTime); |
3215 | return zoneMSecs - data.standardTimeOffset * 1000; |
3216 | } else { |
3217 | msecsToTime(data.atMSecsSinceEpoch + data.offsetFromUtc * 1000, zoneDate, zoneTime); |
3218 | return data.atMSecsSinceEpoch; |
3219 | } |
3220 | } |
3221 | #endif // timezone |
3222 | |
3223 | /***************************************************************************** |
3224 | QDateTime member functions |
3225 | *****************************************************************************/ |
3226 | |
3227 | /*! |
3228 | \class QDateTime |
3229 | \inmodule QtCore |
3230 | \ingroup shared |
3231 | \reentrant |
3232 | \brief The QDateTime class provides date and time functions. |
3233 | |
3234 | |
3235 | A QDateTime object encodes a calendar date and a clock time (a |
3236 | "datetime"). It combines features of the QDate and QTime classes. |
3237 | It can read the current datetime from the system clock. It |
3238 | provides functions for comparing datetimes and for manipulating a |
3239 | datetime by adding a number of seconds, days, months, or years. |
3240 | |
3241 | QDateTime can describe datetimes with respect to \l{Qt::LocalTime}{local |
3242 | time}, to \l{Qt::UTC}{UTC}, to a specified \l{Qt::OffsetFromUTC}{offset from |
3243 | UTC} or to a specified \l{Qt::TimeZone}{time zone}, in conjunction with the |
3244 | QTimeZone class. For example, a time zone of "Europe/Berlin" will apply the |
3245 | daylight-saving rules as used in Germany since 1970. In contrast, an offset |
3246 | from UTC of +3600 seconds is one hour ahead of UTC (usually written in ISO |
3247 | standard notation as "UTC+01:00"), with no daylight-saving offset or |
3248 | changes. When using either local time or a specified time zone, time-zone |
3249 | transitions such as the starts and ends of daylight-saving time (DST; but |
3250 | see below) are taken into account. The choice of system used to represent a |
3251 | datetime is described as its "timespec". |
3252 | |
3253 | A QDateTime object is typically created either by giving a date and time |
3254 | explicitly in the constructor, or by using a static function such as |
3255 | currentDateTime() or fromMSecsSinceEpoch(). The date and time can be changed |
3256 | with setDate() and setTime(). A datetime can also be set using the |
3257 | setMSecsSinceEpoch() function that takes the time, in milliseconds, since |
3258 | 00:00:00 on January 1, 1970. The fromString() function returns a QDateTime, |
3259 | given a string and a date format used to interpret the date within the |
3260 | string. |
3261 | |
3262 | QDateTime::currentDateTime() returns a QDateTime that expresses the current |
3263 | time with respect to local time. QDateTime::currentDateTimeUtc() returns a |
3264 | QDateTime that expresses the current time with respect to UTC. |
3265 | |
3266 | The date() and time() functions provide access to the date and |
3267 | time parts of the datetime. The same information is provided in |
3268 | textual format by the toString() function. |
3269 | |
3270 | QDateTime provides a full set of operators to compare two |
3271 | QDateTime objects, where smaller means earlier and larger means |
3272 | later. |
3273 | |
3274 | You can increment (or decrement) a datetime by a given number of |
3275 | milliseconds using addMSecs(), seconds using addSecs(), or days using |
3276 | addDays(). Similarly, you can use addMonths() and addYears(). The daysTo() |
3277 | function returns the number of days between two datetimes, secsTo() returns |
3278 | the number of seconds between two datetimes, and msecsTo() returns the |
3279 | number of milliseconds between two datetimes. These operations are aware of |
3280 | daylight-saving time (DST) and other time-zone transitions, where |
3281 | applicable. |
3282 | |
3283 | Use toTimeSpec() to express a datetime in local time or UTC, |
3284 | toOffsetFromUtc() to express in terms of an offset from UTC, or toTimeZone() |
3285 | to express it with respect to a general time zone. You can use timeSpec() to |
3286 | find out what time-spec a QDateTime object stores its time relative to. When |
3287 | that is Qt::TimeZone, you can use timeZone() to find out which zone it is |
3288 | using. |
3289 | |
3290 | \note QDateTime does not account for leap seconds. |
3291 | |
3292 | \section1 Remarks |
3293 | |
3294 | \note All conversion to and from string formats is done using the C locale. |
3295 | For localized conversions, see QLocale. |
3296 | |
3297 | \note There is no year 0 in the Gregorian calendar. Dates in that year are |
3298 | considered invalid. The year -1 is the year "1 before Christ" or "1 before |
3299 | common era." The day before 1 January 1 CE is 31 December 1 BCE. |
3300 | |
3301 | \section2 Range of Valid Dates |
3302 | |
3303 | The range of values that QDateTime can represent is dependent on the |
3304 | internal storage implementation. QDateTime is currently stored in a qint64 |
3305 | as a serial msecs value encoding the date and time. This restricts the date |
3306 | range to about +/- 292 million years, compared to the QDate range of +/- 2 |
3307 | billion years. Care must be taken when creating a QDateTime with extreme |
3308 | values that you do not overflow the storage. The exact range of supported |
3309 | values varies depending on the Qt::TimeSpec and time zone. |
3310 | |
3311 | \section2 Use of Timezones |
3312 | |
3313 | QDateTime uses the system's time zone information to determine the current |
3314 | local time zone and its offset from UTC. If the system is not configured |
3315 | correctly or not up-to-date, QDateTime will give wrong results. |
3316 | |
3317 | QDateTime likewise uses system-provided information to determine the offsets |
3318 | of other timezones from UTC. If this information is incomplete or out of |
3319 | date, QDateTime will give wrong results. See the QTimeZone documentation for |
3320 | more details. |
3321 | |
3322 | On modern Unix systems, this means QDateTime usually has accurate |
3323 | information about historical transitions (including DST, see below) whenever |
3324 | possible. On Windows, where the system doesn't support historical timezone |
3325 | data, historical accuracy is not maintained with respect to timezone |
3326 | transitions, notably including DST. |
3327 | |
3328 | \section2 Daylight-Saving Time (DST) |
3329 | |
3330 | QDateTime takes into account transitions between Standard Time and |
3331 | Daylight-Saving Time. For example, if the transition is at 2am and the clock |
3332 | goes forward to 3am, then there is a "missing" hour from 02:00:00 to |
3333 | 02:59:59.999 which QDateTime considers to be invalid. Any date arithmetic |
3334 | performed will take this missing hour into account and return a valid |
3335 | result. For example, adding one minute to 01:59:59 will get 03:00:00. |
3336 | |
3337 | The range of valid dates taking DST into account is 1970-01-01 to the |
3338 | present, and rules are in place for handling DST correctly until 2037-12-31, |
3339 | but these could change. For dates after 2037, QDateTime makes a \e{best |
3340 | guess} using the rules for year 2037, but we can't guarantee accuracy; |
3341 | indeed, for \e{any} future date, the time-zone may change its rules before |
3342 | that date comes around. For dates before 1970, QDateTime doesn't take DST |
3343 | changes into account, even if the system's time zone database provides that |
3344 | information, although it does take into account changes to the time-zone's |
3345 | standard offset, where this information is available. |
3346 | |
3347 | \section2 Offsets From UTC |
3348 | |
3349 | There is no explicit size restriction on an offset from UTC, but there is an |
3350 | implicit limit imposed when using the toString() and fromString() methods |
3351 | which use a [+|-]hh:mm format, effectively limiting the range to +/- 99 |
3352 | hours and 59 minutes and whole minutes only. Note that currently no time |
3353 | zone lies outside the range of +/- 14 hours. |
3354 | |
3355 | \sa QDate, QTime, QDateTimeEdit, QTimeZone |
3356 | */ |
3357 | |
3358 | /*! |
3359 | \since 5.14 |
3360 | \enum QDateTime::YearRange |
3361 | |
3362 | This enumerated type describes the range of years (in the Gregorian |
3363 | calendar) representable by QDateTime: |
3364 | |
3365 | \value First The later parts of this year are representable |
3366 | \value Last The earlier parts of this year are representable |
3367 | |
3368 | All dates strictly between these two years are also representable. |
3369 | Note, however, that the Gregorian Calendar has no year zero. |
3370 | |
3371 | \note QDate can describe dates in a wider range of years. For most |
3372 | purposes, this makes little difference, as the range of years that QDateTime |
3373 | can support reaches 292 million years either side of 1970. |
3374 | |
3375 | \sa isValid(), QDate |
3376 | */ |
3377 | |
3378 | /*! |
3379 | Constructs a null datetime. |
3380 | |
3381 | A null datetime is invalid, since its date and time are invalid. |
3382 | |
3383 | \sa isValid() |
3384 | */ |
3385 | QDateTime::QDateTime() noexcept |
3386 | { |
3387 | } |
3388 | |
3389 | /*! |
3390 | Constructs a datetime with the given \a date and \a time, using |
3391 | the time specification defined by \a spec and \a offsetSeconds seconds. |
3392 | |
3393 | If \a date is valid and \a time is not, the time will be set to midnight. |
3394 | |
3395 | If the \a spec is not Qt::OffsetFromUTC then \a offsetSeconds will be ignored. |
3396 | |
3397 | If the \a spec is Qt::OffsetFromUTC and \a offsetSeconds is 0 then the |
3398 | timeSpec() will be set to Qt::UTC, i.e. an offset of 0 seconds. |
3399 | |
3400 | If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime, |
3401 | i.e. the current system time zone. To create a Qt::TimeZone datetime |
3402 | use the correct constructor. |
3403 | */ |
3404 | |
3405 | QDateTime::QDateTime(QDate date, QTime time, Qt::TimeSpec spec, int offsetSeconds) |
3406 | : d(QDateTimePrivate::create(date, time, spec, offsetSeconds)) |
3407 | { |
3408 | } |
3409 | |
3410 | #if QT_CONFIG(timezone) |
3411 | /*! |
3412 | \since 5.2 |
3413 | |
3414 | Constructs a datetime with the given \a date and \a time, using |
3415 | the Time Zone specified by \a timeZone. |
3416 | |
3417 | If \a date is valid and \a time is not, the time will be set to 00:00:00. |
3418 | |
3419 | If \a timeZone is invalid then the datetime will be invalid. |
3420 | */ |
3421 | |
3422 | QDateTime::QDateTime(QDate date, QTime time, const QTimeZone &timeZone) |
3423 | : d(QDateTimePrivate::create(date, time, timeZone)) |
3424 | { |
3425 | } |
3426 | #endif // timezone |
3427 | |
3428 | /*! |
3429 | Constructs a copy of the \a other datetime. |
3430 | */ |
3431 | QDateTime::QDateTime(const QDateTime &other) noexcept |
3432 | : d(other.d) |
3433 | { |
3434 | } |
3435 | |
3436 | /*! |
3437 | \since 5.8 |
3438 | Moves the content of the temporary \a other datetime to this object and |
3439 | leaves \a other in an unspecified (but proper) state. |
3440 | */ |
3441 | QDateTime::QDateTime(QDateTime &&other) noexcept |
3442 | : d(std::move(other.d)) |
3443 | { |
3444 | } |
3445 | |
3446 | /*! |
3447 | Destroys the datetime. |
3448 | */ |
3449 | QDateTime::~QDateTime() |
3450 | { |
3451 | } |
3452 | |
3453 | /*! |
3454 | Makes a copy of the \a other datetime and returns a reference to the |
3455 | copy. |
3456 | */ |
3457 | |
3458 | QDateTime &QDateTime::operator=(const QDateTime &other) noexcept |
3459 | { |
3460 | d = other.d; |
3461 | return *this; |
3462 | } |
3463 | /*! |
3464 | \fn void QDateTime::swap(QDateTime &other) |
3465 | \since 5.0 |
3466 | |
3467 | Swaps this datetime with \a other. This operation is very fast |
3468 | and never fails. |
3469 | */ |
3470 | |
3471 | /*! |
3472 | Returns \c true if both the date and the time are null; otherwise |
3473 | returns \c false. A null datetime is invalid. |
3474 | |
3475 | \sa QDate::isNull(), QTime::isNull(), isValid() |
3476 | */ |
3477 | |
3478 | bool QDateTime::isNull() const |
3479 | { |
3480 | auto status = getStatus(d); |
3481 | return !status.testFlag(QDateTimePrivate::ValidDate) && |
3482 | !status.testFlag(QDateTimePrivate::ValidTime); |
3483 | } |
3484 | |
3485 | /*! |
3486 | Returns \c true if both the date and the time are valid and they are valid in |
3487 | the current Qt::TimeSpec, otherwise returns \c false. |
3488 | |
3489 | If the timeSpec() is Qt::LocalTime or Qt::TimeZone then the date and time are |
3490 | checked to see if they fall in the Standard Time to Daylight-Saving Time transition |
3491 | hour, i.e. if the transition is at 2am and the clock goes forward to 3am |
3492 | then the time from 02:00:00 to 02:59:59.999 is considered to be invalid. |
3493 | |
3494 | \sa QDateTime::YearRange, QDate::isValid(), QTime::isValid() |
3495 | */ |
3496 | |
3497 | bool QDateTime::isValid() const |
3498 | { |
3499 | auto status = getStatus(d); |
3500 | return status & QDateTimePrivate::ValidDateTime; |
3501 | } |
3502 | |
3503 | /*! |
3504 | Returns the date part of the datetime. |
3505 | |
3506 | \sa setDate(), time(), timeSpec() |
3507 | */ |
3508 | |
3509 | QDate QDateTime::date() const |
3510 | { |
3511 | auto status = getStatus(d); |
3512 | if (!status.testFlag(QDateTimePrivate::ValidDate)) |
3513 | return QDate(); |
3514 | QDate dt; |
3515 | msecsToTime(getMSecs(d), &dt, nullptr); |
3516 | return dt; |
3517 | } |
3518 | |
3519 | /*! |
3520 | Returns the time part of the datetime. |
3521 | |
3522 | \sa setTime(), date(), timeSpec() |
3523 | */ |
3524 | |
3525 | QTime QDateTime::time() const |
3526 | { |
3527 | auto status = getStatus(d); |
3528 | if (!status.testFlag(QDateTimePrivate::ValidTime)) |
3529 | return QTime(); |
3530 | QTime tm; |
3531 | msecsToTime(getMSecs(d), nullptr, &tm); |
3532 | return tm; |
3533 | } |
3534 | |
3535 | /*! |
3536 | Returns the time specification of the datetime. |
3537 | |
3538 | \sa setTimeSpec(), date(), time(), Qt::TimeSpec |
3539 | */ |
3540 | |
3541 | Qt::TimeSpec QDateTime::timeSpec() const |
3542 | { |
3543 | return getSpec(d); |
3544 | } |
3545 | |
3546 | #if QT_CONFIG(timezone) |
3547 | /*! |
3548 | \since 5.2 |
3549 | |
3550 | Returns the time zone of the datetime. |
3551 | |
3552 | If the timeSpec() is Qt::LocalTime then an instance of the current system |
3553 | time zone will be returned. Note however that if you copy this time zone |
3554 | the instance will not remain in sync if the system time zone changes. |
3555 | |
3556 | \sa setTimeZone(), Qt::TimeSpec |
3557 | */ |
3558 | |
3559 | QTimeZone QDateTime::timeZone() const |
3560 | { |
3561 | switch (getSpec(d)) { |
3562 | case Qt::UTC: |
3563 | return QTimeZone::utc(); |
3564 | case Qt::OffsetFromUTC: |
3565 | return QTimeZone(d->m_offsetFromUtc); |
3566 | case Qt::TimeZone: |
3567 | if (d->m_timeZone.isValid()) |
3568 | return d->m_timeZone; |
3569 | break; |
3570 | case Qt::LocalTime: |
3571 | return QTimeZone::systemTimeZone(); |
3572 | } |
3573 | return QTimeZone(); |
3574 | } |
3575 | #endif // timezone |
3576 | |
3577 | /*! |
3578 | \since 5.2 |
3579 | |
3580 | Returns this date-time's Offset From UTC in seconds. |
3581 | |
3582 | The result depends on timeSpec(): |
3583 | \list |
3584 | \li \c Qt::UTC The offset is 0. |
3585 | \li \c Qt::OffsetFromUTC The offset is the value originally set. |
3586 | \li \c Qt::LocalTime The local time's offset from UTC is returned. |
3587 | \li \c Qt::TimeZone The offset used by the time-zone is returned. |
3588 | \endlist |
3589 | |
3590 | For the last two, the offset at this date and time will be returned, taking |
3591 | account of Daylight-Saving Offset unless the date precedes the start of |
3592 | 1970. The offset is the difference between the local time or time in the |
3593 | given time-zone and UTC time; it is positive in time-zones ahead of UTC |
3594 | (East of The Prime Meridian), negative for those behind UTC (West of The |
3595 | Prime Meridian). |
3596 | |
3597 | \sa setOffsetFromUtc() |
3598 | */ |
3599 | |
3600 | int QDateTime::offsetFromUtc() const |
3601 | { |
3602 | if (!d.isShort()) |
3603 | return d->m_offsetFromUtc; |
3604 | if (!isValid()) |
3605 | return 0; |
3606 | |
3607 | auto spec = getSpec(d); |
3608 | if (spec == Qt::LocalTime) { |
3609 | // we didn't cache the value, so we need to calculate it now... |
3610 | qint64 msecs = getMSecs(d); |
3611 | return (msecs - toMSecsSinceEpoch()) / 1000; |
3612 | } |
3613 | |
3614 | Q_ASSERT(spec == Qt::UTC); |
3615 | return 0; |
3616 | } |
3617 | |
3618 | /*! |
3619 | \since 5.2 |
3620 | |
3621 | Returns the Time Zone Abbreviation for the datetime. |
3622 | |
3623 | If the timeSpec() is Qt::UTC this will be "UTC". |
3624 | |
3625 | If the timeSpec() is Qt::OffsetFromUTC this will be in the format |
3626 | "UTC[+-]00:00". |
3627 | |
3628 | If the timeSpec() is Qt::LocalTime then the host system is queried for the |
3629 | correct abbreviation. |
3630 | |
3631 | Note that abbreviations may or may not be localized. |
3632 | |
3633 | Note too that the abbreviation is not guaranteed to be a unique value, |
3634 | i.e. different time zones may have the same abbreviation. |
3635 | |
3636 | \sa timeSpec() |
3637 | */ |
3638 | |
3639 | QString QDateTime::timeZoneAbbreviation() const |
3640 | { |
3641 | if (!isValid()) |
3642 | return QString(); |
3643 | |
3644 | switch (getSpec(d)) { |
3645 | case Qt::UTC: |
3646 | return QLatin1String("UTC" ); |
3647 | case Qt::OffsetFromUTC: |
3648 | return QLatin1String("UTC" ) + toOffsetString(Qt::ISODate, d->m_offsetFromUtc); |
3649 | case Qt::TimeZone: |
3650 | #if !QT_CONFIG(timezone) |
3651 | break; |
3652 | #else |
3653 | Q_ASSERT(d->m_timeZone.isValid()); |
3654 | return d->m_timeZone.d->abbreviation(toMSecsSinceEpoch()); |
3655 | #endif // timezone |
3656 | case Qt::LocalTime: { |
3657 | QString abbrev; |
3658 | auto status = extractDaylightStatus(getStatus(d)); |
3659 | localMSecsToEpochMSecs(getMSecs(d), &status, nullptr, nullptr, &abbrev); |
3660 | return abbrev; |
3661 | } |
3662 | } |
3663 | return QString(); |
3664 | } |
3665 | |
3666 | /*! |
3667 | \since 5.2 |
3668 | |
3669 | Returns if this datetime falls in Daylight-Saving Time. |
3670 | |
3671 | If the Qt::TimeSpec is not Qt::LocalTime or Qt::TimeZone then will always |
3672 | return false. |
3673 | |
3674 | \sa timeSpec() |
3675 | */ |
3676 | |
3677 | bool QDateTime::isDaylightTime() const |
3678 | { |
3679 | if (!isValid()) |
3680 | return false; |
3681 | |
3682 | switch (getSpec(d)) { |
3683 | case Qt::UTC: |
3684 | case Qt::OffsetFromUTC: |
3685 | return false; |
3686 | case Qt::TimeZone: |
3687 | #if !QT_CONFIG(timezone) |
3688 | break; |
3689 | #else |
3690 | Q_ASSERT(d->m_timeZone.isValid()); |
3691 | return d->m_timeZone.d->isDaylightTime(toMSecsSinceEpoch()); |
3692 | #endif // timezone |
3693 | case Qt::LocalTime: { |
3694 | auto status = extractDaylightStatus(getStatus(d)); |
3695 | if (status == QDateTimePrivate::UnknownDaylightTime) |
3696 | localMSecsToEpochMSecs(getMSecs(d), &status); |
3697 | return (status == QDateTimePrivate::DaylightTime); |
3698 | } |
3699 | } |
3700 | return false; |
3701 | } |
3702 | |
3703 | /*! |
3704 | Sets the date part of this datetime to \a date. If no time is set yet, it |
3705 | is set to midnight. If \a date is invalid, this QDateTime becomes invalid. |
3706 | |
3707 | \sa date(), setTime(), setTimeSpec() |
3708 | */ |
3709 | |
3710 | void QDateTime::setDate(QDate date) |
3711 | { |
3712 | setDateTime(d, date, time()); |
3713 | checkValidDateTime(d); |
3714 | } |
3715 | |
3716 | /*! |
3717 | Sets the time part of this datetime to \a time. If \a time is not valid, |
3718 | this function sets it to midnight. Therefore, it's possible to clear any |
3719 | set time in a QDateTime by setting it to a default QTime: |
3720 | |
3721 | \code |
3722 | QDateTime dt = QDateTime::currentDateTime(); |
3723 | dt.setTime(QTime()); |
3724 | \endcode |
3725 | |
3726 | \sa time(), setDate(), setTimeSpec() |
3727 | */ |
3728 | |
3729 | void QDateTime::setTime(QTime time) |
3730 | { |
3731 | setDateTime(d, date(), time); |
3732 | checkValidDateTime(d); |
3733 | } |
3734 | |
3735 | /*! |
3736 | Sets the time specification used in this datetime to \a spec. |
3737 | The datetime will refer to a different point in time. |
3738 | |
3739 | If \a spec is Qt::OffsetFromUTC then the timeSpec() will be set |
3740 | to Qt::UTC, i.e. an effective offset of 0. |
3741 | |
3742 | If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime, |
3743 | i.e. the current system time zone. |
3744 | |
3745 | Example: |
3746 | \snippet code/src_corelib_time_qdatetime.cpp 19 |
3747 | |
3748 | \sa timeSpec(), setDate(), setTime(), setTimeZone(), Qt::TimeSpec |
3749 | */ |
3750 | |
3751 | void QDateTime::setTimeSpec(Qt::TimeSpec spec) |
3752 | { |
3753 | QT_PREPEND_NAMESPACE(setTimeSpec(d, spec, 0)); |
3754 | if (spec == Qt::OffsetFromUTC || spec == Qt::UTC) |
3755 | refreshSimpleDateTime(d); |
3756 | else |
3757 | refreshZonedDateTime(d, Qt::LocalTime); |
3758 | } |
3759 | |
3760 | /*! |
3761 | \since 5.2 |
3762 | |
3763 | Sets the timeSpec() to Qt::OffsetFromUTC and the offset to \a offsetSeconds. |
3764 | The datetime will refer to a different point in time. |
3765 | |
3766 | The maximum and minimum offset is 14 positive or negative hours. If |
3767 | \a offsetSeconds is larger or smaller than that, then the result is |
3768 | undefined. |
3769 | |
3770 | If \a offsetSeconds is 0 then the timeSpec() will be set to Qt::UTC. |
3771 | |
3772 | \sa isValid(), offsetFromUtc() |
3773 | */ |
3774 | |
3775 | void QDateTime::setOffsetFromUtc(int offsetSeconds) |
3776 | { |
3777 | QT_PREPEND_NAMESPACE(setTimeSpec(d, Qt::OffsetFromUTC, offsetSeconds)); |
3778 | refreshSimpleDateTime(d); |
3779 | } |
3780 | |
3781 | #if QT_CONFIG(timezone) |
3782 | /*! |
3783 | \since 5.2 |
3784 | |
3785 | Sets the time zone used in this datetime to \a toZone. |
3786 | The datetime will refer to a different point in time. |
3787 | |
3788 | If \a toZone is invalid then the datetime will be invalid. |
3789 | |
3790 | \sa timeZone(), Qt::TimeSpec |
3791 | */ |
3792 | |
3793 | void QDateTime::setTimeZone(const QTimeZone &toZone) |
3794 | { |
3795 | d.detach(); // always detach |
3796 | d->m_status = mergeSpec(d->m_status, Qt::TimeZone); |
3797 | d->m_offsetFromUtc = 0; |
3798 | d->m_timeZone = toZone; |
3799 | refreshZonedDateTime(d, Qt::TimeZone); |
3800 | } |
3801 | #endif // timezone |
3802 | |
3803 | /*! |
3804 | \since 4.7 |
3805 | |
3806 | Returns the datetime as the number of milliseconds that have passed |
3807 | since 1970-01-01T00:00:00.000, Coordinated Universal Time (Qt::UTC). |
3808 | |
3809 | On systems that do not support time zones, this function will |
3810 | behave as if local time were Qt::UTC. |
3811 | |
3812 | The behavior for this function is undefined if the datetime stored in |
3813 | this object is not valid. However, for all valid dates, this function |
3814 | returns a unique value. |
3815 | |
3816 | \sa toSecsSinceEpoch(), setMSecsSinceEpoch() |
3817 | */ |
3818 | qint64 QDateTime::toMSecsSinceEpoch() const |
3819 | { |
3820 | // Note: QDateTimeParser relies on this producing a useful result, even when |
3821 | // !isValid(), at least when the invalidity is a time in a fall-back (that |
3822 | // we'll have adjusted to lie outside it, but marked invalid because it's |
3823 | // not what was asked for). Other things may be doing similar. |
3824 | switch (getSpec(d)) { |
3825 | case Qt::UTC: |
3826 | return getMSecs(d); |
3827 | |
3828 | case Qt::OffsetFromUTC: |
3829 | Q_ASSERT(!d.isShort()); |
3830 | return d->m_msecs - (d->m_offsetFromUtc * 1000); |
3831 | |
3832 | case Qt::LocalTime: { |
3833 | // recalculate the local timezone |
3834 | auto status = extractDaylightStatus(getStatus(d)); |
3835 | // If short, use offset saved by refreshZonedDateTime() on creation: |
3836 | if (!d.isShort()) |
3837 | return d->m_msecs - d->m_offsetFromUtc * 1000; |
3838 | // Offset from UTC not recorded: need to recompute. |
3839 | return localMSecsToEpochMSecs(getMSecs(d), &status); |
3840 | } |
3841 | |
3842 | case Qt::TimeZone: |
3843 | Q_ASSERT(!d.isShort()); |
3844 | #if QT_CONFIG(timezone) |
3845 | // Use offset refreshZonedDateTime() saved creation: |
3846 | if (d->m_timeZone.isValid()) |
3847 | return d->m_msecs - d->m_offsetFromUtc * 1000; |
3848 | #endif |
3849 | return 0; |
3850 | } |
3851 | Q_UNREACHABLE(); |
3852 | return 0; |
3853 | } |
3854 | |
3855 | /*! |
3856 | \since 5.8 |
3857 | |
3858 | Returns the datetime as the number of seconds that have passed since |
3859 | 1970-01-01T00:00:00.000, Coordinated Universal Time (Qt::UTC). |
3860 | |
3861 | On systems that do not support time zones, this function will |
3862 | behave as if local time were Qt::UTC. |
3863 | |
3864 | The behavior for this function is undefined if the datetime stored in |
3865 | this object is not valid. However, for all valid dates, this function |
3866 | returns a unique value. |
3867 | |
3868 | \sa toMSecsSinceEpoch(), setSecsSinceEpoch() |
3869 | */ |
3870 | qint64 QDateTime::toSecsSinceEpoch() const |
3871 | { |
3872 | return toMSecsSinceEpoch() / 1000; |
3873 | } |
3874 | |
3875 | /*! |
3876 | \since 4.7 |
3877 | |
3878 | Sets the date and time given the number of milliseconds \a msecs that have |
3879 | passed since 1970-01-01T00:00:00.000, Coordinated Universal Time |
3880 | (Qt::UTC). On systems that do not support time zones this function |
3881 | will behave as if local time were Qt::UTC. |
3882 | |
3883 | Note that passing the minimum of \c qint64 |
3884 | (\c{std::numeric_limits<qint64>::min()}) to \a msecs will result in |
3885 | undefined behavior. |
3886 | |
3887 | \sa toMSecsSinceEpoch(), setSecsSinceEpoch() |
3888 | */ |
3889 | void QDateTime::setMSecsSinceEpoch(qint64 msecs) |
3890 | { |
3891 | auto status = getStatus(d); |
3892 | const auto spec = extractSpec(status); |
3893 | |
3894 | status &= ~QDateTimePrivate::ValidityMask; |
3895 | switch (spec) { |
3896 | case Qt::UTC: |
3897 | status |= QDateTimePrivate::ValidWhenMask; |
3898 | break; |
3899 | case Qt::OffsetFromUTC: |
3900 | msecs = msecs + (d->m_offsetFromUtc * 1000); |
3901 | status |= QDateTimePrivate::ValidWhenMask; |
3902 | break; |
3903 | case Qt::TimeZone: |
3904 | Q_ASSERT(!d.isShort()); |
3905 | #if QT_CONFIG(timezone) |
3906 | d.detach(); |
3907 | if (!d->m_timeZone.isValid()) |
3908 | break; |
3909 | // Docs state any LocalTime before 1970-01-01 will *not* have any DST applied |
3910 | // but all affected times afterwards will have DST applied. |
3911 | if (msecs >= 0) { |
3912 | status = mergeDaylightStatus(status, |
3913 | d->m_timeZone.d->isDaylightTime(msecs) |
3914 | ? QDateTimePrivate::DaylightTime |
3915 | : QDateTimePrivate::StandardTime); |
3916 | d->m_offsetFromUtc = d->m_timeZone.d->offsetFromUtc(msecs); |
3917 | } else { |
3918 | status = mergeDaylightStatus(status, QDateTimePrivate::StandardTime); |
3919 | d->m_offsetFromUtc = d->m_timeZone.d->standardTimeOffset(msecs); |
3920 | } |
3921 | if (!add_overflow(msecs, qint64(d->m_offsetFromUtc * 1000), &msecs)) |
3922 | status |= QDateTimePrivate::ValidWhenMask; |
3923 | #endif // timezone |
3924 | break; |
3925 | case Qt::LocalTime: { |
3926 | QDate dt; |
3927 | QTime tm; |
3928 | QDateTimePrivate::DaylightStatus dstStatus; |
3929 | epochMSecsToLocalTime(msecs, &dt, &tm, &dstStatus); |
3930 | setDateTime(d, dt, tm); |
3931 | refreshZonedDateTime(d, spec); // FIXME: we do this again, below |
3932 | msecs = getMSecs(d); |
3933 | status = mergeDaylightStatus(getStatus(d), dstStatus); |
3934 | break; |
3935 | } |
3936 | } |
3937 | |
3938 | if (msecsCanBeSmall(msecs) && d.isShort()) { |
3939 | // we can keep short |
3940 | d.data.msecs = qintptr(msecs); |
3941 | d.data.status = status; |
3942 | } else { |
3943 | d.detach(); |
3944 | d->m_status = status & ~QDateTimePrivate::ShortData; |
3945 | d->m_msecs = msecs; |
3946 | } |
3947 | |
3948 | if (spec == Qt::LocalTime || spec == Qt::TimeZone) |
3949 | refreshZonedDateTime(d, spec); |
3950 | } |
3951 | |
3952 | /*! |
3953 | \since 5.8 |
3954 | |
3955 | Sets the date and time given the number of seconds \a secs that have |
3956 | passed since 1970-01-01T00:00:00.000, Coordinated Universal Time |
3957 | (Qt::UTC). On systems that do not support time zones this function |
3958 | will behave as if local time were Qt::UTC. |
3959 | |
3960 | \sa toSecsSinceEpoch(), setMSecsSinceEpoch() |
3961 | */ |
3962 | void QDateTime::setSecsSinceEpoch(qint64 secs) |
3963 | { |
3964 | qint64 msecs; |
3965 | if (!mul_overflow(secs, std::integral_constant<qint64, 1000>(), &msecs)) { |
3966 | setMSecsSinceEpoch(msecs); |
3967 | } else if (d.isShort()) { |
3968 | d.data.status &= ~QDateTimePrivate::ValidWhenMask; |
3969 | } else { |
3970 | d.detach(); |
3971 | d->m_status &= ~QDateTimePrivate::ValidWhenMask; |
3972 | } |
3973 | } |
3974 | |
3975 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
3976 | /*! |
3977 | \overload |
3978 | |
3979 | Returns the datetime as a string in the \a format given. |
3980 | |
3981 | If the \a format is Qt::TextDate, the string is formatted in the default |
3982 | way. The day and month names will be in English. An example of this |
3983 | formatting is "Wed May 20 03:40:13 1998". For localized formatting, see |
3984 | \l{QLocale::toString()}. |
3985 | |
3986 | If the \a format is Qt::ISODate, the string format corresponds |
3987 | to the ISO 8601 extended specification for representations of |
3988 | dates and times, taking the form yyyy-MM-ddTHH:mm:ss[Z|[+|-]HH:mm], |
3989 | depending on the timeSpec() of the QDateTime. If the timeSpec() |
3990 | is Qt::UTC, Z will be appended to the string; if the timeSpec() is |
3991 | Qt::OffsetFromUTC, the offset in hours and minutes from UTC will |
3992 | be appended to the string. To include milliseconds in the ISO 8601 |
3993 | date, use the \a format Qt::ISODateWithMs, which corresponds to |
3994 | yyyy-MM-ddTHH:mm:ss.zzz[Z|[+|-]HH:mm]. |
3995 | |
3996 | If the \a format is Qt::RFC2822Date, the string is formatted |
3997 | following \l{RFC 2822}. |
3998 | |
3999 | If the datetime is invalid, an empty string will be returned. |
4000 | |
4001 | \warning The Qt::ISODate format is only valid for years in the |
4002 | range 0 to 9999. |
4003 | |
4004 | \sa fromString(), QDate::toString(), QTime::toString(), |
4005 | QLocale::toString() |
4006 | */ |
4007 | QString QDateTime::toString(Qt::DateFormat format) const |
4008 | { |
4009 | QString buf; |
4010 | if (!isValid()) |
4011 | return buf; |
4012 | |
4013 | switch (format) { |
4014 | case Qt::RFC2822Date: |
4015 | buf = QLocale::c().toString(*this, u"dd MMM yyyy hh:mm:ss " ); |
4016 | buf += toOffsetString(Qt::TextDate, offsetFromUtc()); |
4017 | return buf; |
4018 | default: |
4019 | case Qt::TextDate: { |
4020 | const QPair<QDate, QTime> p = getDateTime(d); |
4021 | buf = toStringTextDate(p.first); |
4022 | // Insert time between date's day and year: |
4023 | buf.insert(buf.lastIndexOf(u' '), |
4024 | u' ' + p.second.toString(Qt::TextDate)); |
4025 | // Append zone/offset indicator, as appropriate: |
4026 | switch (timeSpec()) { |
4027 | case Qt::LocalTime: |
4028 | break; |
4029 | #if QT_CONFIG(timezone) |
4030 | case Qt::TimeZone: |
4031 | buf += u' ' + d->m_timeZone.abbreviation(*this); |
4032 | break; |
4033 | #endif |
4034 | default: |
4035 | buf += QLatin1String(" GMT" ); |
4036 | if (getSpec(d) == Qt::OffsetFromUTC) |
4037 | buf += toOffsetString(Qt::TextDate, offsetFromUtc()); |
4038 | } |
4039 | return buf; |
4040 | } |
4041 | case Qt::ISODate: |
4042 | case Qt::ISODateWithMs: { |
4043 | const QPair<QDate, QTime> p = getDateTime(d); |
4044 | buf = toStringIsoDate(p.first); |
4045 | if (buf.isEmpty()) |
4046 | return QString(); // failed to convert |
4047 | buf += u'T' + p.second.toString(format); |
4048 | switch (getSpec(d)) { |
4049 | case Qt::UTC: |
4050 | buf += u'Z'; |
4051 | break; |
4052 | case Qt::OffsetFromUTC: |
4053 | #if QT_CONFIG(timezone) |
4054 | case Qt::TimeZone: |
4055 | #endif |
4056 | buf += toOffsetString(Qt::ISODate, offsetFromUtc()); |
4057 | break; |
4058 | default: |
4059 | break; |
4060 | } |
4061 | return buf; |
4062 | } |
4063 | } |
4064 | } |
4065 | |
4066 | /*! |
4067 | \fn QString QDateTime::toString(const QString &format, QCalendar cal) const |
4068 | \fn QString QDateTime::toString(QStringView format, QCalendar cal) const |
4069 | |
4070 | Returns the datetime as a string. The \a format parameter determines the |
4071 | format of the result string. If \a cal is supplied, it determines the calendar |
4072 | used to represent the date; it defaults to Gregorian. See QTime::toString() |
4073 | and QDate::toString() for the supported specifiers for time and date, |
4074 | respectively. |
4075 | |
4076 | Any sequence of characters enclosed in single quotes will be included |
4077 | verbatim in the output string (stripped of the quotes), even if it contains |
4078 | formatting characters. Two consecutive single quotes ("''") are replaced by |
4079 | a single quote in the output. All other characters in the format string are |
4080 | included verbatim in the output string. |
4081 | |
4082 | Formats without separators (e.g. "ddMM") are supported but must be used with |
4083 | care, as the resulting strings aren't always reliably readable (e.g. if "dM" |
4084 | produces "212" it could mean either the 2nd of December or the 21st of |
4085 | February). |
4086 | |
4087 | Example format strings (assumed that the QDateTime is 21 May 2001 |
4088 | 14:13:09.120): |
4089 | |
4090 | \table |
4091 | \header \li Format \li Result |
4092 | \row \li dd.MM.yyyy \li 21.05.2001 |
4093 | \row \li ddd MMMM d yy \li Tue May 21 01 |
4094 | \row \li hh:mm:ss.zzz \li 14:13:09.120 |
4095 | \row \li hh:mm:ss.z \li 14:13:09.12 |
4096 | \row \li h:m:s ap \li 2:13:9 pm |
4097 | \endtable |
4098 | |
4099 | If the datetime is invalid, an empty string will be returned. |
4100 | |
4101 | \note Day and month names as well as AM/PM indication are given in English (C locale). |
4102 | If localized month and day names and localized forms of AM/PM are used, use |
4103 | QLocale::system().toDateTime(). |
4104 | |
4105 | \sa fromString(), QDate::toString(), QTime::toString(), QLocale::toString() |
4106 | */ |
4107 | QString QDateTime::toString(QStringView format, QCalendar cal) const |
4108 | { |
4109 | return QLocale::c().toString(*this, format, cal); |
4110 | } |
4111 | #endif // datestring |
4112 | |
4113 | static inline void massageAdjustedDateTime(QDateTimeData &d, QDate date, QTime time) |
4114 | { |
4115 | /* |
4116 | If we have just adjusted to a day with a DST transition, our given time |
4117 | may lie in the transition hour (either missing or duplicated). For any |
4118 | other time, telling mktime (deep in the bowels of localMSecsToEpochMSecs) |
4119 | we don't know its DST-ness will produce no adjustment (just a decision as |
4120 | to its DST-ness); but for a time in spring's missing hour it'll adjust the |
4121 | time while picking a DST-ness. (Handling of autumn is trickier, as either |
4122 | DST-ness is valid, without adjusting the time. We might want to propagate |
4123 | the daylight status in that case, but it's hard to do so without breaking |
4124 | (far more common) other cases; and it makes little difference, as the two |
4125 | answers do then differ only in DST-ness.) |
4126 | */ |
4127 | auto spec = getSpec(d); |
4128 | if (spec == Qt::LocalTime) { |
4129 | QDateTimePrivate::DaylightStatus status = QDateTimePrivate::UnknownDaylightTime; |
4130 | localMSecsToEpochMSecs(timeToMSecs(date, time), &status, &date, &time); |
4131 | #if QT_CONFIG(timezone) |
4132 | } else if (spec == Qt::TimeZone && d->m_timeZone.isValid()) { |
4133 | QDateTimePrivate::zoneMSecsToEpochMSecs(timeToMSecs(date, time), |
4134 | d->m_timeZone, |
4135 | QDateTimePrivate::UnknownDaylightTime, |
4136 | &date, &time); |
4137 | #endif // timezone |
4138 | } |
4139 | setDateTime(d, date, time); |
4140 | checkValidDateTime(d); |
4141 | } |
4142 | |
4143 | /*! |
4144 | Returns a QDateTime object containing a datetime \a ndays days |
4145 | later than the datetime of this object (or earlier if \a ndays is |
4146 | negative). |
4147 | |
4148 | If the timeSpec() is Qt::LocalTime and the resulting |
4149 | date and time fall in the Standard Time to Daylight-Saving Time transition |
4150 | hour then the result will be adjusted accordingly, i.e. if the transition |
4151 | is at 2am and the clock goes forward to 3am and the result falls between |
4152 | 2am and 3am then the result will be adjusted to fall after 3am. |
4153 | |
4154 | \sa daysTo(), addMonths(), addYears(), addSecs() |
4155 | */ |
4156 | |
4157 | QDateTime QDateTime::addDays(qint64 ndays) const |
4158 | { |
4159 | if (isNull()) |
4160 | return QDateTime(); |
4161 | |
4162 | QDateTime dt(*this); |
4163 | QPair<QDate, QTime> p = getDateTime(d); |
4164 | massageAdjustedDateTime(dt.d, p.first.addDays(ndays), p.second); |
4165 | return dt; |
4166 | } |
4167 | |
4168 | /*! |
4169 | Returns a QDateTime object containing a datetime \a nmonths months |
4170 | later than the datetime of this object (or earlier if \a nmonths |
4171 | is negative). |
4172 | |
4173 | If the timeSpec() is Qt::LocalTime and the resulting |
4174 | date and time fall in the Standard Time to Daylight-Saving Time transition |
4175 | hour then the result will be adjusted accordingly, i.e. if the transition |
4176 | is at 2am and the clock goes forward to 3am and the result falls between |
4177 | 2am and 3am then the result will be adjusted to fall after 3am. |
4178 | |
4179 | \sa daysTo(), addDays(), addYears(), addSecs() |
4180 | */ |
4181 | |
4182 | QDateTime QDateTime::addMonths(int nmonths) const |
4183 | { |
4184 | if (isNull()) |
4185 | return QDateTime(); |
4186 | |
4187 | QDateTime dt(*this); |
4188 | QPair<QDate, QTime> p = getDateTime(d); |
4189 | massageAdjustedDateTime(dt.d, p.first.addMonths(nmonths), p.second); |
4190 | return dt; |
4191 | } |
4192 | |
4193 | /*! |
4194 | Returns a QDateTime object containing a datetime \a nyears years |
4195 | later than the datetime of this object (or earlier if \a nyears is |
4196 | negative). |
4197 | |
4198 | If the timeSpec() is Qt::LocalTime and the resulting |
4199 | date and time fall in the Standard Time to Daylight-Saving Time transition |
4200 | hour then the result will be adjusted accordingly, i.e. if the transition |
4201 | is at 2am and the clock goes forward to 3am and the result falls between |
4202 | 2am and 3am then the result will be adjusted to fall after 3am. |
4203 | |
4204 | \sa daysTo(), addDays(), addMonths(), addSecs() |
4205 | */ |
4206 | |
4207 | QDateTime QDateTime::addYears(int nyears) const |
4208 | { |
4209 | if (isNull()) |
4210 | return QDateTime(); |
4211 | |
4212 | QDateTime dt(*this); |
4213 | QPair<QDate, QTime> p = getDateTime(d); |
4214 | massageAdjustedDateTime(dt.d, p.first.addYears(nyears), p.second); |
4215 | return dt; |
4216 | } |
4217 | |
4218 | /*! |
4219 | Returns a QDateTime object containing a datetime \a s seconds |
4220 | later than the datetime of this object (or earlier if \a s is |
4221 | negative). |
4222 | |
4223 | If this datetime is invalid, an invalid datetime will be returned. |
4224 | |
4225 | \sa addMSecs(), secsTo(), addDays(), addMonths(), addYears() |
4226 | */ |
4227 | |
4228 | QDateTime QDateTime::addSecs(qint64 s) const |
4229 | { |
4230 | qint64 msecs; |
4231 | if (mul_overflow(s, std::integral_constant<qint64, 1000>(), &msecs)) |
4232 | return QDateTime(); |
4233 | return addMSecs(msecs); |
4234 | } |
4235 | |
4236 | /*! |
4237 | Returns a QDateTime object containing a datetime \a msecs miliseconds |
4238 | later than the datetime of this object (or earlier if \a msecs is |
4239 | negative). |
4240 | |
4241 | If this datetime is invalid, an invalid datetime will be returned. |
4242 | |
4243 | \sa addSecs(), msecsTo(), addDays(), addMonths(), addYears() |
4244 | */ |
4245 | QDateTime QDateTime::addMSecs(qint64 msecs) const |
4246 | { |
4247 | if (!isValid()) |
4248 | return QDateTime(); |
4249 | |
4250 | QDateTime dt(*this); |
4251 | switch (getSpec(d)) { |
4252 | case Qt::LocalTime: |
4253 | case Qt::TimeZone: |
4254 | // Convert to real UTC first in case this crosses a DST transition: |
4255 | if (!add_overflow(toMSecsSinceEpoch(), msecs, &msecs)) { |
4256 | dt.setMSecsSinceEpoch(msecs); |
4257 | } else if (dt.d.isShort()) { |
4258 | dt.d.data.status &= ~QDateTimePrivate::ValidWhenMask; |
4259 | } else { |
4260 | dt.d.detach(); |
4261 | dt.d->m_status &= ~QDateTimePrivate::ValidWhenMask; |
4262 | } |
4263 | break; |
4264 | case Qt::UTC: |
4265 | case Qt::OffsetFromUTC: |
4266 | // No need to convert, just add on |
4267 | if (add_overflow(getMSecs(d), msecs, &msecs)) { |
4268 | if (dt.d.isShort()) { |
4269 | dt.d.data.status &= ~QDateTimePrivate::ValidWhenMask; |
4270 | } else { |
4271 | dt.d.detach(); |
4272 | dt.d->m_status &= ~QDateTimePrivate::ValidWhenMask; |
4273 | } |
4274 | } else if (d.isShort()) { |
4275 | // need to check if we need to enlarge first |
4276 | if (msecsCanBeSmall(msecs)) { |
4277 | dt.d.data.msecs = qintptr(msecs); |
4278 | } else { |
4279 | dt.d.detach(); |
4280 | dt.d->m_msecs = msecs; |
4281 | } |
4282 | } else { |
4283 | dt.d.detach(); |
4284 | dt.d->m_msecs = msecs; |
4285 | } |
4286 | break; |
4287 | } |
4288 | return dt; |
4289 | } |
4290 | |
4291 | /*! |
4292 | Returns the number of days from this datetime to the \a other |
4293 | datetime. The number of days is counted as the number of times |
4294 | midnight is reached between this datetime to the \a other |
4295 | datetime. This means that a 10 minute difference from 23:55 to |
4296 | 0:05 the next day counts as one day. |
4297 | |
4298 | If the \a other datetime is earlier than this datetime, |
4299 | the value returned is negative. |
4300 | |
4301 | Example: |
4302 | \snippet code/src_corelib_time_qdatetime.cpp 15 |
4303 | |
4304 | \sa addDays(), secsTo(), msecsTo() |
4305 | */ |
4306 | |
4307 | qint64 QDateTime::daysTo(const QDateTime &other) const |
4308 | { |
4309 | return date().daysTo(other.date()); |
4310 | } |
4311 | |
4312 | /*! |
4313 | Returns the number of seconds from this datetime to the \a other |
4314 | datetime. If the \a other datetime is earlier than this datetime, |
4315 | the value returned is negative. |
4316 | |
4317 | Before performing the comparison, the two datetimes are converted |
4318 | to Qt::UTC to ensure that the result is correct if daylight-saving |
4319 | (DST) applies to one of the two datetimes but not the other. |
4320 | |
4321 | Returns 0 if either datetime is invalid. |
4322 | |
4323 | Example: |
4324 | \snippet code/src_corelib_time_qdatetime.cpp 11 |
4325 | |
4326 | \sa addSecs(), daysTo(), QTime::secsTo() |
4327 | */ |
4328 | |
4329 | qint64 QDateTime::secsTo(const QDateTime &other) const |
4330 | { |
4331 | return msecsTo(other) / 1000; |
4332 | } |
4333 | |
4334 | /*! |
4335 | Returns the number of milliseconds from this datetime to the \a other |
4336 | datetime. If the \a other datetime is earlier than this datetime, |
4337 | the value returned is negative. |
4338 | |
4339 | Before performing the comparison, the two datetimes are converted |
4340 | to Qt::UTC to ensure that the result is correct if daylight-saving |
4341 | (DST) applies to one of the two datetimes and but not the other. |
4342 | |
4343 | Returns 0 if either datetime is invalid. |
4344 | |
4345 | \sa addMSecs(), daysTo(), QTime::msecsTo() |
4346 | */ |
4347 | |
4348 | qint64 QDateTime::msecsTo(const QDateTime &other) const |
4349 | { |
4350 | if (!isValid() || !other.isValid()) |
4351 | return 0; |
4352 | |
4353 | return other.toMSecsSinceEpoch() - toMSecsSinceEpoch(); |
4354 | } |
4355 | |
4356 | /*! |
4357 | Returns a copy of this datetime converted to the given time |
4358 | \a spec. |
4359 | |
4360 | If \a spec is Qt::OffsetFromUTC then it is set to Qt::UTC. To set to a |
4361 | spec of Qt::OffsetFromUTC use toOffsetFromUtc(). |
4362 | |
4363 | If \a spec is Qt::TimeZone then it is set to Qt::LocalTime, |
4364 | i.e. the local Time Zone. |
4365 | |
4366 | Example: |
4367 | \snippet code/src_corelib_time_qdatetime.cpp 16 |
4368 | |
4369 | \sa timeSpec(), toTimeZone(), toOffsetFromUtc() |
4370 | */ |
4371 | |
4372 | QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const |
4373 | { |
4374 | if (getSpec(d) == spec && (spec == Qt::UTC || spec == Qt::LocalTime)) |
4375 | return *this; |
4376 | |
4377 | if (!isValid()) { |
4378 | QDateTime ret = *this; |
4379 | ret.setTimeSpec(spec); |
4380 | return ret; |
4381 | } |
4382 | |
4383 | return fromMSecsSinceEpoch(toMSecsSinceEpoch(), spec, 0); |
4384 | } |
4385 | |
4386 | /*! |
4387 | \since 5.2 |
4388 | |
4389 | \fn QDateTime QDateTime::toOffsetFromUtc(int offsetSeconds) const |
4390 | |
4391 | Returns a copy of this datetime converted to a spec of Qt::OffsetFromUTC |
4392 | with the given \a offsetSeconds. |
4393 | |
4394 | If the \a offsetSeconds equals 0 then a UTC datetime will be returned |
4395 | |
4396 | \sa setOffsetFromUtc(), offsetFromUtc(), toTimeSpec() |
4397 | */ |
4398 | |
4399 | QDateTime QDateTime::toOffsetFromUtc(int offsetSeconds) const |
4400 | { |
4401 | if (getSpec(d) == Qt::OffsetFromUTC |
4402 | && d->m_offsetFromUtc == offsetSeconds) |
4403 | return *this; |
4404 | |
4405 | if (!isValid()) { |
4406 | QDateTime ret = *this; |
4407 | ret.setOffsetFromUtc(offsetSeconds); |
4408 | return ret; |
4409 | } |
4410 | |
4411 | return fromMSecsSinceEpoch(toMSecsSinceEpoch(), Qt::OffsetFromUTC, offsetSeconds); |
4412 | } |
4413 | |
4414 | #if QT_CONFIG(timezone) |
4415 | /*! |
4416 | \since 5.2 |
4417 | |
4418 | Returns a copy of this datetime converted to the given \a timeZone |
4419 | |
4420 | \sa timeZone(), toTimeSpec() |
4421 | */ |
4422 | |
4423 | QDateTime QDateTime::toTimeZone(const QTimeZone &timeZone) const |
4424 | { |
4425 | if (getSpec(d) == Qt::TimeZone && d->m_timeZone == timeZone) |
4426 | return *this; |
4427 | |
4428 | if (!isValid()) { |
4429 | QDateTime ret = *this; |
4430 | ret.setTimeZone(timeZone); |
4431 | return ret; |
4432 | } |
4433 | |
4434 | return fromMSecsSinceEpoch(toMSecsSinceEpoch(), timeZone); |
4435 | } |
4436 | #endif // timezone |
4437 | |
4438 | /*! |
4439 | Returns \c true if this datetime is equal to the \a other datetime; |
4440 | otherwise returns \c false. |
4441 | |
4442 | Since 5.14, all invalid datetimes are equal to one another and differ from |
4443 | all other datetimes. |
4444 | |
4445 | \sa operator!=() |
4446 | */ |
4447 | |
4448 | bool QDateTime::operator==(const QDateTime &other) const |
4449 | { |
4450 | if (!isValid()) |
4451 | return !other.isValid(); |
4452 | if (!other.isValid()) |
4453 | return false; |
4454 | |
4455 | if (usesSameOffset(d, other.d)) |
4456 | return getMSecs(d) == getMSecs(other.d); |
4457 | |
4458 | // Convert to UTC and compare |
4459 | return toMSecsSinceEpoch() == other.toMSecsSinceEpoch(); |
4460 | } |
4461 | |
4462 | /*! |
4463 | \fn bool QDateTime::operator!=(const QDateTime &other) const |
4464 | |
4465 | Returns \c true if this datetime is different from the \a other |
4466 | datetime; otherwise returns \c false. |
4467 | |
4468 | Two datetimes are different if either the date, the time, or the time zone |
4469 | components are different. Since 5.14, any invalid datetime is less than all |
4470 | valid datetimes. |
4471 | |
4472 | \sa operator==() |
4473 | */ |
4474 | |
4475 | /*! |
4476 | Returns \c true if this datetime is earlier than the \a other |
4477 | datetime; otherwise returns \c false. |
4478 | */ |
4479 | |
4480 | bool QDateTime::operator<(const QDateTime &other) const |
4481 | { |
4482 | if (!isValid()) |
4483 | return other.isValid(); |
4484 | if (!other.isValid()) |
4485 | return false; |
4486 | |
4487 | if (usesSameOffset(d, other.d)) |
4488 | return getMSecs(d) < getMSecs(other.d); |
4489 | |
4490 | // Convert to UTC and compare |
4491 | return toMSecsSinceEpoch() < other.toMSecsSinceEpoch(); |
4492 | } |
4493 | |
4494 | /*! |
4495 | \fn bool QDateTime::operator<=(const QDateTime &other) const |
4496 | |
4497 | Returns \c true if this datetime is earlier than or equal to the |
4498 | \a other datetime; otherwise returns \c false. |
4499 | */ |
4500 | |
4501 | /*! |
4502 | \fn bool QDateTime::operator>(const QDateTime &other) const |
4503 | |
4504 | Returns \c true if this datetime is later than the \a other datetime; |
4505 | otherwise returns \c false. |
4506 | */ |
4507 | |
4508 | /*! |
4509 | \fn bool QDateTime::operator>=(const QDateTime &other) const |
4510 | |
4511 | Returns \c true if this datetime is later than or equal to the |
4512 | \a other datetime; otherwise returns \c false. |
4513 | */ |
4514 | |
4515 | /*! |
4516 | \fn QDateTime QDateTime::currentDateTime() |
4517 | Returns the current datetime, as reported by the system clock, in |
4518 | the local time zone. |
4519 | |
4520 | \sa currentDateTimeUtc(), QDate::currentDate(), QTime::currentTime(), toTimeSpec() |
4521 | */ |
4522 | |
4523 | /*! |
4524 | \fn QDateTime QDateTime::currentDateTimeUtc() |
4525 | \since 4.7 |
4526 | Returns the current datetime, as reported by the system clock, in |
4527 | UTC. |
4528 | |
4529 | \sa currentDateTime(), QDate::currentDate(), QTime::currentTime(), toTimeSpec() |
4530 | */ |
4531 | |
4532 | /*! |
4533 | \fn qint64 QDateTime::currentMSecsSinceEpoch() |
4534 | \since 4.7 |
4535 | |
4536 | Returns the number of milliseconds since 1970-01-01T00:00:00 Universal |
4537 | Coordinated Time. This number is like the POSIX time_t variable, but |
4538 | expressed in milliseconds instead. |
4539 | |
4540 | \sa currentDateTime(), currentDateTimeUtc(), toTimeSpec() |
4541 | */ |
4542 | |
4543 | /*! |
4544 | \fn qint64 QDateTime::currentSecsSinceEpoch() |
4545 | \since 5.8 |
4546 | |
4547 | Returns the number of seconds since 1970-01-01T00:00:00 Universal |
4548 | Coordinated Time. |
4549 | |
4550 | \sa currentMSecsSinceEpoch() |
4551 | */ |
4552 | |
4553 | #if defined(Q_OS_WIN) |
4554 | static inline uint msecsFromDecomposed(int hour, int minute, int sec, int msec = 0) |
4555 | { |
4556 | return MSECS_PER_HOUR * hour + MSECS_PER_MIN * minute + 1000 * sec + msec; |
4557 | } |
4558 | |
4559 | QDate QDate::currentDate() |
4560 | { |
4561 | SYSTEMTIME st; |
4562 | memset(&st, 0, sizeof(SYSTEMTIME)); |
4563 | GetLocalTime(&st); |
4564 | return QDate(st.wYear, st.wMonth, st.wDay); |
4565 | } |
4566 | |
4567 | QTime QTime::currentTime() |
4568 | { |
4569 | QTime ct; |
4570 | SYSTEMTIME st; |
4571 | memset(&st, 0, sizeof(SYSTEMTIME)); |
4572 | GetLocalTime(&st); |
4573 | ct.setHMS(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); |
4574 | return ct; |
4575 | } |
4576 | |
4577 | QDateTime QDateTime::currentDateTime() |
4578 | { |
4579 | QTime t; |
4580 | SYSTEMTIME st; |
4581 | memset(&st, 0, sizeof(SYSTEMTIME)); |
4582 | GetLocalTime(&st); |
4583 | QDate d(st.wYear, st.wMonth, st.wDay); |
4584 | t.mds = msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); |
4585 | return QDateTime(d, t); |
4586 | } |
4587 | |
4588 | QDateTime QDateTime::currentDateTimeUtc() |
4589 | { |
4590 | QTime t; |
4591 | SYSTEMTIME st; |
4592 | memset(&st, 0, sizeof(SYSTEMTIME)); |
4593 | GetSystemTime(&st); |
4594 | QDate d(st.wYear, st.wMonth, st.wDay); |
4595 | t.mds = msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); |
4596 | return QDateTime(d, t, Qt::UTC); |
4597 | } |
4598 | |
4599 | qint64 QDateTime::currentMSecsSinceEpoch() noexcept |
4600 | { |
4601 | SYSTEMTIME st; |
4602 | memset(&st, 0, sizeof(SYSTEMTIME)); |
4603 | GetSystemTime(&st); |
4604 | const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay)); |
4605 | |
4606 | return msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds) + |
4607 | daysAfterEpoch * Q_INT64_C(86400000); |
4608 | } |
4609 | |
4610 | qint64 QDateTime::currentSecsSinceEpoch() noexcept |
4611 | { |
4612 | SYSTEMTIME st; |
4613 | memset(&st, 0, sizeof(SYSTEMTIME)); |
4614 | GetSystemTime(&st); |
4615 | const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay)); |
4616 | |
4617 | return st.wHour * SECS_PER_HOUR + st.wMinute * SECS_PER_MIN + st.wSecond + |
4618 | daysAfterEpoch * Q_INT64_C(86400); |
4619 | } |
4620 | |
4621 | #elif defined(Q_OS_UNIX) |
4622 | QDate QDate::currentDate() |
4623 | { |
4624 | return QDateTime::currentDateTime().date(); |
4625 | } |
4626 | |
4627 | QTime QTime::currentTime() |
4628 | { |
4629 | return QDateTime::currentDateTime().time(); |
4630 | } |
4631 | |
4632 | QDateTime QDateTime::currentDateTime() |
4633 | { |
4634 | return fromMSecsSinceEpoch(currentMSecsSinceEpoch(), Qt::LocalTime); |
4635 | } |
4636 | |
4637 | QDateTime QDateTime::currentDateTimeUtc() |
4638 | { |
4639 | return fromMSecsSinceEpoch(currentMSecsSinceEpoch(), Qt::UTC); |
4640 | } |
4641 | |
4642 | qint64 QDateTime::currentMSecsSinceEpoch() noexcept |
4643 | { |
4644 | // posix compliant system |
4645 | // we have milliseconds |
4646 | struct timeval tv; |
4647 | gettimeofday(&tv, nullptr); |
4648 | return qint64(tv.tv_sec) * Q_INT64_C(1000) + tv.tv_usec / 1000; |
4649 | } |
4650 | |
4651 | qint64 QDateTime::currentSecsSinceEpoch() noexcept |
4652 | { |
4653 | struct timeval tv; |
4654 | gettimeofday(&tv, nullptr); |
4655 | return qint64(tv.tv_sec); |
4656 | } |
4657 | #else |
4658 | #error "What system is this?" |
4659 | #endif |
4660 | |
4661 | /*! |
4662 | Returns a datetime whose date and time are the number of milliseconds \a msecs |
4663 | that have passed since 1970-01-01T00:00:00.000, Coordinated Universal |
4664 | Time (Qt::UTC) and converted to the given \a spec. |
4665 | |
4666 | Note that there are possible values for \a msecs that lie outside the valid |
4667 | range of QDateTime, both negative and positive. The behavior of this |
4668 | function is undefined for those values. |
4669 | |
4670 | If the \a spec is not Qt::OffsetFromUTC then the \a offsetSeconds will be |
4671 | ignored. If the \a spec is Qt::OffsetFromUTC and the \a offsetSeconds is 0 |
4672 | then the spec will be set to Qt::UTC, i.e. an offset of 0 seconds. |
4673 | |
4674 | If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime, |
4675 | i.e. the current system time zone. |
4676 | |
4677 | \sa toMSecsSinceEpoch(), setMSecsSinceEpoch() |
4678 | */ |
4679 | QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec, int offsetSeconds) |
4680 | { |
4681 | QDateTime dt; |
4682 | QT_PREPEND_NAMESPACE(setTimeSpec(dt.d, spec, offsetSeconds)); |
4683 | dt.setMSecsSinceEpoch(msecs); |
4684 | return dt; |
4685 | } |
4686 | |
4687 | /*! |
4688 | \since 5.8 |
4689 | |
4690 | Returns a datetime whose date and time are the number of seconds \a secs |
4691 | that have passed since 1970-01-01T00:00:00.000, Coordinated Universal |
4692 | Time (Qt::UTC) and converted to the given \a spec. |
4693 | |
4694 | Note that there are possible values for \a secs that lie outside the valid |
4695 | range of QDateTime, both negative and positive. The behavior of this |
4696 | function is undefined for those values. |
4697 | |
4698 | If the \a spec is not Qt::OffsetFromUTC then the \a offsetSeconds will be |
4699 | ignored. If the \a spec is Qt::OffsetFromUTC and the \a offsetSeconds is 0 |
4700 | then the spec will be set to Qt::UTC, i.e. an offset of 0 seconds. |
4701 | |
4702 | If \a spec is Qt::TimeZone then the spec will be set to Qt::LocalTime, |
4703 | i.e. the current system time zone. |
4704 | |
4705 | \sa toSecsSinceEpoch(), setSecsSinceEpoch() |
4706 | */ |
4707 | QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec, int offsetSeconds) |
4708 | { |
4709 | constexpr qint64 maxSeconds = std::numeric_limits<qint64>::max() / 1000; |
4710 | constexpr qint64 minSeconds = std::numeric_limits<qint64>::min() / 1000; |
4711 | if (secs > maxSeconds || secs < minSeconds) |
4712 | return QDateTime(); // Would {und,ov}erflow |
4713 | return fromMSecsSinceEpoch(secs * 1000, spec, offsetSeconds); |
4714 | } |
4715 | |
4716 | #if QT_CONFIG(timezone) |
4717 | /*! |
4718 | \since 5.2 |
4719 | |
4720 | Returns a datetime whose date and time are the number of milliseconds \a msecs |
4721 | that have passed since 1970-01-01T00:00:00.000, Coordinated Universal |
4722 | Time (Qt::UTC) and with the given \a timeZone. |
4723 | |
4724 | \sa fromSecsSinceEpoch() |
4725 | */ |
4726 | QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone) |
4727 | { |
4728 | QDateTime dt; |
4729 | dt.setTimeZone(timeZone); |
4730 | if (timeZone.isValid()) |
4731 | dt.setMSecsSinceEpoch(msecs); |
4732 | return dt; |
4733 | } |
4734 | |
4735 | /*! |
4736 | \since 5.8 |
4737 | |
4738 | Returns a datetime whose date and time are the number of seconds \a secs |
4739 | that have passed since 1970-01-01T00:00:00.000, Coordinated Universal |
4740 | Time (Qt::UTC) and with the given \a timeZone. |
4741 | |
4742 | \sa fromMSecsSinceEpoch() |
4743 | */ |
4744 | QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, const QTimeZone &timeZone) |
4745 | { |
4746 | constexpr qint64 maxSeconds = std::numeric_limits<qint64>::max() / 1000; |
4747 | constexpr qint64 minSeconds = std::numeric_limits<qint64>::min() / 1000; |
4748 | if (secs > maxSeconds || secs < minSeconds) |
4749 | return QDateTime(); // Would {und,ov}erflow |
4750 | return fromMSecsSinceEpoch(secs * 1000, timeZone); |
4751 | } |
4752 | #endif |
4753 | |
4754 | #if QT_CONFIG(datestring) // depends on, so implies, textdate |
4755 | |
4756 | /*! |
4757 | \fn QDateTime QDateTime::fromString(const QString &string, Qt::DateFormat format) |
4758 | |
4759 | Returns the QDateTime represented by the \a string, using the |
4760 | \a format given, or an invalid datetime if this is not possible. |
4761 | |
4762 | Note for Qt::TextDate: only English short month names (e.g. "Jan" in short |
4763 | form or "January" in long form) are recognized. |
4764 | |
4765 | \sa toString(), QLocale::toDateTime() |
4766 | */ |
4767 | |
4768 | /*! |
4769 | \overload |
4770 | \since 6.0 |
4771 | */ |
4772 | QDateTime QDateTime::fromString(QStringView string, Qt::DateFormat format) |
4773 | { |
4774 | if (string.isEmpty()) |
4775 | return QDateTime(); |
4776 | |
4777 | switch (format) { |
4778 | case Qt::RFC2822Date: { |
4779 | const ParsedRfcDateTime rfc = rfcDateImpl(string); |
4780 | |
4781 | if (!rfc.date.isValid() || !rfc.time.isValid()) |
4782 | return QDateTime(); |
4783 | |
4784 | QDateTime dateTime(rfc.date, rfc.time, Qt::UTC); |
4785 | dateTime.setOffsetFromUtc(rfc.utcOffset); |
4786 | return dateTime; |
4787 | } |
4788 | case Qt::ISODate: |
4789 | case Qt::ISODateWithMs: { |
4790 | const int size = string.size(); |
4791 | if (size < 10) |
4792 | return QDateTime(); |
4793 | |
4794 | QDate date = QDate::fromString(string.first(10), Qt::ISODate); |
4795 | if (!date.isValid()) |
4796 | return QDateTime(); |
4797 | if (size == 10) |
4798 | return date.startOfDay(); |
4799 | |
4800 | Qt::TimeSpec spec = Qt::LocalTime; |
4801 | QStringView isoString = string.sliced(10); // trim "yyyy-MM-dd" |
4802 | |
4803 | // Must be left with T (or space) and at least one digit for the hour: |
4804 | if (isoString.size() < 2 |
4805 | || !(isoString.startsWith(u'T', Qt::CaseInsensitive) |
4806 | // RFC 3339 (section 5.6) allows a space here. (It actually |
4807 | // allows any separator one considers more readable, merely |
4808 | // giving space as an example - but let's not go wild !) |
4809 | || isoString.startsWith(u' '))) { |
4810 | return QDateTime(); |
4811 | } |
4812 | isoString = isoString.sliced(1); // trim 'T' (or space) |
4813 | |
4814 | int offset = 0; |
4815 | // Check end of string for Time Zone definition, either Z for UTC or [+-]HH:mm for Offset |
4816 | if (isoString.endsWith(u'Z', Qt::CaseInsensitive)) { |
4817 | spec = Qt::UTC; |
4818 | isoString.chop(1); // trim 'Z' |
4819 | } else { |
4820 | // the loop below is faster but functionally equal to: |
4821 | // const int signIndex = isoString.indexOf(QRegulargExpression(QStringLiteral("[+-]"))); |
4822 | int signIndex = isoString.size() - 1; |
4823 | Q_ASSERT(signIndex >= 0); |
4824 | bool found = false; |
4825 | do { |
4826 | QChar character(isoString[signIndex]); |
4827 | found = character == u'+' || character == u'-'; |
4828 | } while (!found && --signIndex >= 0); |
4829 | |
4830 | if (found) { |
4831 | bool ok; |
4832 | offset = fromOffsetString(isoString.sliced(signIndex), &ok); |
4833 | if (!ok) |
4834 | return QDateTime(); |
4835 | isoString = isoString.first(signIndex); |
4836 | spec = Qt::OffsetFromUTC; |
4837 | } |
4838 | } |
4839 | |
4840 | // Might be end of day (24:00, including variants), which QTime considers invalid. |
4841 | // ISO 8601 (section 4.2.3) says that 24:00 is equivalent to 00:00 the next day. |
4842 | bool isMidnight24 = false; |
4843 | QTime time = fromIsoTimeString(isoString, format, &isMidnight24); |
4844 | if (!time.isValid()) |
4845 | return QDateTime(); |
4846 | if (isMidnight24) // time is 0:0, but we want the start of next day: |
4847 | return date.addDays(1).startOfDay(spec, offset); |
4848 | return QDateTime(date, time, spec, offset); |
4849 | } |
4850 | case Qt::TextDate: { |
4851 | QList<QStringView> parts = string.split(u' ', Qt::SkipEmptyParts); |
4852 | |
4853 | if ((parts.count() < 5) || (parts.count() > 6)) |
4854 | return QDateTime(); |
4855 | |
4856 | // Accept "Sun Dec 1 13:02:00 1974" and "Sun 1. Dec 13:02:00 1974" |
4857 | |
4858 | // Year and time can be in either order. |
4859 | // Guess which by looking for ':' in the time |
4860 | int yearPart = 3; |
4861 | int timePart = 3; |
4862 | if (parts.at(3).contains(u':')) |
4863 | yearPart = 4; |
4864 | else if (parts.at(4).contains(u':')) |
4865 | timePart = 4; |
4866 | else |
4867 | return QDateTime(); |
4868 | |
4869 | int month = 0; |
4870 | int day = 0; |
4871 | bool ok = false; |
4872 | |
4873 | int year = parts.at(yearPart).toInt(&ok); |
4874 | if (!ok || year == 0) |
4875 | return QDateTime(); |
4876 | |
4877 | // Next try month then day |
4878 | month = fromShortMonthName(parts.at(1)); |
4879 | if (month) |
4880 | day = parts.at(2).toInt(&ok); |
4881 | |
4882 | // If failed, try day then month |
4883 | if (!ok || !month || !day) { |
4884 | month = fromShortMonthName(parts.at(2)); |
4885 | if (month) { |
4886 | QStringView dayPart = parts.at(1); |
4887 | if (dayPart.endsWith(u'.')) |
4888 | day = dayPart.chopped(1).toInt(&ok); |
4889 | } |
4890 | } |
4891 | |
4892 | // If both failed, give up |
4893 | if (!ok || !month || !day) |
4894 | return QDateTime(); |
4895 | |
4896 | const QDate date(year, month, day); |
4897 | if (!date.isValid()) |
4898 | return QDateTime(); |
4899 | |
4900 | const QTime time = fromIsoTimeString(parts.at(timePart), format, nullptr); |
4901 | if (!time.isValid()) |
4902 | return QDateTime(); |
4903 | |
4904 | if (parts.count() == 5) |
4905 | return QDateTime(date, time, Qt::LocalTime); |
4906 | |
4907 | QStringView tz = parts.at(5); |
4908 | if (!tz.startsWith(QLatin1String("GMT" ), Qt::CaseInsensitive)) |
4909 | return QDateTime(); |
4910 | tz = tz.sliced(3); |
4911 | if (!tz.isEmpty()) { |
4912 | int offset = fromOffsetString(tz, &ok); |
4913 | if (!ok) |
4914 | return QDateTime(); |
4915 | return QDateTime(date, time, Qt::OffsetFromUTC, offset); |
4916 | } else { |
4917 | return QDateTime(date, time, Qt::UTC); |
4918 | } |
4919 | } |
4920 | } |
4921 | |
4922 | return QDateTime(); |
4923 | } |
4924 | |
4925 | /*! |
4926 | \fn QDateTime QDateTime::fromString(const QString &string, const QString &format, QCalendar cal) |
4927 | |
4928 | Returns the QDateTime represented by the \a string, using the \a |
4929 | format given, or an invalid datetime if the string cannot be parsed. |
4930 | |
4931 | Uses the calendar \a cal if supplied, else Gregorian. |
4932 | |
4933 | In addition to the expressions, recognized in the format string to represent |
4934 | parts of the date and time, by QDate::fromString() and QTime::fromString(), |
4935 | this method supports: |
4936 | |
4937 | \table |
4938 | \header \li Expression \li Output |
4939 | \row \li t \li the timezone (for example "CEST") |
4940 | \endtable |
4941 | |
4942 | If no 't' format specifier is present, the system's local time-zone is used. |
4943 | For the defaults of all other fields, see QDate::fromString() and QTime::fromString(). |
4944 | |
4945 | For example: |
4946 | |
4947 | \snippet code/src_corelib_time_qdatetime.cpp 14 |
4948 | |
4949 | All other input characters will be treated as text. Any non-empty sequence |
4950 | of characters enclosed in single quotes will also be treated (stripped of |
4951 | the quotes) as text and not be interpreted as expressions. |
4952 | |
4953 | \snippet code/src_corelib_time_qdatetime.cpp 12 |
4954 | |
4955 | If the format is not satisfied, an invalid QDateTime is returned. |
4956 | The expressions that don't have leading zeroes (d, M, h, m, s, z) will be |
4957 | greedy. This means that they will use two digits (or three, for z) even if this will |
4958 | put them outside the range and/or leave too few digits for other |
4959 | sections. |
4960 | |
4961 | \snippet code/src_corelib_time_qdatetime.cpp 13 |
4962 | |
4963 | This could have meant 1 January 00:30.00 but the M will grab |
4964 | two digits. |
4965 | |
4966 | Incorrectly specified fields of the \a string will cause an invalid |
4967 | QDateTime to be returned. For example, consider the following code, |
4968 | where the two digit year 12 is read as 1912 (see the table below for all |
4969 | field defaults); the resulting datetime is invalid because 23 April 1912 |
4970 | was a Tuesday, not a Monday: |
4971 | |
4972 | \snippet code/src_corelib_time_qdatetime.cpp 20 |
4973 | |
4974 | The correct code is: |
4975 | |
4976 | \snippet code/src_corelib_time_qdatetime.cpp 21 |
4977 | |
4978 | \note Day and month names as well as AM/PM indication must be given in English (C locale). |
4979 | If localized month and day names and localized forms of AM/PM are used, use |
4980 | QLocale::system().toDateTime(). |
4981 | |
4982 | \sa toString(), QDate::fromString(), QTime::fromString(), |
4983 | QLocale::toDateTime() |
4984 | */ |
4985 | |
4986 | /*! |
4987 | \fn QDateTime QDateTime::fromString(QStringView string, QStringView format, QCalendar cal) |
4988 | \overload |
4989 | \since 6.0 |
4990 | */ |
4991 | |
4992 | /*! |
4993 | \overload |
4994 | \since 6.0 |
4995 | */ |
4996 | QDateTime QDateTime::fromString(const QString &string, QStringView format, QCalendar cal) |
4997 | { |
4998 | #if QT_CONFIG(datetimeparser) |
4999 | QDateTime datetime; |
5000 | |
5001 | QDateTimeParser dt(QMetaType::QDateTime, QDateTimeParser::FromString, cal); |
5002 | dt.setDefaultLocale(QLocale::c()); |
5003 | if (dt.parseFormat(format) && dt.fromString(string, &datetime)) |
5004 | return datetime; |
5005 | #else |
5006 | Q_UNUSED(string); |
5007 | Q_UNUSED(format); |
5008 | Q_UNUSED(cal); |
5009 | #endif |
5010 | return QDateTime(); |
5011 | } |
5012 | |
5013 | #endif // datestring |
5014 | /*! |
5015 | \fn QDateTime QDateTime::toLocalTime() const |
5016 | |
5017 | Returns a datetime containing the date and time information in |
5018 | this datetime, but specified using the Qt::LocalTime definition. |
5019 | |
5020 | Example: |
5021 | |
5022 | \snippet code/src_corelib_time_qdatetime.cpp 17 |
5023 | |
5024 | \sa toTimeSpec() |
5025 | */ |
5026 | |
5027 | /*! |
5028 | \fn QDateTime QDateTime::toUTC() const |
5029 | |
5030 | Returns a datetime containing the date and time information in |
5031 | this datetime, but specified using the Qt::UTC definition. |
5032 | |
5033 | Example: |
5034 | |
5035 | \snippet code/src_corelib_time_qdatetime.cpp 18 |
5036 | |
5037 | \sa toTimeSpec() |
5038 | */ |
5039 | |
5040 | /***************************************************************************** |
5041 | Date/time stream functions |
5042 | *****************************************************************************/ |
5043 | |
5044 | #ifndef QT_NO_DATASTREAM |
5045 | /*! |
5046 | \relates QDate |
5047 | |
5048 | Writes the \a date to stream \a out. |
5049 | |
5050 | \sa {Serializing Qt Data Types} |
5051 | */ |
5052 | |
5053 | QDataStream &operator<<(QDataStream &out, QDate date) |
5054 | { |
5055 | if (out.version() < QDataStream::Qt_5_0) |
5056 | return out << quint32(date.jd); |
5057 | else |
5058 | return out << qint64(date.jd); |
5059 | } |
5060 | |
5061 | /*! |
5062 | \relates QDate |
5063 | |
5064 | Reads a date from stream \a in into the \a date. |
5065 | |
5066 | \sa {Serializing Qt Data Types} |
5067 | */ |
5068 | |
5069 | QDataStream &operator>>(QDataStream &in, QDate &date) |
5070 | { |
5071 | if (in.version() < QDataStream::Qt_5_0) { |
5072 | quint32 jd; |
5073 | in >> jd; |
5074 | // Older versions consider 0 an invalid jd. |
5075 | date.jd = (jd != 0 ? jd : QDate::nullJd()); |
5076 | } else { |
5077 | qint64 jd; |
5078 | in >> jd; |
5079 | date.jd = jd; |
5080 | } |
5081 | |
5082 | return in; |
5083 | } |
5084 | |
5085 | /*! |
5086 | \relates QTime |
5087 | |
5088 | Writes \a time to stream \a out. |
5089 | |
5090 | \sa {Serializing Qt Data Types} |
5091 | */ |
5092 | |
5093 | QDataStream &operator<<(QDataStream &out, QTime time) |
5094 | { |
5095 | if (out.version() >= QDataStream::Qt_4_0) { |
5096 | return out << quint32(time.mds); |
5097 | } else { |
5098 | // Qt3 had no support for reading -1, QTime() was valid and serialized as 0 |
5099 | return out << quint32(time.isNull() ? 0 : time.mds); |
5100 | } |
5101 | } |
5102 | |
5103 | /*! |
5104 | \relates QTime |
5105 | |
5106 | Reads a time from stream \a in into the given \a time. |
5107 | |
5108 | \sa {Serializing Qt Data Types} |
5109 | */ |
5110 | |
5111 | QDataStream &operator>>(QDataStream &in, QTime &time) |
5112 | { |
5113 | quint32 ds; |
5114 | in >> ds; |
5115 | if (in.version() >= QDataStream::Qt_4_0) { |
5116 | time.mds = int(ds); |
5117 | } else { |
5118 | // Qt3 would write 0 for a null time |
5119 | time.mds = (ds == 0) ? QTime::NullTime : int(ds); |
5120 | } |
5121 | return in; |
5122 | } |
5123 | |
5124 | /*! |
5125 | \relates QDateTime |
5126 | |
5127 | Writes \a dateTime to the \a out stream. |
5128 | |
5129 | \sa {Serializing Qt Data Types} |
5130 | */ |
5131 | QDataStream &operator<<(QDataStream &out, const QDateTime &dateTime) |
5132 | { |
5133 | QPair<QDate, QTime> dateAndTime; |
5134 | |
5135 | if (out.version() >= QDataStream::Qt_5_2) { |
5136 | |
5137 | // In 5.2 we switched to using Qt::TimeSpec and added offset support |
5138 | dateAndTime = getDateTime(dateTime.d); |
5139 | out << dateAndTime << qint8(dateTime.timeSpec()); |
5140 | if (dateTime.timeSpec() == Qt::OffsetFromUTC) |
5141 | out << qint32(dateTime.offsetFromUtc()); |
5142 | #if QT_CONFIG(timezone) |
5143 | else if (dateTime.timeSpec() == Qt::TimeZone) |
5144 | out << dateTime.timeZone(); |
5145 | #endif // timezone |
5146 | |
5147 | } else if (out.version() == QDataStream::Qt_5_0) { |
5148 | |
5149 | // In Qt 5.0 we incorrectly serialised all datetimes as UTC. |
5150 | // This approach is wrong and should not be used again; it breaks |
5151 | // the guarantee that a deserialised local datetime is the same time |
5152 | // of day, regardless of which timezone it was serialised in. |
5153 | dateAndTime = getDateTime((dateTime.isValid() ? dateTime.toUTC() : dateTime).d); |
5154 | out << dateAndTime << qint8(dateTime.timeSpec()); |
5155 | |
5156 | } else if (out.version() >= QDataStream::Qt_4_0) { |
5157 | |
5158 | // From 4.0 to 5.1 (except 5.0) we used QDateTimePrivate::Spec |
5159 | dateAndTime = getDateTime(dateTime.d); |
5160 | out << dateAndTime; |
5161 | switch (dateTime.timeSpec()) { |
5162 | case Qt::UTC: |
5163 | out << (qint8)QDateTimePrivate::UTC; |
5164 | break; |
5165 | case Qt::OffsetFromUTC: |
5166 | out << (qint8)QDateTimePrivate::OffsetFromUTC; |
5167 | break; |
5168 | case Qt::TimeZone: |
5169 | out << (qint8)QDateTimePrivate::TimeZone; |
5170 | break; |
5171 | case Qt::LocalTime: |
5172 | out << (qint8)QDateTimePrivate::LocalUnknown; |
5173 | break; |
5174 | } |
5175 | |
5176 | } else { // version < QDataStream::Qt_4_0 |
5177 | |
5178 | // Before 4.0 there was no TimeSpec, only Qt::LocalTime was supported |
5179 | dateAndTime = getDateTime(dateTime.d); |
5180 | out << dateAndTime; |
5181 | |
5182 | } |
5183 | |
5184 | return out; |
5185 | } |
5186 | |
5187 | /*! |
5188 | \relates QDateTime |
5189 | |
5190 | Reads a datetime from the stream \a in into \a dateTime. |
5191 | |
5192 | \sa {Serializing Qt Data Types} |
5193 | */ |
5194 | |
5195 | QDataStream &operator>>(QDataStream &in, QDateTime &dateTime) |
5196 | { |
5197 | QDate dt; |
5198 | QTime tm; |
5199 | qint8 ts = 0; |
5200 | Qt::TimeSpec spec = Qt::LocalTime; |
5201 | qint32 offset = 0; |
5202 | #if QT_CONFIG(timezone) |
5203 | QTimeZone tz; |
5204 | #endif // timezone |
5205 | |
5206 | if (in.version() >= QDataStream::Qt_5_2) { |
5207 | |
5208 | // In 5.2 we switched to using Qt::TimeSpec and added offset support |
5209 | in >> dt >> tm >> ts; |
5210 | spec = static_cast<Qt::TimeSpec>(ts); |
5211 | if (spec == Qt::OffsetFromUTC) { |
5212 | in >> offset; |
5213 | dateTime = QDateTime(dt, tm, spec, offset); |
5214 | #if QT_CONFIG(timezone) |
5215 | } else if (spec == Qt::TimeZone) { |
5216 | in >> tz; |
5217 | dateTime = QDateTime(dt, tm, tz); |
5218 | #endif // timezone |
5219 | } else { |
5220 | dateTime = QDateTime(dt, tm, spec); |
5221 | } |
5222 | |
5223 | } else if (in.version() == QDataStream::Qt_5_0) { |
5224 | |
5225 | // In Qt 5.0 we incorrectly serialised all datetimes as UTC |
5226 | in >> dt >> tm >> ts; |
5227 | spec = static_cast<Qt::TimeSpec>(ts); |
5228 | dateTime = QDateTime(dt, tm, Qt::UTC); |
5229 | dateTime = dateTime.toTimeSpec(spec); |
5230 | |
5231 | } else if (in.version() >= QDataStream::Qt_4_0) { |
5232 | |
5233 | // From 4.0 to 5.1 (except 5.0) we used QDateTimePrivate::Spec |
5234 | in >> dt >> tm >> ts; |
5235 | switch ((QDateTimePrivate::Spec)ts) { |
5236 | case QDateTimePrivate::UTC: |
5237 | spec = Qt::UTC; |
5238 | break; |
5239 | case QDateTimePrivate::OffsetFromUTC: |
5240 | spec = Qt::OffsetFromUTC; |
5241 | break; |
5242 | case QDateTimePrivate::TimeZone: |
5243 | spec = Qt::TimeZone; |
5244 | #if QT_CONFIG(timezone) |
5245 | // FIXME: need to use a different constructor ! |
5246 | #endif |
5247 | break; |
5248 | case QDateTimePrivate::LocalUnknown: |
5249 | case QDateTimePrivate::LocalStandard: |
5250 | case QDateTimePrivate::LocalDST: |
5251 | spec = Qt::LocalTime; |
5252 | break; |
5253 | } |
5254 | dateTime = QDateTime(dt, tm, spec, offset); |
5255 | |
5256 | } else { // version < QDataStream::Qt_4_0 |
5257 | |
5258 | // Before 4.0 there was no TimeSpec, only Qt::LocalTime was supported |
5259 | in >> dt >> tm; |
5260 | dateTime = QDateTime(dt, tm, spec, offset); |
5261 | |
5262 | } |
5263 | |
5264 | return in; |
5265 | } |
5266 | #endif // QT_NO_DATASTREAM |
5267 | |
5268 | /***************************************************************************** |
5269 | Date / Time Debug Streams |
5270 | *****************************************************************************/ |
5271 | |
5272 | #if !defined(QT_NO_DEBUG_STREAM) && QT_CONFIG(datestring) |
5273 | QDebug operator<<(QDebug dbg, QDate date) |
5274 | { |
5275 | QDebugStateSaver saver(dbg); |
5276 | dbg.nospace() << "QDate(" ; |
5277 | if (date.isValid()) |
5278 | dbg.nospace() << date.toString(Qt::ISODate); |
5279 | else |
5280 | dbg.nospace() << "Invalid" ; |
5281 | dbg.nospace() << ')'; |
5282 | return dbg; |
5283 | } |
5284 | |
5285 | QDebug operator<<(QDebug dbg, QTime time) |
5286 | { |
5287 | QDebugStateSaver saver(dbg); |
5288 | dbg.nospace() << "QTime(" ; |
5289 | if (time.isValid()) |
5290 | dbg.nospace() << time.toString(u"HH:mm:ss.zzz" ); |
5291 | else |
5292 | dbg.nospace() << "Invalid" ; |
5293 | dbg.nospace() << ')'; |
5294 | return dbg; |
5295 | } |
5296 | |
5297 | QDebug operator<<(QDebug dbg, const QDateTime &date) |
5298 | { |
5299 | QDebugStateSaver saver(dbg); |
5300 | dbg.nospace() << "QDateTime(" ; |
5301 | if (date.isValid()) { |
5302 | const Qt::TimeSpec ts = date.timeSpec(); |
5303 | dbg.noquote() << date.toString(u"yyyy-MM-dd HH:mm:ss.zzz t" ) |
5304 | << ' ' << ts; |
5305 | switch (ts) { |
5306 | case Qt::UTC: |
5307 | break; |
5308 | case Qt::OffsetFromUTC: |
5309 | dbg.space() << date.offsetFromUtc() << 's'; |
5310 | break; |
5311 | case Qt::TimeZone: |
5312 | #if QT_CONFIG(timezone) |
5313 | dbg.space() << date.timeZone().id(); |
5314 | #endif // timezone |
5315 | break; |
5316 | case Qt::LocalTime: |
5317 | break; |
5318 | } |
5319 | } else { |
5320 | dbg.nospace() << "Invalid" ; |
5321 | } |
5322 | return dbg.nospace() << ')'; |
5323 | } |
5324 | #endif // debug_stream && datestring |
5325 | |
5326 | /*! \fn size_t qHash(const QDateTime &key, size_t seed = 0) |
5327 | \relates QHash |
5328 | \since 5.0 |
5329 | |
5330 | Returns the hash value for the \a key, using \a seed to seed the calculation. |
5331 | */ |
5332 | size_t qHash(const QDateTime &key, size_t seed) |
5333 | { |
5334 | // Use to toMSecsSinceEpoch instead of individual qHash functions for |
5335 | // QDate/QTime/spec/offset because QDateTime::operator== converts both arguments |
5336 | // to the same timezone. If we don't, qHash would return different hashes for |
5337 | // two QDateTimes that are equivalent once converted to the same timezone. |
5338 | return key.isValid() ? qHash(key.toMSecsSinceEpoch(), seed) : seed; |
5339 | } |
5340 | |
5341 | /*! \fn size_t qHash(QDate key, size_t seed = 0) |
5342 | \relates QHash |
5343 | \since 5.0 |
5344 | |
5345 | Returns the hash value for the \a key, using \a seed to seed the calculation. |
5346 | */ |
5347 | size_t qHash(QDate key, size_t seed) noexcept |
5348 | { |
5349 | return qHash(key.toJulianDay(), seed); |
5350 | } |
5351 | |
5352 | /*! \fn size_t qHash(QTime key, size_t seed = 0) |
5353 | \relates QHash |
5354 | \since 5.0 |
5355 | |
5356 | Returns the hash value for the \a key, using \a seed to seed the calculation. |
5357 | */ |
5358 | size_t qHash(QTime key, size_t seed) noexcept |
5359 | { |
5360 | return qHash(key.msecsSinceStartOfDay(), seed); |
5361 | } |
5362 | |
5363 | QT_END_NAMESPACE |
5364 | |