diff options
Diffstat (limited to 'src/corelib/time/qtimezoneprivate_icu.cpp')
-rw-r--r-- | src/corelib/time/qtimezoneprivate_icu.cpp | 133 |
1 files changed, 45 insertions, 88 deletions
diff --git a/src/corelib/time/qtimezoneprivate_icu.cpp b/src/corelib/time/qtimezoneprivate_icu.cpp index c071e7d549..a9fe68b83c 100644 --- a/src/corelib/time/qtimezoneprivate_icu.cpp +++ b/src/corelib/time/qtimezoneprivate_icu.cpp @@ -4,6 +4,7 @@ #include "qtimezone.h" #include "qtimezoneprivate_p.h" +#include "qtimezonelocale_p.h" #include <unicode/ucal.h> @@ -22,27 +23,6 @@ QT_BEGIN_NAMESPACE // ICU utilities -// Convert TimeType and NameType into ICU UCalendarDisplayNameType -static UCalendarDisplayNameType ucalDisplayNameType(QTimeZone::TimeType timeType, QTimeZone::NameType nameType) -{ - // TODO ICU C UCalendarDisplayNameType does not support full set of C++ TimeZone::EDisplayType - switch (nameType) { - case QTimeZone::ShortName : - case QTimeZone::OffsetName : - if (timeType == QTimeZone::DaylightTime) - return UCAL_SHORT_DST; - // Includes GenericTime - return UCAL_SHORT_STANDARD; - case QTimeZone::DefaultName : - case QTimeZone::LongName : - if (timeType == QTimeZone::DaylightTime) - return UCAL_DST; - // Includes GenericTime - return UCAL_STANDARD; - } - return UCAL_STANDARD; -} - // Qt wrapper around ucal_getDefaultTimeZone() static QByteArray ucalDefaultTimeZoneId() { @@ -69,44 +49,6 @@ static QByteArray ucalDefaultTimeZoneId() return QByteArray(); } -// Qt wrapper around ucal_getTimeZoneDisplayName() -static QString ucalTimeZoneDisplayName(UCalendar *ucal, QTimeZone::TimeType timeType, - QTimeZone::NameType nameType, - const QString &localeCode) -{ - int32_t size = 50; - QString result(size, Qt::Uninitialized); - UErrorCode status = U_ZERO_ERROR; - - // size = ucal_getTimeZoneDisplayName(cal, type, locale, result, resultLength, status) - size = ucal_getTimeZoneDisplayName(ucal, - ucalDisplayNameType(timeType, nameType), - localeCode.toUtf8(), - reinterpret_cast<UChar *>(result.data()), - size, - &status); - - // If overflow, then resize and retry - if (status == U_BUFFER_OVERFLOW_ERROR) { - result.resize(size); - status = U_ZERO_ERROR; - size = ucal_getTimeZoneDisplayName(ucal, - ucalDisplayNameType(timeType, nameType), - localeCode.toUtf8(), - reinterpret_cast<UChar *>(result.data()), - size, - &status); - } - - // If successful on first or second go, resize and return - if (U_SUCCESS(status)) { - result.resize(size); - return result; - } - - return QString(); -} - // Qt wrapper around ucal_get() for offsets static bool ucalOffsetsAtTime(UCalendar *m_ucal, qint64 atMSecsSinceEpoch, int *utcOffset, int *dstOffset) @@ -153,7 +95,7 @@ static QTimeZonePrivate::Data ucalTimeZoneTransition(UCalendar *m_ucal, UTimeZoneTransitionType type, qint64 atMSecsSinceEpoch) { - QTimeZonePrivate::Data tran = QTimeZonePrivate::invalidData(); + QTimeZonePrivate::Data tran; // Clone the ucal so we don't change the shared object UErrorCode status = U_ZERO_ERROR; @@ -203,13 +145,11 @@ static QTimeZonePrivate::Data ucalTimeZoneTransition(UCalendar *m_ucal, tran.offsetFromUtc = utc + dst; tran.standardTimeOffset = utc; tran.daylightTimeOffset = dst; - // TODO No ICU API, use short name instead - if (dst == 0) - tran.abbreviation = ucalTimeZoneDisplayName(m_ucal, QTimeZone::StandardTime, - QTimeZone::ShortName, QLocale().name()); - else - tran.abbreviation = ucalTimeZoneDisplayName(m_ucal, QTimeZone::DaylightTime, - QTimeZone::ShortName, QLocale().name()); + // TODO No ICU API, use short name as abbreviation. + QTimeZone::TimeType timeType = dst == 0 ? QTimeZone::StandardTime : QTimeZone::DaylightTime; + using namespace QtTimeZoneLocale; + tran.abbreviation = ucalTimeZoneDisplayName(m_ucal, timeType, + QTimeZone::ShortName, QLocale().name()); return tran; } #endif // U_ICU_VERSION_SHORT @@ -254,8 +194,8 @@ QIcuTimeZonePrivate::QIcuTimeZonePrivate() QIcuTimeZonePrivate::QIcuTimeZonePrivate(const QByteArray &ianaId) : m_ucal(nullptr) { - // Need to check validity here as ICu will create a GMT tz if name is invalid - if (availableTimeZoneIds().contains(ianaId)) + // ICU misleadingly maps invalid IDs to GMT. + if (isTimeZoneIdAvailable(ianaId)) init(ianaId); } @@ -301,28 +241,26 @@ QString QIcuTimeZonePrivate::displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType, const QLocale &locale) const { - // Return standard offset format name as ICU C api doesn't support it yet + // Base class has handled OffsetName if we came via the other overload. if (nameType == QTimeZone::OffsetName) { - const Data nowData = data(QDateTime::currentMSecsSinceEpoch()); - // We can't use transitions reliably to find out right dst offset - // Instead use dst offset api to try get it if needed + int offset = standardTimeOffset(QDateTime::currentMSecsSinceEpoch()); + // We can't use transitions reliably to find out right DST offset. + // Instead use DST offset API to try to get it, when needed: if (timeType == QTimeZone::DaylightTime) - return isoOffsetFormat(nowData.standardTimeOffset + ucalDaylightOffset(m_id)); - else - return isoOffsetFormat(nowData.standardTimeOffset); + offset += ucalDaylightOffset(m_id); + // This is only valid for times since the most recent standard offset + // change; for earlier times, caller must use the other overload. + + // Use our own formating for offset names (ICU C API doesn't support it + // and we may as well be self-consistent anyway). + return isoOffsetFormat(offset); } + // Technically this may be suspect, if locale isn't QLocale(), since that's + // what we used when constructing m_ucal; does ICU cope with inconsistency ? + using namespace QtTimeZoneLocale; return ucalTimeZoneDisplayName(m_ucal, timeType, nameType, locale.name()); } -QString QIcuTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const -{ - // TODO No ICU API, use short name instead - if (isDaylightTime(atMSecsSinceEpoch)) - return displayName(QTimeZone::DaylightTime, QTimeZone::ShortName, QLocale()); - else - return displayName(QTimeZone::StandardTime, QTimeZone::ShortName, QLocale()); -} - int QIcuTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const { int stdOffset = 0; @@ -387,7 +325,7 @@ bool QIcuTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const QTimeZonePrivate::Data QIcuTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const { // Available in ICU C++ api, and draft C api in v50 - QTimeZonePrivate::Data data = invalidData(); + QTimeZonePrivate::Data data; #if U_ICU_VERSION_MAJOR_NUM >= 50 data = ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE, forMSecsSinceEpoch); @@ -420,7 +358,7 @@ QTimeZonePrivate::Data QIcuTimeZonePrivate::nextTransition(qint64 afterMSecsSinc return ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_NEXT, afterMSecsSinceEpoch); #else Q_UNUSED(afterMSecsSinceEpoch); - return invalidData(); + return {}; #endif } @@ -431,7 +369,7 @@ QTimeZonePrivate::Data QIcuTimeZonePrivate::previousTransition(qint64 beforeMSec return ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_PREVIOUS, beforeMSecsSinceEpoch); #else Q_UNUSED(beforeMSecsSinceEpoch); - return invalidData(); + return {}; #endif } @@ -442,6 +380,25 @@ QByteArray QIcuTimeZonePrivate::systemTimeZoneId() const return ucalDefaultTimeZoneId(); } +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; +} + QList<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds() const { UErrorCode status = U_ZERO_ERROR; |