From 2d51dfbefa7c42ab063dfd3be578e9dd676a1944 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 10 Mar 2021 13:57:17 +0100 Subject: [PATCH] Fix and clean up utterly confused (and confusing) transition tests Various QDateTime tests relating to transitions * used a nomenclature that made them confusing to thing about; and * expected identically-initiallized variables to behave differently. The latter, naturally, lead to "expected fail" tests. Rewrote the tests to get the date-times they want to test at by means that avoid the ambiguities inherent in QDateTime's lack of a way to distinguish the two passes through the repeated hour in a fall-back (QTBUG-79923) and added commented-out tests indicating what should be true once that ambiguity is resolved. Verified the DST status is as expected in the cases where that's the correct distinction between date-times with the same date and time. Renamed various things to (hopefully) make them more intelligible. In the process, purged some leading 0s from numbers in code. Fixes: QTBUG-68936 Change-Id: Id7a348995238b70dcb81a96edb8a3fa5315f86fa Reviewed-by: Thiago Macieira --- .../corelib/time/qdatetime/tst_qdatetime.cpp | 442 ++++++++++-------- 1 file changed, 241 insertions(+), 201 deletions(-) diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp index d8fa3436b60..e03bb995be6 100644 --- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp @@ -2836,7 +2836,7 @@ void tst_QDateTime::fromStringStringFormat_data() << QString("1947-05-11 03:23:45.678 Europe/Berlin") << QString("yyyy-MM-dd hh:mm:ss.zzz t") // That's in the hour skipped - expecting an invalid DateTime - << QDateTime(QDate(1947, 05, 11), QTime(3, 23, 45, 678), berlintz); + << QDateTime(QDate(1947, 5, 11), QTime(3, 23, 45, 678), berlintz); } #endif QTest::newRow("late") << QString("9999-12-31T23:59:59.999Z") @@ -2859,7 +2859,7 @@ void tst_QDateTime::fromStringStringFormat_data() // test unicode QTest::newRow("unicode handling") << QString(u8"2005🤣06🤣28T07🤣57🤣30.001Z") << QString(u8"yyyy🤣MM🤣ddThh🤣mm🤣ss.zt") - << QDateTime(QDate(2005, 06, 28), QTime(07, 57, 30, 1), Qt::UTC); + << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 1), Qt::UTC); // QTBUG-84349 QTest::newRow("QTBUG-84349: positive sign in month") @@ -3299,9 +3299,13 @@ void tst_QDateTime::daylightTransitions() const // 2011-10-30 03:00:00 CEST became 02:00:00 CET at msecs = 1319936400000 // 2012-03-25 02:00:00 CET became 03:00:00 CEST at msecs = 1332637200000 // 2012-10-28 03:00:00 CEST became 02:00:00 CET at msecs = 1351386000000 - const qint64 daylight2012 = 1332637200000; - const qint64 standard2012 = 1351386000000; + QCOMPARE(QDate(2012, 3, 25).dayOfWeek(), 7); + QCOMPARE(QDate(2012, 10, 28).dayOfWeek(), 7); + const qint64 spring2012 = 1332637200000; + const qint64 autumn2012 = 1351386000000; const qint64 msecsOneHour = 3600000; + QCOMPARE(spring2012, QDateTime(QDate(2012, 3, 25), QTime(1, 0), Qt::UTC).toMSecsSinceEpoch()); + QCOMPARE(autumn2012, QDateTime(QDate(2012, 10, 28), QTime(1, 0), Qt::UTC).toMSecsSinceEpoch()); // Test for correct behviour for StandardTime -> DaylightTime transition, i.e. missing hour @@ -3311,38 +3315,38 @@ void tst_QDateTime::daylightTransitions() const QVERIFY(before.isValid()); QCOMPARE(before.date(), QDate(2012, 3, 25)); QCOMPARE(before.time(), QTime(1, 59, 59, 999)); - QCOMPARE(before.toMSecsSinceEpoch(), daylight2012 - 1); + QCOMPARE(before.toMSecsSinceEpoch(), spring2012 - 1); QDateTime missing(QDate(2012, 3, 25), QTime(2, 0)); QVERIFY(!missing.isValid()); QCOMPARE(missing.date(), QDate(2012, 3, 25)); QCOMPARE(missing.time(), QTime(2, 0)); // datetimeparser relies on toMSecsSinceEpoch to still work: - QCOMPARE(missing.toMSecsSinceEpoch(), daylight2012); + QCOMPARE(missing.toMSecsSinceEpoch(), spring2012); QDateTime after(QDate(2012, 3, 25), QTime(3, 0)); QVERIFY(after.isValid()); QCOMPARE(after.date(), QDate(2012, 3, 25)); QCOMPARE(after.time(), QTime(3, 0)); - QCOMPARE(after.toMSecsSinceEpoch(), daylight2012); + QCOMPARE(after.toMSecsSinceEpoch(), spring2012); // Test round-tripping of msecs - before.setMSecsSinceEpoch(daylight2012 - 1); + before.setMSecsSinceEpoch(spring2012 - 1); QVERIFY(before.isValid()); QCOMPARE(before.date(), QDate(2012, 3, 25)); QCOMPARE(before.time(), QTime(1, 59, 59, 999)); - QCOMPARE(before.toMSecsSinceEpoch(), daylight2012 -1); + QCOMPARE(before.toMSecsSinceEpoch(), spring2012 -1); - after.setMSecsSinceEpoch(daylight2012); + after.setMSecsSinceEpoch(spring2012); QVERIFY(after.isValid()); QCOMPARE(after.date(), QDate(2012, 3, 25)); QCOMPARE(after.time(), QTime(3, 0)); - QCOMPARE(after.toMSecsSinceEpoch(), daylight2012); + QCOMPARE(after.toMSecsSinceEpoch(), spring2012); // Test changing time spec re-validates the date/time - QDateTime utc(QDate(2012, 3, 25), QTime(2, 00, 0), Qt::UTC); + QDateTime utc(QDate(2012, 3, 25), QTime(2, 0), Qt::UTC); QVERIFY(utc.isValid()); QCOMPARE(utc.date(), QDate(2012, 3, 25)); QCOMPARE(utc.time(), QTime(2, 0)); @@ -3383,272 +3387,300 @@ void tst_QDateTime::daylightTransitions() const test = QDateTime(QDate(2012, 3, 25), QTime(1, 0)); QVERIFY(test.isValid()); - QCOMPARE(test.toMSecsSinceEpoch(), daylight2012 - msecsOneHour); + QCOMPARE(test.toMSecsSinceEpoch(), spring2012 - msecsOneHour); test = test.addMSecs(msecsOneHour); CHECK_SPRING_FORWARD(test); if (handled) - QCOMPARE(test.toMSecsSinceEpoch(), daylight2012); + QCOMPARE(test.toMSecsSinceEpoch(), spring2012); #undef CHECK_SPRING_FORWARD - // Test for correct behviour for DaylightTime -> StandardTime transition, i.e. second occurrence + // Test for correct behviour for DaylightTime -> StandardTime transition, fall-back + // TODO (QTBUG-79923): Compare to results of direct QDateTime(date, time, fold) + // construction; see Prior/Post commented-out tests. - // Test setting date and time in first and second occurrence will be valid + QDateTime autumnMidnight = QDate(2012, 10, 28).startOfDay(); + QVERIFY(autumnMidnight.isValid()); + // QCOMPARE(autumnMidnight, QDateTime(QDate(2012, 10, 28), QTime(2, 0), Prior)); + QCOMPARE(autumnMidnight.date(), QDate(2012, 10, 28)); + QCOMPARE(autumnMidnight.time(), QTime(0, 0)); + QCOMPARE(autumnMidnight.toMSecsSinceEpoch(), autumn2012 - 3 * msecsOneHour); - // 1 hour before transition is 2:00:00 FirstOccurrence - QDateTime hourBefore(QDate(2012, 10, 28), QTime(2, 0)); - QVERIFY(hourBefore.isValid()); - QCOMPARE(hourBefore.date(), QDate(2012, 10, 28)); - QCOMPARE(hourBefore.time(), QTime(2, 0)); -#ifdef Q_OS_WIN - // Windows uses SecondOccurrence - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); -#endif // Q_OS_WIN - QCOMPARE(hourBefore.toMSecsSinceEpoch(), standard2012 - msecsOneHour); + QDateTime startFirst = autumnMidnight.addMSecs(2 * msecsOneHour); + QVERIFY(startFirst.isValid()); + // QCOMPARE(startFirst, QDateTime(QDate(2012, 10, 28), QTime(2, 0), Prior)); + QCOMPARE(startFirst.date(), QDate(2012, 10, 28)); + QCOMPARE(startFirst.time(), QTime(2, 0)); + QCOMPARE(startFirst.toMSecsSinceEpoch(), autumn2012 - msecsOneHour); // 1 msec before transition is 2:59:59.999 FirstOccurrence - QDateTime msecBefore(QDate(2012, 10, 28), QTime(2, 59, 59, 999)); - QVERIFY(msecBefore.isValid()); - QCOMPARE(msecBefore.date(), QDate(2012, 10, 28)); - QCOMPARE(msecBefore.time(), QTime(2, 59, 59, 999)); -#if defined(Q_OS_DARWIN) || defined(Q_OS_WIN) || defined(Q_OS_QNX) || defined(Q_OS_ANDROID) - // Win and Mac uses SecondOccurrence here - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); -#endif // Q_OS_MAC - QCOMPARE(msecBefore.toMSecsSinceEpoch(), standard2012 - 1); - - // At transition is 2:00:00 SecondOccurrence - QDateTime atTran(QDate(2012, 10, 28), QTime(2, 0)); - QVERIFY(atTran.isValid()); - QCOMPARE(atTran.date(), QDate(2012, 10, 28)); - QCOMPARE(atTran.time(), QTime(2, 0)); -#ifndef Q_OS_WIN - // Windows uses SecondOccurrence - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); -#endif // Q_OS_WIN - QCOMPARE(atTran.toMSecsSinceEpoch(), standard2012); + QDateTime endFirst = startFirst.addMSecs(msecsOneHour - 1); + QVERIFY(endFirst.isValid()); + // QCOMPARE(endFirst, QDateTime(QDate(2012, 10, 28), QTime(2, 59, 59, 999), Prior)); + QCOMPARE(endFirst.date(), QDate(2012, 10, 28)); + QCOMPARE(endFirst.time(), QTime(2, 59, 59, 999)); + QCOMPARE(endFirst.toMSecsSinceEpoch(), autumn2012 - 1); + + // At the transition, starting the second pass + QDateTime startRepeat = endFirst.addMSecs(1); + QVERIFY(startRepeat.isValid()); + // QCOMPARE(startRepeat, QDateTime(QDate(2012, 10, 28), QTime(2, 0), Post)); + QCOMPARE(startRepeat.date(), QDate(2012, 10, 28)); + QCOMPARE(startRepeat.time(), QTime(2, 0)); + QCOMPARE(startRepeat.toMSecsSinceEpoch(), autumn2012); // 59:59.999 after transition is 2:59:59.999 SecondOccurrence - QDateTime afterTran(QDate(2012, 10, 28), QTime(2, 59, 59, 999)); - QVERIFY(afterTran.isValid()); - QCOMPARE(afterTran.date(), QDate(2012, 10, 28)); - QCOMPARE(afterTran.time(), QTime(2, 59, 59, 999)); -#ifdef __GLIBCXX__ - // Linux (i.e. glibc) mktime bug reuses last calculation - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); -#endif // Q_OS_UNIX - QCOMPARE(afterTran.toMSecsSinceEpoch(), standard2012 + msecsOneHour - 1); - - // 1 hour after transition is 3:00:00 FirstOccurrence - QDateTime hourAfter(QDate(2012, 10, 28), QTime(3, 0)); + QDateTime endRepeat = endFirst.addMSecs(msecsOneHour); + QVERIFY(endRepeat.isValid()); + // QCOMPARE(endRepeat, QDateTime(QDate(2012, 10, 28), QTime(2, 59, 59, 999), Post)); + QCOMPARE(endRepeat.date(), QDate(2012, 10, 28)); + QCOMPARE(endRepeat.time(), QTime(2, 59, 59, 999)); + QCOMPARE(endRepeat.toMSecsSinceEpoch(), autumn2012 + msecsOneHour - 1); + + // 1 hour after transition is 3:00:00 (not ambiguous) + QDateTime hourAfter = endRepeat.addMSecs(1); QVERIFY(hourAfter.isValid()); + QCOMPARE(hourAfter, QDateTime(QDate(2012, 10, 28), QTime(3, 0))); QCOMPARE(hourAfter.date(), QDate(2012, 10, 28)); QCOMPARE(hourAfter.time(), QTime(3, 0)); - QCOMPARE(hourAfter.toMSecsSinceEpoch(), standard2012 + msecsOneHour); + QCOMPARE(hourAfter.toMSecsSinceEpoch(), autumn2012 + msecsOneHour); // Test round-tripping of msecs // 1 hour before transition is 2:00:00 FirstOccurrence - hourBefore.setMSecsSinceEpoch(standard2012 - msecsOneHour); - QVERIFY(hourBefore.isValid()); - QCOMPARE(hourBefore.date(), QDate(2012, 10, 28)); - QCOMPARE(hourBefore.time(), QTime(2, 0)); - QCOMPARE(hourBefore.toMSecsSinceEpoch(), standard2012 - msecsOneHour); + startFirst.setMSecsSinceEpoch(autumn2012 - msecsOneHour); + QVERIFY(startFirst.isValid()); + QCOMPARE(startFirst.date(), QDate(2012, 10, 28)); + QCOMPARE(startFirst.time(), QTime(2, 0)); + QCOMPARE(startFirst.toMSecsSinceEpoch(), autumn2012 - msecsOneHour); // 1 msec before transition is 2:59:59.999 FirstOccurrence - msecBefore.setMSecsSinceEpoch(standard2012 - 1); - QVERIFY(msecBefore.isValid()); - QCOMPARE(msecBefore.date(), QDate(2012, 10, 28)); - QCOMPARE(msecBefore.time(), QTime(2, 59, 59, 999)); - QCOMPARE(msecBefore.toMSecsSinceEpoch(), standard2012 - 1); + endFirst.setMSecsSinceEpoch(autumn2012 - 1); + QVERIFY(endFirst.isValid()); + QCOMPARE(endFirst.date(), QDate(2012, 10, 28)); + QCOMPARE(endFirst.time(), QTime(2, 59, 59, 999)); + QCOMPARE(endFirst.toMSecsSinceEpoch(), autumn2012 - 1); // At transition is 2:00:00 SecondOccurrence - atTran.setMSecsSinceEpoch(standard2012); - QVERIFY(atTran.isValid()); - QCOMPARE(atTran.date(), QDate(2012, 10, 28)); - QCOMPARE(atTran.time(), QTime(2, 0)); - QCOMPARE(atTran.toMSecsSinceEpoch(), standard2012); + startRepeat.setMSecsSinceEpoch(autumn2012); + QVERIFY(startRepeat.isValid()); + QCOMPARE(startRepeat.date(), QDate(2012, 10, 28)); + QCOMPARE(startRepeat.time(), QTime(2, 0)); + QCOMPARE(startRepeat.toMSecsSinceEpoch(), autumn2012); // 59:59.999 after transition is 2:59:59.999 SecondOccurrence - afterTran.setMSecsSinceEpoch(standard2012 + msecsOneHour - 1); - QVERIFY(afterTran.isValid()); - QCOMPARE(afterTran.date(), QDate(2012, 10, 28)); - QCOMPARE(afterTran.time(), QTime(2, 59, 59, 999)); - QCOMPARE(afterTran.toMSecsSinceEpoch(), standard2012 + msecsOneHour - 1); - - // 1 hour after transition is 3:00:00 FirstOccurrence - hourAfter.setMSecsSinceEpoch(standard2012 + msecsOneHour); + endRepeat.setMSecsSinceEpoch(autumn2012 + msecsOneHour - 1); + QVERIFY(endRepeat.isValid()); + QCOMPARE(endRepeat.date(), QDate(2012, 10, 28)); + QCOMPARE(endRepeat.time(), QTime(2, 59, 59, 999)); + QCOMPARE(endRepeat.toMSecsSinceEpoch(), autumn2012 + msecsOneHour - 1); + + // 1 hour after transition is 3:00:00 (unambiguous) + hourAfter.setMSecsSinceEpoch(autumn2012 + msecsOneHour); QVERIFY(hourAfter.isValid()); QCOMPARE(hourAfter.date(), QDate(2012, 10, 28)); QCOMPARE(hourAfter.time(), QTime(3, 0)); - QCOMPARE(hourAfter.toMSecsSinceEpoch(), standard2012 + msecsOneHour); + QCOMPARE(hourAfter.toMSecsSinceEpoch(), autumn2012 + msecsOneHour); - // Test date maths, result is always FirstOccurrence + // Test date maths - // Add year to get to tran FirstOccurrence + // Add year to a DST moment to hit start of first pass: test = QDateTime(QDate(2011, 10, 28), QTime(2, 0)); + QVERIFY(test.isDaylightTime()); // Before last Sunday in month test = test.addYears(1); QVERIFY(test.isValid()); + QVERIFY(test.isDaylightTime()); QCOMPARE(test.date(), QDate(2012, 10, 28)); QCOMPARE(test.time(), QTime(2, 0)); -#ifdef Q_OS_WIN - // Windows uses SecondOccurrence - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); -#endif // Q_OS_WIN - QCOMPARE(test.toMSecsSinceEpoch(), standard2012 - msecsOneHour); + // QCOMPARE(test, QDateTime(QDate(2012, 10, 28), QTime(2, 0), Prior)); + QCOMPARE(test.toMSecsSinceEpoch(), autumn2012 - msecsOneHour); + + // Subtract year from post-tran time to hit start of second pass: + test = QDateTime(QDate(2013, 10, 28), QTime(2, 0)); + QVERIFY(!test.isDaylightTime()); // After last Sundy in month + test = test.addYears(-1); + QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); + QCOMPARE(test.date(), QDate(2012, 10, 28)); + QCOMPARE(test.time(), QTime(2, 0)); + // QCOMPARE(test, QDateTime(QDate(2012, 10, 28), QTime(2, 0), Post)); + QCOMPARE(test.toMSecsSinceEpoch(), autumn2012); - // Add year to get to after tran FirstOccurrence + // Add year to get to after the repeated hour test = QDateTime(QDate(2011, 10, 28), QTime(3, 0)); + QVERIFY(test.isDaylightTime()); // Before last Sunday in month test = test.addYears(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2012, 10, 28)); QCOMPARE(test.time(), QTime(3, 0)); - QCOMPARE(test.toMSecsSinceEpoch(), standard2012 + msecsOneHour); + QCOMPARE(test, QDateTime(QDate(2012, 10, 28), QTime(3, 0))); + QCOMPARE(test.toMSecsSinceEpoch(), autumn2012 + msecsOneHour); - // Add year to tran FirstOccurrence - test = QDateTime(QDate(2011, 10, 30), QTime(2, 0)); + // Add year to start of first pass: + test = QDateTime(QDate(2011, 10, 30), QTime(1, 0)).addMSecs(msecsOneHour); + QVERIFY(test.isDaylightTime()); test = test.addYears(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); // After last Sunday in month QCOMPARE(test.date(), QDate(2012, 10, 30)); QCOMPARE(test.time(), QTime(2, 0)); + QCOMPARE(test, QDateTime(QDate(2012, 10, 30), QTime(2, 0))); - // Add year to tran SecondOccurrence - test = QDateTime(QDate(2011, 10, 30), QTime(2, 0)); // TODO SecondOccurrence + // Add year to start of second pass: + test = QDateTime(QDate(2011, 10, 30), QTime(3, 0)).addMSecs(-msecsOneHour); + QVERIFY(!test.isDaylightTime()); test = test.addYears(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); // Same as before QCOMPARE(test.date(), QDate(2012, 10, 30)); QCOMPARE(test.time(), QTime(2, 0)); + QCOMPARE(test, QDateTime(QDate(2012, 10, 30), QTime(2, 0))); - // Add year to after tran FirstOccurrence + // Add year to after second pass: test = QDateTime(QDate(2011, 10, 30), QTime(3, 0)); + QVERIFY(!test.isDaylightTime()); test = test.addYears(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2012, 10, 30)); QCOMPARE(test.time(), QTime(3, 0)); + QCOMPARE(test, QDateTime(QDate(2012, 10, 30), QTime(3, 0))); - // Add month to get to tran FirstOccurrence + // Add month to get to start of first pass test = QDateTime(QDate(2012, 9, 28), QTime(2, 0)); + QVERIFY(test.isDaylightTime()); test = test.addMonths(1); QVERIFY(test.isValid()); + QVERIFY(test.isDaylightTime()); QCOMPARE(test.date(), QDate(2012, 10, 28)); QCOMPARE(test.time(), QTime(2, 0)); -#ifdef Q_OS_WIN - // Windows uses SecondOccurrence - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); -#endif // Q_OS_WIN - QCOMPARE(test.toMSecsSinceEpoch(), standard2012 - msecsOneHour); + // QCOMPARE(test, QDateTime(QDate(2012, 10, 28), QTime(2, 0), Prior)); + QCOMPARE(test.toMSecsSinceEpoch(), autumn2012 - msecsOneHour); - // Add month to get to after tran FirstOccurrence + // Add month to get to after second pass (unambiguous) test = QDateTime(QDate(2012, 9, 28), QTime(3, 0)); + QVERIFY(test.isDaylightTime()); test = test.addMonths(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2012, 10, 28)); QCOMPARE(test.time(), QTime(3, 0)); - QCOMPARE(test.toMSecsSinceEpoch(), standard2012 + msecsOneHour); + QCOMPARE(test, QDateTime(QDate(2012, 10, 28), QTime(3, 0))); + QCOMPARE(test.toMSecsSinceEpoch(), autumn2012 + msecsOneHour); - // Add month to tran FirstOccurrence - test = QDateTime(QDate(2011, 10, 30), QTime(2, 0)); + // Add month to start of first pass + test = QDateTime(QDate(2011, 10, 30), QTime(1, 0)).addMSecs(msecsOneHour); + QVERIFY(test.isDaylightTime()); test = test.addMonths(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2011, 11, 30)); QCOMPARE(test.time(), QTime(2, 0)); + QCOMPARE(test, QDateTime(QDate(2011, 11, 30), QTime(2, 0))); - // Add month to tran SecondOccurrence - test = QDateTime(QDate(2011, 10, 30), QTime(2, 0)); // TODO SecondOccurrence + // Add month to end of second pass + test = QDateTime(QDate(2011, 10, 30), QTime(3, 0)).addMSecs(-msecsOneHour); + QVERIFY(!test.isDaylightTime()); test = test.addMonths(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2011, 11, 30)); QCOMPARE(test.time(), QTime(2, 0)); + QCOMPARE(test, QDateTime(QDate(2011, 11, 30), QTime(2, 0))); - // Add month to after tran FirstOccurrence + // Add month to after after second pass (unambiguous) test = QDateTime(QDate(2011, 10, 30), QTime(3, 0)); + QVERIFY(!test.isDaylightTime()); test = test.addMonths(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2011, 11, 30)); QCOMPARE(test.time(), QTime(3, 0)); + QCOMPARE(test, QDateTime(QDate(2011, 11, 30), QTime(3, 0))); - // Add day to get to tran FirstOccurrence + // Add day to get to start of first pass test = QDateTime(QDate(2012, 10, 27), QTime(2, 0)); + QVERIFY(test.isDaylightTime()); test = test.addDays(1); QVERIFY(test.isValid()); + QVERIFY(test.isDaylightTime()); QCOMPARE(test.date(), QDate(2012, 10, 28)); QCOMPARE(test.time(), QTime(2, 0)); -#ifdef Q_OS_WIN - // Windows uses SecondOccurrence - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); -#endif // Q_OS_WIN - QCOMPARE(test.toMSecsSinceEpoch(), standard2012 - msecsOneHour); + // QCOMPARE(test, QDateTime(QDate(2012, 10, 28), QTime(2, 0), Prior)); + QCOMPARE(test.toMSecsSinceEpoch(), autumn2012 - msecsOneHour); - // Add day to get to after tran FirstOccurrence + // Add day to get to after second pass (unambiguous) test = QDateTime(QDate(2012, 10, 27), QTime(3, 0)); + QVERIFY(test.isDaylightTime()); test = test.addDays(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2012, 10, 28)); QCOMPARE(test.time(), QTime(3, 0)); - QCOMPARE(test.toMSecsSinceEpoch(), standard2012 + msecsOneHour); + QCOMPARE(test, QDateTime(QDate(2012, 10, 28), QTime(3, 0))); + QCOMPARE(test.toMSecsSinceEpoch(), autumn2012 + msecsOneHour); - // Add day to tran FirstOccurrence - test = QDateTime(QDate(2011, 10, 30), QTime(2, 0)); + // Add day to start of first pass + test = QDateTime(QDate(2011, 10, 30), QTime(1, 0)).addMSecs(msecsOneHour); + QVERIFY(test.isDaylightTime()); test = test.addDays(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2011, 10, 31)); QCOMPARE(test.time(), QTime(2, 0)); + QCOMPARE(test, QDateTime(QDate(2011, 10, 31), QTime(2, 0))); - // Add day to tran SecondOccurrence - test = QDateTime(QDate(2011, 10, 30), QTime(2, 0)); // TODO SecondOccurrence + // Add day to start of second pass + test = QDateTime(QDate(2011, 10, 30), QTime(3, 0)).addMSecs(-msecsOneHour); + QVERIFY(!test.isDaylightTime()); test = test.addDays(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2011, 10, 31)); QCOMPARE(test.time(), QTime(2, 0)); + QCOMPARE(test, QDateTime(QDate(2011, 10, 31), QTime(2, 0))); - // Add day to after tran FirstOccurrence + // Add day to after second pass (unambiguous) test = QDateTime(QDate(2011, 10, 30), QTime(3, 0)); + QVERIFY(!test.isDaylightTime()); test = test.addDays(1); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2011, 10, 31)); QCOMPARE(test.time(), QTime(3, 0)); + QCOMPARE(test, QDateTime(QDate(2011, 10, 31), QTime(3, 0))); - // Add hour to get to tran FirstOccurrence + // Add hour to get to start of first pass test = QDateTime(QDate(2012, 10, 28), QTime(1, 0)); + QVERIFY(test.isDaylightTime()); test = test.addMSecs(msecsOneHour); QVERIFY(test.isValid()); + QVERIFY(test.isDaylightTime()); QCOMPARE(test.date(), QDate(2012, 10, 28)); QCOMPARE(test.time(), QTime(2, 0)); - QCOMPARE(test.toMSecsSinceEpoch(), standard2012 - msecsOneHour); + // QCOMPARE(test, QDateTime(QDate(2012, 10, 28), QTime(2, 0), Prior)); + QCOMPARE(test.toMSecsSinceEpoch(), autumn2012 - msecsOneHour); - // Add hour to tran FirstOccurrence to get to tran SecondOccurrence - test = QDateTime(QDate(2012, 10, 28), QTime(2, 0)); + // Add hour to start of first pass to get to start of second pass test = test.addMSecs(msecsOneHour); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2012, 10, 28)); -#ifdef Q_OS_WIN - // Windows uses SecondOccurrence - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); -#endif // Q_OS_WIN QCOMPARE(test.time(), QTime(2, 0)); -#ifdef Q_OS_WIN - // Windows uses SecondOccurrence - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); -#endif // Q_OS_WIN - QCOMPARE(test.toMSecsSinceEpoch(), standard2012); + // QCOMPARE(test, QDateTime(QDate(2012, 10, 28), QTime(2, 0), Post)); + QCOMPARE(test.toMSecsSinceEpoch(), autumn2012); - // Add hour to tran SecondOccurrence to get to after tran FirstOccurrence - test = QDateTime(QDate(2012, 10, 28), QTime(2, 0)); // TODO SecondOccurrence + // Add hour to start of second pass to get to after second pass test = test.addMSecs(msecsOneHour); QVERIFY(test.isValid()); + QVERIFY(!test.isDaylightTime()); QCOMPARE(test.date(), QDate(2012, 10, 28)); -#if defined(Q_OS_DARWIN) || defined(Q_OS_QNX) || defined(Q_OS_ANDROID) - // Mac uses FirstOccurrence, Windows uses SecondOccurrence, Linux uses last calculation - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); -#endif // Q_OS_WIN QCOMPARE(test.time(), QTime(3, 0)); -#if defined(Q_OS_DARWIN) || defined(Q_OS_QNX) || defined(Q_OS_ANDROID) - // Mac uses FirstOccurrence, Windows uses SecondOccurrence, Linux uses last calculation - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); -#endif // Q_OS_WIN - QCOMPARE(test.toMSecsSinceEpoch(), standard2012 + msecsOneHour); + QCOMPARE(test, QDateTime(QDate(2012, 10, 28), QTime(3, 0))); + QCOMPARE(test.toMSecsSinceEpoch(), autumn2012 + msecsOneHour); } void tst_QDateTime::timeZones() const @@ -3764,85 +3796,93 @@ void tst_QDateTime::timeZones() const QTimeZone cet("Europe/Oslo"); // Standard Time to Daylight Time 2013 on 2013-03-31 is 2:00 local time / 1:00 UTC - qint64 stdToDstMSecs = 1364691600000; + const qint64 gapMSecs = 1364691600000; + QCOMPARE(gapMSecs, QDateTime(QDate(2013, 3, 31), QTime(1, 0), Qt::UTC).toMSecsSinceEpoch()); // Test MSecs to local // - Test 1 msec before tran = 01:59:59.999 - QDateTime beforeDst = QDateTime::fromMSecsSinceEpoch(stdToDstMSecs - 1, cet); - QCOMPARE(beforeDst.date(), QDate(2013, 3, 31)); - QCOMPARE(beforeDst.time(), QTime(1, 59, 59, 999)); + QDateTime beforeGap = QDateTime::fromMSecsSinceEpoch(gapMSecs - 1, cet); + QCOMPARE(beforeGap.date(), QDate(2013, 3, 31)); + QCOMPARE(beforeGap.time(), QTime(1, 59, 59, 999)); // - Test at tran = 03:00:00 - QDateTime atDst = QDateTime::fromMSecsSinceEpoch(stdToDstMSecs, cet); - QCOMPARE(atDst.date(), QDate(2013, 3, 31)); - QCOMPARE(atDst.time(), QTime(3, 0)); + QDateTime atGap = QDateTime::fromMSecsSinceEpoch(gapMSecs, cet); + QCOMPARE(atGap.date(), QDate(2013, 3, 31)); + QCOMPARE(atGap.time(), QTime(3, 0)); // Test local to MSecs // - Test 1 msec before tran = 01:59:59.999 - beforeDst = QDateTime(QDate(2013, 3, 31), QTime(1, 59, 59, 999), cet); - QCOMPARE(beforeDst.toMSecsSinceEpoch(), stdToDstMSecs - 1); + beforeGap = QDateTime(QDate(2013, 3, 31), QTime(1, 59, 59, 999), cet); + QCOMPARE(beforeGap.toMSecsSinceEpoch(), gapMSecs - 1); // - Test at tran = 03:00:00 - atDst = QDateTime(QDate(2013, 3, 31), QTime(3, 0), cet); - QCOMPARE(atDst.toMSecsSinceEpoch(), stdToDstMSecs); + atGap = QDateTime(QDate(2013, 3, 31), QTime(3, 0), cet); + QCOMPARE(atGap.toMSecsSinceEpoch(), gapMSecs); // - Test transition hole, setting 03:00:00 is valid - atDst = QDateTime(QDate(2013, 3, 31), QTime(3, 0), cet); - QVERIFY(atDst.isValid()); - QCOMPARE(atDst.date(), QDate(2013, 3, 31)); - QCOMPARE(atDst.time(), QTime(3, 0)); - QCOMPARE(atDst.toMSecsSinceEpoch(), stdToDstMSecs); + atGap = QDateTime(QDate(2013, 3, 31), QTime(3, 0), cet); + QVERIFY(atGap.isValid()); + QCOMPARE(atGap.date(), QDate(2013, 3, 31)); + QCOMPARE(atGap.time(), QTime(3, 0)); + QCOMPARE(atGap.toMSecsSinceEpoch(), gapMSecs); // - Test transition hole, setting 02:00:00 is invalid - atDst = QDateTime(QDate(2013, 3, 31), QTime(2, 0), cet); - QVERIFY(!atDst.isValid()); - QCOMPARE(atDst.date(), QDate(2013, 3, 31)); - QCOMPARE(atDst.time(), QTime(2, 0)); + QDateTime inGap = QDateTime(QDate(2013, 3, 31), QTime(2, 0), cet); + QVERIFY(!inGap.isValid()); + QCOMPARE(inGap.date(), QDate(2013, 3, 31)); + QCOMPARE(inGap.time(), QTime(2, 0)); // - Test transition hole, setting 02:59:59.999 is invalid - atDst = QDateTime(QDate(2013, 3, 31), QTime(2, 59, 59, 999), cet); - QVERIFY(!atDst.isValid()); - QCOMPARE(atDst.date(), QDate(2013, 3, 31)); - QCOMPARE(atDst.time(), QTime(2, 59, 59, 999)); + inGap = QDateTime(QDate(2013, 3, 31), QTime(2, 59, 59, 999), cet); + QVERIFY(!inGap.isValid()); + QCOMPARE(inGap.date(), QDate(2013, 3, 31)); + QCOMPARE(inGap.time(), QTime(2, 59, 59, 999)); // Standard Time to Daylight Time 2013 on 2013-10-27 is 3:00 local time / 1:00 UTC - qint64 dstToStdMSecs = 1382835600000; + const qint64 replayMSecs = 1382835600000; + QCOMPARE(replayMSecs, QDateTime(QDate(2013, 10, 27), QTime(1, 0), Qt::UTC).toMSecsSinceEpoch()); // Test MSecs to local // - Test 1 hour before tran = 02:00:00 local first occurrence - QDateTime hourBeforeStd = QDateTime::fromMSecsSinceEpoch(dstToStdMSecs - 3600000, cet); - QCOMPARE(hourBeforeStd.date(), QDate(2013, 10, 27)); - QCOMPARE(hourBeforeStd.time(), QTime(2, 0)); + QDateTime startFirst = QDateTime::fromMSecsSinceEpoch(replayMSecs - 3600000, cet); + QCOMPARE(startFirst.date(), QDate(2013, 10, 27)); + QCOMPARE(startFirst.time(), QTime(2, 0)); // - Test 1 msec before tran = 02:59:59.999 local first occurrence - QDateTime msecBeforeStd = QDateTime::fromMSecsSinceEpoch(dstToStdMSecs - 1, cet); - QCOMPARE(msecBeforeStd.date(), QDate(2013, 10, 27)); - QCOMPARE(msecBeforeStd.time(), QTime(2, 59, 59, 999)); + QDateTime endFirst = QDateTime::fromMSecsSinceEpoch(replayMSecs - 1, cet); + QCOMPARE(endFirst.date(), QDate(2013, 10, 27)); + QCOMPARE(endFirst.time(), QTime(2, 59, 59, 999)); // - Test at tran = 03:00:00 local becomes 02:00:00 local second occurrence - QDateTime atStd = QDateTime::fromMSecsSinceEpoch(dstToStdMSecs, cet); - QCOMPARE(atStd.date(), QDate(2013, 10, 27)); - QCOMPARE(atStd.time(), QTime(2, 0)); + QDateTime startRepeat = QDateTime::fromMSecsSinceEpoch(replayMSecs, cet); + QCOMPARE(startRepeat.date(), QDate(2013, 10, 27)); + QCOMPARE(startRepeat.time(), QTime(2, 0)); // - Test 59 mins after tran = 02:59:59.999 local second occurrence - QDateTime afterStd = QDateTime::fromMSecsSinceEpoch(dstToStdMSecs + 3600000 -1, cet); - QCOMPARE(afterStd.date(), QDate(2013, 10, 27)); - QCOMPARE(afterStd.time(), QTime(2, 59, 59, 999)); + QDateTime endRepeat = QDateTime::fromMSecsSinceEpoch(replayMSecs + 3600000 -1, cet); + QCOMPARE(endRepeat.date(), QDate(2013, 10, 27)); + QCOMPARE(endRepeat.time(), QTime(2, 59, 59, 999)); // - Test 1 hour after tran = 03:00:00 local - QDateTime hourAfterStd = QDateTime::fromMSecsSinceEpoch(dstToStdMSecs + 3600000, cet); - QCOMPARE(hourAfterStd.date(), QDate(2013, 10, 27)); - QCOMPARE(hourAfterStd.time(), QTime(3, 00, 00)); + QDateTime hourAfter = QDateTime::fromMSecsSinceEpoch(replayMSecs + 3600000, cet); + QCOMPARE(hourAfter.date(), QDate(2013, 10, 27)); + QCOMPARE(hourAfter.time(), QTime(3, 0, 0)); + + // TODO (QTBUG-79923): Compare to results of direct QDateTime(date, time, cet, fold) + // construction; see Prior/Post commented-out tests. // Test local to MSecs // - Test first occurrence 02:00:00 = 1 hour before tran - hourBeforeStd = QDateTime(QDate(2013, 10, 27), QTime(2, 0), cet); - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); - QCOMPARE(hourBeforeStd.toMSecsSinceEpoch(), dstToStdMSecs - 3600000); + startFirst = QDateTime(QDate(2013, 10, 27), QTime(1, 59, 59), cet).addSecs(1); + // QCOMPARE(startFirst, QDateTime(QDate(2013, 10, 27), QTime(2, 0), cet, Prior)); + QCOMPARE(startFirst.toMSecsSinceEpoch(), replayMSecs - 3600000); // - Test first occurrence 02:59:59.999 = 1 msec before tran - msecBeforeStd = QDateTime(QDate(2013, 10, 27), QTime(2, 59, 59, 999), cet); - QEXPECT_FAIL("", "QDateTime doesn't properly support Daylight Transitions", Continue); - QCOMPARE(msecBeforeStd.toMSecsSinceEpoch(), dstToStdMSecs - 1); + endFirst = startFirst.addMSecs(3599999); + // QCOMPARE(endFirst, QDateTime(QDate(2013, 10, 27), QTime(2, 59, 59, 999), cet, Prior)); + QCOMPARE(endFirst.toMSecsSinceEpoch(), replayMSecs - 1); // - Test second occurrence 02:00:00 = at tran - atStd = QDateTime(QDate(2013, 10, 27), QTime(2, 0), cet); - QCOMPARE(atStd.toMSecsSinceEpoch(), dstToStdMSecs); - // - Test second occurrence 03:00:00 = 59 mins after tran - afterStd = QDateTime(QDate(2013, 10, 27), QTime(2, 59, 59, 999), cet); - QCOMPARE(afterStd.toMSecsSinceEpoch(), dstToStdMSecs + 3600000 - 1); - // - Test 03:00:00 = 1 hour after tran - hourAfterStd = QDateTime(QDate(2013, 10, 27), QTime(3, 0), cet); - QCOMPARE(hourAfterStd.toMSecsSinceEpoch(), dstToStdMSecs + 3600000); + startRepeat = endFirst.addMSecs(1); + // QCOMPARE(startRepeat, QDateTime(QDate(2013, 10, 27), QTime(2, 0), cet, Post)); + QCOMPARE(startRepeat.toMSecsSinceEpoch(), replayMSecs); + // - Test second occurrence 02:59:59.999 = 1 msec before 1 hour after tran + endRepeat = startRepeat.addMSecs(3599999); + // QCOMPARE(endRepeat, QDateTime(QDate(2013, 10, 27), QTime(2, 59, 59, 999), cet, Post)); + QCOMPARE(endRepeat.toMSecsSinceEpoch(), replayMSecs + 3600000 - 1); + // - Test 03:00:00 = 1 hour after tran (no ambiguity) + hourAfter = endRepeat.addMSecs(1); + QCOMPARE(hourAfter, QDateTime(QDate(2013, 10, 27), QTime(3, 0), cet)); + QCOMPARE(hourAfter.toMSecsSinceEpoch(), replayMSecs + 3600000); // Test Time Zone that has transitions but no future transitions afer a given date QTimeZone sgt("Asia/Singapore");