diff --git a/src/corelib/time/qtimezone.cpp b/src/corelib/time/qtimezone.cpp index a89fe191a65..ec2eda92fd2 100644 --- a/src/corelib/time/qtimezone.cpp +++ b/src/corelib/time/qtimezone.cpp @@ -1529,10 +1529,11 @@ QList QTimeZone::availableTimeZoneIds() /*! Returns a list of all available IANA time zone IDs for a given \a territory. - As a special case, a \a territory of \l {QLocale::}{AnyTerritory} selects - those time zones that have no known territorial association, such as UTC. If - you require a list of all time zone IDs for all territories then use the - standard availableTimeZoneIds() method. + As a special case, a \a territory of \l {QLocale::} {AnyTerritory} selects + those time zones that have a non-territorial association, such as UTC, while + \l {QLocale::}{World} selects those time-zones for which there is a global + default IANA ID. If you require a list of all time zone IDs for all + territories then use the standard availableTimeZoneIds() method. This method is only available when feature \c timezone is enabled. @@ -1601,8 +1602,14 @@ QByteArray QTimeZone::windowsIdToDefaultIanaId(const QByteArray &windowsId) Because a Windows ID can cover several IANA IDs within a given territory, the most frequently used IANA ID in that territory is returned. - As a special case, \l{QLocale::}{AnyTerritory} returns the default of those - IANA IDs that have no known territorial association. + As a special case, \l {QLocale::} {AnyTerritory} returns the default of + those IANA IDs that have a non-territorial association, while \l {QLocale::} + {World} returns the default for the given \a windowsId in territories that + have no specific association with it. + + If the return is empty, there is no IANA ID specific to the given \a + territory for this \a windowsId. It is reasonable, in this case, to fall + back to \c{windowsIdToDefaultIanaId(windowsId)}. This method is only available when feature \c timezone is enabled. @@ -1633,8 +1640,10 @@ QList QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId) /*! Returns all the IANA IDs for a given \a windowsId and \a territory. - As a special case, \l{QLocale::}{AnyTerritory} selects those IANA IDs that - have no known territorial association. + As a special case, \l{QLocale::} {AnyTerritory} selects those IANA IDs that + have a non-territorial association, while \l {QLocale::} {World} selects the + default for the given \a windowsId in territories that have no specific + association with it. The returned list is in order of frequency of usage, i.e. larger zones within a territory are listed first. diff --git a/src/corelib/time/qtimezoneprivate.cpp b/src/corelib/time/qtimezoneprivate.cpp index 32a4055f85a..a258b847d9a 100644 --- a/src/corelib/time/qtimezoneprivate.cpp +++ b/src/corelib/time/qtimezoneprivate.cpp @@ -630,10 +630,17 @@ QList QTimeZonePrivate::availableTimeZoneIds(QLocale::Territory terr regions = QtTimeZoneLocale::ianaIdsForTerritory(territory); #endif // Get all Zones in the table associated with this territory: - for (const ZoneData &data : zoneDataTable) { - if (data.territory == territory) { - for (auto l1 : data.ids()) - regions << QByteArrayView(l1.data(), l1.size()); + if (territory == QLocale::World) { + // World names are filtered out of zoneDataTable to provide the defaults + // in windowsDataTable. + for (const WindowsData &data : windowsDataTable) + regions << data.ianaId(); + } else { + for (const ZoneData &data : zoneDataTable) { + if (data.territory == territory) { + for (auto l1 : data.ids()) + regions << QByteArrayView(l1.data(), l1.size()); + } } } return selectAvailable(std::move(regions), availableTimeZoneIds()); @@ -803,6 +810,8 @@ QByteArray QTimeZonePrivate::ianaIdToWindowsId(const QByteArray &id) return toWindowsIdLiteral(data.windowsIdKey); } } + // If the IANA ID is the default for any Windows ID, it has already shown up + // as an ID for it in some territory; no need to search windowsDataTable[]. return QByteArray(); } @@ -812,8 +821,7 @@ QByteArray QTimeZonePrivate::windowsIdToDefaultIanaId(const QByteArray &windowsI windowsId, earlierWindowsId); if (data != std::end(windowsDataTable) && data->windowsId() == windowsId) { QByteArrayView id = data->ianaId(); - if (qsizetype cut = id.indexOf(' '); cut >= 0) - id = id.first(cut); + Q_ASSERT(id.indexOf(' ') == -1); return id.toByteArray(); } return QByteArray(); @@ -837,6 +845,9 @@ QList QTimeZonePrivate::windowsIdToIanaIds(const QByteArray &windows for (auto l1 : data->ids()) list << QByteArray(l1.data(), l1.size()); } + // The default, windowsIdToDefaultIanaId(windowsId), is always an entry for + // at least one territory: cldr.py asserts this, in readWindowsTimeZones(). + // So we don't need to add it here. // Return the full list in alpha order std::sort(list.begin(), list.end()); @@ -847,16 +858,21 @@ QList QTimeZonePrivate::windowsIdToIanaIds(const QByteArray &windows QLocale::Territory territory) { QList list; - const quint16 windowsIdKey = toWindowsIdKey(windowsId); - const qint16 land = static_cast(territory); - for (auto data = zoneStartForWindowsId(windowsIdKey); - data != std::end(zoneDataTable) && data->windowsIdKey == windowsIdKey; - ++data) { - // Return the region matches in preference order - if (data->territory == land) { - for (auto l1 : data->ids()) - list << QByteArray(l1.data(), l1.size()); - break; + if (territory == QLocale::World) { + // World data are in windowsDataTable, not zoneDataTable. + list << windowsIdToDefaultIanaId(windowsId); + } else { + const quint16 windowsIdKey = toWindowsIdKey(windowsId); + const qint16 land = static_cast(territory); + for (auto data = zoneStartForWindowsId(windowsIdKey); + data != std::end(zoneDataTable) && data->windowsIdKey == windowsIdKey; + ++data) { + // Return the region matches in preference order + if (data->territory == land) { + for (auto l1 : data->ids()) + list << QByteArray(l1.data(), l1.size()); + break; + } } } diff --git a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp index 27389a8618d..18433a391ac 100644 --- a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp @@ -973,10 +973,16 @@ void tst_QTimeZone::availableTimeZoneIds() qDebug() << QTimeZone::availableTimeZoneIds(0); qDebug() << ""; } else { - //Just test the calls work, we cannot know what any test machine has available + // Test the calls work: QList listAll = QTimeZone::availableTimeZoneIds(); - QList listUs = QTimeZone::availableTimeZoneIds(QLocale::UnitedStates); - QList listZero = QTimeZone::availableTimeZoneIds(0); + QList list001 = QTimeZone::availableTimeZoneIds(QLocale::World); + QList listUsa = QTimeZone::availableTimeZoneIds(QLocale::UnitedStates); + QList listGmt = QTimeZone::availableTimeZoneIds(0); + // We cannot know what any test machine has available, so can't test contents. + // But we can do a consistency check: + QCOMPARE_LT(list001.size(), listAll.size()); + QCOMPARE_LT(listUsa.size(), listAll.size()); + QCOMPARE_LT(listGmt.size(), listAll.size()); } } @@ -1034,9 +1040,9 @@ void tst_QTimeZone::windowsId() /* Current Windows zones for "Central Standard Time": Region IANA Id(s) - Default "America/Chicago" + World "America/Chicago" (the default) Canada "America/Winnipeg America/Rankin_Inlet America/Resolute" - Mexico "America/Matamoros" + Mexico "America/Matamoros America/Ojinaga" USA "America/Chicago America/Indiana/Knox America/Indiana/Tell_City America/Menominee" "America/North_Dakota/Beulah America/North_Dakota/Center" "America/North_Dakota/New_Salem" @@ -1055,6 +1061,8 @@ void tst_QTimeZone::windowsId() // Check default value QCOMPARE(QTimeZone::windowsIdToDefaultIanaId("Central Standard Time"), QByteArray("America/Chicago")); + QCOMPARE(QTimeZone::windowsIdToDefaultIanaId("Central Standard Time", QLocale::World), + QByteArray("America/Chicago")); QCOMPARE(QTimeZone::windowsIdToDefaultIanaId("Central Standard Time", QLocale::Canada), QByteArray("America/Winnipeg")); QCOMPARE(QTimeZone::windowsIdToDefaultIanaId("Central Standard Time", QLocale::AnyTerritory), @@ -1072,6 +1080,11 @@ void tst_QTimeZone::windowsId() }; QCOMPARE(QTimeZone::windowsIdToIanaIds("Central Standard Time"), list); } + { + const QList list = { "America/Chicago" }; + QCOMPARE(QTimeZone::windowsIdToIanaIds("Central Standard Time", QLocale::World), + list); + } { // Check country with no match returns empty list const QList empty; diff --git a/util/locale_database/cldr.py b/util/locale_database/cldr.py index 4b61ba97047..0e5154b8b57 100644 --- a/util/locale_database/cldr.py +++ b/util/locale_database/cldr.py @@ -620,6 +620,12 @@ enumdata.py (keeping the old name as an alias): else: windows.append((wid, code, ' '.join(ianas))) + # For each Windows ID, its default zone is its zone for at + # least some territory: + assert all(any(True for w, code, seq in windows + if w == wid and zone in seq.split()) + for wid, zone in defaults.items()), (defaults, windows) + return defaults, windows def readMetaZoneMap(self, alias: dict[str, str]