summaryrefslogtreecommitdiffstats
path: root/src/corelib/time/qtimezoneprivate_icu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/time/qtimezoneprivate_icu.cpp')
-rw-r--r--src/corelib/time/qtimezoneprivate_icu.cpp133
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;