Fix transitionEachZone() failures on Android

In QTimeZonePrivate::dataForLocalTime(), mistrust the Android
backend's hasDaylightTime(), as it has a comment saying it only knows
about future transitions, not past. This caller of it really needs to
query "has ever had a transition", which this doesn't answer. Many
zones that have no plans for future transitions have had transitions
in the past; these were failing the transitionEachZone() test.

In the process, refine the test itself, making sure we catch some
quirk cases that shouldn't arise and making the debug message on
failure more informative (while eliding the zone name, as this is part
of the test name anyway, so added to the output by qDebug() itself).

Fixes: QTBUG-69131
Change-Id: I88a0528182c247acb8b6327b40516178e455bcc0
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Edward Welbourne 2021-02-11 11:29:19 +01:00
parent 88d8309290
commit e8c52d0e8c
3 changed files with 25 additions and 175 deletions

View File

@ -258,8 +258,13 @@ QTimeZonePrivate::Data QTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
// Private only method for use by QDateTime to convert local msecs to epoch msecs // Private only method for use by QDateTime to convert local msecs to epoch msecs
QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs, int hint) const QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs, int hint) const
{ {
#if !defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_EMBEDDED)
// The Android back-end's hasDaylightTime() is only true for zones with
// transitions in the future; we need it to mean "has ever had a transition"
// though, so can't trust it here.
if (!hasDaylightTime()) // No DST means same offset for all local msecs if (!hasDaylightTime()) // No DST means same offset for all local msecs
return data(forLocalMSecs - standardTimeOffset(forLocalMSecs) * 1000); return data(forLocalMSecs - standardTimeOffset(forLocalMSecs) * 1000);
#endif
/* /*
We need a UTC time at which to ask for the offset, in order to be able to We need a UTC time at which to ask for the offset, in order to be able to

View File

@ -1,161 +0,0 @@
# See qtbase/src/testlib/qtestblacklist.cpp for format
# QTBUG-69131
[transitionEachZone:America/Cancun@2010]
android
[transitionEachZone:America/Eirunepe@2010]
android
[transitionEachZone:America/Montevideo@2010]
android
[transitionEachZone:America/Porto_Acre@2010]
android
[transitionEachZone:America/Rio_Branco@2010]
android
[transitionEachZone:Asia/Anadyr@2010]
android
[transitionEachZone:Asia/Chita@2010]
android
[transitionEachZone:Asia/Kamchatka@2010]
android
[transitionEachZone:Asia/Khandyga@2010]
android
[transitionEachZone:Asia/Magadan@2010]
android
[transitionEachZone:Asia/Novokuznetsk@2010]
android
[transitionEachZone:Asia/Pyongyang@2010]
android
[transitionEachZone:Asia/Ust-Nera@2010]
android
[transitionEachZone:Asia/Yerevan@2010]
android
[transitionEachZone:Europe/Kaliningrad@2010]
android
[transitionEachZone:Europe/Minsk@2010]
android
[transitionEachZone:Europe/Moscow@2010]
android
[transitionEachZone:Europe/Samara@2010]
android
[transitionEachZone:Europe/Simferopol@2010]
android
[transitionEachZone:Europe/Volgograd@2010]
android
[transitionEachZone:W-SU@2010]
android
[transitionEachZone:Africa/Bissau@1970]
android
[transitionEachZone:Africa/Juba@1970]
android
[transitionEachZone:Africa/Khartoum@1970]
android
[transitionEachZone:America/Metlakatla@1970]
android
[transitionEachZone:America/Montevideo@1970]
android
[transitionEachZone:America/Paramaribo@1970]
android
[transitionEachZone:America/Santarem@1970]
android
[transitionEachZone:America/Santo_Domingo@1970]
android
[transitionEachZone:Asia/Anadyr@1970]
android
[transitionEachZone:Asia/Bahrain@1970]
android
[transitionEachZone:Asia/Chita@1970]
android
[transitionEachZone:Asia/Dushanbe@1970]
android
[transitionEachZone:Asia/Ho_Chi_Minh@1970]
android
[transitionEachZone:Asia/Kathmandu@1970]
android
[transitionEachZone:Asia/Katmandu@1970]
android
[transitionEachZone:Asia/Kuala_Lumpur@1970]
android
[transitionEachZone:Asia/Magadan@1970]
android
[transitionEachZone:Asia/Novosibirsk@1970]
android
[transitionEachZone:Asia/Pontianak@1970]
android
[transitionEachZone:Asia/Pyongyang@1970]
android
[transitionEachZone:Asia/Qatar@1970]
android
[transitionEachZone:Asia/Qyzylorda@1970]
android
[transitionEachZone:Asia/Saigon@1970]
android
[transitionEachZone:Asia/Sakhalin@1970]
android
[transitionEachZone:Asia/Singapore@1970]
android
[transitionEachZone:Asia/Tashkent@1970]
android
[transitionEachZone:Asia/Thimbu@1970]
android
[transitionEachZone:Asia/Thimphu@1970]
android
[transitionEachZone:Asia/Ust-Nera@1970]
android
[transitionEachZone:Atlantic/Cape_Verde@1970]
android
[transitionEachZone:Chile/EasterIsland@1970]
android
[transitionEachZone:Europe/Kaliningrad@1970]
android
[transitionEachZone:Pacific/Bougainville@1970]
android
[transitionEachZone:Pacific/Easter@1970]
android
[transitionEachZone:Pacific/Enderbury@1970]
android
[transitionEachZone:Pacific/Galapagos@1970]
android
[transitionEachZone:Pacific/Kiritimati@1970]
android
[transitionEachZone:Pacific/Kosrae@1970]
android
[transitionEachZone:Pacific/Kwajalein@1970]
android
[transitionEachZone:Pacific/Nauru@1970]
android
[transitionEachZone:Pacific/Niue@1970]
android
[transitionEachZone:Singapore@1970]
android
[transitionEachZone:Brazil/Acre@2010]
android
[transitionEachZone:Pacific/Bougainville@2010]
android
[transitionEachZone:Africa/Algiers@1970]
android
[transitionEachZone:Africa/Monrovia@1970]
android
[transitionEachZone:Kwajalein@1970]
android
[transitionEachZone:Indian/Chagos@1970]
android
[transitionEachZone:Europe/Volgograd@1970]
android
[transitionEachZone:Atlantic/Stanley@1970]
android
[transitionEachZone:Antarctica/Mawson@1970]
android
[transitionEachZone:America/Swift_Current@1970]
android
[transitionEachZone:America/Guyana@1970]
android
[transitionEachZone:America/Grand_Turk@1970]
android
[transitionEachZone:America/Dawson_Creek@1970]
android
[transitionEachZone:America/Cancun@1970]
android
[transitionEachZone:America/Caracas@1970]
android
[transitionEachZone:America/Danmarkshavn@1970]
android

View File

@ -577,7 +577,7 @@ void tst_QTimeZone::transitionEachZone_data()
QTest::addColumn<int>("start"); QTest::addColumn<int>("start");
QTest::addColumn<int>("stop"); QTest::addColumn<int>("stop");
struct { const struct {
qint64 baseSecs; qint64 baseSecs;
int start, stop; int start, stop;
int year; int year;
@ -587,7 +587,7 @@ void tst_QTimeZone::transitionEachZone_data()
}; };
const auto zones = QTimeZone::availableTimeZoneIds(); const auto zones = QTimeZone::availableTimeZoneIds();
for (int k = sizeof(table) / sizeof(table[0]); k-- > 0; ) { for (int k = std::size(table); k-- > 0; ) {
for (const QByteArray &zone : zones) { for (const QByteArray &zone : zones) {
const QString name = QString::asprintf("%s@%d", zone.constData(), table[k].year); const QString name = QString::asprintf("%s@%d", zone.constData(), table[k].year);
QTest::newRow(name.toUtf8().constData()) QTest::newRow(name.toUtf8().constData())
@ -603,25 +603,31 @@ void tst_QTimeZone::transitionEachZone()
{ {
// Regression test: round-trip fromMsecs/toMSecs should be idempotent; but // Regression test: round-trip fromMsecs/toMSecs should be idempotent; but
// various zones failed during fall-back transitions. // various zones failed during fall-back transitions.
QFETCH(QByteArray, zone); QFETCH(const QByteArray, zone);
QFETCH(qint64, secs); QFETCH(const qint64, secs);
QFETCH(int, start); QFETCH(const int, start);
QFETCH(int, stop); QFETCH(const int, stop);
QTimeZone named(zone); const QTimeZone named(zone);
if (!named.isValid())
QSKIP("Supposedly available zone is not valid");
if (named.id() != zone)
QSKIP("Supposedly available zone's id does not match");
for (int i = start; i < stop; i++) { for (int i = start; i < stop; i++) {
#ifdef USING_WIN_TZ #ifdef USING_WIN_TZ
// See QTBUG-64985: MS's TZ APIs' misdescription of Europe/Samara leads // See QTBUG-64985: MS's TZ APIs' misdescription of Europe/Samara leads
// to mis-disambiguation of its fall-back here. // to mis-disambiguation of its fall-back here.
if (zone == "Europe/Samara" && i == -3) { if (zone == "Europe/Samara" && i == -3)
continue; continue;
}
#endif #endif
qint64 here = secs + i * 3600; const qint64 here = secs + i * 3600;
QDateTime when = QDateTime::fromMSecsSinceEpoch(here * 1000, named); const QDateTime when = QDateTime::fromSecsSinceEpoch(here, named);
qint64 stamp = when.toMSecsSinceEpoch(); const qint64 stamp = when.toMSecsSinceEpoch();
if (here * 1000 != stamp) // (The +1 is due to using *1*:30 as baseSecs.) if (here * 1000 != stamp) {
qDebug() << "Failing for" << zone << "at half past" << (i + 1) << "UTC"; // (The +1 is due to using _1_:30 as baseSecs.)
qDebug("Failing at half past %d UTC (offset %d in %s)", i + 1, when.offsetFromUtc(),
QLocale::countryToString(named.country()).toUtf8().constData());
}
QCOMPARE(stamp % 1000, 0); QCOMPARE(stamp % 1000, 0);
QCOMPARE(here - stamp / 1000, 0); QCOMPARE(here - stamp / 1000, 0);
} }