diff --git a/src/corelib/time/qtimezoneprivate.cpp b/src/corelib/time/qtimezoneprivate.cpp index ffbed73bf0..876f5ac371 100644 --- a/src/corelib/time/qtimezoneprivate.cpp +++ b/src/corelib/time/qtimezoneprivate.cpp @@ -626,7 +626,7 @@ static QList selectAvailable(QList &&desired, return result; } -QList QTimeZonePrivate::availableTimeZoneIds(QLocale::Territory territory) const +QList QTimeZonePrivate::matchingTimeZoneIds(QLocale::Territory territory) const { // Default fall-back mode, use the zoneTable to find Region of know Zones QList regions; @@ -644,10 +644,15 @@ QList QTimeZonePrivate::availableTimeZoneIds(QLocale::Territory terr } } } - return selectAvailable(std::move(regions), availableTimeZoneIds()); + return regions; } -QList QTimeZonePrivate::availableTimeZoneIds(int offsetFromUtc) const +QList QTimeZonePrivate::availableTimeZoneIds(QLocale::Territory territory) const +{ + return selectAvailable(matchingTimeZoneIds(territory), availableTimeZoneIds()); +} + +QList QTimeZonePrivate::matchingTimeZoneIds(int offsetFromUtc) const { // Default fall-back mode: use the zoneTable to find offsets of know zones. QList offsets; @@ -662,7 +667,12 @@ QList QTimeZonePrivate::availableTimeZoneIds(int offsetFromUtc) cons } } } - return selectAvailable(std::move(offsets), availableTimeZoneIds()); + return offsets; +} + +QList QTimeZonePrivate::availableTimeZoneIds(int offsetFromUtc) const +{ + return selectAvailable(matchingTimeZoneIds(offsetFromUtc), availableTimeZoneIds()); } #ifndef QT_NO_DATASTREAM diff --git a/src/corelib/time/qtimezoneprivate_icu.cpp b/src/corelib/time/qtimezoneprivate_icu.cpp index 8369a42f22..c7039e157c 100644 --- a/src/corelib/time/qtimezoneprivate_icu.cpp +++ b/src/corelib/time/qtimezoneprivate_icu.cpp @@ -425,6 +425,8 @@ QList QIcuTimeZonePrivate::availableTimeZoneIds(QLocale::Territory t if (U_SUCCESS(status)) result = uenumToIdList(uenum); uenum_close(uenum); + // We could merge in what matchingTimeZoneIds(territory) gives us, but + // hopefully that's redundant, as ICU packages CLDR. return result; } @@ -439,6 +441,8 @@ QList QIcuTimeZonePrivate::availableTimeZoneIds(int offsetFromUtc) c if (U_SUCCESS(status)) result = uenumToIdList(uenum); uenum_close(uenum); + // We could merge in what matchingTimeZoneIds(offsetFromUtc) gives us, but + // hopefully that's redundant, as ICU packages CLDR. return result; #else return QTimeZonePrivate::availableTimeZoneIds(offsetFromUtc); diff --git a/src/corelib/time/qtimezoneprivate_p.h b/src/corelib/time/qtimezoneprivate_p.h index fcaab52db4..d54220b6e6 100644 --- a/src/corelib/time/qtimezoneprivate_p.h +++ b/src/corelib/time/qtimezoneprivate_p.h @@ -153,6 +153,12 @@ public: return QByteArrayLiteral("UTC"); } +protected: + // Zones CLDR data says match a condition. + // Use to filter what the backend has available. + QList matchingTimeZoneIds(QLocale::Territory territory) const; + QList matchingTimeZoneIds(int utcOffset) const; + #if QT_CONFIG(timezone_locale) private: // Defined in qtimezonelocale.cpp diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp index c31b4dc965..253f1367d9 100644 --- a/src/corelib/time/qtimezoneprivate_tz.cpp +++ b/src/corelib/time/qtimezoneprivate_tz.cpp @@ -41,6 +41,7 @@ using namespace Qt::StringLiterals; */ struct QTzTimeZone { + // TODO: for zone1970.tab we'll need a set of territories: QLocale::Territory territory = QLocale::AnyTerritory; QByteArray comment; }; @@ -80,7 +81,7 @@ static bool openZoneInfo(const QString &name, QFile *file) // Parse zone.tab table for territory information, read directories to ensure we // find all installed zones (many are omitted from zone.tab; even more from -// zone1970.tab). +// zone1970.tab; see also QTBUG-64941). static QTzTimeZoneHash loadTzTimeZones() { QFile tzif; @@ -1250,13 +1251,38 @@ QList QTzTimeZonePrivate::availableTimeZoneIds() const QList QTzTimeZonePrivate::availableTimeZoneIds(QLocale::Territory territory) const { - // TODO AnyTerritory QList result; for (auto it = tzZones->cbegin(), end = tzZones->cend(); it != end; ++it) { if (it.value().territory == territory) result << it.key(); } std::sort(result.begin(), result.end()); + + // Since zone.tab only knows about one territory per zone, and is somewhat + // incomplete, we may well miss some zones that CLDR associates with the + // territory. So merge with those from CLDR that we do support. + const auto unWantedZone = [territory](QByteArrayView id) { + // We only want to add zones if they are known and we don't already have them: + auto it = tzZones->constFind(id); + return it == tzZones->end() || it->territory == territory; + }; + QList cldrViews = matchingTimeZoneIds(territory); + std::sort(cldrViews.begin(), cldrViews.end()); + const auto uniqueEnd = std::unique(cldrViews.begin(), cldrViews.end()); + const auto prunedEnd = std::remove_if(cldrViews.begin(), uniqueEnd, unWantedZone); + const auto cldrSize = std::distance(cldrViews.begin(), prunedEnd); + if (cldrSize) { + QList cldrList; + cldrList.reserve(cldrSize); + for (auto it = cldrViews.begin(); it != prunedEnd; ++it) + cldrList.emplace_back(it->toByteArray()); + QList joined; + joined.reserve(result.size() + cldrSize); + std::set_union(result.begin(), result.end(), cldrList.begin(), cldrList.end(), + std::back_inserter(joined)); + result = joined; + } + return result; } diff --git a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp index dfa5a8ccfd..c5745a1dcc 100644 --- a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp @@ -980,6 +980,11 @@ void tst_QTimeZone::availableTimeZoneIds() QCOMPARE_LT(list001.size(), listAll.size()); QCOMPARE_LT(listUsa.size(), listAll.size()); QCOMPARE_LT(listGmt.size(), listAll.size()); + // And we do know CLDR data supplies some entries to each: + QCOMPARE_GT(listAll.size(), 0); + QCOMPARE_GT(list001.size(), 0); + QCOMPARE_GT(listUsa.size(), 0); + QCOMPARE_GT(listGmt.size(), 0); } }