Cope if mktime() deems times in a spring forward gap to be invalid

In tst_QDateTime::springForward(), we test correct handling of times
in the gap; these are formally invalid and a mktime() implementation
may reasonably reject them causing our date-time code to produce an
invalid result.  So handle that case gracefully in the tests, only
insisting on consistency between the two ways of preparing the date.
In one test, package the repeated code I was going to adapt into a
macro to save repeitition.

Task-number: QTBUG-68832
Task-number: QTBUG-68839
Change-Id: Ib8a16ff007f4e75ab2ccff05b1ccf00a45e50dc8
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Edward Welbourne 2018-06-14 12:18:20 +02:00 committed by Mårten Nordheim
parent b248a35a09
commit 9f8938b89a

View File

@ -1868,12 +1868,14 @@ void tst_QDateTime::springForward()
QFETCH(int, adjust);
QDateTime direct = QDateTime(day.addDays(-step), time, Qt::LocalTime).addDays(step);
QCOMPARE(direct.date(), day);
QCOMPARE(direct.time().minute(), time.minute());
QCOMPARE(direct.time().second(), time.second());
int off = direct.time().hour() - time.hour();
QVERIFY(off == 1 || off == -1);
// Note: function doc claims always +1, but this should be reviewed !
if (direct.isValid()) { // mktime() may deem a time in the gap invalid
QCOMPARE(direct.date(), day);
QCOMPARE(direct.time().minute(), time.minute());
QCOMPARE(direct.time().second(), time.second());
int off = direct.time().hour() - time.hour();
QVERIFY(off == 1 || off == -1);
// Note: function doc claims always +1, but this should be reviewed !
}
// Repeat, but getting there via .toLocalTime():
QDateTime detour = QDateTime(day.addDays(-step),
@ -1881,7 +1883,11 @@ void tst_QDateTime::springForward()
Qt::UTC).toLocalTime();
QCOMPARE(detour.time(), time);
detour = detour.addDays(step);
QCOMPARE(detour, direct); // Insist on consistency.
// Insist on consistency:
if (direct.isValid())
QCOMPARE(detour, direct);
else
QVERIFY(!detour.isValid());
}
void tst_QDateTime::operator_eqeq_data()
@ -2901,38 +2907,40 @@ void tst_QDateTime::daylightTransitions() const
QCOMPARE(utc.date(), QDate(2012, 3, 25));
QCOMPARE(utc.time(), QTime(2, 0, 0));
// Test date maths, if result falls in missing hour then becomes next hour
// Test date maths, if result falls in missing hour then becomes next
// hour (or is always invalid; mktime() may reject gap-times).
QDateTime test(QDate(2011, 3, 25), QTime(2, 0, 0));
QVERIFY(test.isValid());
test = test.addYears(1);
QVERIFY(test.isValid());
QCOMPARE(test.date(), QDate(2012, 3, 25));
QCOMPARE(test.time(), QTime(3, 0, 0));
const bool handled = test.isValid();
#define CHECK_SPRING_FORWARD(test) \
if (test.isValid()) { \
QCOMPARE(test.date(), QDate(2012, 3, 25)); \
QCOMPARE(test.time(), QTime(3, 0, 0)); \
} else { \
QVERIFY(!handled); \
}
CHECK_SPRING_FORWARD(test);
test = QDateTime(QDate(2012, 2, 25), QTime(2, 0, 0));
QVERIFY(test.isValid());
test = test.addMonths(1);
QVERIFY(test.isValid());
QCOMPARE(test.date(), QDate(2012, 3, 25));
QCOMPARE(test.time(), QTime(3, 0, 0));
CHECK_SPRING_FORWARD(test);
test = QDateTime(QDate(2012, 3, 24), QTime(2, 0, 0));
QVERIFY(test.isValid());
test = test.addDays(1);
QVERIFY(test.isValid());
QCOMPARE(test.date(), QDate(2012, 3, 25));
QCOMPARE(test.time(), QTime(3, 0, 0));
CHECK_SPRING_FORWARD(test);
test = QDateTime(QDate(2012, 3, 25), QTime(1, 0, 0));
QVERIFY(test.isValid());
QCOMPARE(test.toMSecsSinceEpoch(), daylight2012 - msecsOneHour);
test = test.addMSecs(msecsOneHour);
QVERIFY(test.isValid());
QCOMPARE(test.date(), QDate(2012, 3, 25));
QCOMPARE(test.time(), QTime(3, 0, 0));
QCOMPARE(test.toMSecsSinceEpoch(), daylight2012);
CHECK_SPRING_FORWARD(test);
if (handled)
QCOMPARE(test.toMSecsSinceEpoch(), daylight2012);
#undef CHECK_SPRING_FORWARD
// Test for correct behviour for DaylightTime -> StandardTime transition, i.e. second occurrence
@ -2954,7 +2962,7 @@ void tst_QDateTime::daylightTransitions() const
QVERIFY(msecBefore.isValid());
QCOMPARE(msecBefore.date(), QDate(2012, 10, 28));
QCOMPARE(msecBefore.time(), QTime(2, 59, 59, 999));
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_QNX)
#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
@ -2976,8 +2984,8 @@ void tst_QDateTime::daylightTransitions() const
QVERIFY(afterTran.isValid());
QCOMPARE(afterTran.date(), QDate(2012, 10, 28));
QCOMPARE(afterTran.time(), QTime(2, 59, 59, 999));
#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_QNX)
// Linux mktime bug uses last calculation
#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);
@ -3183,12 +3191,12 @@ void tst_QDateTime::daylightTransitions() const
test = test.addMSecs(msecsOneHour);
QVERIFY(test.isValid());
QCOMPARE(test.date(), QDate(2012, 10, 28));
#if defined(Q_OS_MAC) || defined(Q_OS_QNX)
#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, 0));
#if defined(Q_OS_MAC) || defined(Q_OS_QNX)
#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