10#include <unicode/ucal.h>
20
21
22
23
28static QByteArray ucalDefaultTimeZoneId()
31 QString result(size, Qt::Uninitialized);
32 UErrorCode status = U_ZERO_ERROR;
35 size = ucal_getDefaultTimeZone(
reinterpret_cast<UChar *>(result.data()), size, &status);
38 if (status == U_BUFFER_OVERFLOW_ERROR) {
40 status = U_ZERO_ERROR;
41 size = ucal_getDefaultTimeZone(
reinterpret_cast<UChar *>(result.data()), size, &status);
45 if (U_SUCCESS(status)) {
47 return std::move(result).toUtf8();
55 int *utcOffset,
int *dstOffset)
61 UErrorCode status = U_ZERO_ERROR;
62 UCalendar *ucal = ucal_clone(m_ucal, &status);
63 if (!U_SUCCESS(status))
67 status = U_ZERO_ERROR;
68 ucal_setMillis(ucal, atMSecsSinceEpoch, &status);
71 if (U_SUCCESS(status)) {
72 status = U_ZERO_ERROR;
74 utc = ucal_get(ucal, UCAL_ZONE_OFFSET, &status) / 1000;
78 if (U_SUCCESS(status)) {
79 status = U_ZERO_ERROR;
81 dst = ucal_get(ucal, UCAL_DST_OFFSET, &status) / 1000;
85 if (U_SUCCESS(status)) {
93#if U_ICU_VERSION_MAJOR_NUM
>= 50
95static QTimeZonePrivate::Data ucalTimeZoneTransition(UCalendar *m_ucal,
96 UTimeZoneTransitionType type,
97 qint64 atMSecsSinceEpoch)
99 QTimeZonePrivate::Data tran;
102 UErrorCode status = U_ZERO_ERROR;
103 UCalendar *ucal = ucal_clone(m_ucal, &status);
104 if (!U_SUCCESS(status))
108 status = U_ZERO_ERROR;
109 const UDate when = UDate(atMSecsSinceEpoch);
110 ucal_setMillis(ucal, when, &status);
114 status = U_ZERO_ERROR;
115 bool ok = ucal_getTimeZoneTransitionDate(ucal, type, &tranMSecs, &status);
118 if (U_SUCCESS(status) && ok && type == UCAL_TZ_TRANSITION_NEXT) {
122 ok = tranMSecs > when;
126 if (U_SUCCESS(status) && ok) {
127 status = U_ZERO_ERROR;
128 ucal_setMillis(ucal, tranMSecs, &status);
132 if (U_SUCCESS(status) && ok) {
133 status = U_ZERO_ERROR;
134 utc = ucal_get(ucal, UCAL_ZONE_OFFSET, &status) / 1000;
138 if (U_SUCCESS(status) && ok) {
139 status = U_ZERO_ERROR;
140 dst = ucal_get(ucal, UCAL_DST_OFFSET, &status) / 1000;
144 if (!U_SUCCESS(status) || !ok)
146 tran.atMSecsSinceEpoch = qint64(tranMSecs);
147 tran.offsetFromUtc = utc + dst;
148 tran.standardTimeOffset = utc;
149 tran.daylightTimeOffset = dst;
151 QTimeZone::TimeType timeType = dst == 0 ? QTimeZone::StandardTime : QTimeZone::DaylightTime;
152 using namespace QtTimeZoneLocale;
153 tran.abbreviation = ucalTimeZoneDisplayName(m_ucal, timeType,
154 QTimeZone::ShortName, QLocale().name().toUtf8());
162 QList<QByteArray> list;
164 UErrorCode status = U_ZERO_ERROR;
166 QByteArray result = uenum_next(uenum, &size, &status);
167 while (U_SUCCESS(status) && !result.isEmpty()) {
170 const QByteArrayView zone = QTimeZonePrivate::aliasToIana(result);
171 if (!zone.isEmpty()) {
172 list << zone.toByteArray();
173 Q_ASSERT(QTimeZonePrivate::aliasToIana(zone).isEmpty());
175 status = U_ZERO_ERROR;
176 result = uenum_next(uenum, &size, &status);
179 std::sort(list.begin(), list.end());
180 list.erase(
std::unique(list.begin(), list.end()), list.end());
187 UErrorCode status = U_ZERO_ERROR;
188 const QString utf16 = QString::fromLatin1(id);
189 const int32_t dstMSecs = ucal_getDSTSavings(
190 reinterpret_cast<
const UChar *>(utf16.data()), &status);
191 return U_SUCCESS(status) ? dstMSecs / 1000 : 0;
195QIcuTimeZonePrivate::QIcuTimeZonePrivate()
199 init(ucalDefaultTimeZoneId());
203QIcuTimeZonePrivate::QIcuTimeZonePrivate(
const QByteArray &ianaId)
207 if (isTimeZoneIdAvailable(ianaId))
211QIcuTimeZonePrivate::QIcuTimeZonePrivate(
const QIcuTimeZonePrivate &other)
212 : QTimeZonePrivate(other), m_ucal(
nullptr)
215 UErrorCode status = U_ZERO_ERROR;
216 m_ucal = ucal_clone(other.m_ucal, &status);
217 if (!U_SUCCESS(status)) {
223QIcuTimeZonePrivate::~QIcuTimeZonePrivate()
228QIcuTimeZonePrivate *QIcuTimeZonePrivate::clone()
const
230 return new QIcuTimeZonePrivate(*
this);
233void QIcuTimeZonePrivate::init(
const QByteArray &ianaId)
237 const QString id = QString::fromUtf8(m_id);
238 UErrorCode status = U_ZERO_ERROR;
240 m_ucal = ucal_open(
reinterpret_cast<
const UChar *>(id.data()), id.size(),
241 QLocale().name().toUtf8(), UCAL_GREGORIAN, &status);
243 if (!U_SUCCESS(status)) {
249QString QIcuTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
250 QTimeZone::NameType nameType,
251 const QLocale &locale)
const
254 if (nameType == QTimeZone::OffsetName) {
255 int offset = standardTimeOffset(QDateTime::currentMSecsSinceEpoch());
258 if (timeType == QTimeZone::DaylightTime)
259 offset += ucalDaylightOffset(m_id);
265 return isoOffsetFormat(offset);
269 using namespace QtTimeZoneLocale;
270 return ucalTimeZoneDisplayName(m_ucal, timeType, nameType, locale.name().toUtf8());
273int QIcuTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch)
const
277 ucalOffsetsAtTime(m_ucal, atMSecsSinceEpoch, &stdOffset, & dstOffset);
278 return stdOffset + dstOffset;
281int QIcuTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch)
const
285 ucalOffsetsAtTime(m_ucal, atMSecsSinceEpoch, &stdOffset, & dstOffset);
289int QIcuTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch)
const
293 ucalOffsetsAtTime(m_ucal, atMSecsSinceEpoch, &stdOffset, & dstOffset);
297bool QIcuTimeZonePrivate::hasDaylightTime()
const
299 if (ucalDaylightOffset(m_id) != 0)
301#if U_ICU_VERSION_MAJOR_NUM
>= 50
302 for (qint64 when = minMSecs(); when != invalidMSecs(); ) {
303 auto data = nextTransition(when);
304 if (data.daylightTimeOffset && data.daylightTimeOffset != invalidSeconds())
306 when = data.atMSecsSinceEpoch;
312bool QIcuTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch)
const
315 UErrorCode status = U_ZERO_ERROR;
316 UCalendar *ucal = ucal_clone(m_ucal, &status);
317 if (!U_SUCCESS(status))
321 status = U_ZERO_ERROR;
322 ucal_setMillis(ucal, atMSecsSinceEpoch, &status);
325 if (U_SUCCESS(status)) {
326 status = U_ZERO_ERROR;
327 result = ucal_inDaylightTime(ucal, &status);
334QTimeZonePrivate::Data QIcuTimeZonePrivate::data(qint64 forMSecsSinceEpoch)
const
338#if U_ICU_VERSION_MAJOR_NUM
>= 50
339 data = ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE,
341 if (data.atMSecsSinceEpoch == invalidMSecs())
344 ucalOffsetsAtTime(m_ucal, forMSecsSinceEpoch, &data.standardTimeOffset,
345 &data.daylightTimeOffset);
346 data.offsetFromUtc = data.standardTimeOffset + data.daylightTimeOffset;
348 using namespace QtTimeZoneLocale;
349 QTimeZone::TimeType timeType
350 = data.daylightTimeOffset ? QTimeZone::DaylightTime : QTimeZone::StandardTime;
351 data.abbreviation = ucalTimeZoneDisplayName(m_ucal, timeType, QTimeZone::ShortName,
352 QLocale().name().toUtf8());
354 data.atMSecsSinceEpoch = forMSecsSinceEpoch;
358bool QIcuTimeZonePrivate::hasTransitions()
const
361#if U_ICU_VERSION_MAJOR_NUM
>= 50
368QTimeZonePrivate::Data QIcuTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch)
const
371#if U_ICU_VERSION_MAJOR_NUM
>= 50
372 return ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_NEXT, afterMSecsSinceEpoch);
374 Q_UNUSED(afterMSecsSinceEpoch);
379QTimeZonePrivate::Data QIcuTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch)
const
382#if U_ICU_VERSION_MAJOR_NUM
>= 50
383 return ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_PREVIOUS, beforeMSecsSinceEpoch);
385 Q_UNUSED(beforeMSecsSinceEpoch);
390QByteArray QIcuTimeZonePrivate::systemTimeZoneId()
const
394 return ucalDefaultTimeZoneId();
397bool QIcuTimeZonePrivate::isTimeZoneIdAvailable(QByteArrayView ianaId)
const
399 return QtTimeZoneLocale::ucalKnownTimeZoneId(QString::fromUtf8(ianaId));
402QList<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds()
const
404 UErrorCode status = U_ZERO_ERROR;
405 UEnumeration *uenum = ucal_openTimeZones(&status);
407 QList<QByteArray> result;
408 if (U_SUCCESS(status))
409 result = uenumToIdList(uenum);
414QList<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds(QLocale::Territory territory)
const
416 const QLatin1StringView regionCode = QLocalePrivate::territoryToCode(territory);
417 const QByteArray regionCodeUtf8 = QString(regionCode).toUtf8();
418 UErrorCode status = U_ZERO_ERROR;
419 UEnumeration *uenum = ucal_openCountryTimeZones(regionCodeUtf8.data(), &status);
420 QList<QByteArray> result;
421 if (U_SUCCESS(status))
422 result = uenumToIdList(uenum);
429QList<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds(
int offsetFromUtc)
const
432#if U_ICU_VERSION_MAJOR_NUM
>= 49
|| (U_ICU_VERSION_MAJOR_NUM
== 4
&& U_ICU_VERSION_MINOR_NUM
== 8
)
433 UErrorCode status = U_ZERO_ERROR;
434 UEnumeration *uenum = ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY,
nullptr,
435 &offsetFromUtc, &status);
436 QList<QByteArray> result;
437 if (U_SUCCESS(status))
438 result = uenumToIdList(uenum);
444 return QTimeZonePrivate::availableTimeZoneIds(offsetFromUtc);
static bool ucalOffsetsAtTime(UCalendar *m_ucal, qint64 atMSecsSinceEpoch, int *utcOffset, int *dstOffset)
static int ucalDaylightOffset(const QByteArray &id)
static QList< QByteArray > uenumToIdList(UEnumeration *uenum)