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 <ivan.solovev@qt.io>
This commit is contained in:
Edward Welbourne 2024-10-30 12:44:52 +01:00
parent 7a8cefd6fb
commit 35066b28c8
3 changed files with 23 additions and 6 deletions

View File

@ -1486,8 +1486,18 @@ bool QTimeZone::isTimeZoneIdAvailable(const QByteArray &ianaId)
|| global_tz->backend->isTimeZoneIdAvailable(ianaId);
}
[[maybe_unused]] static bool isUniqueSorted(const QList<QByteArray> &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<QByteArray>());
}
static QList<QByteArray> set_union(const QList<QByteArray> &l1, const QList<QByteArray> &l2)
{
Q_ASSERT(isUniqueSorted(l1));
Q_ASSERT(isUniqueSorted(l2));
QList<QByteArray> result;
result.reserve(l1.size() + l2.size());
std::set_union(l1.begin(), l1.end(),
@ -1511,6 +1521,7 @@ static QList<QByteArray> set_union(const QList<QByteArray> &l1, const QList<QByt
QList<QByteArray> 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());
}

View File

@ -203,14 +203,19 @@ QList<QByteArray> QAndroidTimeZonePrivate::availableTimeZoneIds() const
{
using namespace QtJniTypes;
const QJniArray androidAvailableIdList = TimeZone::callStaticMethod<String[]>("getAvailableIDs");
const QJniArray androidAvailableIdList
= TimeZone::callStaticMethod<String[]>("getAvailableIDs");
// Does not document order of entries.
QList<QByteArray> availableTimeZoneIdList;
availableTimeZoneIdList.reserve(androidAvailableIdList.size());
QList<QByteArray> 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

View File

@ -408,9 +408,10 @@ QList<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds() const
{
UErrorCode status = U_ZERO_ERROR;
UEnumeration *uenum = ucal_openTimeZones(&status);
// Does not document order of entries.
QList<QByteArray> result;
if (U_SUCCESS(status))
result = uenumToIdList(uenum);
result = uenumToIdList(uenum); // Ensures sorted, unique.
uenum_close(uenum);
return result;
}