172QString QTimeZonePrivate::displayName(qint64 atMSecsSinceEpoch,
173 QTimeZone::NameType nameType,
174 const QLocale &locale)
const
176 const Data tran = data(atMSecsSinceEpoch);
177 if (tran.atMSecsSinceEpoch != invalidMSecs()) {
178 if (nameType == QTimeZone::OffsetName && isAnglicLocale(locale))
179 return isoOffsetFormat(tran.offsetFromUtc);
180 if (nameType == QTimeZone::ShortName && isDataLocale(locale))
181 return tran.abbreviation;
183 QTimeZone::TimeType timeType
184 = tran.daylightTimeOffset != 0 ? QTimeZone::DaylightTime : QTimeZone::StandardTime;
185#if QT_CONFIG(timezone_locale)
186 return localeName(atMSecsSinceEpoch, tran.offsetFromUtc, timeType, nameType, locale);
188 return displayName(timeType, nameType, locale);
308QDateTimePrivate::ZoneState QTimeZonePrivate::stateAtZoneTime(
309 qint64 forLocalMSecs, QDateTimePrivate::TransitionOptions resolve)
const
311 auto dataToState = [](
const Data &d) {
312 return QDateTimePrivate::ZoneState(d.atMSecsSinceEpoch + d.offsetFromUtc * 1000,
314 d.daylightTimeOffset ? QDateTimePrivate::DaylightTime
315 : QDateTimePrivate::StandardTime);
319
320
321
322
323
324
325
326 std::integral_constant<qint64, 17 * 3600 * 1000> seventeenHoursInMSecs;
327 static_assert(-seventeenHoursInMSecs / 1000 < QTimeZone::MinUtcOffsetSecs
328 && seventeenHoursInMSecs / 1000 > QTimeZone::MaxUtcOffsetSecs);
331 const qint64 recent =
332 qSubOverflow(forLocalMSecs, seventeenHoursInMSecs, &millis) || millis < minMSecs()
333 ? minMSecs() : millis;
335 const qint64 imminent =
336 qAddOverflow(forLocalMSecs, seventeenHoursInMSecs, &millis)
337 ? maxMSecs() : millis;
339 Q_ASSERT(recent < imminent && seventeenHoursInMSecs < imminent - recent + 1);
341 const Data past = data(recent), future = data(imminent);
342 if (future.atMSecsSinceEpoch == invalidMSecs()
343 && past.atMSecsSinceEpoch == invalidMSecs()) {
346 return { forLocalMSecs };
349 if (Q_LIKELY(past.offsetFromUtc == future.offsetFromUtc
350 && past.standardTimeOffset == future.standardTimeOffset
352 && past.abbreviation == future.abbreviation)) {
354 data.atMSecsSinceEpoch = forLocalMSecs - future.offsetFromUtc * 1000;
355 return dataToState(data);
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384 if (hasTransitions()) {
386
387
388
389
390
391
392
393
394
395
400 Q_ASSERT(forLocalMSecs < 0 ||
401 forLocalMSecs - tran.offsetFromUtc * 1000 >= tran.atMSecsSinceEpoch);
403 Data nextTran = nextTransition(tran.atMSecsSinceEpoch);
405
406
407
408
409
410
411
412 while (nextTran.atMSecsSinceEpoch != invalidMSecs()
413 && forLocalMSecs > nextTran.atMSecsSinceEpoch + nextTran.offsetFromUtc * 1000) {
414 Data newTran = nextTransition(nextTran.atMSecsSinceEpoch);
415 if (newTran.atMSecsSinceEpoch == invalidMSecs()
416 || newTran.atMSecsSinceEpoch + newTran.offsetFromUtc * 1000 > imminent) {
423 const qint64 nextStart = nextTran.atMSecsSinceEpoch;
426 if (tran.atMSecsSinceEpoch != invalidMSecs()) {
428 Q_ASSERT(forLocalMSecs < 0
429 || forLocalMSecs - tran.offsetFromUtc * 1000 > tran.atMSecsSinceEpoch);
431 tran.atMSecsSinceEpoch = forLocalMSecs - tran.offsetFromUtc * 1000;
439 if (nextStart == invalidMSecs() && tran.offsetFromUtc == future.offsetFromUtc)
440 return dataToState(tran);
443 if (tran.atMSecsSinceEpoch != invalidMSecs() && nextStart != invalidMSecs()) {
445
446
447
448
449
450
451
452
453
455 nextTran.atMSecsSinceEpoch = forLocalMSecs - nextTran.offsetFromUtc * 1000;
457 bool fallBack =
false;
458 if (nextStart > nextTran.atMSecsSinceEpoch) {
460 if (nextStart > tran.atMSecsSinceEpoch)
461 return dataToState(tran);
463 Q_ASSERT(tran.offsetFromUtc < nextTran.offsetFromUtc);
465 }
else if (nextStart <= tran.atMSecsSinceEpoch) {
467 return dataToState(nextTran);
469 Q_ASSERT(nextTran.offsetFromUtc < tran.offsetFromUtc);
477 = resolve.testFlag(QDateTimePrivate::FlipForReverseDst)
478 && (fallBack ? !tran.daylightTimeOffset && nextTran.daylightTimeOffset
479 : tran.daylightTimeOffset && !nextTran.daylightTimeOffset);
482 if (resolve.testFlag(flipped
483 ? QDateTimePrivate::FoldUseBefore
484 : QDateTimePrivate::FoldUseAfter)) {
485 return dataToState(nextTran);
487 if (resolve.testFlag(flipped
488 ? QDateTimePrivate::FoldUseAfter
489 : QDateTimePrivate::FoldUseBefore)) {
490 return dataToState(tran);
494
495
496
497
498
499 std::swap(tran.atMSecsSinceEpoch, nextTran.atMSecsSinceEpoch);
500 if (resolve.testFlag(flipped
501 ? QDateTimePrivate::GapUseBefore
502 : QDateTimePrivate::GapUseAfter))
503 return dataToState(nextTran);
504 if (resolve.testFlag(flipped
505 ? QDateTimePrivate::GapUseAfter
506 : QDateTimePrivate::GapUseBefore))
507 return dataToState(tran);
510 return {forLocalMSecs};
517 qint64 utcEpochMSecs;
520 int early = past.offsetFromUtc;
521 int late = future.offsetFromUtc;
522 if (early == late || late == invalidSeconds()) {
523 if (early == invalidSeconds()
524 || qSubOverflow(forLocalMSecs, early * qint64(1000), &utcEpochMSecs)) {
525 return {forLocalMSecs};
529 const qint64 forEarly = forLocalMSecs - early * 1000;
530 const qint64 forLate = forLocalMSecs - late * 1000;
533 const bool earlyOk = offsetFromUtc(forEarly) == early;
534 const bool lateOk = offsetFromUtc(forLate) == late;
538 Q_ASSERT(early > late);
540 if (resolve.testFlag(QDateTimePrivate::FoldUseBefore))
541 utcEpochMSecs = forEarly;
542 else if (resolve.testFlag(QDateTimePrivate::FoldUseAfter))
543 utcEpochMSecs = forLate;
545 return {forLocalMSecs};
548 utcEpochMSecs = forEarly;
552 utcEpochMSecs = forLate;
555 Q_ASSERT(late > early);
556 const int dstStep = (late - early) * 1000;
557 if (resolve.testFlag(QDateTimePrivate::GapUseBefore))
558 utcEpochMSecs = forEarly - dstStep;
559 else if (resolve.testFlag(QDateTimePrivate::GapUseAfter))
560 utcEpochMSecs = forLate + dstStep;
562 return {forLocalMSecs};
566 return dataToState(data(utcEpochMSecs));
806findUtcOffsetPrefix(QStringView text,
const QLocale &locale)
810 qsizetype signLen = 0;
812 auto signStart = [&signLen, &sign, locale](QStringView str) {
813 QString signStr = locale.negativeSign();
814 if (str.startsWith(signStr)) {
816 signLen = signStr.size();
820 if (str.startsWith(u'\u2212')) {
825 signStr = locale.positiveSign();
826 if (str.startsWith(signStr)) {
828 signLen = signStr.size();
834 if (!((text.startsWith(u"UTC") || text.startsWith(u"GMT")) && signStart(text.sliced(3))))
837 QStringView offset = text.sliced(3 + signLen);
838 QStringIterator iter(offset);
839 qsizetype hourEnd = 0, hmMid = 0, minEnd = 0;
842 while (iter.hasNext()) {
844 if (!QChar::isDigit(ch))
850 hourEnd = std::exchange(hmMid, std::exchange(minEnd, iter.index()));
852 if (digits < 1 || digits > 4)
855 QStringView hourStr, minStr;
856 if (digits < 3 && iter.hasNext() && QChar::isPunct(ch)) {
858 hmMid = iter.index();
860 while (mindig < 2 && iter.hasNext() && QChar::isDigit(iter.next())) {
862 minEnd = iter.index();
865 minStr = offset.first(minEnd).sliced(hmMid);
869 minStr = offset.first(minEnd).sliced(hourEnd);
871 hourStr = offset.first(hourEnd);
874 uint hour = 0, minute = 0;
875 if (!hourStr.isEmpty())
876 hour = locale.toUInt(hourStr, &ok);
877 if (ok && !minStr.isEmpty()) {
878 minute = locale.toUInt(minStr, &ok);
880 if ((!ok || minute >= 60) && minEnd > hourEnd + minStr.size()) {
889 constexpr int MaxOffsetSeconds
890 = qMax(QTimeZone::MaxUtcOffsetSecs, -QTimeZone::MinUtcOffsetSecs);
891 if (!ok || (hour * 60 + minute) * 60 > MaxOffsetSeconds)
901 std::snprintf(buffer,
sizeof(buffer),
"UTC%c%02u:%02u", sign, hour, minute);
903 std::snprintf(buffer,
sizeof(buffer),
"UTC%c%02u", sign, hour);
905 return { QByteArray(buffer, qstrnlen(buffer,
sizeof(buffer))),
906 3 + signLen + minEnd,
907 QTimeZone::GenericTime };
1164QUtcTimeZonePrivate::QUtcTimeZonePrivate(qint32 offsetSeconds)
1169 const auto data = std::lower_bound(std::begin(utcDataTable), std::end(utcDataTable),
1170 offsetSeconds, atLowerUtcOffset);
1171 if (data != std::end(utcDataTable) && data->offsetFromUtc == offsetSeconds) {
1172 QByteArrayView ianaId = data->id();
1173 qsizetype cut = ianaId.indexOf(
' ');
1174 QByteArrayView cutId = (cut < 0 ? ianaId : ianaId.first(cut));
1175 if (cutId == utcQByteArray()) {
1177 id = utcQByteArray();
1178 name = utcQString();
1181 id = cutId.toByteArray();
1182 name = QString::fromUtf8(id);
1184 Q_ASSERT(!name.isEmpty());
1186 name = isoOffsetFormat(offsetSeconds, QTimeZone::ShortName);
1189 init(id, offsetSeconds, name, name, QLocale::AnyTerritory, name);
1271QString QUtcTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
1272 QTimeZone::NameType nameType,
1273 const QLocale &locale)
const
1275#if QT_CONFIG(timezone_locale)
1276 QString name = QTimeZonePrivate::displayName(timeType, nameType, locale);
1282 const auto matchesFallback = [](
int offset, QStringView name) {
1284 int seconds = offset % 60;
1285 int rounded = offset
1286 + (seconds > 30 || (seconds == 30 && (offset / 60) % 2)
1288 : (seconds < -30 || (seconds == -30 && (offset / 60) % 2)
1291 const QString avoid = isoOffsetFormat(rounded);
1294 Q_ASSERT(avoid.startsWith(
"UTC"_L1));
1295 Q_ASSERT(avoid.size() == 9);
1298 if (!(name.startsWith(
"GMT"_L1) || name.startsWith(
"UTC"_L1)) || name.size() < 5)
1301 QStringView tail{avoid};
1302 tail = tail.sliced(3);
1303 if (tail.endsWith(
":00"_L1))
1304 tail = tail.chopped(3);
1305 if (name.sliced(3) == tail)
1308 const QChar sign = name[3] == u'\u2212' ? u'-' : name[3];
1310 return sign == tail[0] && tail.sliced(tail[1] == u'0' ? 2 : 1) == name.sliced(4);
1312 if (!name.isEmpty() && (m_name.isEmpty() || !matchesFallback(m_offsetFromUtc, name)))
1318 if (nameType == QTimeZone::ShortName)
1319 return m_abbreviation;
1320 if (nameType == QTimeZone::OffsetName)
1321 return isoOffsetFormat(m_offsetFromUtc);
1390QList<QByteArray> QUtcTimeZonePrivate::availableTimeZoneIds(qint32 offsetSeconds)
const
1394 QList<QByteArray> result;
1395 const auto data = std::lower_bound(std::begin(utcDataTable), std::end(utcDataTable),
1396 offsetSeconds, atLowerUtcOffset);
1397 if (data != std::end(utcDataTable) && data->offsetFromUtc == offsetSeconds) {
1398 QByteArrayView id = data->id();
1400 while ((cut = id.indexOf(
' ')) >= 0) {
1401 result << id.first(cut).toByteArray();
1402 id = id.sliced(cut + 1);
1404 result << id.toByteArray();
1409 QByteArray isoName = isoOffsetFormat(offsetSeconds, QTimeZone::ShortName).toUtf8();
1410 if (offsetFromUtcString(isoName) == qint64(offsetSeconds) && !result.contains(isoName))
1413 std::sort(result.begin(), result.end());