1/****************************************************************************
2**
3** Copyright (C) 2021 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 test suite of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtTest/QtTest>
31#include <time.h>
32#include <qdatetime.h>
33#include <private/qdatetime_p.h>
34#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
35# include <locale.h>
36#endif
37
38#ifdef Q_OS_WIN
39# include <qt_windows.h>
40#endif
41
42class tst_QDateTime : public QObject
43{
44 Q_OBJECT
45
46public:
47 tst_QDateTime();
48
49 static QString str( int y, int month, int d, int h, int min, int s );
50 static QDateTime dt( const QString& str );
51public slots:
52 void initTestCase();
53 void init();
54private slots:
55 void ctor();
56 void operator_eq();
57 void isNull();
58 void isValid();
59 void date();
60 void time();
61 void timeSpec();
62 void toSecsSinceEpoch_data();
63 void toSecsSinceEpoch();
64#if QT_DEPRECATED_SINCE(5, 8)
65 void toTime_t_data();
66 void toTime_t();
67#endif
68 void daylightSavingsTimeChange_data();
69 void daylightSavingsTimeChange();
70 void springForward_data();
71 void springForward();
72 void setDate();
73 void setTime_data();
74 void setTime();
75 void setTimeSpec_data();
76 void setTimeSpec();
77 void setSecsSinceEpoch();
78 void setMSecsSinceEpoch_data();
79 void setMSecsSinceEpoch();
80 void fromMSecsSinceEpoch_data();
81 void fromMSecsSinceEpoch();
82 void toString_isoDate_data();
83 void toString_isoDate();
84 void toString_isoDate_extra();
85#if QT_CONFIG(datestring)
86 void toString_textDate_data();
87 void toString_textDate();
88 void toString_textDate_extra();
89#endif
90 void toString_rfcDate_data();
91 void toString_rfcDate();
92 void toString_enumformat();
93 void toString_strformat();
94 void addDays();
95 void addMonths();
96 void addMonths_data();
97 void addYears();
98 void addYears_data();
99 void addSecs_data();
100 void addSecs();
101 void addMSecs_data();
102 void addMSecs();
103 void toTimeSpec_data();
104 void toTimeSpec();
105 void toLocalTime_data();
106 void toLocalTime();
107 void toUTC_data();
108 void toUTC();
109 void daysTo();
110 void secsTo_data();
111 void secsTo();
112 void msecsTo_data();
113 void msecsTo();
114 void operator_eqeq_data();
115 void operator_eqeq();
116 void operator_insert_extract_data();
117 void operator_insert_extract();
118 void currentDateTime();
119 void currentDateTimeUtc();
120 void currentDateTimeUtc2();
121 void fromStringDateFormat_data();
122 void fromStringDateFormat();
123 void fromStringStringFormat_data();
124 void fromStringStringFormat();
125 void fromStringStringFormat_localTimeZone_data();
126 void fromStringStringFormat_localTimeZone();
127#if defined(Q_OS_WIN) && QT_CONFIG(textdate)
128 void fromString_LOCALE_ILDATE();
129#endif
130#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
131 void fromStringToStringLocale_data();
132 void fromStringToStringLocale();
133#endif // ### Qt 6: remove
134
135 void offsetFromUtc();
136 void setOffsetFromUtc();
137 void toOffsetFromUtc();
138
139 void zoneAtTime_data();
140 void zoneAtTime();
141 void timeZoneAbbreviation();
142
143 void getDate();
144
145 void fewDigitsInYear() const;
146 void printNegativeYear() const;
147#if QT_CONFIG(textdate)
148 void roundtripTextDate() const;
149#endif
150 void utcOffsetLessThan() const;
151
152 void isDaylightTime() const;
153 void daylightTransitions() const;
154 void timeZones() const;
155 void systemTimeZoneChange_data() const;
156 void systemTimeZoneChange() const;
157
158 void invalid_data() const;
159 void invalid() const;
160 void range() const;
161
162 void macTypes();
163
164private:
165 enum { LocalTimeIsUtc = 0, LocalTimeAheadOfUtc = 1, LocalTimeBehindUtc = -1} localTimeType;
166 bool zoneIsCET;
167 QDate defDate() const { return QDate(1900, 1, 1); }
168 QTime defTime() const { return QTime(0, 0, 0); }
169 QDateTime defDateTime() const { return QDateTime(defDate(), defTime()); }
170 QDateTime invalidDateTime() const { return QDateTime(invalidDate(), invalidTime()); }
171 QDate invalidDate() const { return QDate(); }
172 QTime invalidTime() const { return QTime(-1, -1, -1); }
173
174 class TimeZoneRollback
175 {
176 const QByteArray prior;
177 public:
178 // Save the previous timezone so we can restore it afterwards, otherwise
179 // later tests may break:
180 explicit TimeZoneRollback(const QByteArray &zone) : prior(qgetenv(varName: "TZ"))
181 { reset(zone); }
182 void reset(const QByteArray &zone)
183 {
184 qputenv(varName: "TZ", value: zone.constData());
185 qTzSet();
186 }
187 ~TimeZoneRollback()
188 {
189 if (prior.isNull())
190 qunsetenv(varName: "TZ");
191 else
192 qputenv(varName: "TZ", value: prior.constData());
193 qTzSet();
194 }
195 };
196};
197
198Q_DECLARE_METATYPE(Qt::TimeSpec)
199Q_DECLARE_METATYPE(Qt::DateFormat)
200
201tst_QDateTime::tst_QDateTime()
202{
203#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
204 // Some tests depend on C locale - BF&I it with belt *and* braces:
205 qputenv(varName: "LC_ALL", value: "C");
206 setlocale(LC_ALL, locale: "C");
207 // Need to do this as early as possible, before anything accesses the
208 // QSystemLocale singleton; once it exists, there's no changing it.
209#endif // remove for ### Qt 6
210
211 /*
212 Due to some jurisdictions changing their zones and rules, it's possible
213 for a non-CET zone to accidentally match CET at a few tested moments but
214 be different a few years later or earlier. This would lead to tests
215 failing if run in the partially-aliasing zone (e.g. Algeria, Lybia). So
216 test thoroughly; ideally at every mid-winter or mid-summer in whose
217 half-year any test below assumes zoneIsCET means what it says. (Tests at
218 or near a DST transition implicate both of the half-years that meet
219 there.) Years outside the 1970--2038 range, however, are likely not
220 properly handled by the TZ-database; and QDateTime explicitly handles them
221 differently, so don't probe them here.
222 */
223 const uint day = 24 * 3600; // in seconds
224 zoneIsCET = (QDateTime(QDate(2038, 1, 19), QTime(4, 14, 7)).toSecsSinceEpoch() == 0x7fffffff
225 // Entries a year apart robustly differ by multiples of day.
226 && QDateTime(QDate(2015, 7, 1), QTime()).toSecsSinceEpoch() == 1435701600
227 && QDateTime(QDate(2015, 1, 1), QTime()).toSecsSinceEpoch() == 1420066800
228 && QDateTime(QDate(2013, 7, 1), QTime()).toSecsSinceEpoch() == 1372629600
229 && QDateTime(QDate(2013, 1, 1), QTime()).toSecsSinceEpoch() == 1356994800
230 && QDateTime(QDate(2012, 7, 1), QTime()).toSecsSinceEpoch() == 1341093600
231 && QDateTime(QDate(2012, 1, 1), QTime()).toSecsSinceEpoch() == 1325372400
232 && QDateTime(QDate(2008, 7, 1), QTime()).toSecsSinceEpoch() == 1214863200
233 && QDateTime(QDate(2004, 1, 1), QTime()).toSecsSinceEpoch() == 1072911600
234 && QDateTime(QDate(2000, 1, 1), QTime()).toSecsSinceEpoch() == 946681200
235 && QDateTime(QDate(1990, 7, 1), QTime()).toSecsSinceEpoch() == 646783200
236 && QDateTime(QDate(1990, 1, 1), QTime()).toSecsSinceEpoch() == 631148400
237 && QDateTime(QDate(1979, 1, 1), QTime()).toSecsSinceEpoch() == 283993200
238 // .toSecsSinceEpoch() returns -1 for everything before this:
239 && QDateTime(QDate(1970, 1, 1), QTime(1, 0, 0)).toSecsSinceEpoch() == 0);
240 // Use .toMSecsSinceEpoch() if you really need to test anything earlier.
241
242 /*
243 Again, rule changes can cause a TZ to look like UTC at some sample dates
244 but deviate at some date relevant to a test using localTimeType. These
245 tests mostly use years outside the 1970--2038 range for which TZ data is
246 credible, so we can't helpfully be exhaustive. So scan a sample of years'
247 starts and middles.
248 */
249 const int sampled = 3;
250 // UTC starts of months in 2004, 2038 and 1970:
251 qint64 jans[sampled] = { 12418 * day, 24837 * day, 0 };
252 qint64 juls[sampled] = { 12600 * day, 25018 * day, 181 * day };
253 localTimeType = LocalTimeIsUtc;
254 for (int i = sampled; i-- > 0; ) {
255 QDateTime jan = QDateTime::fromSecsSinceEpoch(secs: jans[i]);
256 QDateTime jul = QDateTime::fromSecsSinceEpoch(secs: juls[i]);
257 if (jan.date().year() < 1970 || jul.date().month() < 7) {
258 localTimeType = LocalTimeBehindUtc;
259 break;
260 } else if (jan.time().hour() > 0 || jul.time().hour() > 0
261 || jan.date().day() > 1 || jul.date().day() > 1) {
262 localTimeType = LocalTimeAheadOfUtc;
263 break;
264 }
265 }
266 /*
267 Even so, TZ=Africa/Algiers will fail fromMSecsSinceEpoch(-1) because it
268 switched from WET without DST (i.e. UTC) in the late 1960s to WET with DST
269 for all of 1970 - so they had a DST transition *on the epoch*. They've
270 since switched to CET with no DST, making life simple; but our tests for
271 mistakes around the epoch can't tell the difference between what Algeria
272 really did and the symptoms we can believe a bug might produce: there's
273 not much we can do about that, that wouldn't hide real bugs.
274 */
275}
276
277void tst_QDateTime::initTestCase()
278{
279 // Never construct a message like this in an i18n context...
280 const char *typemsg1 = "exactly";
281 const char *typemsg2 = "and therefore not";
282 switch (localTimeType) {
283 case LocalTimeIsUtc:
284 break;
285 case LocalTimeBehindUtc:
286 typemsg1 = "behind";
287 break;
288 case LocalTimeAheadOfUtc:
289 typemsg1 = "ahead of";
290 typemsg2 = zoneIsCET ? "and is" : "but isn't";
291 break;
292 }
293
294 qDebug() << "Current local time detected to be"
295 << typemsg1
296 << "UTC"
297 << typemsg2
298 << "the Central European timezone";
299}
300
301void tst_QDateTime::init()
302{
303#if defined(Q_OS_WIN32) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
304 SetThreadLocale(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT));
305#endif // ### Qt 6: remove
306}
307
308QString tst_QDateTime::str( int y, int month, int d, int h, int min, int s )
309{
310 return QDateTime( QDate(y, month, d), QTime(h, min, s) ).toString( format: Qt::ISODate );
311}
312
313QDateTime tst_QDateTime::dt( const QString& str )
314{
315 if ( str == "INVALID" ) {
316 return QDateTime();
317 } else {
318 return QDateTime::fromString( s: str, f: Qt::ISODate );
319 }
320}
321
322void tst_QDateTime::ctor()
323{
324 QDateTime dt1(QDate(2004, 1, 2), QTime(1, 2, 3));
325 QCOMPARE(dt1.timeSpec(), Qt::LocalTime);
326 QDateTime dt2(QDate(2004, 1, 2), QTime(1, 2, 3), Qt::LocalTime);
327 QCOMPARE(dt2.timeSpec(), Qt::LocalTime);
328 QDateTime dt3(QDate(2004, 1, 2), QTime(1, 2, 3), Qt::UTC);
329 QCOMPARE(dt3.timeSpec(), Qt::UTC);
330
331 QVERIFY(dt1 == dt2);
332 if (zoneIsCET) {
333 QVERIFY(dt1 != dt3);
334 QVERIFY(dt1 < dt3);
335 QVERIFY(dt1.addSecs(3600).toUTC() == dt3);
336 }
337
338 // Test OffsetFromUTC constructors
339 QDate offsetDate(2013, 1, 1);
340 QTime offsetTime(1, 2, 3);
341
342 QDateTime offset1(offsetDate, offsetTime, Qt::OffsetFromUTC);
343 QCOMPARE(offset1.timeSpec(), Qt::UTC);
344 QCOMPARE(offset1.offsetFromUtc(), 0);
345 QCOMPARE(offset1.date(), offsetDate);
346 QCOMPARE(offset1.time(), offsetTime);
347
348 QDateTime offset2(offsetDate, offsetTime, Qt::OffsetFromUTC, 0);
349 QCOMPARE(offset2.timeSpec(), Qt::UTC);
350 QCOMPARE(offset2.offsetFromUtc(), 0);
351 QCOMPARE(offset2.date(), offsetDate);
352 QCOMPARE(offset2.time(), offsetTime);
353
354 QDateTime offset3(offsetDate, offsetTime, Qt::OffsetFromUTC, 60 * 60);
355 QCOMPARE(offset3.timeSpec(), Qt::OffsetFromUTC);
356 QCOMPARE(offset3.offsetFromUtc(), 60 * 60);
357 QCOMPARE(offset3.date(), offsetDate);
358 QCOMPARE(offset3.time(), offsetTime);
359
360 QDateTime offset4(offsetDate, QTime(), Qt::OffsetFromUTC, 60 * 60);
361 QCOMPARE(offset4.timeSpec(), Qt::OffsetFromUTC);
362 QCOMPARE(offset4.offsetFromUtc(), 60 * 60);
363 QCOMPARE(offset4.date(), offsetDate);
364 QCOMPARE(offset4.time(), QTime(0, 0, 0));
365}
366
367void tst_QDateTime::operator_eq()
368{
369 QVERIFY(QDateTime() != QDateTime(QDate(1970, 1, 1), QTime(0, 0))); // QTBUG-79006
370 QDateTime dt1(QDate(2004, 3, 24), QTime(23, 45, 57), Qt::UTC);
371 QDateTime dt2(QDate(2005, 3, 11), QTime(), Qt::UTC);
372 dt2 = dt1;
373 QVERIFY(dt1 == dt2);
374}
375
376void tst_QDateTime::isNull()
377{
378 QDateTime dt1;
379 QVERIFY(dt1.isNull());
380 dt1.setDate(QDate());
381 QVERIFY(dt1.isNull());
382 dt1.setTime(QTime());
383 QVERIFY(dt1.isNull());
384 dt1.setTimeSpec(Qt::UTC);
385 QVERIFY(dt1.isNull()); // maybe it should return false?
386
387 dt1.setDate(QDate(2004, 1, 2));
388 QVERIFY(!dt1.isNull());
389 dt1.setTime(QTime(12, 34, 56));
390 QVERIFY(!dt1.isNull());
391 dt1.setTime(QTime());
392 QVERIFY(!dt1.isNull());
393}
394
395void tst_QDateTime::isValid()
396{
397 QDateTime dt1;
398 QVERIFY(!dt1.isValid());
399 dt1.setDate(QDate());
400 QVERIFY(!dt1.isValid());
401 dt1.setTime(QTime());
402 QVERIFY(!dt1.isValid());
403 dt1.setTimeSpec(Qt::UTC);
404 QVERIFY(!dt1.isValid());
405
406 dt1.setDate(QDate(2004, 1, 2));
407 QVERIFY(dt1.isValid());
408 dt1.setDate(QDate());
409 QVERIFY(!dt1.isValid());
410 dt1.setTime(QTime(12, 34, 56));
411 QVERIFY(!dt1.isValid());
412 dt1.setTime(QTime());
413 QVERIFY(!dt1.isValid());
414}
415
416void tst_QDateTime::date()
417{
418 QDateTime dt1(QDate(2004, 3, 24), QTime(23, 45, 57), Qt::LocalTime);
419 QCOMPARE(dt1.date(), QDate(2004, 3, 24));
420
421 QDateTime dt2(QDate(2004, 3, 25), QTime(0, 45, 57), Qt::LocalTime);
422 QCOMPARE(dt2.date(), QDate(2004, 3, 25));
423
424 QDateTime dt3(QDate(2004, 3, 24), QTime(23, 45, 57), Qt::UTC);
425 QCOMPARE(dt3.date(), QDate(2004, 3, 24));
426
427 QDateTime dt4(QDate(2004, 3, 25), QTime(0, 45, 57), Qt::UTC);
428 QCOMPARE(dt4.date(), QDate(2004, 3, 25));
429}
430
431void tst_QDateTime::time()
432{
433 QDateTime dt1(QDate(2004, 3, 24), QTime(23, 45, 57), Qt::LocalTime);
434 QCOMPARE(dt1.time(), QTime(23, 45, 57));
435
436 QDateTime dt2(QDate(2004, 3, 25), QTime(0, 45, 57), Qt::LocalTime);
437 QCOMPARE(dt2.time(), QTime(0, 45, 57));
438
439 QDateTime dt3(QDate(2004, 3, 24), QTime(23, 45, 57), Qt::UTC);
440 QCOMPARE(dt3.time(), QTime(23, 45, 57));
441
442 QDateTime dt4(QDate(2004, 3, 25), QTime(0, 45, 57), Qt::UTC);
443 QCOMPARE(dt4.time(), QTime(0, 45, 57));
444}
445
446void tst_QDateTime::timeSpec()
447{
448 QDateTime dt1(QDate(2004, 1, 24), QTime(23, 45, 57));
449 QCOMPARE(dt1.timeSpec(), Qt::LocalTime);
450 QCOMPARE(dt1.addDays(0).timeSpec(), Qt::LocalTime);
451 QCOMPARE(dt1.addMonths(0).timeSpec(), Qt::LocalTime);
452 QCOMPARE(dt1.addMonths(6).timeSpec(), Qt::LocalTime);
453 QCOMPARE(dt1.addYears(0).timeSpec(), Qt::LocalTime);
454 QCOMPARE(dt1.addSecs(0).timeSpec(), Qt::LocalTime);
455 QCOMPARE(dt1.addSecs(86400 * 185).timeSpec(), Qt::LocalTime);
456 QCOMPARE(dt1.toTimeSpec(Qt::LocalTime).timeSpec(), Qt::LocalTime);
457 QCOMPARE(dt1.toTimeSpec(Qt::UTC).timeSpec(), Qt::UTC);
458
459 QDateTime dt2(QDate(2004, 1, 24), QTime(23, 45, 57), Qt::LocalTime);
460 QCOMPARE(dt2.timeSpec(), Qt::LocalTime);
461
462 QDateTime dt3(QDate(2004, 1, 25), QTime(0, 45, 57), Qt::UTC);
463 QCOMPARE(dt3.timeSpec(), Qt::UTC);
464
465 QDateTime dt4 = QDateTime::currentDateTime();
466 QCOMPARE(dt4.timeSpec(), Qt::LocalTime);
467}
468
469void tst_QDateTime::setDate()
470{
471 QDateTime dt1(QDate(2004, 3, 25), QTime(0, 45, 57), Qt::UTC);
472 dt1.setDate(QDate(2004, 6, 25));
473 QCOMPARE(dt1.date(), QDate(2004, 6, 25));
474 QCOMPARE(dt1.time(), QTime(0, 45, 57));
475 QCOMPARE(dt1.timeSpec(), Qt::UTC);
476
477 QDateTime dt2(QDate(2004, 3, 25), QTime(0, 45, 57), Qt::LocalTime);
478 dt2.setDate(QDate(2004, 6, 25));
479 QCOMPARE(dt2.date(), QDate(2004, 6, 25));
480 QCOMPARE(dt2.time(), QTime(0, 45, 57));
481 QCOMPARE(dt2.timeSpec(), Qt::LocalTime);
482
483 QDateTime dt3(QDate(4004, 3, 25), QTime(0, 45, 57), Qt::UTC);
484 dt3.setDate(QDate(4004, 6, 25));
485 QCOMPARE(dt3.date(), QDate(4004, 6, 25));
486 QCOMPARE(dt3.time(), QTime(0, 45, 57));
487 QCOMPARE(dt3.timeSpec(), Qt::UTC);
488
489 QDateTime dt4(QDate(4004, 3, 25), QTime(0, 45, 57), Qt::LocalTime);
490 dt4.setDate(QDate(4004, 6, 25));
491 QCOMPARE(dt4.date(), QDate(4004, 6, 25));
492 QCOMPARE(dt4.time(), QTime(0, 45, 57));
493 QCOMPARE(dt4.timeSpec(), Qt::LocalTime);
494
495 QDateTime dt5(QDate(1760, 3, 25), QTime(0, 45, 57), Qt::UTC);
496 dt5.setDate(QDate(1760, 6, 25));
497 QCOMPARE(dt5.date(), QDate(1760, 6, 25));
498 QCOMPARE(dt5.time(), QTime(0, 45, 57));
499 QCOMPARE(dt5.timeSpec(), Qt::UTC);
500
501 QDateTime dt6(QDate(1760, 3, 25), QTime(0, 45, 57), Qt::LocalTime);
502 dt6.setDate(QDate(1760, 6, 25));
503 QCOMPARE(dt6.date(), QDate(1760, 6, 25));
504 QCOMPARE(dt6.time(), QTime(0, 45, 57));
505 QCOMPARE(dt6.timeSpec(), Qt::LocalTime);
506}
507
508void tst_QDateTime::setTime_data()
509{
510 QTest::addColumn<QDateTime>(name: "dateTime");
511 QTest::addColumn<QTime>(name: "newTime");
512
513 QTest::newRow(dataTag: "data0") << QDateTime(QDate(2004, 3, 25), QTime(0, 45, 57), Qt::UTC) << QTime(23, 11, 22);
514 QTest::newRow(dataTag: "data1") << QDateTime(QDate(2004, 3, 25), QTime(0, 45, 57), Qt::LocalTime) << QTime(23, 11, 22);
515 QTest::newRow(dataTag: "data2") << QDateTime(QDate(4004, 3, 25), QTime(0, 45, 57), Qt::UTC) << QTime(23, 11, 22);
516 QTest::newRow(dataTag: "data3") << QDateTime(QDate(4004, 3, 25), QTime(0, 45, 57), Qt::LocalTime) << QTime(23, 11, 22);
517 QTest::newRow(dataTag: "data4") << QDateTime(QDate(1760, 3, 25), QTime(0, 45, 57), Qt::UTC) << QTime(23, 11, 22);
518 QTest::newRow(dataTag: "data5") << QDateTime(QDate(1760, 3, 25), QTime(0, 45, 57), Qt::LocalTime) << QTime(23, 11, 22);
519
520 QTest::newRow(dataTag: "set on std/dst") << QDateTime::currentDateTime() << QTime(23, 11, 22);
521}
522
523void tst_QDateTime::setTime()
524{
525 QFETCH(QDateTime, dateTime);
526 QFETCH(QTime, newTime);
527
528 const QDate expectedDate(dateTime.date());
529 const Qt::TimeSpec expectedTimeSpec(dateTime.timeSpec());
530
531 dateTime.setTime(newTime);
532
533 QCOMPARE(dateTime.date(), expectedDate);
534 QCOMPARE(dateTime.time(), newTime);
535 QCOMPARE(dateTime.timeSpec(), expectedTimeSpec);
536}
537
538void tst_QDateTime::setTimeSpec_data()
539{
540 QTest::addColumn<QDateTime>(name: "dateTime");
541 QTest::addColumn<Qt::TimeSpec>(name: "newTimeSpec");
542
543 QTest::newRow(dataTag: "UTC => UTC") << QDateTime(QDate(2004, 3, 25), QTime(0, 45, 57), Qt::UTC) << Qt::UTC;
544 QTest::newRow(dataTag: "UTC => LocalTime") << QDateTime(QDate(2004, 3, 25), QTime(0, 45, 57), Qt::UTC) << Qt::LocalTime;
545 QTest::newRow(dataTag: "UTC => OffsetFromUTC") << QDateTime(QDate(2004, 3, 25), QTime(0, 45, 57), Qt::UTC) << Qt::OffsetFromUTC;
546}
547
548void tst_QDateTime::setTimeSpec()
549{
550 QFETCH(QDateTime, dateTime);
551 QFETCH(Qt::TimeSpec, newTimeSpec);
552
553 const QDate expectedDate(dateTime.date());
554 const QTime expectedTime(dateTime.time());
555
556 dateTime.setTimeSpec(newTimeSpec);
557
558 QCOMPARE(dateTime.date(), expectedDate);
559 QCOMPARE(dateTime.time(), expectedTime);
560 if (newTimeSpec == Qt::OffsetFromUTC)
561 QCOMPARE(dateTime.timeSpec(), Qt::UTC);
562 else
563 QCOMPARE(dateTime.timeSpec(), newTimeSpec);
564}
565
566void tst_QDateTime::setSecsSinceEpoch()
567{
568 QDateTime dt1;
569 dt1.setSecsSinceEpoch(0);
570 QCOMPARE(dt1.toUTC(), QDateTime(QDate(1970, 1, 1), QTime(), Qt::UTC));
571 QCOMPARE(dt1.timeSpec(), Qt::LocalTime);
572
573 dt1.setTimeSpec(Qt::UTC);
574 dt1.setSecsSinceEpoch(0);
575 QCOMPARE(dt1, QDateTime(QDate(1970, 1, 1), QTime(), Qt::UTC));
576 QCOMPARE(dt1.timeSpec(), Qt::UTC);
577
578 dt1.setSecsSinceEpoch(123456);
579 QCOMPARE(dt1, QDateTime(QDate(1970, 1, 2), QTime(10, 17, 36), Qt::UTC));
580 if (zoneIsCET) {
581 QDateTime dt2;
582 dt2.setSecsSinceEpoch(123456);
583 QCOMPARE(dt2, QDateTime(QDate(1970, 1, 2), QTime(11, 17, 36), Qt::LocalTime));
584 }
585
586 dt1.setSecsSinceEpoch((uint)(quint32)-123456);
587 QCOMPARE(dt1, QDateTime(QDate(2106, 2, 5), QTime(20, 10, 40), Qt::UTC));
588 if (zoneIsCET) {
589 QDateTime dt2;
590 dt2.setSecsSinceEpoch((uint)(quint32)-123456);
591 QCOMPARE(dt2, QDateTime(QDate(2106, 2, 5), QTime(21, 10, 40), Qt::LocalTime));
592 }
593
594 dt1.setSecsSinceEpoch(1214567890);
595 QCOMPARE(dt1, QDateTime(QDate(2008, 6, 27), QTime(11, 58, 10), Qt::UTC));
596 if (zoneIsCET) {
597 QDateTime dt2;
598 dt2.setSecsSinceEpoch(1214567890);
599 QCOMPARE(dt2, QDateTime(QDate(2008, 6, 27), QTime(13, 58, 10), Qt::LocalTime));
600 }
601
602 dt1.setSecsSinceEpoch(0x7FFFFFFF);
603 QCOMPARE(dt1, QDateTime(QDate(2038, 1, 19), QTime(3, 14, 7), Qt::UTC));
604 if (zoneIsCET) {
605 QDateTime dt2;
606 dt2.setSecsSinceEpoch(0x7FFFFFFF);
607 QCOMPARE(dt2, QDateTime(QDate(2038, 1, 19), QTime(4, 14, 7), Qt::LocalTime));
608 }
609
610 dt1 = QDateTime(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::OffsetFromUTC, 60 * 60);
611 dt1.setSecsSinceEpoch(123456);
612 QCOMPARE(dt1, QDateTime(QDate(1970, 1, 2), QTime(10, 17, 36), Qt::UTC));
613 QCOMPARE(dt1.timeSpec(), Qt::OffsetFromUTC);
614 QCOMPARE(dt1.offsetFromUtc(), 60 * 60);
615}
616
617void tst_QDateTime::setMSecsSinceEpoch_data()
618{
619 QTest::addColumn<qint64>(name: "msecs");
620 QTest::addColumn<QDateTime>(name: "utc");
621 QTest::addColumn<QDateTime>(name: "cet");
622
623 QTest::newRow(dataTag: "zero")
624 << Q_INT64_C(0)
625 << QDateTime(QDate(1970, 1, 1), QTime(), Qt::UTC)
626 << QDateTime(QDate(1970, 1, 1), QTime(1, 0));
627 QTest::newRow(dataTag: "-1")
628 << Q_INT64_C(-1)
629 << QDateTime(QDate(1969, 12, 31), QTime(23, 59, 59, 999), Qt::UTC)
630 << QDateTime(QDate(1970, 1, 1), QTime(0, 59, 59, 999));
631 QTest::newRow(dataTag: "123456789")
632 << Q_INT64_C(123456789)
633 << QDateTime(QDate(1970, 1, 2), QTime(10, 17, 36, 789), Qt::UTC)
634 << QDateTime(QDate(1970, 1, 2), QTime(11, 17, 36, 789), Qt::LocalTime);
635 QTest::newRow(dataTag: "-123456789")
636 << Q_INT64_C(-123456789)
637 << QDateTime(QDate(1969, 12, 30), QTime(13, 42, 23, 211), Qt::UTC)
638 << QDateTime(QDate(1969, 12, 30), QTime(14, 42, 23, 211), Qt::LocalTime);
639 QTest::newRow(dataTag: "non-time_t")
640 << (Q_INT64_C(1000) << 32)
641 << QDateTime(QDate(2106, 2, 7), QTime(6, 28, 16), Qt::UTC)
642 << QDateTime(QDate(2106, 2, 7), QTime(7, 28, 16));
643 QTest::newRow(dataTag: "very-large")
644 << (Q_INT64_C(123456) << 32)
645 << QDateTime(QDate(18772, 8, 15), QTime(1, 8, 14, 976), Qt::UTC)
646 << QDateTime(QDate(18772, 8, 15), QTime(3, 8, 14, 976));
647 QTest::newRow(dataTag: "old min (Tue Nov 25 00:00:00 -4714)")
648 << Q_INT64_C(-210866716800000)
649 << QDateTime(QDate::fromJulianDay(jd_: 1), QTime(), Qt::UTC)
650 << QDateTime(QDate::fromJulianDay(jd_: 1), QTime(1, 0));
651 QTest::newRow(dataTag: "old max (Tue Jun 3 21:59:59 5874898)")
652 << Q_INT64_C(185331720376799999)
653 << QDateTime(QDate::fromJulianDay(jd_: 0x7fffffff), QTime(21, 59, 59, 999), Qt::UTC)
654 << QDateTime(QDate::fromJulianDay(jd_: 0x7fffffff), QTime(23, 59, 59, 999));
655 QTest::newRow(dataTag: "min")
656 // Use -max(), which is min() + 1, to simplify filtering out overflow cases:
657 << -std::numeric_limits<qint64>::max()
658 << QDateTime(QDate(-292275056, 5, 16), QTime(16, 47, 4, 193), Qt::UTC)
659 << QDateTime(QDate(-292275056, 5, 16), QTime(17, 47, 4, 193), Qt::LocalTime);
660 QTest::newRow(dataTag: "max")
661 << std::numeric_limits<qint64>::max()
662 << QDateTime(QDate(292278994, 8, 17), QTime(7, 12, 55, 807), Qt::UTC)
663 << QDateTime(QDate(292278994, 8, 17), QTime(9, 12, 55, 807), Qt::LocalTime);
664}
665
666void tst_QDateTime::setMSecsSinceEpoch()
667{
668 QFETCH(qint64, msecs);
669 QFETCH(QDateTime, utc);
670 QFETCH(QDateTime, cet);
671
672 QDateTime dt;
673 dt.setTimeSpec(Qt::UTC);
674 dt.setMSecsSinceEpoch(msecs);
675
676 QCOMPARE(dt, utc);
677 QCOMPARE(dt.date(), utc.date());
678 QCOMPARE(dt.time(), utc.time());
679 QCOMPARE(dt.timeSpec(), Qt::UTC);
680
681 {
682 QDateTime dt1 = QDateTime::fromMSecsSinceEpoch(msecs, spec: Qt::UTC);
683 QCOMPARE(dt1, utc);
684 QCOMPARE(dt1.date(), utc.date());
685 QCOMPARE(dt1.time(), utc.time());
686 QCOMPARE(dt1.timeSpec(), Qt::UTC);
687 }
688 {
689 QDateTime dt1(utc.date(), utc.time(), Qt::UTC);
690 QCOMPARE(dt1, utc);
691 QCOMPARE(dt1.date(), utc.date());
692 QCOMPARE(dt1.time(), utc.time());
693 QCOMPARE(dt1.timeSpec(), Qt::UTC);
694 }
695 {
696 // used to fail to clear the ShortData bit, causing corruption
697 QDateTime dt1 = dt.addDays(days: 0);
698 QCOMPARE(dt1, utc);
699 QCOMPARE(dt1.date(), utc.date());
700 QCOMPARE(dt1.time(), utc.time());
701 QCOMPARE(dt1.timeSpec(), Qt::UTC);
702 }
703
704 if (zoneIsCET) {
705 QCOMPARE(dt.toLocalTime(), cet);
706
707 // Test converting from LocalTime to UTC back to LocalTime.
708 QDateTime localDt;
709 localDt.setTimeSpec(Qt::LocalTime);
710 localDt.setMSecsSinceEpoch(msecs);
711
712 // LocalTime will overflow for max
713 if (msecs != std::numeric_limits<qint64>::max())
714 QCOMPARE(localDt, utc);
715 QCOMPARE(localDt.timeSpec(), Qt::LocalTime);
716
717 // Compare result for LocalTime to TimeZone
718 QDateTime dt2;
719#if QT_CONFIG(timezone)
720 QTimeZone europe("Europe/Oslo");
721 dt2.setTimeZone(europe);
722#endif
723 dt2.setMSecsSinceEpoch(msecs);
724 QCOMPARE(dt2.date(), cet.date());
725
726 // don't compare the time if the date is too early or too late: prior
727 // to 1916, timezones in Europe were not standardised and some OS APIs
728 // have hard limits. Let's restrict it to the 32-bit Unix range
729 if (dt2.date().year() >= 1970 && dt2.date().year() <= 2037)
730 QCOMPARE(dt2.time(), cet.time());
731#if QT_CONFIG(timezone)
732 QCOMPARE(dt2.timeSpec(), Qt::TimeZone);
733 QCOMPARE(dt2.timeZone(), europe);
734#endif
735 }
736
737 QCOMPARE(dt.toMSecsSinceEpoch(), msecs);
738
739 if (quint64(msecs / 1000) < 0xFFFFFFFF) {
740 QCOMPARE(qint64(dt.toSecsSinceEpoch()), msecs / 1000);
741 }
742
743 QDateTime reference(QDate(1970, 1, 1), QTime(), Qt::UTC);
744 QCOMPARE(dt, reference.addMSecs(msecs));
745}
746
747void tst_QDateTime::fromMSecsSinceEpoch_data()
748{
749 setMSecsSinceEpoch_data();
750}
751
752void tst_QDateTime::fromMSecsSinceEpoch()
753{
754 QFETCH(qint64, msecs);
755 QFETCH(QDateTime, utc);
756 QFETCH(QDateTime, cet);
757
758 QDateTime dtLocal = QDateTime::fromMSecsSinceEpoch(msecs, spec: Qt::LocalTime);
759 QDateTime dtUtc = QDateTime::fromMSecsSinceEpoch(msecs, spec: Qt::UTC);
760 QDateTime dtOffset = QDateTime::fromMSecsSinceEpoch(msecs, spec: Qt::OffsetFromUTC, offsetFromUtc: 60*60);
761
762 // LocalTime will overflow for "min" or "max" tests, depending on whether
763 // you're East or West of Greenwich. In UTC, we won't overflow.
764 if (localTimeType == LocalTimeIsUtc
765 || msecs != std::numeric_limits<qint64>::max() * localTimeType)
766 QCOMPARE(dtLocal, utc);
767
768 QCOMPARE(dtUtc, utc);
769 QCOMPARE(dtUtc.date(), utc.date());
770 QCOMPARE(dtUtc.time(), utc.time());
771
772 QCOMPARE(dtOffset, utc);
773 QCOMPARE(dtOffset.offsetFromUtc(), 60*60);
774 // // OffsetFromUTC will overflow for max
775 if (msecs != std::numeric_limits<qint64>::max())
776 QCOMPARE(dtOffset.time(), utc.time().addMSecs(60*60*1000));
777
778 if (zoneIsCET) {
779 QCOMPARE(dtLocal.toLocalTime(), cet);
780 QCOMPARE(dtUtc.toLocalTime(), cet);
781 QCOMPARE(dtOffset.toLocalTime(), cet);
782 }
783
784 // LocalTime will overflow for max
785 if (msecs != std::numeric_limits<qint64>::max())
786 QCOMPARE(dtLocal.toMSecsSinceEpoch(), msecs);
787 QCOMPARE(dtUtc.toMSecsSinceEpoch(), msecs);
788 QCOMPARE(dtOffset.toMSecsSinceEpoch(), msecs);
789
790 if (quint64(msecs / 1000) < 0xFFFFFFFF) {
791 QCOMPARE(qint64(dtLocal.toSecsSinceEpoch()), msecs / 1000);
792 QCOMPARE(qint64(dtUtc.toSecsSinceEpoch()), msecs / 1000);
793 QCOMPARE(qint64(dtOffset.toSecsSinceEpoch()), msecs / 1000);
794 }
795
796 QDateTime reference(QDate(1970, 1, 1), QTime(), Qt::UTC);
797 // LocalTime will overflow for max
798 if (msecs != std::numeric_limits<qint64>::max())
799 QCOMPARE(dtLocal, reference.addMSecs(msecs));
800 QCOMPARE(dtUtc, reference.addMSecs(msecs));
801 QCOMPARE(dtOffset, reference.addMSecs(msecs));
802}
803
804void tst_QDateTime::toString_isoDate_data()
805{
806 QTest::addColumn<QDateTime>(name: "datetime");
807 QTest::addColumn<Qt::DateFormat>(name: "format");
808 QTest::addColumn<QString>(name: "expected");
809
810 QTest::newRow(dataTag: "localtime")
811 << QDateTime(QDate(1978, 11, 9), QTime(13, 28, 34))
812 << Qt::ISODate << QString("1978-11-09T13:28:34");
813 QTest::newRow(dataTag: "UTC")
814 << QDateTime(QDate(1978, 11, 9), QTime(13, 28, 34), Qt::UTC)
815 << Qt::ISODate << QString("1978-11-09T13:28:34Z");
816 QDateTime dt(QDate(1978, 11, 9), QTime(13, 28, 34));
817 dt.setOffsetFromUtc(19800);
818 QTest::newRow(dataTag: "positive OffsetFromUTC")
819 << dt << Qt::ISODate
820 << QString("1978-11-09T13:28:34+05:30");
821 dt.setOffsetFromUtc(-7200);
822 QTest::newRow(dataTag: "negative OffsetFromUTC")
823 << dt << Qt::ISODate
824 << QString("1978-11-09T13:28:34-02:00");
825 dt.setOffsetFromUtc(-900);
826 QTest::newRow(dataTag: "negative non-integral OffsetFromUTC")
827 << dt << Qt::ISODate
828 << QString("1978-11-09T13:28:34-00:15");
829 QTest::newRow(dataTag: "invalid") // ISODate < 2019 doesn't allow -ve year numbers; QTBUG-91070
830 << QDateTime(QDate(-1, 11, 9), QTime(13, 28, 34), Qt::UTC)
831 << Qt::ISODate << QString();
832 QTest::newRow(dataTag: "without-ms")
833 << QDateTime(QDate(1978, 11, 9), QTime(13, 28, 34, 20))
834 << Qt::ISODate << QString("1978-11-09T13:28:34");
835 QTest::newRow(dataTag: "with-ms")
836 << QDateTime(QDate(1978, 11, 9), QTime(13, 28, 34, 20))
837 << Qt::ISODateWithMs << QString("1978-11-09T13:28:34.020");
838}
839
840void tst_QDateTime::toString_isoDate()
841{
842 QFETCH(const QDateTime, datetime);
843 QFETCH(const Qt::DateFormat, format);
844 QFETCH(const QString, expected);
845
846#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
847 QLocale oldLocale;
848 QLocale::setDefault(QLocale("en_US"));
849#endif // ### Qt 6: remove
850
851 const QString result = datetime.toString(format);
852 QCOMPARE(result, expected);
853
854 const QDateTime resultDatetime = QDateTime::fromString(s: result, f: format);
855 if (QByteArray(QTest::currentDataTag()) == "invalid") {
856 QCOMPARE(resultDatetime, QDateTime());
857 } else {
858 const QDateTime when =
859 QByteArray(QTest::currentDataTag()) == "without-ms"
860 ? datetime.addMSecs(msecs: -datetime.time().msec()) : datetime;
861 QCOMPARE(resultDatetime.toMSecsSinceEpoch(), when.toMSecsSinceEpoch());
862 QCOMPARE(resultDatetime, when);
863 QCOMPARE(resultDatetime.date(), when.date());
864 QCOMPARE(resultDatetime.time(), when.time());
865 QCOMPARE(resultDatetime.timeSpec(), when.timeSpec());
866 QCOMPARE(resultDatetime.offsetFromUtc(), when.offsetFromUtc());
867 }
868
869#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
870 QLocale::setDefault(oldLocale);
871#endif // ### Qt 6: remove
872}
873
874void tst_QDateTime::toString_isoDate_extra()
875{
876 QDateTime dt = QDateTime::fromMSecsSinceEpoch(msecs: 0, spec: Qt::UTC);
877 QCOMPARE(dt.toString(Qt::ISODate), QLatin1String("1970-01-01T00:00:00Z"));
878#if QT_CONFIG(timezone)
879 QTimeZone PST("America/Vancouver");
880 if (PST.isValid()) {
881 dt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: PST);
882 QCOMPARE(dt.toString(Qt::ISODate), QLatin1String("1969-12-31T16:00:00-08:00"));
883 } else {
884 qDebug(msg: "Missed zone test: no America/Vancouver zone available");
885 }
886 QTimeZone CET("Europe/Berlin");
887 if (CET.isValid()) {
888 dt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: CET);
889 QCOMPARE(dt.toString(Qt::ISODate), QLatin1String("1970-01-01T01:00:00+01:00"));
890 } else {
891 qDebug(msg: "Missed zone test: no Europe/Berlin zone available");
892 }
893#endif // timezone
894}
895
896#if QT_CONFIG(datestring) // depends on textdate
897void tst_QDateTime::toString_textDate_data()
898{
899 QTest::addColumn<QDateTime>(name: "datetime");
900 QTest::addColumn<QString>(name: "expected");
901
902 QString wednesdayJanuary = QLocale::system().dayName(3, format: QLocale::ShortFormat)
903 + ' ' + QLocale::system().monthName(1, format: QLocale::ShortFormat);
904
905 QTest::newRow(dataTag: "localtime") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::LocalTime)
906 << wednesdayJanuary + QString(" 2 01:02:03 2013");
907 QTest::newRow(dataTag: "utc") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::UTC)
908 << wednesdayJanuary + QString(" 2 01:02:03 2013 GMT");
909 QTest::newRow(dataTag: "offset+") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::OffsetFromUTC,
910 10 * 60 * 60)
911 << wednesdayJanuary + QString(" 2 01:02:03 2013 GMT+1000");
912 QTest::newRow(dataTag: "offset-") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::OffsetFromUTC,
913 -10 * 60 * 60)
914 << wednesdayJanuary + QString(" 2 01:02:03 2013 GMT-1000");
915 QTest::newRow(dataTag: "invalid") << QDateTime()
916 << QString("");
917}
918
919void tst_QDateTime::toString_textDate()
920{
921 QFETCH(QDateTime, datetime);
922 QFETCH(QString, expected);
923
924 QString result = datetime.toString(format: Qt::TextDate);
925 QCOMPARE(result, expected);
926
927 QDateTime resultDatetime = QDateTime::fromString(s: result, f: Qt::TextDate);
928 QCOMPARE(resultDatetime, datetime);
929 QCOMPARE(resultDatetime.date(), datetime.date());
930 QCOMPARE(resultDatetime.time(), datetime.time());
931 QCOMPARE(resultDatetime.timeSpec(), datetime.timeSpec());
932 QCOMPARE(resultDatetime.offsetFromUtc(), datetime.offsetFromUtc());
933}
934
935void tst_QDateTime::toString_textDate_extra()
936{
937 QLatin1String GMT("GMT");
938 QDateTime dt = QDateTime::fromMSecsSinceEpoch(msecs: 0, spec: Qt::LocalTime);
939 QVERIFY(!dt.toString().endsWith(GMT));
940 dt = QDateTime::fromMSecsSinceEpoch(msecs: 0, spec: Qt::UTC).toLocalTime();
941 QVERIFY(!dt.toString().endsWith(GMT));
942
943#if QT_CONFIG(timezone)
944# if defined Q_OS_UNIX && !defined Q_OS_DARWIN && !defined Q_OS_ANDROID
945# define CORRECT_ZONE_ABBREV
946# endif // QTBUG-57320, QTBUG-57298, QTBUG-68833
947 if (QTimeZone::systemTimeZone().offsetFromUtc(atDateTime: dt))
948 QVERIFY(dt.toString() != QLatin1String("Thu Jan 1 00:00:00 1970"));
949 else
950 QCOMPARE(dt.toString(), QLatin1String("Thu Jan 1 00:00:00 1970"));
951
952 QTimeZone PST("America/Vancouver");
953 if (PST.isValid()) {
954 dt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: PST);
955# ifdef CORRECT_ZONE_ABBREV
956 QCOMPARE(dt.toString(), QLatin1String("Wed Dec 31 16:00:00 1969 PST"));
957# else
958 QVERIFY(dt.toString().startsWith(QLatin1String("Wed Dec 31 16:00:00 1969 ")));
959# endif
960 dt = dt.toLocalTime();
961 QVERIFY(!dt.toString().endsWith(GMT));
962 } else {
963 qDebug(msg: "Missed zone test: no America/Vancouver zone available");
964 }
965 QTimeZone CET("Europe/Berlin");
966 if (CET.isValid()) {
967 dt = QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: CET);
968# ifdef CORRECT_ZONE_ABBREV
969 QCOMPARE(dt.toString(), QLatin1String("Thu Jan 1 01:00:00 1970 CET"));
970# else
971 QVERIFY(dt.toString().startsWith(QLatin1String("Thu Jan 1 01:00:00 1970 ")));
972# endif
973 dt = dt.toLocalTime();
974 QVERIFY(!dt.toString().endsWith(GMT));
975 } else {
976 qDebug(msg: "Missed zone test: no Europe/Berlin zone available");
977 }
978#else // timezone
979 if (dt.offsetFromUtc())
980 QVERIFY(dt.toString() != QLatin1String("Thu Jan 1 00:00:00 1970"));
981 else
982 QCOMPARE(dt.toString(), QLatin1String("Thu Jan 1 00:00:00 1970"));
983#endif
984 dt = QDateTime::fromMSecsSinceEpoch(msecs: 0, spec: Qt::UTC);
985 QVERIFY(dt.toString().endsWith(GMT));
986}
987#endif // datestring
988
989void tst_QDateTime::toString_rfcDate_data()
990{
991 QTest::addColumn<QDateTime>(name: "dt");
992 QTest::addColumn<QString>(name: "formatted");
993
994 if (zoneIsCET) {
995 QTest::newRow(dataTag: "localtime")
996 << QDateTime(QDate(1978, 11, 9), QTime(13, 28, 34))
997 << QString("09 Nov 1978 13:28:34 +0100");
998 }
999 QTest::newRow(dataTag: "UTC")
1000 << QDateTime(QDate(1978, 11, 9), QTime(13, 28, 34), Qt::UTC)
1001 << QString("09 Nov 1978 13:28:34 +0000");
1002 QDateTime dt(QDate(1978, 11, 9), QTime(13, 28, 34));
1003 dt.setOffsetFromUtc(19800);
1004 QTest::newRow(dataTag: "positive OffsetFromUTC")
1005 << dt
1006 << QString("09 Nov 1978 13:28:34 +0530");
1007 dt.setOffsetFromUtc(-7200);
1008 QTest::newRow(dataTag: "negative OffsetFromUTC")
1009 << dt
1010 << QString("09 Nov 1978 13:28:34 -0200");
1011 QTest::newRow(dataTag: "invalid")
1012 << QDateTime(QDate(1978, 13, 9), QTime(13, 28, 34), Qt::UTC)
1013 << QString();
1014 QTest::newRow(dataTag: "999 milliseconds UTC")
1015 << QDateTime(QDate(2000, 1, 1), QTime(13, 28, 34, 999), Qt::UTC)
1016 << QString("01 Jan 2000 13:28:34 +0000");
1017}
1018
1019void tst_QDateTime::toString_rfcDate()
1020{
1021 QFETCH(QDateTime, dt);
1022 QFETCH(QString, formatted);
1023
1024 // Set to non-English locale to confirm still uses English
1025 QLocale oldLocale;
1026 QLocale::setDefault(QLocale("de_DE"));
1027 QString actual(dt.toString(format: Qt::RFC2822Date));
1028 QLocale::setDefault(oldLocale);
1029 QCOMPARE(actual, formatted);
1030}
1031
1032void tst_QDateTime::toString_enumformat()
1033{
1034 QDateTime dt1(QDate(1995, 5, 20), QTime(12, 34, 56));
1035
1036#if QT_CONFIG(textdate)
1037 QString str1 = dt1.toString(format: Qt::TextDate);
1038 QVERIFY(!str1.isEmpty()); // It's locale-dependent everywhere
1039#endif
1040
1041 QString str2 = dt1.toString(format: Qt::ISODate);
1042 QCOMPARE(str2, QString("1995-05-20T12:34:56"));
1043
1044 QString str3 = dt1.toString(format: Qt::LocalDate);
1045 QVERIFY(!str3.isEmpty());
1046 //check for date/time components in any order
1047 //year may be 2 or 4 digits
1048 QVERIFY(str3.contains("95"));
1049 //day and month may be in numeric or word form
1050 QVERIFY(str3.contains("12"));
1051 QVERIFY(str3.contains("34"));
1052 //seconds may be absent
1053}
1054
1055void tst_QDateTime::addDays()
1056{
1057 for (int pass = 0; pass < 2; ++pass) {
1058 QDateTime dt(QDate(2004, 1, 1), QTime(12, 34, 56), pass == 0 ? Qt::LocalTime : Qt::UTC);
1059 dt = dt.addDays(days: 185);
1060 QVERIFY(dt.date().year() == 2004 && dt.date().month() == 7 && dt.date().day() == 4);
1061 QVERIFY(dt.time().hour() == 12 && dt.time().minute() == 34 && dt.time().second() == 56
1062 && dt.time().msec() == 0);
1063 QCOMPARE(dt.timeSpec(), (pass == 0 ? Qt::LocalTime : Qt::UTC));
1064
1065 dt = dt.addDays(days: -185);
1066 QCOMPARE(dt.date(), QDate(2004, 1, 1));
1067 QCOMPARE(dt.time(), QTime(12, 34, 56));
1068 }
1069
1070 QDateTime dt(QDate(1752, 9, 14), QTime(12, 34, 56));
1071 while (dt.date().year() < 8000) {
1072 int year = dt.date().year();
1073 if (QDate::isLeapYear(year: year + 1))
1074 dt = dt.addDays(days: 366);
1075 else
1076 dt = dt.addDays(days: 365);
1077 QCOMPARE(dt.date(), QDate(year + 1, 9, 14));
1078 QCOMPARE(dt.time(), QTime(12, 34, 56));
1079 }
1080
1081 // Test preserves TimeSpec
1082 QDateTime dt1(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::UTC);
1083 QDateTime dt2 = dt1.addDays(days: 2);
1084 QCOMPARE(dt2.date(), QDate(2013, 1, 3));
1085 QCOMPARE(dt2.time(), QTime(0, 0, 0));
1086 QCOMPARE(dt2.timeSpec(), Qt::UTC);
1087
1088 dt1 = QDateTime(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::LocalTime);
1089 dt2 = dt1.addDays(days: 2);
1090 QCOMPARE(dt2.date(), QDate(2013, 1, 3));
1091 QCOMPARE(dt2.time(), QTime(0, 0, 0));
1092 QCOMPARE(dt2.timeSpec(), Qt::LocalTime);
1093
1094 dt1 = QDateTime(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::OffsetFromUTC, 60*60);
1095 dt2 = dt1.addDays(days: 2);
1096 QCOMPARE(dt2.date(), QDate(2013, 1, 3));
1097 QCOMPARE(dt2.time(), QTime(0, 0, 0));
1098 QCOMPARE(dt2.timeSpec(), Qt::OffsetFromUTC);
1099 QCOMPARE(dt2.offsetFromUtc(), 60 * 60);
1100
1101#if QT_CONFIG(timezone)
1102 const QTimeZone cet("Europe/Oslo");
1103 if (cet.isValid()) {
1104 dt1 = QDate(2022, 1, 10).startOfDay(zone: cet);
1105 dt2 = dt1.addDays(days: 2); // QTBUG-99668: should not assert
1106 QCOMPARE(dt2.date(), QDate(2022, 1, 12));
1107 QCOMPARE(dt2.time(), QTime(0, 0));
1108 QCOMPARE(dt2.timeSpec(), Qt::TimeZone);
1109 QCOMPARE(dt2.timeZone(), cet);
1110 }
1111#endif
1112
1113 // ### test invalid QDateTime()
1114}
1115
1116
1117void tst_QDateTime::addMonths_data()
1118{
1119 QTest::addColumn<int>(name: "months");
1120 QTest::addColumn<QDate>(name: "resultDate");
1121
1122 QTest::newRow(dataTag: "-15") << -15 << QDate(2002, 10, 31);
1123 QTest::newRow(dataTag: "-14") << -14 << QDate(2002, 11, 30);
1124 QTest::newRow(dataTag: "-13") << -13 << QDate(2002, 12, 31);
1125 QTest::newRow(dataTag: "-12") << -12 << QDate(2003, 1, 31);
1126
1127 QTest::newRow(dataTag: "-11") << -11 << QDate(2003, 2, 28);
1128 QTest::newRow(dataTag: "-10") << -10 << QDate(2003, 3, 31);
1129 QTest::newRow(dataTag: "-9") << -9 << QDate(2003, 4, 30);
1130 QTest::newRow(dataTag: "-8") << -8 << QDate(2003, 5, 31);
1131 QTest::newRow(dataTag: "-7") << -7 << QDate(2003, 6, 30);
1132 QTest::newRow(dataTag: "-6") << -6 << QDate(2003, 7, 31);
1133 QTest::newRow(dataTag: "-5") << -5 << QDate(2003, 8, 31);
1134 QTest::newRow(dataTag: "-4") << -4 << QDate(2003, 9, 30);
1135 QTest::newRow(dataTag: "-3") << -3 << QDate(2003, 10, 31);
1136 QTest::newRow(dataTag: "-2") << -2 << QDate(2003, 11, 30);
1137 QTest::newRow(dataTag: "-1") << -1 << QDate(2003, 12, 31);
1138 QTest::newRow(dataTag: "0") << 0 << QDate(2004, 1, 31);
1139 QTest::newRow(dataTag: "1") << 1 << QDate(2004, 2, 29);
1140 QTest::newRow(dataTag: "2") << 2 << QDate(2004, 3, 31);
1141 QTest::newRow(dataTag: "3") << 3 << QDate(2004, 4, 30);
1142 QTest::newRow(dataTag: "4") << 4 << QDate(2004, 5, 31);
1143 QTest::newRow(dataTag: "5") << 5 << QDate(2004, 6, 30);
1144 QTest::newRow(dataTag: "6") << 6 << QDate(2004, 7, 31);
1145 QTest::newRow(dataTag: "7") << 7 << QDate(2004, 8, 31);
1146 QTest::newRow(dataTag: "8") << 8 << QDate(2004, 9, 30);
1147 QTest::newRow(dataTag: "9") << 9 << QDate(2004, 10, 31);
1148 QTest::newRow(dataTag: "10") << 10 << QDate(2004, 11, 30);
1149 QTest::newRow(dataTag: "11") << 11 << QDate(2004, 12, 31);
1150 QTest::newRow(dataTag: "12") << 12 << QDate(2005, 1, 31);
1151 QTest::newRow(dataTag: "13") << 13 << QDate(2005, 2, 28);
1152 QTest::newRow(dataTag: "14") << 14 << QDate(2005, 3, 31);
1153 QTest::newRow(dataTag: "15") << 15 << QDate(2005, 4, 30);
1154}
1155
1156void tst_QDateTime::addMonths()
1157{
1158 QFETCH(int, months);
1159 QFETCH(QDate, resultDate);
1160
1161 QDate testDate(2004, 1, 31);
1162 QTime testTime(12, 34, 56);
1163 QDateTime start(testDate, testTime);
1164 QDateTime end = start.addMonths(months);
1165 QCOMPARE(end.date(), resultDate);
1166 QCOMPARE(end.time(), testTime);
1167 QCOMPARE(end.timeSpec(), Qt::LocalTime);
1168
1169 start = QDateTime(testDate, testTime, Qt::UTC);
1170 end = start.addMonths(months);
1171 QCOMPARE(end.date(), resultDate);
1172 QCOMPARE(end.time(), testTime);
1173 QCOMPARE(end.timeSpec(), Qt::UTC);
1174
1175 start = QDateTime(testDate, testTime, Qt::OffsetFromUTC, 60 * 60);
1176 end = start.addMonths(months);
1177 QCOMPARE(end.date(), resultDate);
1178 QCOMPARE(end.time(), testTime);
1179 QCOMPARE(end.timeSpec(), Qt::OffsetFromUTC);
1180 QCOMPARE(end.offsetFromUtc(), 60 * 60);
1181}
1182
1183void tst_QDateTime::addYears_data()
1184{
1185 QTest::addColumn<int>(name: "years1");
1186 QTest::addColumn<int>(name: "years2");
1187 QTest::addColumn<QDate>(name: "startDate");
1188 QTest::addColumn<QDate>(name: "resultDate");
1189
1190 QTest::newRow(dataTag: "0") << 0 << 0 << QDate(1752, 9, 14) << QDate(1752, 9, 14);
1191 QTest::newRow(dataTag: "4000 - 4000") << 4000 << -4000 << QDate(1752, 9, 14) << QDate(1752, 9, 14);
1192 QTest::newRow(dataTag: "10") << 10 << 0 << QDate(1752, 9, 14) << QDate(1762, 9, 14);
1193 QTest::newRow(dataTag: "0 leap year") << 0 << 0 << QDate(1760, 2, 29) << QDate(1760, 2, 29);
1194 QTest::newRow(dataTag: "1 leap year") << 1 << 0 << QDate(1760, 2, 29) << QDate(1761, 2, 28);
1195 QTest::newRow(dataTag: "2 leap year") << 2 << 0 << QDate(1760, 2, 29) << QDate(1762, 2, 28);
1196 QTest::newRow(dataTag: "3 leap year") << 3 << 0 << QDate(1760, 2, 29) << QDate(1763, 2, 28);
1197 QTest::newRow(dataTag: "4 leap year") << 4 << 0 << QDate(1760, 2, 29) << QDate(1764, 2, 29);
1198
1199 QTest::newRow(dataTag: "toNegative1") << -2000 << 0 << QDate(1752, 9, 14) << QDate(-249, 9, 14);
1200 QTest::newRow(dataTag: "toNegative2") << -1752 << 0 << QDate(1752, 9, 14) << QDate(-1, 9, 14);
1201 QTest::newRow(dataTag: "toNegative3") << -1751 << 0 << QDate(1752, 9, 14) << QDate(1, 9, 14);
1202 QTest::newRow(dataTag: "toPositive1") << 2000 << 0 << QDate(-1752, 9, 14) << QDate(249, 9, 14);
1203 QTest::newRow(dataTag: "toPositive2") << 1752 << 0 << QDate(-1752, 9, 14) << QDate(1, 9, 14);
1204 QTest::newRow(dataTag: "toPositive3") << 1751 << 0 << QDate(-1752, 9, 14) << QDate(-1, 9, 14);
1205}
1206
1207void tst_QDateTime::addYears()
1208{
1209 QFETCH(int, years1);
1210 QFETCH(int, years2);
1211 QFETCH(QDate, startDate);
1212 QFETCH(QDate, resultDate);
1213
1214 QTime testTime(14, 25, 36);
1215 QDateTime start(startDate, testTime);
1216 QDateTime end = start.addYears(years: years1).addYears(years: years2);
1217 QCOMPARE(end.date(), resultDate);
1218 QCOMPARE(end.time(), testTime);
1219 QCOMPARE(end.timeSpec(), Qt::LocalTime);
1220
1221 start = QDateTime(startDate, testTime, Qt::UTC);
1222 end = start.addYears(years: years1).addYears(years: years2);
1223 QCOMPARE(end.date(), resultDate);
1224 QCOMPARE(end.time(), testTime);
1225 QCOMPARE(end.timeSpec(), Qt::UTC);
1226
1227 start = QDateTime(startDate, testTime, Qt::OffsetFromUTC, 60 * 60);
1228 end = start.addYears(years: years1).addYears(years: years2);
1229 QCOMPARE(end.date(), resultDate);
1230 QCOMPARE(end.time(), testTime);
1231 QCOMPARE(end.timeSpec(), Qt::OffsetFromUTC);
1232 QCOMPARE(end.offsetFromUtc(), 60 * 60);
1233}
1234
1235void tst_QDateTime::addSecs_data()
1236{
1237 QTest::addColumn<QDateTime>(name: "dt");
1238 QTest::addColumn<int>(name: "nsecs");
1239 QTest::addColumn<QDateTime>(name: "result");
1240
1241 QTime standardTime(12, 34, 56);
1242 QTime daylightTime(13, 34, 56);
1243
1244 QTest::newRow(dataTag: "utc0") << QDateTime(QDate(2004, 1, 1), standardTime, Qt::UTC) << 86400
1245 << QDateTime(QDate(2004, 1, 2), standardTime, Qt::UTC);
1246 QTest::newRow(dataTag: "utc1") << QDateTime(QDate(2004, 1, 1), standardTime, Qt::UTC) << (86400 * 185)
1247 << QDateTime(QDate(2004, 7, 4), standardTime, Qt::UTC);
1248 QTest::newRow(dataTag: "utc2") << QDateTime(QDate(2004, 1, 1), standardTime, Qt::UTC) << (86400 * 366)
1249 << QDateTime(QDate(2005, 1, 1), standardTime, Qt::UTC);
1250 QTest::newRow(dataTag: "utc3") << QDateTime(QDate(1760, 1, 1), standardTime, Qt::UTC) << 86400
1251 << QDateTime(QDate(1760, 1, 2), standardTime, Qt::UTC);
1252 QTest::newRow(dataTag: "utc4") << QDateTime(QDate(1760, 1, 1), standardTime, Qt::UTC) << (86400 * 185)
1253 << QDateTime(QDate(1760, 7, 4), standardTime, Qt::UTC);
1254 QTest::newRow(dataTag: "utc5") << QDateTime(QDate(1760, 1, 1), standardTime, Qt::UTC) << (86400 * 366)
1255 << QDateTime(QDate(1761, 1, 1), standardTime, Qt::UTC);
1256 QTest::newRow(dataTag: "utc6") << QDateTime(QDate(4000, 1, 1), standardTime, Qt::UTC) << 86400
1257 << QDateTime(QDate(4000, 1, 2), standardTime, Qt::UTC);
1258 QTest::newRow(dataTag: "utc7") << QDateTime(QDate(4000, 1, 1), standardTime, Qt::UTC) << (86400 * 185)
1259 << QDateTime(QDate(4000, 7, 4), standardTime, Qt::UTC);
1260 QTest::newRow(dataTag: "utc8") << QDateTime(QDate(4000, 1, 1), standardTime, Qt::UTC) << (86400 * 366)
1261 << QDateTime(QDate(4001, 1, 1), standardTime, Qt::UTC);
1262 QTest::newRow(dataTag: "utc9") << QDateTime(QDate(4000, 1, 1), standardTime, Qt::UTC) << 0
1263 << QDateTime(QDate(4000, 1, 1), standardTime, Qt::UTC);
1264
1265 if (zoneIsCET) {
1266 QTest::newRow(dataTag: "cet0") << QDateTime(QDate(2004, 1, 1), standardTime, Qt::LocalTime) << 86400
1267 << QDateTime(QDate(2004, 1, 2), standardTime, Qt::LocalTime);
1268 QTest::newRow(dataTag: "cet1") << QDateTime(QDate(2004, 1, 1), standardTime, Qt::LocalTime) << (86400 * 185)
1269 << QDateTime(QDate(2004, 7, 4), daylightTime, Qt::LocalTime);
1270 QTest::newRow(dataTag: "cet2") << QDateTime(QDate(2004, 1, 1), standardTime, Qt::LocalTime) << (86400 * 366)
1271 << QDateTime(QDate(2005, 1, 1), standardTime, Qt::LocalTime);
1272 QTest::newRow(dataTag: "cet3") << QDateTime(QDate(1760, 1, 1), standardTime, Qt::LocalTime) << 86400
1273 << QDateTime(QDate(1760, 1, 2), standardTime, Qt::LocalTime);
1274 QTest::newRow(dataTag: "cet4") << QDateTime(QDate(1760, 1, 1), standardTime, Qt::LocalTime) << (86400 * 185)
1275 << QDateTime(QDate(1760, 7, 4), standardTime, Qt::LocalTime);
1276 QTest::newRow(dataTag: "cet5") << QDateTime(QDate(1760, 1, 1), standardTime, Qt::LocalTime) << (86400 * 366)
1277 << QDateTime(QDate(1761, 1, 1), standardTime, Qt::LocalTime);
1278 QTest::newRow(dataTag: "cet6") << QDateTime(QDate(4000, 1, 1), standardTime, Qt::LocalTime) << 86400
1279 << QDateTime(QDate(4000, 1, 2), standardTime, Qt::LocalTime);
1280 QTest::newRow(dataTag: "cet7") << QDateTime(QDate(4000, 1, 1), standardTime, Qt::LocalTime) << (86400 * 185)
1281 << QDateTime(QDate(4000, 7, 4), daylightTime, Qt::LocalTime);
1282 QTest::newRow(dataTag: "cet8") << QDateTime(QDate(4000, 1, 1), standardTime, Qt::LocalTime) << (86400 * 366)
1283 << QDateTime(QDate(4001, 1, 1), standardTime, Qt::LocalTime);
1284 QTest::newRow(dataTag: "cet9") << QDateTime(QDate(4000, 1, 1), standardTime, Qt::LocalTime) << 0
1285 << QDateTime(QDate(4000, 1, 1), standardTime, Qt::LocalTime);
1286 }
1287
1288 // Year sign change
1289 QTest::newRow(dataTag: "toNegative") << QDateTime(QDate(1, 1, 1), QTime(0, 0, 0), Qt::UTC)
1290 << -1
1291 << QDateTime(QDate(-1, 12, 31), QTime(23, 59, 59), Qt::UTC);
1292 QTest::newRow(dataTag: "toPositive") << QDateTime(QDate(-1, 12, 31), QTime(23, 59, 59), Qt::UTC)
1293 << 1
1294 << QDateTime(QDate(1, 1, 1), QTime(0, 0, 0), Qt::UTC);
1295
1296 QTest::newRow(dataTag: "invalid") << invalidDateTime() << 1 << invalidDateTime();
1297
1298 // Check Offset details are preserved
1299 QTest::newRow(dataTag: "offset0") << QDateTime(QDate(2013, 1, 1), QTime(1, 2, 3),
1300 Qt::OffsetFromUTC, 60 * 60)
1301 << 60 * 60
1302 << QDateTime(QDate(2013, 1, 1), QTime(2, 2, 3),
1303 Qt::OffsetFromUTC, 60 * 60);
1304}
1305
1306void tst_QDateTime::addSecs()
1307{
1308 QFETCH(QDateTime, dt);
1309 QFETCH(int, nsecs);
1310 QFETCH(QDateTime, result);
1311 QDateTime test = dt.addSecs(secs: nsecs);
1312 QCOMPARE(test, result);
1313 QCOMPARE(test.timeSpec(), dt.timeSpec());
1314 if (test.timeSpec() == Qt::OffsetFromUTC)
1315 QCOMPARE(test.offsetFromUtc(), dt.offsetFromUtc());
1316 QCOMPARE(result.addSecs(-nsecs), dt);
1317}
1318
1319void tst_QDateTime::addMSecs_data()
1320{
1321 addSecs_data();
1322}
1323
1324void tst_QDateTime::addMSecs()
1325{
1326 QFETCH(QDateTime, dt);
1327 QFETCH(int, nsecs);
1328 QFETCH(QDateTime, result);
1329
1330 QDateTime test = dt.addMSecs(msecs: qint64(nsecs) * 1000);
1331 QCOMPARE(test, result);
1332 QCOMPARE(test.timeSpec(), dt.timeSpec());
1333 if (test.timeSpec() == Qt::OffsetFromUTC)
1334 QCOMPARE(test.offsetFromUtc(), dt.offsetFromUtc());
1335 QCOMPARE(result.addMSecs(qint64(-nsecs) * 1000), dt);
1336}
1337
1338void tst_QDateTime::toTimeSpec_data()
1339{
1340 QTest::addColumn<QDateTime>(name: "fromUtc");
1341 QTest::addColumn<QDateTime>(name: "fromLocal");
1342
1343 QTime utcTime(4, 20, 30);
1344 QTime localStandardTime(5, 20, 30);
1345 QTime localDaylightTime(6, 20, 30);
1346
1347 QTest::newRow(dataTag: "winter1") << QDateTime(QDate(2004, 1, 1), utcTime, Qt::UTC)
1348 << QDateTime(QDate(2004, 1, 1), localStandardTime, Qt::LocalTime);
1349 QTest::newRow(dataTag: "winter2") << QDateTime(QDate(2004, 2, 29), utcTime, Qt::UTC)
1350 << QDateTime(QDate(2004, 2, 29), localStandardTime, Qt::LocalTime);
1351 QTest::newRow(dataTag: "winter3") << QDateTime(QDate(1760, 2, 29), utcTime, Qt::UTC)
1352 << QDateTime(QDate(1760, 2, 29), localStandardTime, Qt::LocalTime);
1353 QTest::newRow(dataTag: "winter4") << QDateTime(QDate(6000, 2, 29), utcTime, Qt::UTC)
1354 << QDateTime(QDate(6000, 2, 29), localStandardTime, Qt::LocalTime);
1355
1356 // Test mktime boundaries (1970 - 2038) and adjustDate().
1357 QTest::newRow(dataTag: "1969/12/31 23:00 UTC")
1358 << QDateTime(QDate(1969, 12, 31), QTime(23, 0, 0), Qt::UTC)
1359 << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 0), Qt::LocalTime);
1360 QTest::newRow(dataTag: "2037/12/31 23:00 UTC")
1361 << QDateTime(QDate(2037, 12, 31), QTime(23, 0, 0), Qt::UTC)
1362 << QDateTime(QDate(2038, 1, 1), QTime(0, 0, 0), Qt::LocalTime);
1363
1364 QTest::newRow(dataTag: "-271821/4/20 00:00 UTC (JavaScript min date, start of day)")
1365 << QDateTime(QDate(-271821, 4, 20), QTime(0, 0, 0), Qt::UTC)
1366 << QDateTime(QDate(-271821, 4, 20), QTime(1, 0, 0), Qt::LocalTime);
1367 QTest::newRow(dataTag: "-271821/4/20 23:00 UTC (JavaScript min date, end of day)")
1368 << QDateTime(QDate(-271821, 4, 20), QTime(23, 0, 0), Qt::UTC)
1369 << QDateTime(QDate(-271821, 4, 21), QTime(0, 0, 0), Qt::LocalTime);
1370
1371 if (zoneIsCET) {
1372 QTest::newRow(dataTag: "summer1") << QDateTime(QDate(2004, 6, 30), utcTime, Qt::UTC)
1373 << QDateTime(QDate(2004, 6, 30), localDaylightTime, Qt::LocalTime);
1374 QTest::newRow(dataTag: "summer2") << QDateTime(QDate(1760, 6, 30), utcTime, Qt::UTC)
1375 << QDateTime(QDate(1760, 6, 30), localStandardTime, Qt::LocalTime);
1376 QTest::newRow(dataTag: "summer3") << QDateTime(QDate(4000, 6, 30), utcTime, Qt::UTC)
1377 << QDateTime(QDate(4000, 6, 30), localDaylightTime, Qt::LocalTime);
1378
1379 QTest::newRow(dataTag: "275760/9/23 00:00 UTC (JavaScript max date, start of day)")
1380 << QDateTime(QDate(275760, 9, 23), QTime(0, 0, 0), Qt::UTC)
1381 << QDateTime(QDate(275760, 9, 23), QTime(2, 0, 0), Qt::LocalTime);
1382
1383 QTest::newRow(dataTag: "275760/9/23 22:00 UTC (JavaScript max date, end of day)")
1384 << QDateTime(QDate(275760, 9, 23), QTime(22, 0, 0), Qt::UTC)
1385 << QDateTime(QDate(275760, 9, 24), QTime(0, 0, 0), Qt::LocalTime);
1386 }
1387
1388 QTest::newRow(dataTag: "msec") << QDateTime(QDate(4000, 6, 30), utcTime.addMSecs(ms: 1), Qt::UTC)
1389 << QDateTime(QDate(4000, 6, 30), localDaylightTime.addMSecs(ms: 1), Qt::LocalTime);
1390}
1391
1392void tst_QDateTime::toTimeSpec()
1393{
1394 if (zoneIsCET) {
1395 QFETCH(QDateTime, fromUtc);
1396 QFETCH(QDateTime, fromLocal);
1397
1398 QDateTime utcToUtc = fromUtc.toTimeSpec(spec: Qt::UTC);
1399 QDateTime localToLocal = fromLocal.toTimeSpec(spec: Qt::LocalTime);
1400 QDateTime utcToLocal = fromUtc.toTimeSpec(spec: Qt::LocalTime);
1401 QDateTime localToUtc = fromLocal.toTimeSpec(spec: Qt::UTC);
1402 QDateTime utcToOffset = fromUtc.toTimeSpec(spec: Qt::OffsetFromUTC);
1403 QDateTime localToOffset = fromLocal.toTimeSpec(spec: Qt::OffsetFromUTC);
1404
1405 QCOMPARE(utcToUtc, fromUtc);
1406 QCOMPARE(utcToUtc.date(), fromUtc.date());
1407 QCOMPARE(utcToUtc.time(), fromUtc.time());
1408 QCOMPARE(utcToUtc.timeSpec(), Qt::UTC);
1409
1410 QCOMPARE(localToLocal, fromLocal);
1411 QCOMPARE(localToLocal.date(), fromLocal.date());
1412 QCOMPARE(localToLocal.time(), fromLocal.time());
1413 QCOMPARE(localToLocal.timeSpec(), Qt::LocalTime);
1414
1415 QCOMPARE(utcToLocal, fromLocal);
1416 QCOMPARE(utcToLocal.date(), fromLocal.date());
1417 QCOMPARE(utcToLocal.time(), fromLocal.time());
1418 QCOMPARE(utcToLocal.timeSpec(), Qt::LocalTime);
1419 QCOMPARE(utcToLocal.toTimeSpec(Qt::UTC), fromUtc);
1420
1421 QCOMPARE(localToUtc, fromUtc);
1422 QCOMPARE(localToUtc.date(), fromUtc.date());
1423 QCOMPARE(localToUtc.time(), fromUtc.time());
1424 QCOMPARE(localToUtc.timeSpec(), Qt::UTC);
1425 QCOMPARE(localToUtc.toTimeSpec(Qt::LocalTime), fromLocal);
1426
1427 QCOMPARE(utcToUtc, localToUtc);
1428 QCOMPARE(utcToUtc.date(), localToUtc.date());
1429 QCOMPARE(utcToUtc.time(), localToUtc.time());
1430 QCOMPARE(utcToUtc.timeSpec(), Qt::UTC);
1431
1432 QCOMPARE(utcToLocal, localToLocal);
1433 QCOMPARE(utcToLocal.date(), localToLocal.date());
1434 QCOMPARE(utcToLocal.time(), localToLocal.time());
1435 QCOMPARE(utcToLocal.timeSpec(), Qt::LocalTime);
1436
1437 // OffsetToUTC becomes UTC
1438 QCOMPARE(utcToOffset, fromUtc);
1439 QCOMPARE(utcToOffset.date(), fromUtc.date());
1440 QCOMPARE(utcToOffset.time(), fromUtc.time());
1441 QCOMPARE(utcToOffset.timeSpec(), Qt::UTC);
1442 QCOMPARE(utcToOffset.toTimeSpec(Qt::UTC), fromUtc);
1443
1444 QCOMPARE(localToOffset, fromUtc);
1445 QCOMPARE(localToOffset.date(), fromUtc.date());
1446 QCOMPARE(localToOffset.time(), fromUtc.time());
1447 QCOMPARE(localToOffset.timeSpec(), Qt::UTC);
1448 QCOMPARE(localToOffset.toTimeSpec(Qt::LocalTime), fromLocal);
1449 } else {
1450 QSKIP("Not tested with timezone other than Central European (CET/CEST)");
1451 }
1452}
1453
1454void tst_QDateTime::toLocalTime_data()
1455{
1456 toTimeSpec_data();
1457}
1458
1459void tst_QDateTime::toLocalTime()
1460{
1461 if (zoneIsCET) {
1462 QFETCH(QDateTime, fromUtc);
1463 QFETCH(QDateTime, fromLocal);
1464
1465 QCOMPARE(fromLocal.toLocalTime(), fromLocal);
1466 QCOMPARE(fromUtc.toLocalTime(), fromLocal);
1467 QCOMPARE(fromUtc.toLocalTime(), fromLocal.toLocalTime());
1468 } else {
1469 QSKIP("Not tested with timezone other than Central European (CET/CEST)");
1470 }
1471}
1472
1473void tst_QDateTime::toUTC_data()
1474{
1475 toTimeSpec_data();
1476}
1477
1478void tst_QDateTime::toUTC()
1479{
1480 if (zoneIsCET) {
1481 QFETCH(QDateTime, fromUtc);
1482 QFETCH(QDateTime, fromLocal);
1483
1484 QCOMPARE(fromUtc.toUTC(), fromUtc);
1485 QCOMPARE(fromLocal.toUTC(), fromUtc);
1486 QCOMPARE(fromUtc.toUTC(), fromLocal.toUTC());
1487 } else {
1488 QSKIP("Not tested with timezone other than Central European (CET/CEST)");
1489 }
1490
1491 QDateTime dt = QDateTime::currentDateTime();
1492 if(dt.time().msec() == 0){
1493 dt.setTime(dt.time().addMSecs(ms: 1));
1494 }
1495 QString s = dt.toString(format: "zzz");
1496 QString t = dt.toUTC().toString(format: "zzz");
1497 QCOMPARE(s, t);
1498}
1499
1500void tst_QDateTime::daysTo()
1501{
1502 QDateTime dt1(QDate(1760, 1, 2), QTime());
1503 QDateTime dt2(QDate(1760, 2, 2), QTime());
1504 QDateTime dt3(QDate(1760, 3, 2), QTime());
1505
1506 QCOMPARE(dt1.daysTo(dt2), (qint64) 31);
1507 QCOMPARE(dt1.addDays(31), dt2);
1508
1509 QCOMPARE(dt2.daysTo(dt3), (qint64) 29);
1510 QCOMPARE(dt2.addDays(29), dt3);
1511
1512 QCOMPARE(dt1.daysTo(dt3), (qint64) 60);
1513 QCOMPARE(dt1.addDays(60), dt3);
1514
1515 QCOMPARE(dt2.daysTo(dt1), (qint64) -31);
1516 QCOMPARE(dt2.addDays(-31), dt1);
1517
1518 QCOMPARE(dt3.daysTo(dt2), (qint64) -29);
1519 QCOMPARE(dt3.addDays(-29), dt2);
1520
1521 QCOMPARE(dt3.daysTo(dt1), (qint64) -60);
1522 QCOMPARE(dt3.addDays(-60), dt1);
1523}
1524
1525void tst_QDateTime::secsTo_data()
1526{
1527 addSecs_data();
1528
1529 QTest::newRow(dataTag: "disregard milliseconds #1")
1530 << QDateTime(QDate(2012, 3, 7), QTime(0, 58, 0, 0)) << 60
1531 << QDateTime(QDate(2012, 3, 7), QTime(0, 59, 0, 400));
1532
1533 QTest::newRow(dataTag: "disregard milliseconds #2")
1534 << QDateTime(QDate(2012, 3, 7), QTime(0, 59, 0, 0)) << 60
1535 << QDateTime(QDate(2012, 3, 7), QTime(1, 0, 0, 400));
1536}
1537
1538void tst_QDateTime::secsTo()
1539{
1540 QFETCH(QDateTime, dt);
1541 QFETCH(int, nsecs);
1542 QFETCH(QDateTime, result);
1543
1544 if (dt.isValid()) {
1545 QCOMPARE(dt.secsTo(result), (qint64)nsecs);
1546 QCOMPARE(result.secsTo(dt), (qint64)-nsecs);
1547 QVERIFY((dt == result) == (0 == nsecs));
1548 QVERIFY((dt != result) == (0 != nsecs));
1549 QVERIFY((dt < result) == (0 < nsecs));
1550 QVERIFY((dt <= result) == (0 <= nsecs));
1551 QVERIFY((dt > result) == (0 > nsecs));
1552 QVERIFY((dt >= result) == (0 >= nsecs));
1553 } else {
1554 QVERIFY(dt.secsTo(result) == 0);
1555 QVERIFY(result.secsTo(dt) == 0);
1556 }
1557}
1558
1559void tst_QDateTime::msecsTo_data()
1560{
1561 addMSecs_data();
1562}
1563
1564void tst_QDateTime::msecsTo()
1565{
1566 QFETCH(QDateTime, dt);
1567 QFETCH(int, nsecs);
1568 QFETCH(QDateTime, result);
1569
1570 if (dt.isValid()) {
1571 QCOMPARE(dt.msecsTo(result), qint64(nsecs) * 1000);
1572 QCOMPARE(result.msecsTo(dt), -qint64(nsecs) * 1000);
1573 QVERIFY((dt == result) == (0 == (qint64(nsecs) * 1000)));
1574 QVERIFY((dt != result) == (0 != (qint64(nsecs) * 1000)));
1575 QVERIFY((dt < result) == (0 < (qint64(nsecs) * 1000)));
1576 QVERIFY((dt <= result) == (0 <= (qint64(nsecs) * 1000)));
1577 QVERIFY((dt > result) == (0 > (qint64(nsecs) * 1000)));
1578 QVERIFY((dt >= result) == (0 >= (qint64(nsecs) * 1000)));
1579 } else {
1580 QVERIFY(dt.msecsTo(result) == 0);
1581 QVERIFY(result.msecsTo(dt) == 0);
1582 }
1583}
1584
1585void tst_QDateTime::currentDateTime()
1586{
1587 time_t buf1, buf2;
1588 ::time(timer: &buf1);
1589 QDateTime lowerBound;
1590 lowerBound.setSecsSinceEpoch(buf1);
1591
1592 QDateTime dt1 = QDateTime::currentDateTime();
1593 QDateTime dt2 = QDateTime::currentDateTime().toLocalTime();
1594 QDateTime dt3 = QDateTime::currentDateTime().toUTC();
1595
1596 ::time(timer: &buf2);
1597
1598 QDateTime upperBound;
1599 upperBound.setSecsSinceEpoch(buf2);
1600 // Note we must add 2 seconds here because time() may return up to
1601 // 1 second difference from the more accurate method used by QDateTime::currentDateTime()
1602 upperBound = upperBound.addSecs(secs: 2);
1603
1604 QString details = QString("\n"
1605 "lowerBound: %1\n"
1606 "dt1: %2\n"
1607 "dt2: %3\n"
1608 "dt3: %4\n"
1609 "upperBound: %5\n")
1610 .arg(a: lowerBound.toSecsSinceEpoch())
1611 .arg(a: dt1.toSecsSinceEpoch())
1612 .arg(a: dt2.toSecsSinceEpoch())
1613 .arg(a: dt3.toSecsSinceEpoch())
1614 .arg(a: upperBound.toSecsSinceEpoch());
1615
1616 QVERIFY2(lowerBound < upperBound, qPrintable(details));
1617
1618 QVERIFY2(lowerBound <= dt1, qPrintable(details));
1619 QVERIFY2(dt1 < upperBound, qPrintable(details));
1620 QVERIFY2(lowerBound <= dt2, qPrintable(details));
1621 QVERIFY2(dt2 < upperBound, qPrintable(details));
1622 QVERIFY2(lowerBound <= dt3, qPrintable(details));
1623 QVERIFY2(dt3 < upperBound, qPrintable(details));
1624
1625 QVERIFY(dt1.timeSpec() == Qt::LocalTime);
1626 QVERIFY(dt2.timeSpec() == Qt::LocalTime);
1627 QVERIFY(dt3.timeSpec() == Qt::UTC);
1628}
1629
1630void tst_QDateTime::currentDateTimeUtc()
1631{
1632 time_t buf1, buf2;
1633 ::time(timer: &buf1);
1634
1635 QDateTime lowerBound;
1636 lowerBound.setSecsSinceEpoch(buf1);
1637
1638 QDateTime dt1 = QDateTime::currentDateTimeUtc();
1639 QDateTime dt2 = QDateTime::currentDateTimeUtc().toLocalTime();
1640 QDateTime dt3 = QDateTime::currentDateTimeUtc().toUTC();
1641
1642 ::time(timer: &buf2);
1643
1644 QDateTime upperBound;
1645 upperBound.setSecsSinceEpoch(buf2);
1646 // Note we must add 2 seconds here because time() may return up to
1647 // 1 second difference from the more accurate method used by QDateTime::currentDateTime()
1648 upperBound = upperBound.addSecs(secs: 2);
1649
1650 QString details = QString("\n"
1651 "lowerBound: %1\n"
1652 "dt1: %2\n"
1653 "dt2: %3\n"
1654 "dt3: %4\n"
1655 "upperBound: %5\n")
1656 .arg(a: lowerBound.toSecsSinceEpoch())
1657 .arg(a: dt1.toSecsSinceEpoch())
1658 .arg(a: dt2.toSecsSinceEpoch())
1659 .arg(a: dt3.toSecsSinceEpoch())
1660 .arg(a: upperBound.toSecsSinceEpoch());
1661
1662 QVERIFY2(lowerBound < upperBound, qPrintable(details));
1663
1664 QVERIFY2(lowerBound <= dt1, qPrintable(details));
1665 QVERIFY2(dt1 < upperBound, qPrintable(details));
1666 QVERIFY2(lowerBound <= dt2, qPrintable(details));
1667 QVERIFY2(dt2 < upperBound, qPrintable(details));
1668 QVERIFY2(lowerBound <= dt3, qPrintable(details));
1669 QVERIFY2(dt3 < upperBound, qPrintable(details));
1670
1671 QVERIFY(dt1.timeSpec() == Qt::UTC);
1672 QVERIFY(dt2.timeSpec() == Qt::LocalTime);
1673 QVERIFY(dt3.timeSpec() == Qt::UTC);
1674}
1675
1676void tst_QDateTime::currentDateTimeUtc2()
1677{
1678 QDateTime local, utc;
1679 qint64 msec;
1680
1681 // check that we got all down to the same milliseconds
1682 int i = 20;
1683 bool ok = false;
1684 do {
1685 local = QDateTime::currentDateTime();
1686 utc = QDateTime::currentDateTimeUtc();
1687 msec = QDateTime::currentMSecsSinceEpoch();
1688 ok = local.time().msec() == utc.time().msec()
1689 && utc.time().msec() == (msec % 1000);
1690 } while (--i && !ok);
1691
1692 if (!i)
1693 QSKIP("Failed to get the dates within 1 ms of each other");
1694
1695 // seconds and milliseconds should be the same:
1696 QCOMPARE(utc.time().second(), local.time().second());
1697 QCOMPARE(utc.time().msec(), local.time().msec());
1698 QCOMPARE(msec % 1000, qint64(local.time().msec()));
1699 QCOMPARE(msec / 1000 % 60, qint64(local.time().second()));
1700
1701 // the two dates should be equal, actually
1702 QCOMPARE(local.toUTC(), utc);
1703 QCOMPARE(utc.toLocalTime(), local);
1704
1705 // and finally, the SecsSinceEpoch should equal our number
1706 QCOMPARE(qint64(utc.toSecsSinceEpoch()), msec / 1000);
1707 QCOMPARE(qint64(local.toSecsSinceEpoch()), msec / 1000);
1708 QCOMPARE(utc.toMSecsSinceEpoch(), msec);
1709 QCOMPARE(local.toMSecsSinceEpoch(), msec);
1710}
1711
1712void tst_QDateTime::toSecsSinceEpoch_data()
1713{
1714 QTest::addColumn<QString>(name: "dateTimeStr");
1715 QTest::addColumn<bool>(name: "valid");
1716
1717 QTest::newRow( dataTag: "data1" ) << str( y: 1800, month: 1, d: 1, h: 12, min: 0, s: 0 ) << true;
1718 QTest::newRow( dataTag: "data2" ) << str( y: 1969, month: 1, d: 1, h: 12, min: 0, s: 0 ) << true;
1719 QTest::newRow( dataTag: "data3" ) << str( y: 2002, month: 1, d: 1, h: 12, min: 0, s: 0 ) << true;
1720 QTest::newRow( dataTag: "data4" ) << str( y: 2002, month: 6, d: 1, h: 12, min: 0, s: 0 ) << true;
1721 QTest::newRow( dataTag: "data5" ) << QString("INVALID") << false;
1722 QTest::newRow( dataTag: "data6" ) << str( y: 2038, month: 1, d: 1, h: 12, min: 0, s: 0 ) << true;
1723 QTest::newRow( dataTag: "data7" ) << str( y: 2063, month: 4, d: 5, h: 12, min: 0, s: 0 ) << true; // the day of First Contact
1724 QTest::newRow( dataTag: "data8" ) << str( y: 2107, month: 1, d: 1, h: 12, min: 0, s: 0 ) << true;
1725}
1726
1727void tst_QDateTime::toSecsSinceEpoch()
1728{
1729 QFETCH(const QString, dateTimeStr);
1730 const QDateTime datetime = dt(str: dateTimeStr);
1731 QFETCH(const bool, valid);
1732 QCOMPARE(datetime.isValid(), valid);
1733
1734 if (valid) {
1735 const qint64 asSecsSinceEpoch = datetime.toSecsSinceEpoch();
1736 QCOMPARE(asSecsSinceEpoch, datetime.toMSecsSinceEpoch() / 1000);
1737 QCOMPARE(QDateTime::fromSecsSinceEpoch(asSecsSinceEpoch), datetime);
1738 }
1739}
1740
1741#if QT_DEPRECATED_SINCE(5, 8)
1742void tst_QDateTime::toTime_t_data()
1743{
1744 QTest::addColumn<QString>(name: "dateTimeStr");
1745 QTest::addColumn<bool>(name: "res");
1746
1747 QTest::newRow( dataTag: "data1" ) << str( y: 1800, month: 1, d: 1, h: 12, min: 0, s: 0 ) << false;
1748 QTest::newRow( dataTag: "data2" ) << str( y: 1969, month: 1, d: 1, h: 12, min: 0, s: 0 ) << false;
1749 QTest::newRow( dataTag: "data3" ) << str( y: 2002, month: 1, d: 1, h: 12, min: 0, s: 0 ) << true;
1750 QTest::newRow( dataTag: "data4" ) << str( y: 2002, month: 6, d: 1, h: 12, min: 0, s: 0 ) << true;
1751 QTest::newRow( dataTag: "data5" ) << QString("INVALID") << false;
1752 QTest::newRow( dataTag: "data6" ) << str( y: 2038, month: 1, d: 1, h: 12, min: 0, s: 0 ) << true;
1753 QTest::newRow( dataTag: "data7" ) << str( y: 2063, month: 4, d: 5, h: 12, min: 0, s: 0 ) << true; // the day of First Contact
1754 QTest::newRow( dataTag: "data8" ) << str( y: 2107, month: 1, d: 1, h: 12, min: 0, s: 0 )
1755 << bool( sizeof(uint) > 32 && sizeof(time_t) > 32 );
1756}
1757
1758void tst_QDateTime::toTime_t()
1759{
1760 QFETCH( QString, dateTimeStr );
1761 QDateTime datetime = dt( str: dateTimeStr );
1762
1763 uint asTime_t = datetime.toTime_t();
1764 QFETCH( bool, res );
1765 if (res) {
1766 QVERIFY(asTime_t != uint(-1));
1767 QCOMPARE(QDateTime::fromTime_t(asTime_t), datetime);
1768 } else {
1769 QCOMPARE(asTime_t, uint(-1));
1770 }
1771}
1772#endif
1773
1774void tst_QDateTime::daylightSavingsTimeChange_data()
1775{
1776 QTest::addColumn<QDate>(name: "inDST");
1777 QTest::addColumn<QDate>(name: "outDST");
1778 QTest::addColumn<int>(name: "days"); // from in to out; -ve if reversed
1779 QTest::addColumn<int>(name: "months");
1780
1781 QTest::newRow(dataTag: "Autumn") << QDate(2006, 8, 1) << QDate(2006, 12, 1)
1782 << 122 << 4;
1783
1784 QTest::newRow(dataTag: "Spring") << QDate(2006, 5, 1) << QDate(2006, 2, 1)
1785 << -89 << -3;
1786}
1787
1788void tst_QDateTime::daylightSavingsTimeChange()
1789{
1790 // This has grown from a regression test for an old bug where starting with
1791 // a date in DST and then moving to a date outside it (or vice-versa) caused
1792 // 1-hour jumps in time when addSecs() was called.
1793 //
1794 // The bug was caused by QDateTime knowing more than it lets show.
1795 // Internally, if it knows, QDateTime stores a flag indicating if the time is
1796 // DST or not. If it doesn't, it sets to "LocalUnknown". The problem happened
1797 // because some functions did not reset the flag when moving in or out of DST.
1798
1799 // WARNING: This only tests anything if there's a Daylight Savings Time change
1800 // in the current time-zone between inDST and outDST.
1801 // This is true for Central European Time and may be elsewhere.
1802
1803 QFETCH(QDate, inDST);
1804 QFETCH(QDate, outDST);
1805 QFETCH(int, days);
1806 QFETCH(int, months);
1807
1808 // First with simple construction
1809 QDateTime dt = QDateTime(outDST, QTime(0, 0, 0), Qt::LocalTime);
1810 int outDSTsecs = dt.toSecsSinceEpoch();
1811
1812 dt.setDate(inDST);
1813 dt = dt.addSecs(secs: 1);
1814 QCOMPARE(dt, QDateTime(inDST, QTime(0, 0, 1)));
1815
1816 // now using addDays:
1817 dt = dt.addDays(days).addSecs(secs: 1);
1818 QCOMPARE(dt, QDateTime(outDST, QTime(0, 0, 2)));
1819
1820 // ... and back again:
1821 dt = dt.addDays(days: -days).addSecs(secs: 1);
1822 QCOMPARE(dt, QDateTime(inDST, QTime(0, 0, 3)));
1823
1824 // now using addMonths:
1825 dt = dt.addMonths(months).addSecs(secs: 1);
1826 QCOMPARE(dt, QDateTime(outDST, QTime(0, 0, 4)));
1827
1828 // ... and back again:
1829 dt = dt.addMonths(months: -months).addSecs(secs: 1);
1830 QCOMPARE(dt, QDateTime(inDST, QTime(0, 0, 5)));
1831
1832 // now using fromSecsSinceEpoch
1833 dt = QDateTime::fromSecsSinceEpoch(secs: outDSTsecs);
1834 QCOMPARE(dt, QDateTime(outDST, QTime(0, 0, 0)));
1835
1836 dt.setDate(inDST);
1837 dt = dt.addSecs(secs: 60);
1838 QCOMPARE(dt, QDateTime(inDST, QTime(0, 1, 0)));
1839
1840 // using addMonths:
1841 dt = dt.addMonths(months).addSecs(secs: 60);
1842 QCOMPARE(dt, QDateTime(outDST, QTime(0, 2, 0)));
1843 // back again:
1844 dt = dt.addMonths(months: -months).addSecs(secs: 60);
1845 QCOMPARE(dt, QDateTime(inDST, QTime(0, 3, 0)));
1846
1847 // using addDays:
1848 dt = dt.addDays(days).addSecs(secs: 60);
1849 QCOMPARE(dt, QDateTime(outDST, QTime(0, 4, 0)));
1850 // back again:
1851 dt = dt.addDays(days: -days).addSecs(secs: 60);
1852 QCOMPARE(dt, QDateTime(inDST, QTime(0, 5, 0)));
1853
1854 // Now use the result of a UTC -> LocalTime conversion
1855 dt = QDateTime(outDST, QTime(0, 0, 0), Qt::LocalTime).toUTC();
1856 dt = QDateTime(dt.date(), dt.time(), Qt::UTC).toLocalTime();
1857 QCOMPARE(dt, QDateTime(outDST, QTime(0, 0, 0)));
1858
1859 // using addDays:
1860 dt = dt.addDays(days: -days).addSecs(secs: 3600);
1861 QCOMPARE(dt, QDateTime(inDST, QTime(1, 0, 0)));
1862 // back again
1863 dt = dt.addDays(days).addSecs(secs: 3600);
1864 QCOMPARE(dt, QDateTime(outDST, QTime(2, 0, 0)));
1865
1866 // using addMonths:
1867 dt = dt.addMonths(months: -months).addSecs(secs: 3600);
1868 QCOMPARE(dt, QDateTime(inDST, QTime(3, 0, 0)));
1869 // back again:
1870 dt = dt.addMonths(months).addSecs(secs: 3600);
1871 QCOMPARE(dt, QDateTime(outDST, QTime(4, 0, 0)));
1872
1873 // using setDate:
1874 dt.setDate(inDST);
1875 dt = dt.addSecs(secs: 3600);
1876 QCOMPARE(dt, QDateTime(inDST, QTime(5, 0, 0)));
1877}
1878
1879void tst_QDateTime::springForward_data()
1880{
1881 QTest::addColumn<QDate>(name: "day"); // day of DST transition
1882 QTest::addColumn<QTime>(name: "time"); // in the "missing hour"
1883 QTest::addColumn<int>(name: "step"); // days to step; +ve from before, -ve from after
1884 QTest::addColumn<int>(name: "adjust"); // minutes ahead of UTC on day stepped from
1885
1886 /*
1887 Zone tests compare a summer and winter moment's SecsSinceEpoch to known values.
1888 This could in principle be flawed (two DST-using zones in the same
1889 hemisphere with the same DST and standard times but different transition
1890 times) but no actual example is known where this is a problem. Please
1891 document any such conflicts, if discovered.
1892
1893 See http://www.timeanddate.com/time/zones/ for data on more candidates to
1894 test.
1895 */
1896
1897 uint winter = QDateTime(QDate(2015, 1, 1), QTime()).toSecsSinceEpoch();
1898 uint summer = QDateTime(QDate(2015, 7, 1), QTime()).toSecsSinceEpoch();
1899
1900 if (winter == 1420066800 && summer == 1435701600) {
1901 QTest::newRow(dataTag: "CET from day before") << QDate(2015, 3, 29) << QTime(2, 30, 0) << 1 << 60;
1902 QTest::newRow(dataTag: "CET from day after") << QDate(2015, 3, 29) << QTime(2, 30, 0) << -1 << 120;
1903 } else if (winter == 1420063200 && summer == 1435698000) {
1904 // e.g. Finland, where our CI runs ...
1905 QTest::newRow(dataTag: "EET from day before") << QDate(2015, 3, 29) << QTime(3, 30, 0) << 1 << 120;
1906 QTest::newRow(dataTag: "EET from day after") << QDate(2015, 3, 29) << QTime(3, 30, 0) << -1 << 180;
1907 } else if (winter == 1420070400 && summer == 1435705200) {
1908 // Western European Time, WET/WEST; a.k.a. GMT/BST
1909 QTest::newRow(dataTag: "WET from day before") << QDate(2015, 3, 29) << QTime(1, 30, 0) << 1 << 0;
1910 QTest::newRow(dataTag: "WET from day after") << QDate(2015, 3, 29) << QTime(1, 30, 0) << -1 << 60;
1911 } else if (winter == 1420099200 && summer == 1435734000) {
1912 // Western USA, Canada: Pacific Time (e.g. US/Pacific)
1913 QTest::newRow(dataTag: "PT from day before") << QDate(2015, 3, 8) << QTime(2, 30, 0) << 1 << -480;
1914 QTest::newRow(dataTag: "PT from day after") << QDate(2015, 3, 8) << QTime(2, 30, 0) << -1 << -420;
1915 } else if (winter == 1420088400 && summer == 1435723200) {
1916 // Eastern USA, Canada: Eastern Time (e.g. US/Eastern)
1917 QTest::newRow(dataTag: "ET from day before") << QDate(2015, 3, 8) << QTime(2, 30, 0) << 1 << -300;
1918 QTest::newRow(dataTag: "ET from day after") << QDate(2015, 3, 8) << QTime(2, 30, 0) << -1 << -240;
1919 } else {
1920 // Includes the numbers you need to test for your zone, as above:
1921 QString msg(QString::fromLatin1(str: "No spring forward test data for this TZ (%1, %2)"
1922 ).arg(a: winter).arg(a: summer));
1923 QSKIP(qPrintable(msg));
1924 }
1925}
1926
1927void tst_QDateTime::springForward()
1928{
1929 QFETCH(QDate, day);
1930 QFETCH(QTime, time);
1931 QFETCH(int, step);
1932 QFETCH(int, adjust);
1933
1934 QDateTime direct = QDateTime(day.addDays(days: -step), time, Qt::LocalTime).addDays(days: step);
1935 if (direct.isValid()) { // mktime() may deem a time in the gap invalid
1936 QCOMPARE(direct.date(), day);
1937 QCOMPARE(direct.time().minute(), time.minute());
1938 QCOMPARE(direct.time().second(), time.second());
1939 int off = direct.time().hour() - time.hour();
1940 QVERIFY(off == 1 || off == -1);
1941 // Note: function doc claims always +1, but this should be reviewed !
1942 }
1943
1944 // Repeat, but getting there via .toLocalTime():
1945 QDateTime detour = QDateTime(day.addDays(days: -step),
1946 time.addSecs(secs: -60 * adjust),
1947 Qt::UTC).toLocalTime();
1948 QCOMPARE(detour.time(), time);
1949 detour = detour.addDays(days: step);
1950 // Insist on consistency:
1951 if (direct.isValid())
1952 QCOMPARE(detour, direct);
1953 else
1954 QVERIFY(!detour.isValid());
1955}
1956
1957void tst_QDateTime::operator_eqeq_data()
1958{
1959 QTest::addColumn<QDateTime>(name: "dt1");
1960 QTest::addColumn<QDateTime>(name: "dt2");
1961 QTest::addColumn<bool>(name: "expectEqual");
1962 QTest::addColumn<bool>(name: "checkEuro");
1963
1964 QDateTime dateTime1(QDate(2012, 6, 20), QTime(14, 33, 2, 500));
1965 QDateTime dateTime1a = dateTime1.addMSecs(msecs: 1);
1966 QDateTime dateTime2(QDate(2012, 20, 6), QTime(14, 33, 2, 500)); // Invalid
1967 QDateTime dateTime2a = dateTime2.addMSecs(msecs: -1); // Still invalid
1968 QDateTime dateTime3(QDate(1970, 1, 1), QTime(0, 0, 0, 0), Qt::UTC); // UTC epoch
1969 QDateTime dateTime3a = dateTime3.addDays(days: 1);
1970 QDateTime dateTime3b = dateTime3.addDays(days: -1);
1971 // Ensure that different times may be equal when considering timezone.
1972 QDateTime dateTime3c(dateTime3.addSecs(secs: 3600));
1973 dateTime3c.setOffsetFromUtc(3600);
1974 QDateTime dateTime3d(dateTime3.addSecs(secs: -3600));
1975 dateTime3d.setOffsetFromUtc(-3600);
1976 QDateTime dateTime3e(dateTime3.date(), dateTime3.time()); // Local time's epoch
1977
1978 QTest::newRow(dataTag: "data0") << dateTime1 << dateTime1 << true << false;
1979 QTest::newRow(dataTag: "data1") << dateTime2 << dateTime2 << true << false;
1980 QTest::newRow(dataTag: "data2") << dateTime1a << dateTime1a << true << false;
1981 QTest::newRow(dataTag: "data3") << dateTime1 << dateTime2 << false << false;
1982 QTest::newRow(dataTag: "data4") << dateTime1 << dateTime1a << false << false;
1983 QTest::newRow(dataTag: "data5") << dateTime2 << dateTime2a << true << false;
1984 QTest::newRow(dataTag: "data6") << dateTime2 << dateTime3 << false << false;
1985 QTest::newRow(dataTag: "data7") << dateTime3 << dateTime3a << false << false;
1986 QTest::newRow(dataTag: "data8") << dateTime3 << dateTime3b << false << false;
1987 QTest::newRow(dataTag: "data9") << dateTime3a << dateTime3b << false << false;
1988 QTest::newRow(dataTag: "data10") << dateTime3 << dateTime3c << true << false;
1989 QTest::newRow(dataTag: "data11") << dateTime3 << dateTime3d << true << false;
1990 QTest::newRow(dataTag: "data12") << dateTime3c << dateTime3d << true << false;
1991 if (localTimeType == LocalTimeIsUtc)
1992 QTest::newRow(dataTag: "data13") << dateTime3 << dateTime3e << true << false;
1993 // ... but a zone (sometimes) ahead of or behind UTC (e.g. Europe/London)
1994 // might agree with UTC about the epoch, all the same.
1995
1996 QTest::newRow(dataTag: "invalid == invalid") << invalidDateTime() << invalidDateTime() << true << false;
1997 QTest::newRow(dataTag: "invalid == valid #1") << invalidDateTime() << dateTime1 << false << false;
1998
1999 if (zoneIsCET) {
2000 QTest::newRow(dataTag: "data14") << QDateTime(QDate(2004, 1, 2), QTime(2, 2, 3), Qt::LocalTime)
2001 << QDateTime(QDate(2004, 1, 2), QTime(1, 2, 3), Qt::UTC) << true << true;
2002 }
2003}
2004
2005void tst_QDateTime::operator_eqeq()
2006{
2007 QFETCH(QDateTime, dt1);
2008 QFETCH(QDateTime, dt2);
2009 QFETCH(bool, expectEqual);
2010 QFETCH(bool, checkEuro);
2011
2012 QVERIFY(dt1 == dt1);
2013 QVERIFY(!(dt1 != dt1));
2014
2015 QVERIFY(dt2 == dt2);
2016 QVERIFY(!(dt2 != dt2));
2017
2018 QVERIFY(dt1 != QDateTime::currentDateTime());
2019 QVERIFY(dt2 != QDateTime::currentDateTime());
2020
2021 QVERIFY(dt1.toUTC() == dt1.toUTC());
2022
2023 bool equal = dt1 == dt2;
2024 QCOMPARE(equal, expectEqual);
2025 bool notEqual = dt1 != dt2;
2026 QCOMPARE(notEqual, !expectEqual);
2027
2028 if (equal)
2029 QVERIFY(qHash(dt1) == qHash(dt2));
2030
2031 if (checkEuro && zoneIsCET) {
2032 QVERIFY(dt1.toUTC() == dt2);
2033 QVERIFY(dt1 == dt2.toLocalTime());
2034 }
2035}
2036
2037Q_DECLARE_METATYPE(QDataStream::Version)
2038
2039void tst_QDateTime::operator_insert_extract_data()
2040{
2041 QTest::addColumn<QDateTime>(name: "dateTime");
2042 QTest::addColumn<QString>(name: "serialiseAs");
2043 QTest::addColumn<QString>(name: "deserialiseAs");
2044 QTest::addColumn<QDataStream::Version>(name: "dataStreamVersion");
2045
2046 const QDateTime positiveYear(QDateTime(QDate(2012, 8, 14), QTime(8, 0, 0), Qt::LocalTime));
2047 const QDateTime negativeYear(QDateTime(QDate(-2012, 8, 14), QTime(8, 0, 0), Qt::LocalTime));
2048
2049 const QString westernAustralia(QString::fromLatin1(str: "AWST-8AWDT-9,M10.5.0,M3.5.0/03:00:00"));
2050 const QString hawaii(QString::fromLatin1(str: "HAW10"));
2051
2052 const QDataStream tmpDataStream;
2053 const int thisVersion = tmpDataStream.version();
2054 for (int version = QDataStream::Qt_1_0; version <= thisVersion; ++version) {
2055 const QDataStream::Version dataStreamVersion = static_cast<QDataStream::Version>(version);
2056 const QByteArray vN = QByteArray::number(dataStreamVersion);
2057 const QByteArray pY = positiveYear.toString().toLatin1();
2058 QTest::newRow(dataTag: ('v' + vN + " WA => HAWAII " + pY).constData())
2059 << positiveYear << westernAustralia << hawaii << dataStreamVersion;
2060 QTest::newRow(dataTag: ('v' + vN + " WA => WA " + pY).constData())
2061 << positiveYear << westernAustralia << westernAustralia << dataStreamVersion;
2062 QTest::newRow(dataTag: ('v' + vN + " HAWAII => WA " + negativeYear.toString().toLatin1()).constData())
2063 << negativeYear << hawaii << westernAustralia << dataStreamVersion;
2064 QTest::newRow(dataTag: ('v' + vN + " HAWAII => HAWAII " + pY).constData())
2065 << positiveYear << hawaii << hawaii << dataStreamVersion;
2066 }
2067}
2068
2069void tst_QDateTime::operator_insert_extract()
2070{
2071 QFETCH(QDateTime, dateTime);
2072 QFETCH(QString, serialiseAs);
2073 QFETCH(QString, deserialiseAs);
2074 QFETCH(QDataStream::Version, dataStreamVersion);
2075
2076 // Start off in a certain timezone.
2077 TimeZoneRollback useZone(serialiseAs.toLocal8Bit());
2078 QDateTime dateTimeAsUTC(dateTime.toUTC());
2079
2080 QByteArray byteArray;
2081 {
2082 QDataStream dataStream(&byteArray, QIODevice::WriteOnly);
2083 dataStream.setVersion(dataStreamVersion);
2084 if (dataStreamVersion == QDataStream::Qt_5_0) {
2085 // Qt 5 serialises as UTC and converts back to the stored timeSpec when
2086 // deserialising; we don't need to do it ourselves...
2087 dataStream << dateTime << dateTime;
2088 } else {
2089 // ... but other versions don't, so we have to here.
2090 dataStream << dateTimeAsUTC << dateTimeAsUTC;
2091 // We'll also make sure that a deserialised local datetime is the same
2092 // time of day (potentially different UTC time), regardless of which
2093 // timezone it was serialised in. E.g.: Tue Aug 14 08:00:00 2012
2094 // serialised in WST should be deserialised as Tue Aug 14 08:00:00 2012
2095 // HST.
2096 dataStream << dateTime;
2097 }
2098 }
2099
2100 // Ensure that a change in timezone between serialisation and deserialisation
2101 // still results in identical UTC-converted datetimes.
2102 useZone.reset(zone: deserialiseAs.toLocal8Bit());
2103 QDateTime expectedLocalTime(dateTimeAsUTC.toLocalTime());
2104 {
2105 // Deserialise whole QDateTime at once.
2106 QDataStream dataStream(&byteArray, QIODevice::ReadOnly);
2107 dataStream.setVersion(dataStreamVersion);
2108 QDateTime deserialised;
2109 dataStream >> deserialised;
2110
2111 if (dataStreamVersion == QDataStream::Qt_5_0) {
2112 // Ensure local time is still correct. Again, Qt 5 handles the timeSpec
2113 // conversion (in this case, UTC => LocalTime) for us when deserialising.
2114 QCOMPARE(deserialised, expectedLocalTime);
2115 } else {
2116 if (dataStreamVersion < QDataStream::Qt_4_0) {
2117 // Versions lower than Qt 4 don't serialise the timeSpec, instead
2118 // assuming that everything is LocalTime.
2119 deserialised.setTimeSpec(Qt::UTC);
2120 }
2121 // Qt 4.* versions do serialise the timeSpec, so we only need to convert from UTC here.
2122 deserialised = deserialised.toLocalTime();
2123
2124 QCOMPARE(deserialised, expectedLocalTime);
2125 }
2126 // Sanity check UTC times (operator== already converts its operands to UTC before comparing).
2127 QCOMPARE(deserialised.toUTC(), expectedLocalTime.toUTC());
2128
2129 // Deserialise each component individually.
2130 QDate deserialisedDate;
2131 dataStream >> deserialisedDate;
2132 QTime deserialisedTime;
2133 dataStream >> deserialisedTime;
2134 qint8 deserialisedSpec;
2135 if (dataStreamVersion >= QDataStream::Qt_4_0)
2136 dataStream >> deserialisedSpec;
2137 deserialised = QDateTime(deserialisedDate, deserialisedTime, Qt::UTC);
2138 if (dataStreamVersion >= QDataStream::Qt_4_0)
2139 deserialised = deserialised.toTimeSpec(spec: static_cast<Qt::TimeSpec>(deserialisedSpec));
2140 // Ensure local time is still correct.
2141 QCOMPARE(deserialised, expectedLocalTime);
2142 // Sanity check UTC times.
2143 QCOMPARE(deserialised.toUTC(), expectedLocalTime.toUTC());
2144
2145 if (dataStreamVersion != QDataStream::Qt_5_0) {
2146 // Deserialised local datetime should be the same time of day,
2147 // regardless of which timezone it was serialised in.
2148 QDateTime localDeserialized;
2149 dataStream >> localDeserialized;
2150 QCOMPARE(localDeserialized, dateTime);
2151 }
2152 }
2153}
2154
2155void tst_QDateTime::toString_strformat()
2156{
2157 // Most tests are in QLocale, just test that the api works.
2158 QDate testDate(2013, 1, 1);
2159 QTime testTime(1, 2, 3);
2160 QDateTime testDateTime(testDate, testTime, Qt::UTC);
2161 QCOMPARE(testDate.toString("yyyy-MM-dd"), QString("2013-01-01"));
2162 QCOMPARE(testTime.toString("hh:mm:ss"), QString("01:02:03"));
2163 QCOMPARE(testDateTime.toString("yyyy-MM-dd hh:mm:ss t"), QString("2013-01-01 01:02:03 UTC"));
2164}
2165
2166void tst_QDateTime::fromStringDateFormat_data()
2167{
2168 QTest::addColumn<QString>(name: "dateTimeStr");
2169 QTest::addColumn<Qt::DateFormat>(name: "dateFormat");
2170 QTest::addColumn<QDateTime>(name: "expected");
2171
2172#if QT_CONFIG(textdate)
2173 // Test Qt::TextDate format.
2174 QTest::newRow(dataTag: "text date") << QString::fromLatin1(str: "Tue Jun 17 08:00:10 2003")
2175 << Qt::TextDate << QDateTime(QDate(2003, 6, 17), QTime(8, 0, 10, 0), Qt::LocalTime);
2176 QTest::newRow(dataTag: "text date Year 0999") << QString::fromLatin1(str: "Tue Jun 17 08:00:10 0999")
2177 << Qt::TextDate << QDateTime(QDate(999, 6, 17), QTime(8, 0, 10, 0), Qt::LocalTime);
2178 QTest::newRow(dataTag: "text date Year 999") << QString::fromLatin1(str: "Tue Jun 17 08:00:10 999")
2179 << Qt::TextDate << QDateTime(QDate(999, 6, 17), QTime(8, 0, 10, 0), Qt::LocalTime);
2180 QTest::newRow(dataTag: "text date Year 12345") << QString::fromLatin1(str: "Tue Jun 17 08:00:10 12345")
2181 << Qt::TextDate << QDateTime(QDate(12345, 6, 17), QTime(8, 0, 10, 0), Qt::LocalTime);
2182 QTest::newRow(dataTag: "text date Year -4712") << QString::fromLatin1(str: "Tue Jan 1 00:01:02 -4712")
2183 << Qt::TextDate << QDateTime(QDate(-4712, 1, 1), QTime(0, 1, 2, 0), Qt::LocalTime);
2184 QTest::newRow(dataTag: "text data0") << QString::fromLatin1(str: "Thu Jan 1 00:00:00 1970")
2185 << Qt::TextDate << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 0), Qt::LocalTime);
2186 QTest::newRow(dataTag: "text data1") << QString::fromLatin1(str: "Thu Jan 2 12:34 1970")
2187 << Qt::TextDate << QDateTime(QDate(1970, 1, 2), QTime(12, 34, 0), Qt::LocalTime);
2188 QTest::newRow(dataTag: "text data2") << QString::fromLatin1(str: "Thu Jan 1 00 1970")
2189 << Qt::TextDate << invalidDateTime();
2190 QTest::newRow(dataTag: "text data3") << QString::fromLatin1(str: "Thu Jan 1 00:00:00:00 1970")
2191 << Qt::TextDate << invalidDateTime();
2192 QTest::newRow(dataTag: "text data4") << QString::fromLatin1(str: "Thu 1. Jan 00:00:00 1970")
2193 << Qt::TextDate << QDateTime(QDate(1970, 1, 1), QTime(0, 0), Qt::LocalTime);
2194 QTest::newRow(dataTag: "text data5") << QString::fromLatin1(str: " Thu Jan 1 00:00:00 1970 ")
2195 << Qt::TextDate << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 0), Qt::LocalTime);
2196 QTest::newRow(dataTag: "text data6") << QString::fromLatin1(str: "Thu Jan 1 00:00:00")
2197 << Qt::TextDate << invalidDateTime();
2198 QTest::newRow(dataTag: "text data7") << QString::fromLatin1(str: "Thu Jan 1 1970 00:00:00")
2199 << Qt::TextDate << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 0), Qt::LocalTime);
2200 QTest::newRow(dataTag: "text data8") << QString::fromLatin1(str: "Thu Jan 1 00:12:34 1970 GMT+foo")
2201 << Qt::TextDate << invalidDateTime();
2202 QTest::newRow(dataTag: "text data9") << QString::fromLatin1(str: "Thu Jan 1 00:12:34 1970 GMT")
2203 << Qt::TextDate << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2204 QTest::newRow(dataTag: "text data10") << QString::fromLatin1(str: "Thu Jan 1 00:12:34 1970 GMT-0300")
2205 << Qt::TextDate << QDateTime(QDate(1970, 1, 1), QTime(3, 12, 34), Qt::UTC);
2206 QTest::newRow(dataTag: "text data11") << QString::fromLatin1(str: "Thu Jan 1 00:12:34 1970 GMT+0300")
2207 << Qt::TextDate << QDateTime(QDate(1969, 12, 31), QTime(21, 12, 34), Qt::UTC);
2208 QTest::newRow(dataTag: "text data12") << QString::fromLatin1(str: "Thu Jan 1 00:12:34 1970 gmt")
2209 << Qt::TextDate << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2210 QTest::newRow(dataTag: "text data13") << QString::fromLatin1(str: "Thu Jan 1 1970 00:12:34 GMT+0100")
2211 << Qt::TextDate << QDateTime(QDate(1969, 12, 31), QTime(23, 12, 34), Qt::UTC);
2212 QTest::newRow(dataTag: "text empty") << QString::fromLatin1(str: "")
2213 << Qt::TextDate << invalidDateTime();
2214 QTest::newRow(dataTag: "text too many parts") << QString::fromLatin1(str: "Thu Jan 1 00:12:34 1970 gmt +0100")
2215 << Qt::TextDate << invalidDateTime();
2216 QTest::newRow(dataTag: "text invalid month name") << QString::fromLatin1(str: "Thu Jaz 1 1970 00:12:34")
2217 << Qt::TextDate << invalidDateTime();
2218 QTest::newRow(dataTag: "text invalid date") << QString::fromLatin1(str: "Thu Jan 32 1970 00:12:34")
2219 << Qt::TextDate << invalidDateTime();
2220 QTest::newRow(dataTag: "text invalid day #1") << QString::fromLatin1(str: "Thu Jan XX 1970 00:12:34")
2221 << Qt::TextDate << invalidDateTime();
2222 QTest::newRow(dataTag: "text invalid day #2") << QString::fromLatin1(str: "Thu X. Jan 00:00:00 1970")
2223 << Qt::TextDate << invalidDateTime();
2224 QTest::newRow(dataTag: "text invalid day #3") << QString::fromLatin1(str: "Thu 1 Jan 00:00:00 1970")
2225 << Qt::TextDate << invalidDateTime();
2226 QTest::newRow(dataTag: "text invalid year #1") << QString::fromLatin1(str: "Thu 1. Jan 00:00:00 19X0")
2227 << Qt::TextDate << invalidDateTime();
2228 QTest::newRow(dataTag: "text invalid year #2") << QString::fromLatin1(str: "Thu 1. Jan 19X0 00:00:00")
2229 << Qt::TextDate << invalidDateTime();
2230 QTest::newRow(dataTag: "text invalid hour") << QString::fromLatin1(str: "Thu 1. Jan 1970 0X:00:00")
2231 << Qt::TextDate << invalidDateTime();
2232 QTest::newRow(dataTag: "text invalid minute") << QString::fromLatin1(str: "Thu 1. Jan 1970 00:0X:00")
2233 << Qt::TextDate << invalidDateTime();
2234 QTest::newRow(dataTag: "text invalid second") << QString::fromLatin1(str: "Thu 1. Jan 1970 00:00:0X")
2235 << Qt::TextDate << invalidDateTime();
2236 QTest::newRow(dataTag: "text invalid gmt specifier #1") << QString::fromLatin1(str: "Thu 1. Jan 1970 00:00:00 DMT")
2237 << Qt::TextDate << invalidDateTime();
2238 QTest::newRow(dataTag: "text invalid gmt specifier #2") << QString::fromLatin1(str: "Thu 1. Jan 1970 00:00:00 GMTx0200")
2239 << Qt::TextDate << invalidDateTime();
2240 QTest::newRow(dataTag: "text invalid gmt hour") << QString::fromLatin1(str: "Thu 1. Jan 1970 00:00:00 GMT+0X00")
2241 << Qt::TextDate << invalidDateTime();
2242 QTest::newRow(dataTag: "text invalid gmt minute") << QString::fromLatin1(str: "Thu 1. Jan 1970 00:00:00 GMT+000X")
2243 << Qt::TextDate << invalidDateTime();
2244 QTest::newRow(dataTag: "text second fraction") << QString::fromLatin1(str: "Mon 6. May 2013 01:02:03.456")
2245 << Qt::TextDate << QDateTime(QDate(2013, 5, 6), QTime(1, 2, 3, 456));
2246#endif // textdate
2247
2248 // Test Qt::ISODate format.
2249 QTest::newRow(dataTag: "trailing space") // QTBUG-80445
2250 << QString("2000-01-02 03:04:05.678 ")
2251 << Qt::ISODate << QDateTime(QDate(2000, 1, 2), QTime(3, 4, 5, 678));
2252
2253 // Invalid spaces (but keeping field widths correct):
2254 QTest::newRow(dataTag: "space before millis")
2255 << QString("2000-01-02 03:04:05. 678") << Qt::ISODate << QDateTime();
2256 QTest::newRow(dataTag: "space after seconds")
2257 << QString("2000-01-02 03:04:5 .678") << Qt::ISODate << QDateTime();
2258 QTest::newRow(dataTag: "space before seconds")
2259 << QString("2000-01-02 03:04: 5.678") << Qt::ISODate << QDateTime();
2260 QTest::newRow(dataTag: "space after minutes")
2261 << QString("2000-01-02 03:4 :05.678") << Qt::ISODate << QDateTime();
2262 QTest::newRow(dataTag: "space before minutes")
2263 << QString("2000-01-02 03: 4:05.678") << Qt::ISODate << QDateTime();
2264 QTest::newRow(dataTag: "space after hour")
2265 << QString("2000-01-02 3 :04:05.678") << Qt::ISODate << QDateTime();
2266 QTest::newRow(dataTag: "space before hour")
2267 << QString("2000-01-02 3:04:05.678") << Qt::ISODate << QDateTime();
2268 QTest::newRow(dataTag: "space after day")
2269 << QString("2000-01-2 03:04:05.678") << Qt::ISODate << QDateTime();
2270 QTest::newRow(dataTag: "space before day")
2271 << QString("2000-01- 2 03:04:05.678") << Qt::ISODate << QDateTime();
2272 QTest::newRow(dataTag: "space after month")
2273 << QString("2000-1 -02 03:04:05.678") << Qt::ISODate << QDateTime();
2274 QTest::newRow(dataTag: "space before month")
2275 << QString("2000- 1-02 03:04:05.678") << Qt::ISODate << QDateTime();
2276 QTest::newRow(dataTag: "space after year")
2277 << QString("200 -01-02 03:04:05.678") << Qt::ISODate << QDateTime();
2278
2279 // Spaces as separators:
2280 QTest::newRow(dataTag: "sec-milli space")
2281 << QString("2000-01-02 03:04:05 678") << Qt::ISODate
2282#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
2283 << invalidDateTime();
2284#else
2285 // Should be invalid, but we ignore trailing cruft (in some cases)
2286 << QDateTime(QDate(2000, 1, 2), QTime(3, 4, 5));
2287#endif
2288 QTest::newRow(dataTag: "min-sec space")
2289 << QString("2000-01-02 03:04 05.678") << Qt::ISODate << QDateTime();
2290 QTest::newRow(dataTag: "hour-min space")
2291 << QString("2000-01-02 03 04:05.678") << Qt::ISODate << QDateTime();
2292 QTest::newRow(dataTag: "mon-day space")
2293 << QString("2000-01 02 03:04:05.678") << Qt::ISODate << QDateTime();
2294 QTest::newRow(dataTag: "year-mon space")
2295 << QString("2000 01-02 03:04:05.678") << Qt::ISODate << QDateTime();
2296
2297 // Normal usage:
2298 QTest::newRow(dataTag: "ISO +01:00") << QString::fromLatin1(str: "1987-02-13T13:24:51+01:00")
2299 << Qt::ISODate << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC);
2300 QTest::newRow(dataTag: "ISO +00:01") << QString::fromLatin1(str: "1987-02-13T13:24:51+00:01")
2301 << Qt::ISODate << QDateTime(QDate(1987, 2, 13), QTime(13, 23, 51), Qt::UTC);
2302 QTest::newRow(dataTag: "ISO -01:00") << QString::fromLatin1(str: "1987-02-13T13:24:51-01:00")
2303 << Qt::ISODate << QDateTime(QDate(1987, 2, 13), QTime(14, 24, 51), Qt::UTC);
2304 QTest::newRow(dataTag: "ISO -00:01") << QString::fromLatin1(str: "1987-02-13T13:24:51-00:01")
2305 << Qt::ISODate << QDateTime(QDate(1987, 2, 13), QTime(13, 25, 51), Qt::UTC);
2306 QTest::newRow(dataTag: "ISO +0000") << QString::fromLatin1(str: "1970-01-01T00:12:34+0000")
2307 << Qt::ISODate << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2308 QTest::newRow(dataTag: "ISO +00:00") << QString::fromLatin1(str: "1970-01-01T00:12:34+00:00")
2309 << Qt::ISODate << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2310 QTest::newRow(dataTag: "ISO -03") << QString::fromLatin1(str: "2014-12-15T12:37:09-03")
2311 << Qt::ISODate << QDateTime(QDate(2014, 12, 15), QTime(15, 37, 9), Qt::UTC);
2312 QTest::newRow(dataTag: "ISO zzz-03") << QString::fromLatin1(str: "2014-12-15T12:37:09.745-03")
2313 << Qt::ISODate << QDateTime(QDate(2014, 12, 15), QTime(15, 37, 9, 745), Qt::UTC);
2314 QTest::newRow(dataTag: "ISO -3") << QString::fromLatin1(str: "2014-12-15T12:37:09-3")
2315 << Qt::ISODate << QDateTime(QDate(2014, 12, 15), QTime(15, 37, 9), Qt::UTC);
2316 QTest::newRow(dataTag: "ISO zzz-3") << QString::fromLatin1(str: "2014-12-15T12:37:09.745-3")
2317 << Qt::ISODate << QDateTime(QDate(2014, 12, 15), QTime(15, 37, 9, 745), Qt::UTC);
2318 QTest::newRow(dataTag: "ISO lower-case") << QString::fromLatin1(str: "2005-06-28T07:57:30.002z")
2319 << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 2), Qt::UTC);
2320 // No time specified - defaults to Qt::LocalTime.
2321 QTest::newRow(dataTag: "ISO data3") << QString::fromLatin1(str: "2002-10-01")
2322 << Qt::ISODate << QDateTime(QDate(2002, 10, 1), QTime(0, 0, 0, 0), Qt::LocalTime);
2323 // Excess digits in milliseconds, round correctly:
2324 QTest::newRow(dataTag: "ISO") << QString::fromLatin1(str: "2005-06-28T07:57:30.0010000000Z")
2325 << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 1), Qt::UTC);
2326 QTest::newRow(dataTag: "ISO rounding") << QString::fromLatin1(str: "2005-06-28T07:57:30.0015Z")
2327 << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 2), Qt::UTC);
2328 // ... and accept comma as separator:
2329 QTest::newRow(dataTag: "ISO with comma 1") << QString::fromLatin1(str: "2005-06-28T07:57:30,0040000000Z")
2330 << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 4), Qt::UTC);
2331 QTest::newRow(dataTag: "ISO with comma 2") << QString::fromLatin1(str: "2005-06-28T07:57:30,0015Z")
2332 << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 2), Qt::UTC);
2333 QTest::newRow(dataTag: "ISO with comma 3") << QString::fromLatin1(str: "2005-06-28T07:57:30,0014Z")
2334 << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 1), Qt::UTC);
2335 QTest::newRow(dataTag: "ISO with comma 4") << QString::fromLatin1(str: "2005-06-28T07:57:30,1Z")
2336 << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 100), Qt::UTC);
2337 QTest::newRow(dataTag: "ISO with comma 5") << QString::fromLatin1(str: "2005-06-28T07:57:30,11")
2338 << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 110), Qt::LocalTime);
2339 // 24:00:00 Should be next day according to ISO 8601 section 4.2.3.
2340 QTest::newRow(dataTag: "ISO 24:00") << QString::fromLatin1(str: "2012-06-04T24:00:00")
2341 << Qt::ISODate << QDateTime(QDate(2012, 6, 5), QTime(0, 0, 0, 0), Qt::LocalTime);
2342 QTest::newRow(dataTag: "ISO 24:00 end of month") << QString::fromLatin1(str: "2012-06-30T24:00:00")
2343 << Qt::ISODate << QDateTime(QDate(2012, 7, 1), QTime(0, 0, 0, 0), Qt::LocalTime);
2344 QTest::newRow(dataTag: "ISO 24:00 end of year") << QString::fromLatin1(str: "2012-12-31T24:00:00")
2345 << Qt::ISODate << QDateTime(QDate(2013, 1, 1), QTime(0, 0, 0, 0), Qt::LocalTime);
2346 QTest::newRow(dataTag: "ISO 24:00, fract ms") << QString::fromLatin1(str: "2012-01-01T24:00:00.000")
2347 << Qt::ISODate << QDateTime(QDate(2012, 1, 2), QTime(0, 0, 0, 0), Qt::LocalTime);
2348 QTest::newRow(dataTag: "ISO 24:00 end of year, fract ms") << QString::fromLatin1(str: "2012-12-31T24:00:00.000")
2349 << Qt::ISODate << QDateTime(QDate(2013, 1, 1), QTime(0, 0, 0, 0), Qt::LocalTime);
2350 // Test fractional seconds.
2351 QTest::newRow(dataTag: "ISO .0 of a second (period)") << QString::fromLatin1(str: "2012-01-01T08:00:00.0")
2352 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 0), Qt::LocalTime);
2353 QTest::newRow(dataTag: "ISO .00 of a second (period)") << QString::fromLatin1(str: "2012-01-01T08:00:00.00")
2354 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 0), Qt::LocalTime);
2355 QTest::newRow(dataTag: "ISO .000 of a second (period)") << QString::fromLatin1(str: "2012-01-01T08:00:00.000")
2356 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 0), Qt::LocalTime);
2357 QTest::newRow(dataTag: "ISO .1 of a second (comma)") << QString::fromLatin1(str: "2012-01-01T08:00:00,1")
2358 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 100), Qt::LocalTime);
2359 QTest::newRow(dataTag: "ISO .99 of a second (comma)") << QString::fromLatin1(str: "2012-01-01T08:00:00,99")
2360 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 990), Qt::LocalTime);
2361 QTest::newRow(dataTag: "ISO .998 of a second (comma)") << QString::fromLatin1(str: "2012-01-01T08:00:00,998")
2362 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 998), Qt::LocalTime);
2363 QTest::newRow(dataTag: "ISO .999 of a second (comma)") << QString::fromLatin1(str: "2012-01-01T08:00:00,999")
2364 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 999), Qt::LocalTime);
2365 QTest::newRow(dataTag: "ISO .3335 of a second (comma)") << QString::fromLatin1(str: "2012-01-01T08:00:00,3335")
2366 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 334), Qt::LocalTime);
2367 QTest::newRow(dataTag: "ISO .333333 of a second (comma)") << QString::fromLatin1(str: "2012-01-01T08:00:00,333333")
2368 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 333), Qt::LocalTime);
2369 QTest::newRow(dataTag: "ISO .00009 of a second (period)") << QString::fromLatin1(str: "2012-01-01T08:00:00.00009")
2370 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 0), Qt::LocalTime);
2371 QTest::newRow(dataTag: "ISO no fract specified") << QString::fromLatin1(str: "2012-01-01T08:00:00.")
2372 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 0), Qt::LocalTime);
2373 // Test invalid characters (should ignore invalid characters at end of string).
2374 QTest::newRow(dataTag: "ISO invalid character at end") << QString::fromLatin1(str: "2012-01-01T08:00:00!")
2375#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
2376 << Qt::ISODate << invalidDateTime();
2377#else
2378 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 0), Qt::LocalTime);
2379#endif
2380 QTest::newRow(dataTag: "ISO invalid character at front") << QString::fromLatin1(str: "!2012-01-01T08:00:00")
2381 << Qt::ISODate << invalidDateTime();
2382 QTest::newRow(dataTag: "ISO invalid character both ends") << QString::fromLatin1(str: "!2012-01-01T08:00:00!")
2383 << Qt::ISODate << invalidDateTime();
2384 QTest::newRow(dataTag: "ISO invalid character at front, 2 at back") << QString::fromLatin1(str: "!2012-01-01T08:00:00..")
2385 << Qt::ISODate << invalidDateTime();
2386 QTest::newRow(dataTag: "ISO invalid character 2 at front") << QString::fromLatin1(str: "!!2012-01-01T08:00:00")
2387 << Qt::ISODate << invalidDateTime();
2388 // Test fractional minutes.
2389 QTest::newRow(dataTag: "ISO .0 of a minute (period)") << QString::fromLatin1(str: "2012-01-01T08:00.0")
2390 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 0), Qt::LocalTime);
2391 QTest::newRow(dataTag: "ISO .8 of a minute (period)") << QString::fromLatin1(str: "2012-01-01T08:00.8")
2392 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 48, 0), Qt::LocalTime);
2393 QTest::newRow(dataTag: "ISO .99999 of a minute (period)") << QString::fromLatin1(str: "2012-01-01T08:00.99999")
2394 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 59, 999), Qt::LocalTime);
2395 QTest::newRow(dataTag: "ISO .0 of a minute (comma)") << QString::fromLatin1(str: "2012-01-01T08:00,0")
2396 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 0, 0), Qt::LocalTime);
2397 QTest::newRow(dataTag: "ISO .8 of a minute (comma)") << QString::fromLatin1(str: "2012-01-01T08:00,8")
2398 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 48, 0), Qt::LocalTime);
2399 QTest::newRow(dataTag: "ISO .99999 of a minute (comma)") << QString::fromLatin1(str: "2012-01-01T08:00,99999")
2400 << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 59, 999), Qt::LocalTime);
2401 QTest::newRow(dataTag: "ISO empty") << QString::fromLatin1(str: "") << Qt::ISODate << invalidDateTime();
2402 QTest::newRow(dataTag: "ISO short") << QString::fromLatin1(str: "2017-07-01T") << Qt::ISODate << invalidDateTime();
2403 QTest::newRow(dataTag: "ISO zoned date") << QString::fromLatin1(str: "2017-07-01Z") << Qt::ISODate << invalidDateTime();
2404 QTest::newRow(dataTag: "ISO zoned empty time") << QString::fromLatin1(str: "2017-07-01TZ") << Qt::ISODate << invalidDateTime();
2405 QTest::newRow(dataTag: "ISO mis-punctuated") << QString::fromLatin1(str: "2018/01/30 ") << Qt::ISODate << invalidDateTime();
2406
2407 // Test Qt::RFC2822Date format (RFC 2822).
2408 QTest::newRow(dataTag: "RFC 2822 +0100") << QString::fromLatin1(str: "13 Feb 1987 13:24:51 +0100")
2409 << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC);
2410 QTest::newRow(dataTag: "RFC 2822 after space +0100")
2411 << QString::fromLatin1(str: " 13 Feb 1987 13:24:51 +0100")
2412 << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC);
2413 QTest::newRow(dataTag: "RFC 2822 with day +0100") << QString::fromLatin1(str: "Fri, 13 Feb 1987 13:24:51 +0100")
2414 << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC);
2415 QTest::newRow(dataTag: "RFC 2822 with day after space +0100")
2416 << QString::fromLatin1(str: " Fri, 13 Feb 1987 13:24:51 +0100")
2417 << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC);
2418 QTest::newRow(dataTag: "RFC 2822 -0100") << QString::fromLatin1(str: "13 Feb 1987 13:24:51 -0100")
2419 << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(14, 24, 51), Qt::UTC);
2420 QTest::newRow(dataTag: "RFC 2822 with day -0100") << QString::fromLatin1(str: "Fri, 13 Feb 1987 13:24:51 -0100")
2421 << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(14, 24, 51), Qt::UTC);
2422 QTest::newRow(dataTag: "RFC 2822 +0000") << QString::fromLatin1(str: "01 Jan 1970 00:12:34 +0000")
2423 << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2424 QTest::newRow(dataTag: "RFC 2822 with day +0000") << QString::fromLatin1(str: "Thu, 01 Jan 1970 00:12:34 +0000")
2425 << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2426 QTest::newRow(dataTag: "RFC 2822 +0000") << QString::fromLatin1(str: "01 Jan 1970 00:12:34 +0000")
2427 << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2428 QTest::newRow(dataTag: "RFC 2822 with day +0000") << QString::fromLatin1(str: "Thu, 01 Jan 1970 00:12:34 +0000")
2429 << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2430 // Should be invalid, but current implementation would just ignore the
2431 // offset as trailing junk if we insist on the space:
2432 QTest::newRow(dataTag: "RFC 2822 missing space before +0100")
2433 << QString::fromLatin1(str: "Thu, 01 Jan 1970 00:12:34+0100") << Qt::RFC2822Date
2434 << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::OffsetFromUTC, 3600);
2435 // No timezone assume UTC
2436 QTest::newRow(dataTag: "RFC 2822 no timezone") << QString::fromLatin1(str: "01 Jan 1970 00:12:34")
2437 << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2438 // No time specified
2439 QTest::newRow(dataTag: "RFC 2822 date only") << QString::fromLatin1(str: "01 Nov 2002")
2440 << Qt::RFC2822Date << invalidDateTime();
2441 QTest::newRow(dataTag: "RFC 2822 with day date only") << QString::fromLatin1(str: "Fri, 01 Nov 2002")
2442 << Qt::RFC2822Date << invalidDateTime();
2443 // Test invalid month, day, year
2444 QTest::newRow(dataTag: "RFC 2822 invalid month name") << QString::fromLatin1(str: "13 Fev 1987 13:24:51 +0100")
2445 << Qt::RFC2822Date << invalidDateTime();
2446 QTest::newRow(dataTag: "RFC 2822 invalid day") << QString::fromLatin1(str: "36 Fev 1987 13:24:51 +0100")
2447 << Qt::RFC2822Date << invalidDateTime();
2448 QTest::newRow(dataTag: "RFC 2822 invalid year") << QString::fromLatin1(str: "13 Fev 0000 13:24:51 +0100")
2449 << Qt::RFC2822Date << invalidDateTime();
2450 // Test invalid characters (currently ignoring trailing junk, but see QTBUG-80038).
2451 QTest::newRow(dataTag: "RFC 2822 invalid character at end")
2452 << QString::fromLatin1(str: "01 Jan 2012 08:00:00 +0100!")
2453 << Qt::RFC2822Date << QDateTime(QDate(2012, 1, 1), QTime(7, 0, 0, 0), Qt::UTC);
2454 QTest::newRow(dataTag: "RFC 2822 invalid character at front")
2455 << QString::fromLatin1(str: "!01 Jan 2012 08:00:00 +0100")
2456 << Qt::RFC2822Date << invalidDateTime();
2457 QTest::newRow(dataTag: "RFC 2822 invalid character both ends")
2458 << QString::fromLatin1(str: "!01 Jan 2012 08:00:00 +0100!")
2459 << Qt::RFC2822Date << invalidDateTime();
2460 QTest::newRow(dataTag: "RFC 2822 invalid character at front, 2 at back")
2461 << QString::fromLatin1(str: "!01 Jan 2012 08:00:00 +0100..")
2462 << Qt::RFC2822Date << invalidDateTime();
2463 QTest::newRow(dataTag: "RFC 2822 invalid character 2 at front")
2464 << QString::fromLatin1(str: "!!01 Jan 2012 08:00:00 +0100")
2465 << Qt::RFC2822Date << invalidDateTime();
2466 // The common date text used by the "invalid character" tests, just to be
2467 // sure *it's* not what's invalid:
2468 QTest::newRow(dataTag: "RFC 2822 (not invalid)")
2469 << QString::fromLatin1(str: "01 Jan 2012 08:00:00 +0100")
2470 << Qt::RFC2822Date << QDateTime(QDate(2012, 1, 1), QTime(7, 0, 0, 0), Qt::UTC);
2471
2472 // Test Qt::RFC2822Date format (RFC 850 and 1036, permissive).
2473 QTest::newRow(dataTag: "RFC 850 and 1036 +0100") << QString::fromLatin1(str: "Fri Feb 13 13:24:51 1987 +0100")
2474 << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC);
2475 QTest::newRow(dataTag: "RFC 1036 after space +0100")
2476 << QString::fromLatin1(str: " Fri Feb 13 13:24:51 1987 +0100")
2477 << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC);
2478 QTest::newRow(dataTag: "RFC 850 and 1036 -0100") << QString::fromLatin1(str: "Fri Feb 13 13:24:51 1987 -0100")
2479 << Qt::RFC2822Date << QDateTime(QDate(1987, 2, 13), QTime(14, 24, 51), Qt::UTC);
2480 QTest::newRow(dataTag: "RFC 850 and 1036 +0000") << QString::fromLatin1(str: "Thu Jan 01 00:12:34 1970 +0000")
2481 << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2482 QTest::newRow(dataTag: "RFC 850 and 1036 +0000") << QString::fromLatin1(str: "Thu Jan 01 00:12:34 1970 +0000")
2483 << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2484 // No timezone assume UTC
2485 QTest::newRow(dataTag: "RFC 850 and 1036 no timezone") << QString::fromLatin1(str: "Thu Jan 01 00:12:34 1970")
2486 << Qt::RFC2822Date << QDateTime(QDate(1970, 1, 1), QTime(0, 12, 34), Qt::UTC);
2487 // No time specified
2488 QTest::newRow(dataTag: "RFC 850 and 1036 date only")
2489 << QString::fromLatin1(str: "Fri Nov 01 2002")
2490 << Qt::RFC2822Date << invalidDateTime();
2491 // Test invalid characters (currently ignoring trailing junk, but see QTBUG-80038).
2492 QTest::newRow(dataTag: "RFC 850 and 1036 invalid character at end")
2493 << QString::fromLatin1(str: "Sun Jan 01 08:00:00 2012 +0100!")
2494 << Qt::RFC2822Date << QDateTime(QDate(2012, 1, 1), QTime(7, 0, 0, 0), Qt::UTC);
2495 QTest::newRow(dataTag: "RFC 850 and 1036 invalid character at front")
2496 << QString::fromLatin1(str: "!Sun Jan 01 08:00:00 2012 +0100")
2497 << Qt::RFC2822Date << invalidDateTime();
2498 QTest::newRow(dataTag: "RFC 850 and 1036 invalid character both ends")
2499 << QString::fromLatin1(str: "!Sun Jan 01 08:00:00 2012 +0100!")
2500 << Qt::RFC2822Date << invalidDateTime();
2501 QTest::newRow(dataTag: "RFC 850 and 1036 invalid character at front, 2 at back")
2502 << QString::fromLatin1(str: "!Sun Jan 01 08:00:00 2012 +0100..")
2503 << Qt::RFC2822Date << invalidDateTime();
2504 QTest::newRow(dataTag: "RFC 850 and 1036 invalid character 2 at front")
2505 << QString::fromLatin1(str: "!!Sun Jan 01 08:00:00 2012 +0100")
2506 << Qt::RFC2822Date << invalidDateTime();
2507 // Again, check the text in the "invalid character" tests isn't the source of invalidity:
2508 QTest::newRow(dataTag: "RFC 850 and 1036 (not invalid)")
2509 << QString::fromLatin1(str: "Sun Jan 01 08:00:00 2012 +0100")
2510 << Qt::RFC2822Date << QDateTime(QDate(2012, 1, 1), QTime(7, 0, 0, 0), Qt::UTC);
2511
2512 QTest::newRow(dataTag: "RFC empty") << QString::fromLatin1(str: "") << Qt::RFC2822Date << invalidDateTime();
2513}
2514
2515void tst_QDateTime::fromStringDateFormat()
2516{
2517 QFETCH(QString, dateTimeStr);
2518 QFETCH(Qt::DateFormat, dateFormat);
2519 QFETCH(QDateTime, expected);
2520
2521 QDateTime dateTime = QDateTime::fromString(s: dateTimeStr, f: dateFormat);
2522 QCOMPARE(dateTime, expected);
2523}
2524
2525void tst_QDateTime::fromStringStringFormat_data()
2526{
2527 QTest::addColumn<QString>(name: "string");
2528 QTest::addColumn<QString>(name: "format");
2529 QTest::addColumn<QDateTime>(name: "expected");
2530
2531 QTest::newRow(dataTag: "data0") << QString("101010") << QString("dMyy") << QDateTime(QDate(1910, 10, 10), QTime());
2532 QTest::newRow(dataTag: "data1") << QString("1020") << QString("sss") << invalidDateTime();
2533 QTest::newRow(dataTag: "data2") << QString("1010") << QString("sss") << QDateTime(defDate(), QTime(0, 0, 10));
2534 QTest::newRow(dataTag: "data3") << QString("10hello20") << QString("ss'hello'ss") << invalidDateTime();
2535 QTest::newRow(dataTag: "data4") << QString("10") << QString("''") << invalidDateTime();
2536 QTest::newRow(dataTag: "data5") << QString("10") << QString("'") << invalidDateTime();
2537 QTest::newRow(dataTag: "data6") << QString("pm") << QString("ap") << QDateTime(defDate(), QTime(12, 0, 0));
2538 QTest::newRow(dataTag: "data7") << QString("foo") << QString("ap") << invalidDateTime();
2539 // Day non-conflict should not hide earlier year conflict (1963-03-01 was a
2540 // Friday; asking for Thursday moves this, without conflict, to the 7th):
2541 QTest::newRow(dataTag: "data8") << QString("77 03 1963 Thu") << QString("yy MM yyyy ddd") << invalidDateTime();
2542 QTest::newRow(dataTag: "data9") << QString("101010") << QString("dMyy") << QDateTime(QDate(1910, 10, 10), QTime());
2543 QTest::newRow(dataTag: "data10") << QString("101010") << QString("dMyy") << QDateTime(QDate(1910, 10, 10), QTime());
2544 QTest::newRow(dataTag: "data11") << QString("10 Oct 10") << QString("dd MMM yy") << QDateTime(QDate(1910, 10, 10), QTime());
2545 QTest::newRow(dataTag: "data12") << QString("Fri December 3 2004") << QString("ddd MMMM d yyyy") << QDateTime(QDate(2004, 12, 3), QTime());
2546 QTest::newRow(dataTag: "data13") << QString("30.02.2004") << QString("dd.MM.yyyy") << invalidDateTime();
2547 QTest::newRow(dataTag: "data14") << QString("32.01.2004") << QString("dd.MM.yyyy") << invalidDateTime();
2548 QTest::newRow(dataTag: "data15") << QString("Thu January 2004") << QString("ddd MMMM yyyy") << QDateTime(QDate(2004, 1, 1), QTime());
2549 QTest::newRow(dataTag: "data16") << QString("2005-06-28T07:57:30.001Z")
2550 << QString("yyyy-MM-ddThh:mm:ss.zt")
2551 << QDateTime(QDate(2005, 06, 28), QTime(07, 57, 30, 1), Qt::UTC);
2552 QTest::newRow(dataTag: "utc-time-spec-as:UTC+0")
2553 << QString("2005-06-28T07:57:30.001UTC+0") << QString("yyyy-MM-ddThh:mm:ss.zt")
2554 << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 1), Qt::UTC);
2555 QTest::newRow(dataTag: "utc-time-spec-as:UTC-0")
2556 << QString("2005-06-28T07:57:30.001UTC-0") << QString("yyyy-MM-ddThh:mm:ss.zt")
2557 << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 1), Qt::UTC);
2558 QTest::newRow(dataTag: "offset-from-utc:UTC+1")
2559 << QString("2001-09-13T07:33:01.001 UTC+1") << QString("yyyy-MM-ddThh:mm:ss.z t")
2560 << QDateTime(QDate(2001, 9, 13), QTime(7, 33, 1, 1), Qt::OffsetFromUTC, 3600);
2561 QTest::newRow(dataTag: "offset-from-utc:UTC-11:01")
2562 << QString("2008-09-13T07:33:01.001 UTC-11:01") << QString("yyyy-MM-ddThh:mm:ss.z t")
2563 << QDateTime(QDate(2008, 9, 13), QTime(7, 33, 1, 1), Qt::OffsetFromUTC, -39660);
2564 QTest::newRow(dataTag: "offset-from-utc:UTC+02:57")
2565 << QString("2001-09-15T09:33:01.001UTC+02:57") << QString("yyyy-MM-ddThh:mm:ss.zt")
2566 << QDateTime(QDate(2001, 9, 15), QTime(9, 33, 1, 1), Qt::OffsetFromUTC, 10620);
2567 QTest::newRow(dataTag: "offset-from-utc:-03:00") // RFC 3339 offset format
2568 << QString("2001-09-15T09:33:01.001-03:00") << QString("yyyy-MM-ddThh:mm:ss.zt")
2569 << QDateTime(QDate(2001, 9, 15), QTime(9, 33, 1, 1), Qt::OffsetFromUTC, -10800);
2570 QTest::newRow(dataTag: "offset-from-utc:+0205") // ISO 8601 basic offset format
2571 << QString("2001-09-15T09:33:01.001+0205") << QString("yyyy-MM-ddThh:mm:ss.zt")
2572 << QDateTime(QDate(2001, 9, 15), QTime(9, 33, 1, 1), Qt::OffsetFromUTC, 7500);
2573 QTest::newRow(dataTag: "offset-from-utc:-0401") // ISO 8601 basic offset format
2574 << QString("2001-09-15T09:33:01.001-0401") << QString("yyyy-MM-ddThh:mm:ss.zt")
2575 << QDateTime(QDate(2001, 9, 15), QTime(9, 33, 1, 1), Qt::OffsetFromUTC, -14460);
2576 QTest::newRow(dataTag: "offset-from-utc:+10") // ISO 8601 basic (hour-only) offset format
2577 << QString("2001-09-15T09:33:01.001 +10") << QString("yyyy-MM-ddThh:mm:ss.z t")
2578 << QDateTime(QDate(2001, 9, 15), QTime(9, 33, 1, 1), Qt::OffsetFromUTC, 36000);
2579 QTest::newRow(dataTag: "offset-from-utc:UTC+10:00") // Time-spec specifier at the beginning
2580 << QString("UTC+10:00 2008-10-13T07:33") << QString("t yyyy-MM-ddThh:mm")
2581 << QDateTime(QDate(2008, 10, 13), QTime(7, 33), Qt::OffsetFromUTC, 36000);
2582 QTest::newRow(dataTag: "offset-from-utc:UTC-03:30") // Time-spec specifier in the middle
2583 << QString("2008-10-13 UTC-03:30 11.50") << QString("yyyy-MM-dd t hh.mm")
2584 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), Qt::OffsetFromUTC, -12600);
2585 QTest::newRow(dataTag: "offset-from-utc:UTC-2") // Time-spec specifier joined with text/time
2586 << QString("2008-10-13 UTC-2Z11.50") << QString("yyyy-MM-dd tZhh.mm")
2587 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), Qt::OffsetFromUTC, -7200);
2588 QTest::newRow(dataTag: "offset-from-utc:followed-by-colon")
2589 << QString("2008-10-13 UTC-0100:11.50") << QString("yyyy-MM-dd t:hh.mm")
2590 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), Qt::OffsetFromUTC, -3600);
2591 QTest::newRow(dataTag: "offset-from-utc:late-colon")
2592 << QString("2008-10-13 UTC+05T:11.50") << QString("yyyy-MM-dd tT:hh.mm")
2593 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), Qt::OffsetFromUTC, 18000);
2594 QTest::newRow(dataTag: "offset-from-utc:merged-with-time")
2595 << QString("2008-10-13 UTC+010011.50") << QString("yyyy-MM-dd thh.mm")
2596 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), Qt::OffsetFromUTC, 3600);
2597 QTest::newRow(dataTag: "offset-from-utc:double-colon-delimiter")
2598 << QString("2008-10-13 UTC+12::11.50") << QString("yyyy-MM-dd t::hh.mm")
2599 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), Qt::OffsetFromUTC, 43200);
2600 QTest::newRow(dataTag: "offset-from-utc:3-digit-with-colon")
2601 << QString("2008-10-13 -4:30 11.50") << QString("yyyy-MM-dd t hh.mm")
2602 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), Qt::OffsetFromUTC, -16200);
2603 QTest::newRow(dataTag: "offset-from-utc:merged-with-time")
2604 << QString("2008-10-13 UTC+010011.50") << QString("yyyy-MM-dd thh.mm")
2605 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), Qt::OffsetFromUTC, 3600);
2606 QTest::newRow(dataTag: "offset-from-utc:with-colon-merged-with-time")
2607 << QString("2008-10-13 UTC+01:0011.50") << QString("yyyy-MM-dd thh.mm")
2608 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), Qt::OffsetFromUTC, 3600);
2609 QTest::newRow(dataTag: "invalid-offset-from-utc:out-of-range")
2610 << QString("2001-09-15T09:33:01.001-50") << QString("yyyy-MM-ddThh:mm:ss.zt")
2611 << invalidDateTime();
2612 QTest::newRow(dataTag: "invalid-offset-from-utc:single-digit-format")
2613 << QString("2001-09-15T09:33:01.001+5") << QString("yyyy-MM-ddThh:mm:ss.zt")
2614 << invalidDateTime();
2615 QTest::newRow(dataTag: "invalid-offset-from-utc:three-digit-format")
2616 << QString("2001-09-15T09:33:01.001-701") << QString("yyyy-MM-ddThh:mm:ss.zt")
2617 << invalidDateTime();
2618 QTest::newRow(dataTag: "invalid-offset-from-utc:three-digit-minutes")
2619 << QString("2001-09-15T09:33:01.001+11:570") << QString("yyyy-MM-ddThh:mm:ss.zt")
2620 << invalidDateTime();
2621 QTest::newRow(dataTag: "invalid-offset-from-utc:single-digit-minutes")
2622 << QString("2001-09-15T09:33:01.001+11:5") << QString("yyyy-MM-ddThh:mm:ss.zt")
2623 << invalidDateTime();
2624 QTest::newRow(dataTag: "invalid-offset-from-utc:invalid-sign-symbol")
2625 << QString("2001-09-15T09:33:01.001 ~11:30") << QString("yyyy-MM-ddThh:mm:ss.z t")
2626 << invalidDateTime();
2627 QTest::newRow(dataTag: "invalid-offset-from-utc:symbol-in-hours")
2628 << QString("2001-09-15T09:33:01.001 UTC+o8:30") << QString("yyyy-MM-ddThh:mm:ss.z t")
2629 << invalidDateTime();
2630 QTest::newRow(dataTag: "invalid-offset-from-utc:symbol-in-minutes")
2631 << QString("2001-09-15T09:33:01.001 UTC+08:3i") << QString("yyyy-MM-ddThh:mm:ss.z t")
2632 << invalidDateTime();
2633 QTest::newRow(dataTag: "invalid-offset-from-utc:UTC+123") // Invalid offset (UTC and 3 digit format)
2634 << QString("2001-09-15T09:33:01.001 UTC+123") << QString("yyyy-MM-ddThh:mm:ss.z t")
2635 << invalidDateTime();
2636 QTest::newRow(dataTag: "invalid-offset-from-utc:UTC+00005") // Invalid offset with leading zeroes
2637 << QString("2001-09-15T09:33:01.001 UTC+00005") << QString("yyyy-MM-ddThh:mm:ss.z t")
2638 << invalidDateTime();
2639 QTest::newRow(dataTag: "invalid-offset-from-utc:three-digit-with-colon-delimiter")
2640 << QString("2008-10-13 +123:11.50") << QString("yyyy-MM-dd t:hh.mm")
2641 << invalidDateTime();
2642 QTest::newRow(dataTag: "invalid-offset-from-utc:double-colon-as-part-of-offset")
2643 << QString("2008-10-13 UTC+12::11.50") << QString("yyyy-MM-dd thh.mm")
2644 << invalidDateTime();
2645 QTest::newRow(dataTag: "invalid-offset-from-utc:single-colon-as-part-of-offset")
2646 << QString("2008-10-13 UTC+12::11.50") << QString("yyyy-MM-dd t:hh.mm")
2647 << invalidDateTime();
2648 QTest::newRow(dataTag: "invalid-offset-from-utc:starts-with-colon")
2649 << QString("2008-10-13 UTC+:59 11.50") << QString("yyyy-MM-dd t hh.mm")
2650 << invalidDateTime();
2651 QTest::newRow(dataTag: "invalid-offset-from-utc:empty-offset")
2652 << QString("2008-10-13 UTC+ 11.50") << QString("yyyy-MM-dd t hh.mm")
2653 << invalidDateTime();
2654 QTest::newRow(dataTag: "invalid-offset-from-utc:time-section-instead-of-offset")
2655 << QString("2008-10-13 UTC+11.50") << QString("yyyy-MM-dd thh.mm")
2656 << invalidDateTime();
2657 QTest::newRow(dataTag: "invalid-offset-from-utc:missing-minutes-if-colon")
2658 << QString("2008-10-13 +05: 11.50") << QString("yyyy-MM-dd t hh.mm")
2659 << invalidDateTime();
2660 QTest::newRow(dataTag: "invalid-offset-from-utc:1-digit-minutes-if-colon")
2661 << QString("2008-10-13 UTC+05:1 11.50") << QString("yyyy-MM-dd t hh.mm")
2662 << invalidDateTime();
2663 QTest::newRow(dataTag: "invalid-time-spec:random-symbol")
2664 << QString("2001-09-15T09:33:01.001 $") << QString("yyyy-MM-ddThh:mm:ss.z t")
2665 << invalidDateTime();
2666 QTest::newRow(dataTag: "invalid-time-spec:random-digit")
2667 << QString("2001-09-15T09:33:01.001 1") << QString("yyyy-MM-ddThh:mm:ss.z t")
2668 << invalidDateTime();
2669 QTest::newRow(dataTag: "invalid-offset-from-utc:merged-with-time")
2670 << QString("2008-10-13 UTC+0111.50") << QString("yyyy-MM-dd thh.mm")
2671 << invalidDateTime();
2672 QTest::newRow(dataTag: "invalid-offset-from-utc:with-colon-3-digit-merged-with-time")
2673 << QString("2008-10-13 UTC+01:011.50") << QString("yyyy-MM-dd thh.mm")
2674 << invalidDateTime();
2675 QTest::newRow(dataTag: "invalid-time-spec:empty")
2676 << QString("2001-09-15T09:33:01.001 ") << QString("yyyy-MM-ddThh:mm:ss.z t")
2677 << invalidDateTime();
2678#if QT_CONFIG(timezone)
2679 QTimeZone southBrazil("America/Sao_Paulo");
2680 if (southBrazil.isValid()) {
2681 QTest::newRow(dataTag: "spring-forward-midnight")
2682 << QString("2008-10-19 23:45.678 America/Sao_Paulo") << QString("yyyy-MM-dd mm:ss.zzz t")
2683 // That's in the hour skipped - expect the matching time after the spring-forward, in DST:
2684 << QDateTime(QDate(2008, 10, 19), QTime(1, 23, 45, 678), southBrazil);
2685 }
2686#endif
2687 QTest::newRow(dataTag: "late") << QString("9999-12-31T23:59:59.999Z")
2688 << QString("yyyy-MM-ddThh:mm:ss.zZ")
2689 << QDateTime(QDate(9999, 12, 31), QTime(23, 59, 59, 999));
2690 // Separators match /([^aAdhHMmstyz]*)/
2691 QTest::newRow(dataTag: "oddly-separated") // To show broken-separator's format is valid.
2692 << QStringLiteral("2018 wilful long working block relief 12-19T21:09 cruel blurb encore flux")
2693 << QStringLiteral("yyyy wilful long working block relief MM-ddThh:mm cruel blurb encore flux")
2694 << QDateTime(QDate(2018, 12, 19), QTime(21, 9));
2695 QTest::newRow(dataTag: "broken-separator")
2696 << QStringLiteral("2018 wilful")
2697 << QStringLiteral("yyyy wilful long working block relief MM-ddThh:mm cruel blurb encore flux")
2698 << invalidDateTime();
2699 QTest::newRow(dataTag: "broken-terminator")
2700 << QStringLiteral("2018 wilful long working block relief 12-19T21:09 cruel")
2701 << QStringLiteral("yyyy wilful long working block relief MM-ddThh:mm cruel blurb encore flux")
2702 << invalidDateTime();
2703}
2704
2705void tst_QDateTime::fromStringStringFormat()
2706{
2707 QFETCH(QString, string);
2708 QFETCH(QString, format);
2709 QFETCH(QDateTime, expected);
2710
2711 QDateTime dt = QDateTime::fromString(s: string, format);
2712
2713 QCOMPARE(dt, expected);
2714 if (expected.isValid()) {
2715 QCOMPARE(dt.timeSpec(), expected.timeSpec());
2716#if QT_CONFIG(timezone)
2717 if (expected.timeSpec() == Qt::TimeZone)
2718 QCOMPARE(dt.timeZone(), expected.timeZone());
2719#endif
2720 // OffsetFromUTC needs an offset check - we may as well do it for all:
2721 QCOMPARE(dt.offsetFromUtc(), expected.offsetFromUtc());
2722 } else {
2723 QCOMPARE(dt.isValid(), expected.isValid());
2724 QCOMPARE(dt.toMSecsSinceEpoch(), expected.toMSecsSinceEpoch());
2725 }
2726}
2727
2728void tst_QDateTime::fromStringStringFormat_localTimeZone_data()
2729{
2730 QTest::addColumn<QByteArray>(name: "localTimeZone");
2731 QTest::addColumn<QString>(name: "string");
2732 QTest::addColumn<QString>(name: "format");
2733 QTest::addColumn<QDateTime>(name: "expected");
2734
2735#if QT_CONFIG(timezone)
2736 QTimeZone etcGmtWithOffset("Etc/GMT+3");
2737 if (etcGmtWithOffset.isValid()) {
2738 QTest::newRow(dataTag: "local-timezone-with-offset:Etc/GMT+3") << QByteArrayLiteral("GMT")
2739 << QString("2008-10-13 Etc/GMT+3 11.50") << QString("yyyy-MM-dd t hh.mm")
2740 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), etcGmtWithOffset);
2741 }
2742 QTimeZone gmtWithOffset("GMT-2");
2743 if (gmtWithOffset.isValid()) {
2744 QTest::newRow(dataTag: "local-timezone-with-offset:GMT-2") << QByteArrayLiteral("GMT")
2745 << QString("2008-10-13 GMT-2 11.50") << QString("yyyy-MM-dd t hh.mm")
2746 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), gmtWithOffset);
2747 }
2748 QTimeZone gmt("GMT");
2749 if (gmt.isValid()) {
2750 QTest::newRow(dataTag: "local-timezone-with-offset:GMT") << QByteArrayLiteral("GMT")
2751 << QString("2008-10-13 GMT 11.50") << QString("yyyy-MM-dd t hh.mm")
2752 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), gmt);
2753 }
2754#endif
2755}
2756
2757void tst_QDateTime::fromStringStringFormat_localTimeZone()
2758{
2759 QFETCH(QByteArray, localTimeZone);
2760 TimeZoneRollback useZone(localTimeZone); // enforce test's time zone
2761 fromStringStringFormat(); // call basic fromStringStringFormat test
2762}
2763
2764#if defined(Q_OS_WIN) && QT_CONFIG(textdate)
2765// Windows only
2766void tst_QDateTime::fromString_LOCALE_ILDATE()
2767{
2768 QString date1 = QLatin1String("Sun 1. Dec 13:02:00 1974");
2769 QString date2 = QLatin1String("Sun Dec 1 13:02:00 1974");
2770
2771 QDateTime ref(QDate(1974, 12, 1), QTime(13, 2));
2772 QCOMPARE(ref, QDateTime::fromString(date2, Qt::TextDate));
2773 QCOMPARE(ref, QDateTime::fromString(date1, Qt::TextDate));
2774}
2775#endif
2776
2777#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2778QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
2779
2780void tst_QDateTime::fromStringToStringLocale_data()
2781{
2782 QTest::addColumn<QLocale>(name: "locale");
2783 QTest::addColumn<QDateTime>(name: "dateTime");
2784
2785 QTest::newRow(dataTag: "frFR") << QLocale(QLocale::French, QLocale::France) << QDateTime(QDate(1999, 1, 18), QTime(11, 49, 00));
2786 QTest::newRow(dataTag: "spCO") << QLocale(QLocale::Spanish, QLocale::Colombia) << QDateTime(QDate(1999, 1, 18), QTime(11, 49, 00));
2787}
2788
2789void tst_QDateTime::fromStringToStringLocale()
2790{
2791 QFETCH(QLocale, locale);
2792 QFETCH(QDateTime, dateTime);
2793
2794 QLocale def;
2795 QLocale::setDefault(locale);
2796#define ROUNDTRIP(format) \
2797 QCOMPARE(QDateTime::fromString(dateTime.toString(format), format), dateTime)
2798
2799 ROUNDTRIP(Qt::DefaultLocaleShortDate);
2800 ROUNDTRIP(Qt::SystemLocaleShortDate);
2801
2802 // obsolete
2803 ROUNDTRIP(Qt::SystemLocaleDate);
2804 ROUNDTRIP(Qt::LocaleDate);
2805
2806 ROUNDTRIP(Qt::DefaultLocaleLongDate);
2807 ROUNDTRIP(Qt::SystemLocaleLongDate);
2808#undef ROUNDTRIP
2809 QLocale::setDefault(def);
2810}
2811QT_WARNING_POP
2812#endif // ### Qt 6: remove
2813
2814void tst_QDateTime::offsetFromUtc()
2815{
2816 /* Check default value. */
2817 QCOMPARE(QDateTime().offsetFromUtc(), 0);
2818
2819 // Offset constructor
2820 QDateTime dt1(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, 60 * 60);
2821 QCOMPARE(dt1.offsetFromUtc(), 60 * 60);
2822#if QT_CONFIG(timezone)
2823 QVERIFY(dt1.timeZone().isValid());
2824#endif
2825 dt1 = QDateTime(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, -60 * 60);
2826 QCOMPARE(dt1.offsetFromUtc(), -60 * 60);
2827
2828 // UTC should be 0 offset
2829 QDateTime dt2(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::UTC);
2830 QCOMPARE(dt2.offsetFromUtc(), 0);
2831
2832 // LocalTime should vary
2833 if (zoneIsCET) {
2834 // Time definitely in Standard Time so 1 hour ahead
2835 QDateTime dt3(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::LocalTime);
2836 QCOMPARE(dt3.offsetFromUtc(), 1 * 60 * 60);
2837 // Time definitely in Daylight Time so 2 hours ahead
2838 QDateTime dt4(QDate(2013, 6, 1), QTime(0, 0, 0), Qt::LocalTime);
2839 QCOMPARE(dt4.offsetFromUtc(), 2 * 60 * 60);
2840 } else {
2841 QSKIP("You must test using Central European (CET/CEST) time zone, e.g. TZ=Europe/Oslo");
2842 }
2843
2844#if QT_CONFIG(timezone)
2845 QDateTime dt5(QDate(2013, 1, 1), QTime(0, 0, 0), QTimeZone("Pacific/Auckland"));
2846 QCOMPARE(dt5.offsetFromUtc(), 46800);
2847
2848 QDateTime dt6(QDate(2013, 6, 1), QTime(0, 0, 0), QTimeZone("Pacific/Auckland"));
2849 QCOMPARE(dt6.offsetFromUtc(), 43200);
2850#endif
2851}
2852
2853void tst_QDateTime::setOffsetFromUtc()
2854{
2855 /* Basic tests. */
2856 {
2857 QDateTime dt(QDateTime::currentDateTime());
2858 dt.setTimeSpec(Qt::LocalTime);
2859
2860 dt.setOffsetFromUtc(0);
2861 QCOMPARE(dt.offsetFromUtc(), 0);
2862 QCOMPARE(dt.timeSpec(), Qt::UTC);
2863
2864 dt.setOffsetFromUtc(-100);
2865 QCOMPARE(dt.offsetFromUtc(), -100);
2866 QCOMPARE(dt.timeSpec(), Qt::OffsetFromUTC);
2867 }
2868
2869 /* Test detaching. */
2870 {
2871 QDateTime dt(QDateTime::currentDateTime());
2872 QDateTime dt2(dt);
2873 int offset2 = dt2.offsetFromUtc();
2874
2875 dt.setOffsetFromUtc(501);
2876
2877 QCOMPARE(dt.offsetFromUtc(), 501);
2878 QCOMPARE(dt2.offsetFromUtc(), offset2);
2879 }
2880
2881 /* Check copying. */
2882 {
2883 QDateTime dt(QDateTime::currentDateTime());
2884 dt.setOffsetFromUtc(502);
2885 QCOMPARE(dt.offsetFromUtc(), 502);
2886
2887 QDateTime dt2(dt);
2888 QCOMPARE(dt2.offsetFromUtc(), 502);
2889 }
2890
2891 /* Check assignment. */
2892 {
2893 QDateTime dt(QDateTime::currentDateTime());
2894 dt.setOffsetFromUtc(502);
2895 QDateTime dt2;
2896 dt2 = dt;
2897
2898 QCOMPARE(dt2.offsetFromUtc(), 502);
2899 }
2900
2901 // Check spec persists
2902 QDateTime dt1(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::OffsetFromUTC, 60 * 60);
2903 dt1.setMSecsSinceEpoch(123456789);
2904 QCOMPARE(dt1.timeSpec(), Qt::OffsetFromUTC);
2905 QCOMPARE(dt1.offsetFromUtc(), 60 * 60);
2906 dt1.setSecsSinceEpoch(123456789);
2907 QCOMPARE(dt1.timeSpec(), Qt::OffsetFromUTC);
2908 QCOMPARE(dt1.offsetFromUtc(), 60 * 60);
2909
2910 // Check datastream serialises the offset seconds
2911 QByteArray tmp;
2912 {
2913 QDataStream ds(&tmp, QIODevice::WriteOnly);
2914 ds << dt1;
2915 }
2916 QDateTime dt2;
2917 {
2918 QDataStream ds(&tmp, QIODevice::ReadOnly);
2919 ds >> dt2;
2920 }
2921 QCOMPARE(dt2, dt1);
2922 QCOMPARE(dt2.timeSpec(), Qt::OffsetFromUTC);
2923 QCOMPARE(dt2.offsetFromUtc(), 60 * 60);
2924}
2925
2926void tst_QDateTime::toOffsetFromUtc()
2927{
2928 QDateTime dt1(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::UTC);
2929
2930 QDateTime dt2 = dt1.toOffsetFromUtc(offsetSeconds: 60 * 60);
2931 QCOMPARE(dt2, dt1);
2932 QCOMPARE(dt2.timeSpec(), Qt::OffsetFromUTC);
2933 QCOMPARE(dt2.date(), QDate(2013, 1, 1));
2934 QCOMPARE(dt2.time(), QTime(1, 0, 0));
2935
2936 dt2 = dt1.toOffsetFromUtc(offsetSeconds: 0);
2937 QCOMPARE(dt2, dt1);
2938 QCOMPARE(dt2.timeSpec(), Qt::UTC);
2939 QCOMPARE(dt2.date(), QDate(2013, 1, 1));
2940 QCOMPARE(dt2.time(), QTime(0, 0, 0));
2941
2942 dt2 = dt1.toTimeSpec(spec: Qt::OffsetFromUTC);
2943 QCOMPARE(dt2, dt1);
2944 QCOMPARE(dt2.timeSpec(), Qt::UTC);
2945 QCOMPARE(dt2.date(), QDate(2013, 1, 1));
2946 QCOMPARE(dt2.time(), QTime(0, 0, 0));
2947}
2948
2949void tst_QDateTime::zoneAtTime_data()
2950{
2951 QTest::addColumn<QByteArray>(name: "ianaID");
2952 QTest::addColumn<QDate>(name: "date");
2953 QTest::addColumn<int>(name: "offset");
2954#define ADDROW(name, zone, date, offset) \
2955 QTest::newRow(name) << QByteArray(zone) << (date) << (offset)
2956
2957 // Check DST handling around epoch:
2958 {
2959 QDate epoch(1970, 1, 1);
2960 ADDROW("epoch:UTC", "UTC", epoch, 0);
2961 // Paris and Berlin skipped DST around 1970; but Rome used it.
2962 ADDROW("epoch:CET", "Europe/Rome", epoch, 3600);
2963 ADDROW("epoch:PST", "America/Vancouver", epoch, -8 * 3600);
2964 ADDROW("epoch:EST", "America/New_York", epoch, -5 * 3600);
2965 }
2966 {
2967 // QDateTime deliberately ignores DST before the epoch.
2968 QDate summer69(1969, 8, 15); // Woodstock started
2969 ADDROW("summer69:UTC", "UTC", summer69, 0);
2970 ADDROW("summer69:CET", "Europe/Rome", summer69, 3600);
2971 ADDROW("summer69:PST", "America/Vancouver", summer69, -8 * 3600);
2972 ADDROW("summer69:EST", "America/New_York", summer69, -5 * 3600);
2973 }
2974 {
2975 // ... but takes it into account after:
2976 QDate summer70(1970, 8, 26); // Isle of Wight festival
2977 ADDROW("summer70:UTC", "UTC", summer70, 0);
2978 ADDROW("summer70:CET", "Europe/Rome", summer70, 2 * 3600);
2979 ADDROW("summer70:PST", "America/Vancouver", summer70, -7 * 3600);
2980 ADDROW("summer70:EST", "America/New_York", summer70, -4 * 3600);
2981 }
2982
2983#ifdef Q_OS_ANDROID // QTBUG-68835; gets offset 0 for the affected tests.
2984# define NONANDROIDROW(name, zone, date, offset)
2985#else
2986# define NONANDROIDROW(name, zone, date, offset) ADDROW(name, zone, date, offset)
2987#endif
2988
2989#ifndef Q_OS_WIN
2990 // Bracket a few noteworthy transitions:
2991 ADDROW("before:ACWST", "Australia/Eucla", QDate(1974, 10, 26), 31500); // 8:45
2992 NONANDROIDROW("after:ACWST", "Australia/Eucla", QDate(1974, 10, 27), 35100); // 9:45
2993 NONANDROIDROW("before:NPT", "Asia/Kathmandu", QDate(1985, 12, 31), 19800); // 5:30
2994 ADDROW("after:NPT", "Asia/Kathmandu", QDate(1986, 1, 1), 20700); // 5:45
2995 // The two that have skipped a day (each):
2996 NONANDROIDROW("before:LINT", "Pacific/Kiritimati", QDate(1994, 12, 30), -36000);
2997 ADDROW("after:LINT", "Pacific/Kiritimati", QDate(1995, 1, 2), 14 * 3600);
2998 ADDROW("after:WST", "Pacific/Apia", QDate(2011, 12, 31), 14 * 3600);
2999#endif // MS lacks ACWST, NPT; doesn't grok date-line crossings; and Windows 7 lacks LINT.
3000 ADDROW("before:WST", "Pacific/Apia", QDate(2011, 12, 29), -36000);
3001#undef ADDROW
3002}
3003
3004void tst_QDateTime::zoneAtTime()
3005{
3006#if QT_CONFIG(timezone)
3007 QFETCH(QByteArray, ianaID);
3008 QFETCH(QDate, date);
3009 QFETCH(int, offset);
3010 const QTime noon(12, 0);
3011
3012 QTimeZone zone(ianaID);
3013 QVERIFY(zone.isValid());
3014 QCOMPARE(QDateTime(date, noon, zone).offsetFromUtc(), offset);
3015 if (date.year() < 1970)
3016 QCOMPARE(zone.standardTimeOffset(QDateTime(date, noon, zone)), offset);
3017 else // zone.offsetFromUtc *does* include DST, even before epoch
3018 QCOMPARE(zone.offsetFromUtc(QDateTime(date, noon, zone)), offset);
3019#else
3020 QSKIP("Needs timezone feature enabled");
3021#endif
3022}
3023
3024void tst_QDateTime::timeZoneAbbreviation()
3025{
3026 QDateTime dt1(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, 60 * 60);
3027 QCOMPARE(dt1.timeZoneAbbreviation(), QString("UTC+01:00"));
3028 QDateTime dt2(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, -60 * 60);
3029 QCOMPARE(dt2.timeZoneAbbreviation(), QString("UTC-01:00"));
3030
3031 QDateTime dt3(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::UTC);
3032 QCOMPARE(dt3.timeZoneAbbreviation(), QString("UTC"));
3033
3034 // LocalTime should vary
3035 if (zoneIsCET) {
3036 // Time definitely in Standard Time
3037 QDateTime dt4(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::LocalTime);
3038#ifdef Q_OS_WIN
3039 QEXPECT_FAIL("", "Windows only reports long name (QTBUG-32759)", Continue);
3040#endif
3041 QCOMPARE(dt4.timeZoneAbbreviation(), QStringLiteral("CET"));
3042 // Time definitely in Daylight Time
3043 QDateTime dt5(QDate(2013, 6, 1), QTime(0, 0, 0), Qt::LocalTime);
3044#ifdef Q_OS_WIN
3045 QEXPECT_FAIL("", "Windows only reports long name (QTBUG-32759)", Continue);
3046#endif
3047 QCOMPARE(dt5.timeZoneAbbreviation(), QStringLiteral("CEST"));
3048 } else {
3049 qDebug(msg: "(Skipped some CET-only tests)");
3050 }
3051
3052#if QT_CONFIG(timezone)
3053 const QTimeZone berlin("Europe/Berlin");
3054 const QDateTime jan(QDate(2013, 1, 1).startOfDay(zone: berlin));
3055 const QDateTime jul(QDate(2013, 7, 1).startOfDay(zone: berlin));
3056
3057 QCOMPARE(jan.timeZoneAbbreviation(), berlin.abbreviation(jan));
3058 QCOMPARE(jul.timeZoneAbbreviation(), berlin.abbreviation(jul));
3059#endif
3060}
3061
3062void tst_QDateTime::getDate()
3063{
3064 {
3065 int y = -33, m = -44, d = -55;
3066 QDate date;
3067 date.getDate(year: &y, month: &m, day: &d);
3068 QCOMPARE(date.year(), y);
3069 QCOMPARE(date.month(), m);
3070 QCOMPARE(date.day(), d);
3071
3072 date.getDate(year: 0, month: 0, day: 0);
3073 }
3074
3075 {
3076 int y = -33, m = -44, d = -55;
3077 QDate date(1998, 5, 24);
3078 date.getDate(year: 0, month: &m, day: 0);
3079 date.getDate(year: &y, month: 0, day: 0);
3080 date.getDate(year: 0, month: 0, day: &d);
3081
3082 QCOMPARE(date.year(), y);
3083 QCOMPARE(date.month(), m);
3084 QCOMPARE(date.day(), d);
3085 }
3086}
3087
3088void tst_QDateTime::fewDigitsInYear() const
3089{
3090 const QDateTime three(QDate(300, 10, 11), QTime());
3091 QCOMPARE(three.toString(QLatin1String("yyyy-MM-dd")), QString::fromLatin1("0300-10-11"));
3092
3093 const QDateTime two(QDate(20, 10, 11), QTime());
3094 QCOMPARE(two.toString(QLatin1String("yyyy-MM-dd")), QString::fromLatin1("0020-10-11"));
3095
3096 const QDateTime yyTwo(QDate(30, 10, 11), QTime());
3097 QCOMPARE(yyTwo.toString(QLatin1String("yy-MM-dd")), QString::fromLatin1("30-10-11"));
3098
3099 const QDateTime yyOne(QDate(4, 10, 11), QTime());
3100 QCOMPARE(yyOne.toString(QLatin1String("yy-MM-dd")), QString::fromLatin1("04-10-11"));
3101}
3102
3103void tst_QDateTime::printNegativeYear() const
3104{
3105 {
3106 QDateTime date(QDate(-20, 10, 11).startOfDay());
3107 QVERIFY(date.isValid());
3108 QCOMPARE(date.toString(QLatin1String("yyyy")), QString::fromLatin1("-0020"));
3109 }
3110
3111 {
3112 QDateTime date(QDate(-3, 10, 11).startOfDay());
3113 QVERIFY(date.isValid());
3114 QCOMPARE(date.toString(QLatin1String("yyyy")), QString::fromLatin1("-0003"));
3115 }
3116
3117 {
3118 QDateTime date(QDate(-400, 10, 11).startOfDay());
3119 QVERIFY(date.isValid());
3120 QCOMPARE(date.toString(QLatin1String("yyyy")), QString::fromLatin1("-0400"));
3121 }
3122}
3123
3124#if QT_CONFIG(textdate)
3125void tst_QDateTime::roundtripTextDate() const
3126{
3127 /* This code path should not result in warnings. */
3128 const QDateTime now(QDateTime::currentDateTime());
3129 // TextDate drops millis:
3130 const QDateTime theDateTime(now.addMSecs(msecs: -now.time().msec()));
3131 QCOMPARE(QDateTime::fromString(theDateTime.toString(Qt::TextDate), Qt::TextDate), theDateTime);
3132}
3133#endif
3134
3135void tst_QDateTime::utcOffsetLessThan() const
3136{
3137 QDateTime dt1(QDate(2002, 10, 10), QTime(0, 0, 0));
3138 QDateTime dt2(dt1);
3139
3140 dt1.setOffsetFromUtc(-(2 * 60 * 60)); // Minus two hours.
3141 dt2.setOffsetFromUtc(-(3 * 60 * 60)); // Minus three hours.
3142
3143 QVERIFY(dt1 != dt2);
3144 QVERIFY(!(dt1 == dt2));
3145 QVERIFY(dt1 < dt2);
3146 QVERIFY(!(dt2 < dt1));
3147}
3148
3149void tst_QDateTime::isDaylightTime() const
3150{
3151 QDateTime utc1(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC);
3152 QVERIFY(!utc1.isDaylightTime());
3153 QDateTime utc2(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC);
3154 QVERIFY(!utc2.isDaylightTime());
3155
3156 QDateTime offset1(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::OffsetFromUTC, 1 * 60 * 60);
3157 QVERIFY(!offset1.isDaylightTime());
3158 QDateTime offset2(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::OffsetFromUTC, 1 * 60 * 60);
3159 QVERIFY(!offset2.isDaylightTime());
3160
3161 if (zoneIsCET) {
3162 QDateTime cet1(QDate(2012, 1, 1), QTime(0, 0, 0));
3163 QVERIFY(!cet1.isDaylightTime());
3164 QDateTime cet2(QDate(2012, 6, 1), QTime(0, 0, 0));
3165 QVERIFY(cet2.isDaylightTime());
3166 } else {
3167 QSKIP("You must test using Central European (CET/CEST) time zone, e.g. TZ=Europe/Oslo");
3168 }
3169}
3170
3171void tst_QDateTime::daylightTransitions() const
3172{
3173 if (zoneIsCET) {
3174 // CET transitions occur at 01:00:00 UTC on last Sunday in March and October
3175 // 2011-03-27 02:00:00 CET became 03:00:00 CEST at msecs = 1301187600000
3176 // 2011-10-30 03:00:00 CEST became 02:00:00 CET at msecs = 1319936400000
3177 // 2012-03-25 02:00:00 CET became 03:00:00 CEST at msecs = 1332637200000
3178 // 2012-10-28 03:00:00 CEST became 02:00:00 CET at msecs = 1351386000000
3179 const qint64 daylight2012 = 1332637200000;
3180 const qint64 standard2012 = 1351386000000;
3181 const qint64 msecsOneHour = 3600000;
3182
3183 // Test for correct behviour for StandardTime -> DaylightTime transition, i.e. missing hour
3184
3185 // Test setting date, time in missing hour will be invalid
3186
3187 QDateTime before(QDate(2012, 3, 25), QTime(1, 59, 59, 999));
3188 QVERIFY(before.isValid());
3189 QCOMPARE(before.date(), QDate(2012, 3, 25));
3190 QCOMPARE(before.time(), QTime(1, 59, 59, 999));
3191 QCOMPARE(before.toMSecsSinceEpoch(), daylight2012 - 1);
3192
3193 QDateTime missing(QDate(2012, 3, 25), QTime(2, 0, 0));
3194 QVERIFY(!missing.isValid());
3195 QCOMPARE(missing.date(), QDate(2012, 3, 25));
3196 QCOMPARE(missing.time(), QTime(2, 0));
3197 // datetimeparser relies on toMSecsSinceEpoch to still work:
3198 QCOMPARE(missing.toMSecsSinceEpoch(), daylight2012);
3199
3200 QDateTime after(QDate(2012, 3, 25), QTime(3, 0, 0));
3201 QVERIFY(after.isValid());
3202 QCOMPARE(after.date(), QDate(2012, 3, 25));
3203 QCOMPARE(after.time(), QTime(3, 0, 0));
3204 QCOMPARE(after.toMSecsSinceEpoch(), daylight2012);
3205
3206 // Test round-tripping of msecs
3207
3208 before.setMSecsSinceEpoch(daylight2012 - 1);
3209 QVERIFY(before.isValid());
3210 QCOMPARE(before.date(), QDate(2012, 3, 25));
3211 QCOMPARE(before.time(), QTime(1, 59, 59, 999));
3212 QCOMPARE(before.toMSecsSinceEpoch(), daylight2012 -1);
3213
3214 after.setMSecsSinceEpoch(daylight2012);
3215 QVERIFY(after.isValid());
3216 QCOMPARE(after.date(), QDate(2012, 3, 25));
3217 QCOMPARE(after.time(), QTime(3, 0, 0));
3218 QCOMPARE(after.toMSecsSinceEpoch(), daylight2012);
3219
3220 // Test changing time spec re-validates the date/time
3221
3222 QDateTime utc(QDate(2012, 3, 25), QTime(2, 00, 0), Qt::UTC);
3223 QVERIFY(utc.isValid());
3224 QCOMPARE(utc.date(), QDate(2012, 3, 25));
3225 QCOMPARE(utc.time(), QTime(2, 0, 0));
3226 utc.setTimeSpec(Qt::LocalTime);
3227 QVERIFY(!utc.isValid());
3228 QCOMPARE(utc.date(), QDate(2012, 3, 25));
3229 QCOMPARE(utc.time(), QTime(2, 0, 0));
3230 utc.setTimeSpec(Qt::UTC);
3231 QVERIFY(utc.isValid());
3232 QCOMPARE(utc.date(), QDate(2012, 3, 25));
3233 QCOMPARE(utc.time(), QTime(2, 0, 0));
3234
3235 // Test date maths, if result falls in missing hour then becomes next
3236 // hour (or is always invalid; mktime() may reject gap-times).
3237
3238 QDateTime test(QDate(2011, 3, 25), QTime(2, 0, 0));
3239 QVERIFY(test.isValid());
3240 test = test.addYears(years: 1);
3241 const bool handled = test.isValid();
3242#define CHECK_SPRING_FORWARD(test) \
3243 if (test.isValid()) { \
3244 QCOMPARE(test.date(), QDate(2012, 3, 25)); \
3245 QCOMPARE(test.time(), QTime(3, 0, 0)); \
3246 } else { \
3247 QVERIFY(!handled); \
3248 }
3249 CHECK_SPRING_FORWARD(test);
3250
3251 test = QDateTime(QDate(2012, 2, 25), QTime(2, 0, 0));
3252 QVERIFY(test.isValid());
3253 test = test.addMonths(months: 1);
3254 CHECK_SPRING_FORWARD(test);
3255
3256 test = QDateTime(QDate(2012, 3, 24), QTime(2, 0, 0));
3257 QVERIFY(test.isValid());
3258 test = test.addDays(days: 1);
3259 CHECK_SPRING_FORWARD(test);
3260
3261 test = QDateTime(QDate(2012, 3, 25), QTime(1, 0, 0));
3262 QVERIFY(test.isValid());
3263 QCOMPARE(test.toMSecsSinceEpoch(), daylight2012 - msecsOneHour);
3264 test = test.addMSecs(msecs: msecsOneHour);
3265 CHECK_SPRING_FORWARD(test);
3266 if (handled)
3267 QCOMPARE(test.toMSecsSinceEpoch(), daylight2012);
3268#undef CHECK_SPRING_FORWARD
3269
3270 // Test for correct behviour for DaylightTime -> StandardTime transition, i.e. second occurrence
3271
3272 // Test setting date and time in first and second occurrence will be valid
3273
3274 // 1 hour before transition is 2:00:00 FirstOccurrence
3275 QDateTime hourBefore(QDate(2012, 10, 28), QTime(2, 0, 0));
3276 QVERIFY(hourBefore.isValid());
3277 QCOMPARE(hourBefore.date(), QDate(2012, 10, 28));
3278 QCOMPARE(hourBefore.time(), QTime(2, 0, 0));
3279#ifdef Q_OS_WIN
3280 // Windows uses SecondOccurrence
3281 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3282#endif // Q_OS_WIN
3283 QCOMPARE(hourBefore.toMSecsSinceEpoch(), standard2012 - msecsOneHour);
3284
3285 // 1 msec before transition is 2:59:59.999 FirstOccurrence
3286 QDateTime msecBefore(QDate(2012, 10, 28), QTime(2, 59, 59, 999));
3287 QVERIFY(msecBefore.isValid());
3288 QCOMPARE(msecBefore.date(), QDate(2012, 10, 28));
3289 QCOMPARE(msecBefore.time(), QTime(2, 59, 59, 999));
3290#if defined(Q_OS_DARWIN) || defined(Q_OS_WIN) || defined(Q_OS_QNX) || defined(Q_OS_ANDROID)
3291 // Win and Mac uses SecondOccurrence here
3292 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3293#endif // Q_OS_MAC
3294 QCOMPARE(msecBefore.toMSecsSinceEpoch(), standard2012 - 1);
3295
3296 // At transition is 2:00:00 SecondOccurrence
3297 QDateTime atTran(QDate(2012, 10, 28), QTime(2, 0, 0));
3298 QVERIFY(atTran.isValid());
3299 QCOMPARE(atTran.date(), QDate(2012, 10, 28));
3300 QCOMPARE(atTran.time(), QTime(2, 0, 0));
3301#ifndef Q_OS_WIN
3302 // Windows uses SecondOccurrence
3303 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3304#endif // Q_OS_WIN
3305 QCOMPARE(atTran.toMSecsSinceEpoch(), standard2012);
3306
3307 // 59:59.999 after transition is 2:59:59.999 SecondOccurrence
3308 QDateTime afterTran(QDate(2012, 10, 28), QTime(2, 59, 59, 999));
3309 QVERIFY(afterTran.isValid());
3310 QCOMPARE(afterTran.date(), QDate(2012, 10, 28));
3311 QCOMPARE(afterTran.time(), QTime(2, 59, 59, 999));
3312#ifdef __GLIBCXX__
3313 // Linux (i.e. glibc) mktime bug reuses last calculation
3314 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3315#endif // Q_OS_UNIX
3316 QCOMPARE(afterTran.toMSecsSinceEpoch(), standard2012 + msecsOneHour - 1);
3317
3318 // 1 hour after transition is 3:00:00 FirstOccurrence
3319 QDateTime hourAfter(QDate(2012, 10, 28), QTime(3, 0, 0));
3320 QVERIFY(hourAfter.isValid());
3321 QCOMPARE(hourAfter.date(), QDate(2012, 10, 28));
3322 QCOMPARE(hourAfter.time(), QTime(3, 0, 0));
3323 QCOMPARE(hourAfter.toMSecsSinceEpoch(), standard2012 + msecsOneHour);
3324
3325 // Test round-tripping of msecs
3326
3327 // 1 hour before transition is 2:00:00 FirstOccurrence
3328 hourBefore.setMSecsSinceEpoch(standard2012 - msecsOneHour);
3329 QVERIFY(hourBefore.isValid());
3330 QCOMPARE(hourBefore.date(), QDate(2012, 10, 28));
3331 QCOMPARE(hourBefore.time(), QTime(2, 0, 0));
3332 QCOMPARE(hourBefore.toMSecsSinceEpoch(), standard2012 - msecsOneHour);
3333
3334 // 1 msec before transition is 2:59:59.999 FirstOccurrence
3335 msecBefore.setMSecsSinceEpoch(standard2012 - 1);
3336 QVERIFY(msecBefore.isValid());
3337 QCOMPARE(msecBefore.date(), QDate(2012, 10, 28));
3338 QCOMPARE(msecBefore.time(), QTime(2, 59, 59, 999));
3339 QCOMPARE(msecBefore.toMSecsSinceEpoch(), standard2012 - 1);
3340
3341 // At transition is 2:00:00 SecondOccurrence
3342 atTran.setMSecsSinceEpoch(standard2012);
3343 QVERIFY(atTran.isValid());
3344 QCOMPARE(atTran.date(), QDate(2012, 10, 28));
3345 QCOMPARE(atTran.time(), QTime(2, 0, 0));
3346 QCOMPARE(atTran.toMSecsSinceEpoch(), standard2012);
3347
3348 // 59:59.999 after transition is 2:59:59.999 SecondOccurrence
3349 afterTran.setMSecsSinceEpoch(standard2012 + msecsOneHour - 1);
3350 QVERIFY(afterTran.isValid());
3351 QCOMPARE(afterTran.date(), QDate(2012, 10, 28));
3352 QCOMPARE(afterTran.time(), QTime(2, 59, 59, 999));
3353 QCOMPARE(afterTran.toMSecsSinceEpoch(), standard2012 + msecsOneHour - 1);
3354
3355 // 1 hour after transition is 3:00:00 FirstOccurrence
3356 hourAfter.setMSecsSinceEpoch(standard2012 + msecsOneHour);
3357 QVERIFY(hourAfter.isValid());
3358 QCOMPARE(hourAfter.date(), QDate(2012, 10, 28));
3359 QCOMPARE(hourAfter.time(), QTime(3, 0, 0));
3360 QCOMPARE(hourAfter.toMSecsSinceEpoch(), standard2012 + msecsOneHour);
3361
3362 // Test date maths, result is always FirstOccurrence
3363
3364 // Add year to get to tran FirstOccurrence
3365 test = QDateTime(QDate(2011, 10, 28), QTime(2, 0, 0));
3366 test = test.addYears(years: 1);
3367 QVERIFY(test.isValid());
3368 QCOMPARE(test.date(), QDate(2012, 10, 28));
3369 QCOMPARE(test.time(), QTime(2, 0, 0));
3370#ifdef Q_OS_WIN
3371 // Windows uses SecondOccurrence
3372 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3373#endif // Q_OS_WIN
3374 QCOMPARE(test.toMSecsSinceEpoch(), standard2012 - msecsOneHour);
3375
3376 // Add year to get to after tran FirstOccurrence
3377 test = QDateTime(QDate(2011, 10, 28), QTime(3, 0, 0));
3378 test = test.addYears(years: 1);
3379 QVERIFY(test.isValid());
3380 QCOMPARE(test.date(), QDate(2012, 10, 28));
3381 QCOMPARE(test.time(), QTime(3, 0, 0));
3382 QCOMPARE(test.toMSecsSinceEpoch(), standard2012 + msecsOneHour);
3383
3384 // Add year to tran FirstOccurrence
3385 test = QDateTime(QDate(2011, 10, 30), QTime(2, 0, 0));
3386 test = test.addYears(years: 1);
3387 QVERIFY(test.isValid());
3388 QCOMPARE(test.date(), QDate(2012, 10, 30));
3389 QCOMPARE(test.time(), QTime(2, 0, 0));
3390
3391 // Add year to tran SecondOccurrence
3392 test = QDateTime(QDate(2011, 10, 30), QTime(2, 0, 0)); // TODO SecondOccurrence
3393 test = test.addYears(years: 1);
3394 QVERIFY(test.isValid());
3395 QCOMPARE(test.date(), QDate(2012, 10, 30));
3396 QCOMPARE(test.time(), QTime(2, 0, 0));
3397
3398 // Add year to after tran FirstOccurrence
3399 test = QDateTime(QDate(2011, 10, 30), QTime(3, 0, 0));
3400 test = test.addYears(years: 1);
3401 QVERIFY(test.isValid());
3402 QCOMPARE(test.date(), QDate(2012, 10, 30));
3403 QCOMPARE(test.time(), QTime(3, 0, 0));
3404
3405
3406 // Add month to get to tran FirstOccurrence
3407 test = QDateTime(QDate(2012, 9, 28), QTime(2, 0, 0));
3408 test = test.addMonths(months: 1);
3409 QVERIFY(test.isValid());
3410 QCOMPARE(test.date(), QDate(2012, 10, 28));
3411 QCOMPARE(test.time(), QTime(2, 0, 0));
3412#ifdef Q_OS_WIN
3413 // Windows uses SecondOccurrence
3414 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3415#endif // Q_OS_WIN
3416 QCOMPARE(test.toMSecsSinceEpoch(), standard2012 - msecsOneHour);
3417
3418 // Add month to get to after tran FirstOccurrence
3419 test = QDateTime(QDate(2012, 9, 28), QTime(3, 0, 0));
3420 test = test.addMonths(months: 1);
3421 QVERIFY(test.isValid());
3422 QCOMPARE(test.date(), QDate(2012, 10, 28));
3423 QCOMPARE(test.time(), QTime(3, 0, 0));
3424 QCOMPARE(test.toMSecsSinceEpoch(), standard2012 + msecsOneHour);
3425
3426 // Add month to tran FirstOccurrence
3427 test = QDateTime(QDate(2011, 10, 30), QTime(2, 0, 0));
3428 test = test.addMonths(months: 1);
3429 QVERIFY(test.isValid());
3430 QCOMPARE(test.date(), QDate(2011, 11, 30));
3431 QCOMPARE(test.time(), QTime(2, 0, 0));
3432
3433 // Add month to tran SecondOccurrence
3434 test = QDateTime(QDate(2011, 10, 30), QTime(2, 0, 0)); // TODO SecondOccurrence
3435 test = test.addMonths(months: 1);
3436 QVERIFY(test.isValid());
3437 QCOMPARE(test.date(), QDate(2011, 11, 30));
3438 QCOMPARE(test.time(), QTime(2, 0, 0));
3439
3440 // Add month to after tran FirstOccurrence
3441 test = QDateTime(QDate(2011, 10, 30), QTime(3, 0, 0));
3442 test = test.addMonths(months: 1);
3443 QVERIFY(test.isValid());
3444 QCOMPARE(test.date(), QDate(2011, 11, 30));
3445 QCOMPARE(test.time(), QTime(3, 0, 0));
3446
3447
3448 // Add day to get to tran FirstOccurrence
3449 test = QDateTime(QDate(2012, 10, 27), QTime(2, 0, 0));
3450 test = test.addDays(days: 1);
3451 QVERIFY(test.isValid());
3452 QCOMPARE(test.date(), QDate(2012, 10, 28));
3453 QCOMPARE(test.time(), QTime(2, 0, 0));
3454#ifdef Q_OS_WIN
3455 // Windows uses SecondOccurrence
3456 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3457#endif // Q_OS_WIN
3458 QCOMPARE(test.toMSecsSinceEpoch(), standard2012 - msecsOneHour);
3459
3460 // Add day to get to after tran FirstOccurrence
3461 test = QDateTime(QDate(2012, 10, 27), QTime(3, 0, 0));
3462 test = test.addDays(days: 1);
3463 QVERIFY(test.isValid());
3464 QCOMPARE(test.date(), QDate(2012, 10, 28));
3465 QCOMPARE(test.time(), QTime(3, 0, 0));
3466 QCOMPARE(test.toMSecsSinceEpoch(), standard2012 + msecsOneHour);
3467
3468 // Add day to tran FirstOccurrence
3469 test = QDateTime(QDate(2011, 10, 30), QTime(2, 0, 0));
3470 test = test.addDays(days: 1);
3471 QVERIFY(test.isValid());
3472 QCOMPARE(test.date(), QDate(2011, 10, 31));
3473 QCOMPARE(test.time(), QTime(2, 0, 0));
3474
3475 // Add day to tran SecondOccurrence
3476 test = QDateTime(QDate(2011, 10, 30), QTime(2, 0, 0)); // TODO SecondOccurrence
3477 test = test.addDays(days: 1);
3478 QVERIFY(test.isValid());
3479 QCOMPARE(test.date(), QDate(2011, 10, 31));
3480 QCOMPARE(test.time(), QTime(2, 0, 0));
3481
3482 // Add day to after tran FirstOccurrence
3483 test = QDateTime(QDate(2011, 10, 30), QTime(3, 0, 0));
3484 test = test.addDays(days: 1);
3485 QVERIFY(test.isValid());
3486 QCOMPARE(test.date(), QDate(2011, 10, 31));
3487 QCOMPARE(test.time(), QTime(3, 0, 0));
3488
3489
3490 // Add hour to get to tran FirstOccurrence
3491 test = QDateTime(QDate(2012, 10, 28), QTime(1, 0, 0));
3492 test = test.addMSecs(msecs: msecsOneHour);
3493 QVERIFY(test.isValid());
3494 QCOMPARE(test.date(), QDate(2012, 10, 28));
3495 QCOMPARE(test.time(), QTime(2, 0, 0));
3496 QCOMPARE(test.toMSecsSinceEpoch(), standard2012 - msecsOneHour);
3497
3498 // Add hour to tran FirstOccurrence to get to tran SecondOccurrence
3499 test = QDateTime(QDate(2012, 10, 28), QTime(2, 0, 0));
3500 test = test.addMSecs(msecs: msecsOneHour);
3501 QVERIFY(test.isValid());
3502 QCOMPARE(test.date(), QDate(2012, 10, 28));
3503#ifdef Q_OS_WIN
3504 // Windows uses SecondOccurrence
3505 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3506#endif // Q_OS_WIN
3507 QCOMPARE(test.time(), QTime(2, 0, 0));
3508#ifdef Q_OS_WIN
3509 // Windows uses SecondOccurrence
3510 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3511#endif // Q_OS_WIN
3512 QCOMPARE(test.toMSecsSinceEpoch(), standard2012);
3513
3514 // Add hour to tran SecondOccurrence to get to after tran FirstOccurrence
3515 test = QDateTime(QDate(2012, 10, 28), QTime(2, 0, 0)); // TODO SecondOccurrence
3516 test = test.addMSecs(msecs: msecsOneHour);
3517 QVERIFY(test.isValid());
3518 QCOMPARE(test.date(), QDate(2012, 10, 28));
3519#if defined(Q_OS_DARWIN) || defined(Q_OS_QNX) || defined(Q_OS_ANDROID)
3520 // Mac uses FirstOccurrence, Windows uses SecondOccurrence, Linux uses last calculation
3521 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3522#endif // Q_OS_WIN
3523 QCOMPARE(test.time(), QTime(3, 0, 0));
3524#if defined(Q_OS_DARWIN) || defined(Q_OS_QNX) || defined(Q_OS_ANDROID)
3525 // Mac uses FirstOccurrence, Windows uses SecondOccurrence, Linux uses last calculation
3526 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3527#endif // Q_OS_WIN
3528 QCOMPARE(test.toMSecsSinceEpoch(), standard2012 + msecsOneHour);
3529
3530 } else {
3531 QSKIP("You must test using Central European (CET/CEST) time zone, e.g. TZ=Europe/Oslo");
3532 }
3533}
3534
3535void tst_QDateTime::timeZones() const
3536{
3537#if QT_CONFIG(timezone)
3538 QTimeZone invalidTz = QTimeZone("Vulcan/ShiKahr");
3539 QCOMPARE(invalidTz.isValid(), false);
3540 QDateTime invalidDateTime = QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0), invalidTz);
3541 QCOMPARE(invalidDateTime.isValid(), false);
3542 QCOMPARE(invalidDateTime.date(), QDate(2000, 1, 1));
3543 QCOMPARE(invalidDateTime.time(), QTime(0, 0, 0));
3544
3545 QTimeZone nzTz = QTimeZone("Pacific/Auckland");
3546 QTimeZone nzTzOffset = QTimeZone(12 * 3600);
3547
3548 // During Standard Time NZ is +12:00
3549 QDateTime utcStd(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC);
3550 QDateTime nzStd(QDate(2012, 6, 1), QTime(12, 0, 0), nzTz);
3551 QDateTime nzStdOffset(QDate(2012, 6, 1), QTime(12, 0, 0), nzTzOffset);
3552
3553 QCOMPARE(nzStd.isValid(), true);
3554 QCOMPARE(nzStd.timeSpec(), Qt::TimeZone);
3555 QCOMPARE(nzStd.date(), QDate(2012, 6, 1));
3556 QCOMPARE(nzStd.time(), QTime(12, 0, 0));
3557 QVERIFY(nzStd.timeZone() == nzTz);
3558 QCOMPARE(nzStd.timeZone().id(), QByteArray("Pacific/Auckland"));
3559 QCOMPARE(nzStd.offsetFromUtc(), 43200);
3560 QCOMPARE(nzStd.isDaylightTime(), false);
3561 QCOMPARE(nzStd.toMSecsSinceEpoch(), utcStd.toMSecsSinceEpoch());
3562
3563 QCOMPARE(nzStdOffset.isValid(), true);
3564 QCOMPARE(nzStdOffset.timeSpec(), Qt::TimeZone);
3565 QCOMPARE(nzStdOffset.date(), QDate(2012, 6, 1));
3566 QCOMPARE(nzStdOffset.time(), QTime(12, 0, 0));
3567 QVERIFY(nzStdOffset.timeZone() == nzTzOffset);
3568 QCOMPARE(nzStdOffset.timeZone().id(), QByteArray("UTC+12"));
3569 QCOMPARE(nzStdOffset.offsetFromUtc(), 43200);
3570 QCOMPARE(nzStdOffset.isDaylightTime(), false);
3571 QCOMPARE(nzStdOffset.toMSecsSinceEpoch(), utcStd.toMSecsSinceEpoch());
3572
3573 // During Daylight Time NZ is +13:00
3574 QDateTime utcDst(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC);
3575 QDateTime nzDst(QDate(2012, 1, 1), QTime(13, 0, 0), nzTz);
3576
3577 QCOMPARE(nzDst.isValid(), true);
3578 QCOMPARE(nzDst.date(), QDate(2012, 1, 1));
3579 QCOMPARE(nzDst.time(), QTime(13, 0, 0));
3580 QCOMPARE(nzDst.offsetFromUtc(), 46800);
3581 QCOMPARE(nzDst.isDaylightTime(), true);
3582 QCOMPARE(nzDst.toMSecsSinceEpoch(), utcDst.toMSecsSinceEpoch());
3583
3584 QDateTime utc = nzStd.toUTC();
3585 QCOMPARE(utc.date(), utcStd.date());
3586 QCOMPARE(utc.time(), utcStd.time());
3587
3588 utc = nzDst.toUTC();
3589 QCOMPARE(utc.date(), utcDst.date());
3590 QCOMPARE(utc.time(), utcDst.time());
3591
3592 // Crash test, QTBUG-80146:
3593 QVERIFY(!nzStd.toTimeZone(QTimeZone()).isValid());
3594
3595 // Sydney is 2 hours behind New Zealand
3596 QTimeZone ausTz = QTimeZone("Australia/Sydney");
3597 QDateTime aus = nzStd.toTimeZone(toZone: ausTz);
3598 QCOMPARE(aus.date(), QDate(2012, 6, 1));
3599 QCOMPARE(aus.time(), QTime(10, 0, 0));
3600
3601 QDateTime dt1(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC);
3602 QCOMPARE(dt1.timeSpec(), Qt::UTC);
3603 dt1.setTimeZone(nzTz);
3604 QCOMPARE(dt1.timeSpec(), Qt::TimeZone);
3605 QCOMPARE(dt1.date(), QDate(2012, 6, 1));
3606 QCOMPARE(dt1.time(), QTime(0, 0, 0));
3607 QCOMPARE(dt1.timeZone(), nzTz);
3608
3609 QDateTime dt2 = QDateTime::fromSecsSinceEpoch(secs: 1338465600, timeZone: nzTz);
3610 QCOMPARE(dt2.date(), dt1.date());
3611 QCOMPARE(dt2.time(), dt1.time());
3612 QCOMPARE(dt2.timeSpec(), dt1.timeSpec());
3613 QCOMPARE(dt2.timeZone(), dt1.timeZone());
3614
3615 QDateTime dt3 = QDateTime::fromMSecsSinceEpoch(msecs: 1338465600000, timeZone: nzTz);
3616 QCOMPARE(dt3.date(), dt1.date());
3617 QCOMPARE(dt3.time(), dt1.time());
3618 QCOMPARE(dt3.timeSpec(), dt1.timeSpec());
3619 QCOMPARE(dt3.timeZone(), dt1.timeZone());
3620
3621 // The start of year 1 should be *describable* in any zone (QTBUG-78051)
3622 dt3 = QDateTime(QDate(1, 1, 1), QTime(0, 0, 0), ausTz);
3623 QVERIFY(dt3.isValid());
3624 // Likewise the end of year -1 (a.k.a. 1 BCE).
3625 dt3 = dt3.addMSecs(msecs: -1);
3626 QVERIFY(dt3.isValid());
3627 QCOMPARE(dt3, QDateTime(QDate(-1, 12, 31), QTime(23, 59, 59, 999), ausTz));
3628
3629 // Check datastream serialises the time zone
3630 QByteArray tmp;
3631 {
3632 QDataStream ds(&tmp, QIODevice::WriteOnly);
3633 ds << dt1;
3634 }
3635 QDateTime dt4;
3636 {
3637 QDataStream ds(&tmp, QIODevice::ReadOnly);
3638 ds >> dt4;
3639 }
3640 QCOMPARE(dt4, dt1);
3641 QCOMPARE(dt4.timeSpec(), Qt::TimeZone);
3642 QCOMPARE(dt4.timeZone(), nzTz);
3643
3644 // Check handling of transition times
3645 QTimeZone cet("Europe/Oslo");
3646
3647 // Standard Time to Daylight Time 2013 on 2013-03-31 is 2:00 local time / 1:00 UTC
3648 qint64 stdToDstMSecs = 1364691600000;
3649
3650 // Test MSecs to local
3651 // - Test 1 msec before tran = 01:59:59.999
3652 QDateTime beforeDst = QDateTime::fromMSecsSinceEpoch(msecs: stdToDstMSecs - 1, timeZone: cet);
3653 QCOMPARE(beforeDst.date(), QDate(2013, 3, 31));
3654 QCOMPARE(beforeDst.time(), QTime(1, 59, 59, 999));
3655 // - Test at tran = 03:00:00
3656 QDateTime atDst = QDateTime::fromMSecsSinceEpoch(msecs: stdToDstMSecs, timeZone: cet);
3657 QCOMPARE(atDst.date(), QDate(2013, 3, 31));
3658 QCOMPARE(atDst.time(), QTime(3, 0, 0));
3659
3660 // Test local to MSecs
3661 // - Test 1 msec before tran = 01:59:59.999
3662 beforeDst = QDateTime(QDate(2013, 3, 31), QTime(1, 59, 59, 999), cet);
3663 QCOMPARE(beforeDst.toMSecsSinceEpoch(), stdToDstMSecs - 1);
3664 // - Test at tran = 03:00:00
3665 atDst = QDateTime(QDate(2013, 3, 31), QTime(3, 0, 0), cet);
3666 QCOMPARE(atDst.toMSecsSinceEpoch(), stdToDstMSecs);
3667 // - Test transition hole, setting 03:00:00 is valid
3668 atDst = QDateTime(QDate(2013, 3, 31), QTime(3, 0, 0), cet);
3669 QVERIFY(atDst.isValid());
3670 QCOMPARE(atDst.date(), QDate(2013, 3, 31));
3671 QCOMPARE(atDst.time(), QTime(3, 0, 0));
3672 QCOMPARE(atDst.toMSecsSinceEpoch(), stdToDstMSecs);
3673 // - Test transition hole, setting 02:00:00 is invalid
3674 atDst = QDateTime(QDate(2013, 3, 31), QTime(2, 0, 0), cet);
3675 QVERIFY(!atDst.isValid());
3676 QCOMPARE(atDst.date(), QDate(2013, 3, 31));
3677 QCOMPARE(atDst.time(), QTime(2, 0, 0));
3678 // - Test transition hole, setting 02:59:59.999 is invalid
3679 atDst = QDateTime(QDate(2013, 3, 31), QTime(2, 59, 59, 999), cet);
3680 QVERIFY(!atDst.isValid());
3681 QCOMPARE(atDst.date(), QDate(2013, 3, 31));
3682 QCOMPARE(atDst.time(), QTime(2, 59, 59, 999));
3683
3684 // Standard Time to Daylight Time 2013 on 2013-10-27 is 3:00 local time / 1:00 UTC
3685 qint64 dstToStdMSecs = 1382835600000;
3686
3687 // Test MSecs to local
3688 // - Test 1 hour before tran = 02:00:00 local first occurrence
3689 QDateTime hourBeforeStd = QDateTime::fromMSecsSinceEpoch(msecs: dstToStdMSecs - 3600000, timeZone: cet);
3690 QCOMPARE(hourBeforeStd.date(), QDate(2013, 10, 27));
3691 QCOMPARE(hourBeforeStd.time(), QTime(2, 0, 0));
3692 // - Test 1 msec before tran = 02:59:59.999 local first occurrence
3693 QDateTime msecBeforeStd = QDateTime::fromMSecsSinceEpoch(msecs: dstToStdMSecs - 1, timeZone: cet);
3694 QCOMPARE(msecBeforeStd.date(), QDate(2013, 10, 27));
3695 QCOMPARE(msecBeforeStd.time(), QTime(2, 59, 59, 999));
3696 // - Test at tran = 03:00:00 local becomes 02:00:00 local second occurrence
3697 QDateTime atStd = QDateTime::fromMSecsSinceEpoch(msecs: dstToStdMSecs, timeZone: cet);
3698 QCOMPARE(atStd.date(), QDate(2013, 10, 27));
3699 QCOMPARE(atStd.time(), QTime(2, 0, 0));
3700 // - Test 59 mins after tran = 02:59:59.999 local second occurrence
3701 QDateTime afterStd = QDateTime::fromMSecsSinceEpoch(msecs: dstToStdMSecs + 3600000 -1, timeZone: cet);
3702 QCOMPARE(afterStd.date(), QDate(2013, 10, 27));
3703 QCOMPARE(afterStd.time(), QTime(2, 59, 59, 999));
3704 // - Test 1 hour after tran = 03:00:00 local
3705 QDateTime hourAfterStd = QDateTime::fromMSecsSinceEpoch(msecs: dstToStdMSecs + 3600000, timeZone: cet);
3706 QCOMPARE(hourAfterStd.date(), QDate(2013, 10, 27));
3707 QCOMPARE(hourAfterStd.time(), QTime(3, 00, 00));
3708
3709 // Test local to MSecs
3710 // - Test first occurrence 02:00:00 = 1 hour before tran
3711 hourBeforeStd = QDateTime(QDate(2013, 10, 27), QTime(2, 0, 0), cet);
3712 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3713 QCOMPARE(hourBeforeStd.toMSecsSinceEpoch(), dstToStdMSecs - 3600000);
3714 // - Test first occurrence 02:59:59.999 = 1 msec before tran
3715 msecBeforeStd = QDateTime(QDate(2013, 10, 27), QTime(2, 59, 59, 999), cet);
3716 QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue);
3717 QCOMPARE(msecBeforeStd.toMSecsSinceEpoch(), dstToStdMSecs - 1);
3718 // - Test second occurrence 02:00:00 = at tran
3719 atStd = QDateTime(QDate(2013, 10, 27), QTime(2, 0, 0), cet);
3720 QCOMPARE(atStd.toMSecsSinceEpoch(), dstToStdMSecs);
3721 // - Test second occurrence 03:00:00 = 59 mins after tran
3722 afterStd = QDateTime(QDate(2013, 10, 27), QTime(2, 59, 59, 999), cet);
3723 QCOMPARE(afterStd.toMSecsSinceEpoch(), dstToStdMSecs + 3600000 - 1);
3724 // - Test 03:00:00 = 1 hour after tran
3725 hourAfterStd = QDateTime(QDate(2013, 10, 27), QTime(3, 0, 0), cet);
3726 QCOMPARE(hourAfterStd.toMSecsSinceEpoch(), dstToStdMSecs + 3600000);
3727
3728 // Test Time Zone that has transitions but no future transitions afer a given date
3729 QTimeZone sgt("Asia/Singapore");
3730 QDateTime future(QDate(2015, 1, 1), QTime(0, 0, 0), sgt);
3731 QVERIFY(future.isValid());
3732 QCOMPARE(future.offsetFromUtc(), 28800);
3733#else
3734 QSKIP("Needs timezone feature enabled");
3735#endif
3736}
3737
3738void tst_QDateTime::systemTimeZoneChange_data() const
3739{
3740#ifdef Q_OS_WINRT
3741 QSKIP("UWP applications cannot change the system`s time zone (sandboxing)");
3742#endif
3743 QTest::addColumn<QDate>(name: "date");
3744 QTest::newRow(dataTag: "short") << QDate(1970, 1, 1);
3745 QTest::newRow(dataTag: "2012") << QDate(2012, 6, 1); // short on 64-bit, pimpled on 32-bit
3746 QTest::newRow(dataTag: "pimpled") << QDate(1150000, 6, 1);
3747}
3748
3749void tst_QDateTime::systemTimeZoneChange() const
3750{
3751 QFETCH(const QDate, date);
3752 const QTime early(2, 15, 30);
3753
3754 // Start out in Brisbane time:
3755 TimeZoneRollback useZone(QByteArray("AEST-10:00"));
3756 if (QDateTime(date, early, Qt::LocalTime).offsetFromUtc() != 600 * 60)
3757 QSKIP("Test depends on system support for changing zone to AEST-10:00");
3758#if QT_CONFIG(timezone)
3759 QVERIFY(QTimeZone::systemTimeZone().isValid());
3760#endif
3761
3762 const QDateTime localDate = QDateTime(date, early, Qt::LocalTime);
3763 const QDateTime utcDate = QDateTime(date, early, Qt::UTC);
3764 const qint64 localMsecs = localDate.toMSecsSinceEpoch();
3765 const qint64 utcMsecs = utcDate.toMSecsSinceEpoch();
3766#if QT_CONFIG(timezone)
3767 const QTimeZone aest("Australia/Brisbane"); // no transitions since 1992
3768 // Check that Australia/Brisbane is known:
3769 QVERIFY(aest.isValid());
3770 const QDateTime tzDate = QDateTime(date, early, aest);
3771
3772 // Check we got the right zone !
3773 QVERIFY(tzDate.timeZone().isValid());
3774 QCOMPARE(tzDate.timeZone(), aest);
3775 const qint64 tzMsecs = tzDate.toMSecsSinceEpoch();
3776#endif
3777
3778 // Change to Indian time
3779 useZone.reset(zone: QByteArray("IST-05:30"));
3780 if (QDateTime(date, early, Qt::LocalTime).offsetFromUtc() != 330 * 60)
3781 QSKIP("Test depends on system support for changing zone to IST-05:30");
3782#if QT_CONFIG(timezone)
3783 QVERIFY(QTimeZone::systemTimeZone().isValid());
3784#endif
3785
3786 QCOMPARE(localDate, QDateTime(date, early, Qt::LocalTime));
3787 // Note: localDate.toMSecsSinceEpoch == localMsecs, unchanged, iff localDate is pimpled.
3788 QVERIFY(localMsecs != QDateTime(date, early, Qt::LocalTime).toMSecsSinceEpoch());
3789 QCOMPARE(utcDate, QDateTime(date, early, Qt::UTC));
3790 QCOMPARE(utcDate.toMSecsSinceEpoch(), utcMsecs);
3791#if QT_CONFIG(timezone)
3792 QCOMPARE(tzDate.toMSecsSinceEpoch(), tzMsecs);
3793 QCOMPARE(tzDate.timeZone(), aest);
3794 QCOMPARE(tzDate, QDateTime(date, early, aest));
3795#endif
3796}
3797
3798void tst_QDateTime::invalid_data() const
3799{
3800 QTest::addColumn<QDateTime>(name: "when");
3801 QTest::addColumn<Qt::TimeSpec>(name: "spec");
3802 QTest::addColumn<bool>(name: "goodZone");
3803 QTest::newRow(dataTag: "default") << QDateTime() << Qt::LocalTime << true;
3804
3805 QDateTime invalidDate = QDateTime(QDate(0, 0, 0), QTime(-1, -1, -1));
3806 QTest::newRow(dataTag: "simple") << invalidDate << Qt::LocalTime << true;
3807 QTest::newRow(dataTag: "UTC") << invalidDate.toUTC() << Qt::UTC << true;
3808 QTest::newRow(dataTag: "offset")
3809 << invalidDate.toOffsetFromUtc(offsetSeconds: 3600) << Qt::OffsetFromUTC << true;
3810#if QT_CONFIG(timezone)
3811 QTest::newRow(dataTag: "CET")
3812 << invalidDate.toTimeZone(toZone: QTimeZone("Europe/Oslo")) << Qt::TimeZone << true;
3813
3814 // Crash tests, QTBUG-80146:
3815 QTest::newRow(dataTag: "nozone+construct")
3816 << QDateTime(QDate(1970, 1, 1), QTime(12, 0), QTimeZone()) << Qt::TimeZone << false;
3817 QTest::newRow(dataTag: "nozone+fromMSecs")
3818 << QDateTime::fromMSecsSinceEpoch(msecs: 42, timeZone: QTimeZone()) << Qt::TimeZone << false;
3819 QDateTime valid(QDate(1970, 1, 1), QTime(12, 0), Qt::UTC);
3820 QTest::newRow(dataTag: "tonozone") << valid.toTimeZone(toZone: QTimeZone()) << Qt::TimeZone << false;
3821#endif
3822}
3823
3824void tst_QDateTime::invalid() const
3825{
3826 QFETCH(QDateTime, when);
3827 QFETCH(Qt::TimeSpec, spec);
3828 QFETCH(bool, goodZone);
3829 QVERIFY(!when.isValid());
3830 QCOMPARE(when.timeSpec(), spec);
3831 QCOMPARE(when.timeZoneAbbreviation(), QString());
3832 if (!goodZone)
3833 QCOMPARE(when.toMSecsSinceEpoch(), 0);
3834 QVERIFY(!when.isDaylightTime());
3835#if QT_CONFIG(timezone)
3836 QCOMPARE(when.timeZone().isValid(), goodZone);
3837#endif
3838}
3839
3840void tst_QDateTime::range() const
3841{
3842 using Bounds = std::numeric_limits<qint64>;
3843 QCOMPARE(QDateTime::fromMSecsSinceEpoch(Bounds::min() + 1, Qt::UTC).date().year(),
3844 int(QDateTime::YearRange::First));
3845 QCOMPARE(QDateTime::fromMSecsSinceEpoch(Bounds::max() - 1, Qt::UTC).date().year(),
3846 int(QDateTime::YearRange::Last));
3847}
3848
3849void tst_QDateTime::macTypes()
3850{
3851#ifndef Q_OS_MAC
3852 QSKIP("This is a Apple-only test");
3853#else
3854 extern void tst_QDateTime_macTypes(); // in qdatetime_mac.mm
3855 tst_QDateTime_macTypes();
3856#endif
3857}
3858
3859QTEST_APPLESS_MAIN(tst_QDateTime)
3860#include "tst_qdatetime.moc"
3861

source code of qtbase/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp