From 35066b28c86cb0eb8880b6a4af84f8d02ff7aff0 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 30 Oct 2024 12:44:52 +0100 Subject: [PATCH] Enforce sorting of QTZ backend availableTimeZoneIds() Various pieces of QTZP and QTZ rely (by using std::set_union or std::set_intersection) on the backends supplying the list of available IDs in sorted order. As this isn't documented as part of public API behavior, document it internally and assert it. Add the missing sorting and uniquification for Android; although the Java API used does appear to return a sorted list, its specification [0] says nothing about its ordering, so make sure. In the process, rename a wantonly long variable so the uniquifying doesn't need a line-split. [0] https://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html#getAvailableIDs-- Document, in the ICU backend, that the relevant API doesn't say what order it returns; but also make clear where the backend *does* take care of sorting it and rendering entries unique. Change-Id: I61b4ecf342044e2303b18889c600da5922aec278 Reviewed-by: Ivan Solovev --- src/corelib/time/qtimezone.cpp | 11 +++++++++++ src/corelib/time/qtimezoneprivate_android.cpp | 15 ++++++++++----- src/corelib/time/qtimezoneprivate_icu.cpp | 3 ++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/corelib/time/qtimezone.cpp b/src/corelib/time/qtimezone.cpp index 64501907a9b..a89fe191a65 100644 --- a/src/corelib/time/qtimezone.cpp +++ b/src/corelib/time/qtimezone.cpp @@ -1486,8 +1486,18 @@ bool QTimeZone::isTimeZoneIdAvailable(const QByteArray &ianaId) || global_tz->backend->isTimeZoneIdAvailable(ianaId); } +[[maybe_unused]] static bool isUniqueSorted(const QList &seq) +{ + // Since [..., b, a, ...] isn't unique-sorted if a <= b, at least the + // suggested implementations of is_sorted() and is_sorted_until() imply a + // non-unique sorted list will fail is_sorted() with <= comparison. + return std::is_sorted(seq.begin(), seq.end(), std::less_equal()); +} + static QList set_union(const QList &l1, const QList &l2) { + Q_ASSERT(isUniqueSorted(l1)); + Q_ASSERT(isUniqueSorted(l2)); QList result; result.reserve(l1.size() + l2.size()); std::set_union(l1.begin(), l1.end(), @@ -1511,6 +1521,7 @@ static QList set_union(const QList &l1, const QList QTimeZone::availableTimeZoneIds() { // Backends MUST implement availableTimeZoneIds(). + // The return from each backend MUST be sorted and unique. return set_union(QUtcTimeZonePrivate().availableTimeZoneIds(), global_tz->backend->availableTimeZoneIds()); } diff --git a/src/corelib/time/qtimezoneprivate_android.cpp b/src/corelib/time/qtimezoneprivate_android.cpp index ec0710b1e4e..838f97c746c 100644 --- a/src/corelib/time/qtimezoneprivate_android.cpp +++ b/src/corelib/time/qtimezoneprivate_android.cpp @@ -203,14 +203,19 @@ QList QAndroidTimeZonePrivate::availableTimeZoneIds() const { using namespace QtJniTypes; - const QJniArray androidAvailableIdList = TimeZone::callStaticMethod("getAvailableIDs"); + const QJniArray androidAvailableIdList + = TimeZone::callStaticMethod("getAvailableIDs"); + // Does not document order of entries. - QList availableTimeZoneIdList; - availableTimeZoneIdList.reserve(androidAvailableIdList.size()); + QList result; + result.reserve(androidAvailableIdList.size()); for (const auto &id : androidAvailableIdList) - availableTimeZoneIdList.append(id.toString().toUtf8()); + result.append(id.toString().toUtf8()); - return availableTimeZoneIdList; + // Sort & uniquify (just to be sure; it appears to not need this, but we can't rely on that). + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + return result; } QT_END_NAMESPACE diff --git a/src/corelib/time/qtimezoneprivate_icu.cpp b/src/corelib/time/qtimezoneprivate_icu.cpp index f42ffe07678..30e6b17a7cb 100644 --- a/src/corelib/time/qtimezoneprivate_icu.cpp +++ b/src/corelib/time/qtimezoneprivate_icu.cpp @@ -408,9 +408,10 @@ QList QIcuTimeZonePrivate::availableTimeZoneIds() const { UErrorCode status = U_ZERO_ERROR; UEnumeration *uenum = ucal_openTimeZones(&status); + // Does not document order of entries. QList result; if (U_SUCCESS(status)) - result = uenumToIdList(uenum); + result = uenumToIdList(uenum); // Ensures sorted, unique. uenum_close(uenum); return result; }