Include "is known to ICU" check in QTimeZonePrivate::localeName()

As long noted in the QIcuTimeZonePrivate constructor, we can't trust
ucal_open()'s non-null return to mean it actually recognised the ID we
passed it. So break out the "is available" check to a function in the
QtTimeZoneLocale namespace so the check can be shared with
localeName() when compiled with ICU but some other backend. We were
previously getting GMT as display name for zones unknown to ICU.

Pick-to: 6.9 6.8
Change-Id: I57d57f94d8db7df76f24193a8ef1b5c71b08b0fc
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
This commit is contained in:
Edward Welbourne 2025-04-09 17:00:15 +02:00
parent fa1fab994e
commit fc9a26ea3a
3 changed files with 26 additions and 15 deletions

View File

@ -94,6 +94,24 @@ QString ucalTimeZoneDisplayName(UCalendar *ucal,
return result;
}
bool ucalKnownTimeZoneId(const QString &ianaStr)
{
const UChar *const name = reinterpret_cast<const UChar *>(ianaStr.constData());
// We are not interested in the value, but we have to pass something.
// No known IANA zone name is (up to 2023) longer than 30 characters.
constexpr size_t size = 64;
UChar buffer[size];
// TODO: convert to ucal_getIanaTimeZoneID(), new draft in ICU 74, once we
// can rely on its availability, assuming it works the same once not draft.
UErrorCode status = U_ZERO_ERROR;
UBool isSys = false;
// Returns the length of the IANA zone name (but we don't care):
ucal_getCanonicalTimeZoneID(name, ianaStr.size(), buffer, size, &isSys, &status);
// We're only interested if the result is a "system" (i.e. IANA) ID:
return isSys;
}
} // QtTimeZoneLocale
// Used by TZ backends when ICU is available:
@ -110,6 +128,11 @@ QString QTimeZonePrivate::localeName(qint64 atMSecsSinceEpoch, int offsetFromUtc
return isoOffsetFormat(offsetFromUtc);
const QString id = QString::fromUtf8(m_id);
// Need to check id is known to ICU, since ucal_open() will return a
// misleading "valid" GMT ucal when it doesn't recognise id.
if (!QtTimeZoneLocale::ucalKnownTimeZoneId(id))
return QString();
const QByteArray loc = locale.name().toUtf8();
UErrorCode status = U_ZERO_ERROR;
// TODO: QTBUG-124271 can we cache any of this ?

View File

@ -36,6 +36,8 @@ namespace QtTimeZoneLocale {
QString ucalTimeZoneDisplayName(UCalendar *ucal, QTimeZone::TimeType timeType,
QTimeZone::NameType nameType,
const QByteArray &localeCode);
bool ucalKnownTimeZoneId(const QString &id);
#else
QList<QByteArrayView> ianaIdsForTerritory(QLocale::Territory territory);

View File

@ -387,21 +387,7 @@ QByteArray QIcuTimeZonePrivate::systemTimeZoneId() const
bool QIcuTimeZonePrivate::isTimeZoneIdAvailable(const QByteArray &ianaId) const
{
const QString ianaStr = QString::fromUtf8(ianaId);
const UChar *const name = reinterpret_cast<const UChar *>(ianaStr.constData());
// We are not interested in the value, but we have to pass something.
// No known IANA zone name is (up to 2023) longer than 30 characters.
constexpr size_t size = 64;
UChar buffer[size];
// TODO: convert to ucal_getIanaTimeZoneID(), new draft in ICU 74, once we
// can rely on its availability, assuming it works the same once not draft.
UErrorCode status = U_ZERO_ERROR;
UBool isSys = false;
// Returns the length of the IANA zone name (but we don't care):
ucal_getCanonicalTimeZoneID(name, ianaStr.size(), buffer, size, &isSys, &status);
// We're only interested if the result is a "system" (i.e. IANA) ID:
return isSys;
return QtTimeZoneLocale::ucalKnownTimeZoneId(QString::fromUtf8(ianaId));
}
QList<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds() const