9#include <unicode/ucal.h>
19
20
21
22
27static QByteArray ucalDefaultTimeZoneId()
30 QString result(size, Qt::Uninitialized);
31 UErrorCode status = U_ZERO_ERROR;
34 size = ucal_getDefaultTimeZone(
reinterpret_cast<UChar *>(result.data()), size, &status);
37 if (status == U_BUFFER_OVERFLOW_ERROR) {
39 status = U_ZERO_ERROR;
40 size = ucal_getDefaultTimeZone(
reinterpret_cast<UChar *>(result.data()), size, &status);
44 if (U_SUCCESS(status)) {
46 return std::move(result).toUtf8();
54 int *utcOffset,
int *dstOffset)
60 UErrorCode status = U_ZERO_ERROR;
61 UCalendar *ucal = ucal_clone(m_ucal, &status);
62 if (!U_SUCCESS(status))
66 status = U_ZERO_ERROR;
67 ucal_setMillis(ucal, atMSecsSinceEpoch, &status);
70 if (U_SUCCESS(status)) {
71 status = U_ZERO_ERROR;
73 utc = ucal_get(ucal, UCAL_ZONE_OFFSET, &status) / 1000;
77 if (U_SUCCESS(status)) {
78 status = U_ZERO_ERROR;
80 dst = ucal_get(ucal, UCAL_DST_OFFSET, &status) / 1000;
84 if (U_SUCCESS(status)) {
92#if U_ICU_VERSION_MAJOR_NUM
>= 50
94static QTimeZonePrivate::Data ucalTimeZoneTransition(UCalendar *m_ucal,
95 UTimeZoneTransitionType type,
96 qint64 atMSecsSinceEpoch)
98 QTimeZonePrivate::Data tran;
101 UErrorCode status = U_ZERO_ERROR;
102 UCalendar *ucal = ucal_clone(m_ucal, &status);
103 if (!U_SUCCESS(status))
107 status = U_ZERO_ERROR;
108 ucal_setMillis(ucal, atMSecsSinceEpoch, &status);
112 status = U_ZERO_ERROR;
113 bool ok = ucal_getTimeZoneTransitionDate(ucal, type, &tranMSecs, &status);
116 if (U_SUCCESS(status) && ok && type == UCAL_TZ_TRANSITION_NEXT) {
120 ok = qint64(tranMSecs) > atMSecsSinceEpoch;
124 if (U_SUCCESS(status) && ok) {
125 status = U_ZERO_ERROR;
126 ucal_setMillis(ucal, tranMSecs, &status);
130 if (U_SUCCESS(status) && ok) {
131 status = U_ZERO_ERROR;
132 utc = ucal_get(ucal, UCAL_ZONE_OFFSET, &status) / 1000;
136 if (U_SUCCESS(status) && ok) {
137 status = U_ZERO_ERROR;
138 dst = ucal_get(ucal, UCAL_DST_OFFSET, &status) / 1000;
142 if (!U_SUCCESS(status) || !ok)
144 tran.atMSecsSinceEpoch = tranMSecs;
145 tran.offsetFromUtc = utc + dst;
146 tran.standardTimeOffset = utc;
147 tran.daylightTimeOffset = dst;
149 QTimeZone::TimeType timeType = dst == 0 ? QTimeZone::StandardTime : QTimeZone::DaylightTime;
150 using namespace QtTimeZoneLocale;
151 tran.abbreviation = ucalTimeZoneDisplayName(m_ucal, timeType,
152 QTimeZone::ShortName, QLocale().name().toUtf8());
160 QList<QByteArray> list;
162 UErrorCode status = U_ZERO_ERROR;
164 QByteArray result = uenum_next(uenum, &size, &status);
165 while (U_SUCCESS(status) && !result.isEmpty()) {
167 status = U_ZERO_ERROR;
168 result = uenum_next(uenum, &size, &status);
170 std::sort(list.begin(), list.end());
171 list.erase(
std::unique(list.begin(), list.end()), list.end());
178 UErrorCode status = U_ZERO_ERROR;
179 const QString utf16 = QString::fromLatin1(id);
180 const int32_t dstMSecs = ucal_getDSTSavings(
181 reinterpret_cast<
const UChar *>(utf16.data()), &status);
182 return U_SUCCESS(status) ? dstMSecs / 1000 : 0;
186QIcuTimeZonePrivate::QIcuTimeZonePrivate()
190 init(ucalDefaultTimeZoneId());
194QIcuTimeZonePrivate::QIcuTimeZonePrivate(
const QByteArray &ianaId)
198 if (isTimeZoneIdAvailable(ianaId))
202QIcuTimeZonePrivate::QIcuTimeZonePrivate(
const QIcuTimeZonePrivate &other)
203 : QTimeZonePrivate(other), m_ucal(
nullptr)
206 UErrorCode status = U_ZERO_ERROR;
207 m_ucal = ucal_clone(other.m_ucal, &status);
208 if (!U_SUCCESS(status)) {
214QIcuTimeZonePrivate::~QIcuTimeZonePrivate()
219QIcuTimeZonePrivate *QIcuTimeZonePrivate::clone()
const
221 return new QIcuTimeZonePrivate(*
this);
224void QIcuTimeZonePrivate::init(
const QByteArray &ianaId)
228 const QString id = QString::fromUtf8(m_id);
229 UErrorCode status = U_ZERO_ERROR;
231 m_ucal = ucal_open(
reinterpret_cast<
const UChar *>(id.data()), id.size(),
232 QLocale().name().toUtf8(), UCAL_GREGORIAN, &status);
234 if (!U_SUCCESS(status)) {
240QString QIcuTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
241 QTimeZone::NameType nameType,
242 const QLocale &locale)
const
245 if (nameType == QTimeZone::OffsetName) {
246 int offset = standardTimeOffset(QDateTime::currentMSecsSinceEpoch());
249 if (timeType == QTimeZone::DaylightTime)
250 offset += ucalDaylightOffset(m_id);
256 return isoOffsetFormat(offset);
260 using namespace QtTimeZoneLocale;
261 return ucalTimeZoneDisplayName(m_ucal, timeType, nameType, locale.name().toUtf8());
264int QIcuTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch)
const
268 ucalOffsetsAtTime(m_ucal, atMSecsSinceEpoch, &stdOffset, & dstOffset);
269 return stdOffset + dstOffset;
272int QIcuTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch)
const
276 ucalOffsetsAtTime(m_ucal, atMSecsSinceEpoch, &stdOffset, & dstOffset);
280int QIcuTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch)
const
284 ucalOffsetsAtTime(m_ucal, atMSecsSinceEpoch, &stdOffset, & dstOffset);
288bool QIcuTimeZonePrivate::hasDaylightTime()
const
290 if (ucalDaylightOffset(m_id) != 0)
292#if U_ICU_VERSION_MAJOR_NUM
>= 50
293 for (qint64 when = minMSecs(); when != invalidMSecs(); ) {
294 auto data = nextTransition(when);
295 if (data.daylightTimeOffset && data.daylightTimeOffset != invalidSeconds())
297 when = data.atMSecsSinceEpoch;
303bool QIcuTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch)
const
306 UErrorCode status = U_ZERO_ERROR;
307 UCalendar *ucal = ucal_clone(m_ucal, &status);
308 if (!U_SUCCESS(status))
312 status = U_ZERO_ERROR;
313 ucal_setMillis(ucal, atMSecsSinceEpoch, &status);
316 if (U_SUCCESS(status)) {
317 status = U_ZERO_ERROR;
318 result = ucal_inDaylightTime(ucal, &status);
325QTimeZonePrivate::Data QIcuTimeZonePrivate::data(qint64 forMSecsSinceEpoch)
const
329#if U_ICU_VERSION_MAJOR_NUM
>= 50
330 data = ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE,
332 if (data.atMSecsSinceEpoch == invalidMSecs())
335 ucalOffsetsAtTime(m_ucal, forMSecsSinceEpoch, &data.standardTimeOffset,
336 &data.daylightTimeOffset);
337 data.offsetFromUtc = data.standardTimeOffset + data.daylightTimeOffset;
339 using namespace QtTimeZoneLocale;
340 QTimeZone::TimeType timeType
341 = data.daylightTimeOffset ? QTimeZone::DaylightTime : QTimeZone::StandardTime;
342 data.abbreviation = ucalTimeZoneDisplayName(m_ucal, timeType, QTimeZone::ShortName,
343 QLocale().name().toUtf8());
345 data.atMSecsSinceEpoch = forMSecsSinceEpoch;
349bool QIcuTimeZonePrivate::hasTransitions()
const
352#if U_ICU_VERSION_MAJOR_NUM
>= 50
359QTimeZonePrivate::Data QIcuTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch)
const
362#if U_ICU_VERSION_MAJOR_NUM
>= 50
363 return ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_NEXT, afterMSecsSinceEpoch);
365 Q_UNUSED(afterMSecsSinceEpoch);
370QTimeZonePrivate::Data QIcuTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch)
const
373#if U_ICU_VERSION_MAJOR_NUM
>= 50
374 return ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_PREVIOUS, beforeMSecsSinceEpoch);
376 Q_UNUSED(beforeMSecsSinceEpoch);
381QByteArray QIcuTimeZonePrivate::systemTimeZoneId()
const
385 return ucalDefaultTimeZoneId();
388bool QIcuTimeZonePrivate::isTimeZoneIdAvailable(
const QByteArray &ianaId)
const
390 const QString ianaStr = QString::fromUtf8(ianaId);
391 const UChar *
const name =
reinterpret_cast<
const UChar *>(ianaStr.constData());
394 constexpr size_t size = 64;
399 UErrorCode status = U_ZERO_ERROR;
402 ucal_getCanonicalTimeZoneID(name, ianaStr.size(), buffer, size, &isSys, &status);
407QList<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds()
const
409 UErrorCode status = U_ZERO_ERROR;
410 UEnumeration *uenum = ucal_openTimeZones(&status);
412 QList<QByteArray> result;
413 if (U_SUCCESS(status))
414 result = uenumToIdList(uenum);
419QList<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds(QLocale::Territory territory)
const
421 const QLatin1StringView regionCode = QLocalePrivate::territoryToCode(territory);
422 const QByteArray regionCodeUtf8 = QString(regionCode).toUtf8();
423 UErrorCode status = U_ZERO_ERROR;
424 UEnumeration *uenum = ucal_openCountryTimeZones(regionCodeUtf8.data(), &status);
425 QList<QByteArray> result;
426 if (U_SUCCESS(status))
427 result = uenumToIdList(uenum);
434QList<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds(
int offsetFromUtc)
const
437#if U_ICU_VERSION_MAJOR_NUM
>= 49
|| (U_ICU_VERSION_MAJOR_NUM
== 4
&& U_ICU_VERSION_MINOR_NUM
== 8
)
438 UErrorCode status = U_ZERO_ERROR;
439 UEnumeration *uenum = ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY,
nullptr,
440 &offsetFromUtc, &status);
441 QList<QByteArray> result;
442 if (U_SUCCESS(status))
443 result = uenumToIdList(uenum);
449 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)