176QString QTimeZonePrivate::displayName(qint64 atMSecsSinceEpoch,
177 QTimeZone::NameType nameType,
178 const QLocale &locale)
const
180 const Data tran = data(atMSecsSinceEpoch);
181 if (tran.atMSecsSinceEpoch != invalidMSecs()) {
182 if (nameType == QTimeZone::OffsetName && isAnglicLocale(locale))
183 return isoOffsetFormat(tran.offsetFromUtc);
184 if (nameType == QTimeZone::ShortName && isDataLocale(locale))
185 return tran.abbreviation;
187 QTimeZone::TimeType timeType
188 = tran.daylightTimeOffset != 0 ? QTimeZone::DaylightTime : QTimeZone::StandardTime;
189#if QT_CONFIG(timezone_locale)
190 return localeName(atMSecsSinceEpoch, tran.offsetFromUtc, timeType, nameType, locale);
192 return displayName(timeType, nameType, locale);
312QDateTimePrivate::ZoneState QTimeZonePrivate::stateAtZoneTime(
313 qint64 forLocalMSecs, QDateTimePrivate::TransitionOptions resolve)
const
315 auto dataToState = [](
const Data &d) {
316 return QDateTimePrivate::ZoneState(d.atMSecsSinceEpoch + d.offsetFromUtc * 1000,
318 d.daylightTimeOffset ? QDateTimePrivate::DaylightTime
319 : QDateTimePrivate::StandardTime);
323
324
325
326
327
328
329
330 std::integral_constant<qint64, 17 * 3600 * 1000> seventeenHoursInMSecs;
331 static_assert(-seventeenHoursInMSecs / 1000 < QTimeZone::MinUtcOffsetSecs
332 && seventeenHoursInMSecs / 1000 > QTimeZone::MaxUtcOffsetSecs);
335 const qint64 recent =
336 qSubOverflow(forLocalMSecs, seventeenHoursInMSecs, &millis) || millis < minMSecs()
337 ? minMSecs() : millis;
339 const qint64 imminent =
340 qAddOverflow(forLocalMSecs, seventeenHoursInMSecs, &millis)
341 ? maxMSecs() : millis;
343 Q_ASSERT(recent < imminent && seventeenHoursInMSecs < imminent - recent + 1);
345 const Data past = data(recent), future = data(imminent);
346 if (future.atMSecsSinceEpoch == invalidMSecs()
347 && past.atMSecsSinceEpoch == invalidMSecs()) {
350 return { forLocalMSecs };
353 if (Q_LIKELY(past.offsetFromUtc == future.offsetFromUtc
354 && past.standardTimeOffset == future.standardTimeOffset
356 && past.abbreviation == future.abbreviation)) {
358 data.atMSecsSinceEpoch = forLocalMSecs - future.offsetFromUtc * 1000;
359 return dataToState(data);
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388 if (hasTransitions()) {
390
391
392
393
394
395
396
397
398
399
404 Q_ASSERT(forLocalMSecs < 0 ||
405 forLocalMSecs - tran.offsetFromUtc * 1000 >= tran.atMSecsSinceEpoch);
407 Data nextTran = nextTransition(tran.atMSecsSinceEpoch);
409
410
411
412
413
414
415
416 while (nextTran.atMSecsSinceEpoch != invalidMSecs()
417 && forLocalMSecs > nextTran.atMSecsSinceEpoch + nextTran.offsetFromUtc * 1000) {
418 Data newTran = nextTransition(nextTran.atMSecsSinceEpoch);
419 if (newTran.atMSecsSinceEpoch == invalidMSecs()
420 || newTran.atMSecsSinceEpoch + newTran.offsetFromUtc * 1000 > imminent) {
427 const qint64 nextStart = nextTran.atMSecsSinceEpoch;
430 if (tran.atMSecsSinceEpoch != invalidMSecs()) {
432 Q_ASSERT(forLocalMSecs < 0
433 || forLocalMSecs - tran.offsetFromUtc * 1000 > tran.atMSecsSinceEpoch);
435 tran.atMSecsSinceEpoch = forLocalMSecs - tran.offsetFromUtc * 1000;
443 if (nextStart == invalidMSecs() && tran.offsetFromUtc == future.offsetFromUtc)
444 return dataToState(tran);
447 if (tran.atMSecsSinceEpoch != invalidMSecs() && nextStart != invalidMSecs()) {
449
450
451
452
453
454
455
456
457
459 nextTran.atMSecsSinceEpoch = forLocalMSecs - nextTran.offsetFromUtc * 1000;
461 bool fallBack =
false;
462 if (nextStart > nextTran.atMSecsSinceEpoch) {
464 if (nextStart > tran.atMSecsSinceEpoch)
465 return dataToState(tran);
467 Q_ASSERT(tran.offsetFromUtc < nextTran.offsetFromUtc);
469 }
else if (nextStart <= tran.atMSecsSinceEpoch) {
471 return dataToState(nextTran);
473 Q_ASSERT(nextTran.offsetFromUtc < tran.offsetFromUtc);
481 = resolve.testFlag(QDateTimePrivate::FlipForReverseDst)
482 && (fallBack ? !tran.daylightTimeOffset && nextTran.daylightTimeOffset
483 : tran.daylightTimeOffset && !nextTran.daylightTimeOffset);
486 if (resolve.testFlag(flipped
487 ? QDateTimePrivate::FoldUseBefore
488 : QDateTimePrivate::FoldUseAfter)) {
489 return dataToState(nextTran);
491 if (resolve.testFlag(flipped
492 ? QDateTimePrivate::FoldUseAfter
493 : QDateTimePrivate::FoldUseBefore)) {
494 return dataToState(tran);
498
499
500
501
502
503 std::swap(tran.atMSecsSinceEpoch, nextTran.atMSecsSinceEpoch);
504 if (resolve.testFlag(flipped
505 ? QDateTimePrivate::GapUseBefore
506 : QDateTimePrivate::GapUseAfter))
507 return dataToState(nextTran);
508 if (resolve.testFlag(flipped
509 ? QDateTimePrivate::GapUseAfter
510 : QDateTimePrivate::GapUseBefore))
511 return dataToState(tran);
514 return {forLocalMSecs};
521 qint64 utcEpochMSecs;
524 int early = past.offsetFromUtc;
525 int late = future.offsetFromUtc;
526 if (early == late || late == invalidSeconds()) {
527 if (early == invalidSeconds()
528 || qSubOverflow(forLocalMSecs, early * qint64(1000), &utcEpochMSecs)) {
529 return {forLocalMSecs};
533 const qint64 forEarly = forLocalMSecs - early * 1000;
534 const qint64 forLate = forLocalMSecs - late * 1000;
537 const bool earlyOk = offsetFromUtc(forEarly) == early;
538 const bool lateOk = offsetFromUtc(forLate) == late;
542 Q_ASSERT(early > late);
544 if (resolve.testFlag(QDateTimePrivate::FoldUseBefore))
545 utcEpochMSecs = forEarly;
546 else if (resolve.testFlag(QDateTimePrivate::FoldUseAfter))
547 utcEpochMSecs = forLate;
549 return {forLocalMSecs};
552 utcEpochMSecs = forEarly;
556 utcEpochMSecs = forLate;
559 Q_ASSERT(late > early);
560 const int dstStep = (late - early) * 1000;
561 if (resolve.testFlag(QDateTimePrivate::GapUseBefore))
562 utcEpochMSecs = forEarly - dstStep;
563 else if (resolve.testFlag(QDateTimePrivate::GapUseAfter))
564 utcEpochMSecs = forLate + dstStep;
566 return {forLocalMSecs};
570 return dataToState(data(utcEpochMSecs));
872findUtcOffsetPrefix(QStringView text,
const QLocale &locale)
876 qsizetype signLen = 0;
878 auto signStart = [&signLen, &sign, locale](QStringView str) {
879 QString signStr = locale.negativeSign();
880 if (str.startsWith(signStr)) {
882 signLen = signStr.size();
886 if (str.startsWith(u'\u2212')) {
891 signStr = locale.positiveSign();
892 if (str.startsWith(signStr)) {
894 signLen = signStr.size();
900 if (!((text.startsWith(u"UTC") || text.startsWith(u"GMT")) && signStart(text.sliced(3))))
903 QStringView offset = text.sliced(3 + signLen);
904 QStringIterator iter(offset);
905 qsizetype hourEnd = 0, hmMid = 0, minEnd = 0;
908 while (iter.hasNext()) {
910 if (!QChar::isDigit(ch))
916 hourEnd = std::exchange(hmMid, std::exchange(minEnd, iter.index()));
918 if (digits < 1 || digits > 4)
921 QStringView hourStr, minStr;
922 if (digits < 3 && iter.hasNext() && QChar::isPunct(ch)) {
924 hmMid = iter.index();
926 while (mindig < 2 && iter.hasNext() && QChar::isDigit(iter.next())) {
928 minEnd = iter.index();
931 minStr = offset.first(minEnd).sliced(hmMid);
935 minStr = offset.first(minEnd).sliced(hourEnd);
937 hourStr = offset.first(hourEnd);
940 uint hour = 0, minute = 0;
941 if (!hourStr.isEmpty())
942 hour = locale.toUInt(hourStr, &ok);
943 if (ok && !minStr.isEmpty()) {
944 minute = locale.toUInt(minStr, &ok);
946 if ((!ok || minute >= 60) && minEnd > hourEnd + minStr.size()) {
955 constexpr int MaxOffsetSeconds
956 = qMax(QTimeZone::MaxUtcOffsetSecs, -QTimeZone::MinUtcOffsetSecs);
957 if (!ok || (hour * 60 + minute) * 60 > MaxOffsetSeconds)
967 std::snprintf(buffer,
sizeof(buffer),
"UTC%c%02u:%02u", sign, hour, minute);
969 std::snprintf(buffer,
sizeof(buffer),
"UTC%c%02u", sign, hour);
971 return { QByteArray(buffer, qstrnlen(buffer,
sizeof(buffer))),
972 3 + signLen + minEnd,
973 QTimeZone::GenericTime };
1240QUtcTimeZonePrivate::QUtcTimeZonePrivate(qint32 offsetSeconds)
1245 const auto data = std::lower_bound(std::begin(utcDataTable), std::end(utcDataTable),
1246 offsetSeconds, atLowerUtcOffset);
1247 if (data != std::end(utcDataTable) && data->offsetFromUtc == offsetSeconds) {
1248 QByteArrayView ianaId = data->id();
1249 qsizetype cut = ianaId.indexOf(
' ');
1250 QByteArrayView cutId = (cut < 0 ? ianaId : ianaId.first(cut));
1251 if (cutId == utcQByteArray()) {
1253 id = utcQByteArray();
1254 name = utcQString();
1257 id = cutId.toByteArray();
1258 name = QString::fromUtf8(id);
1260 Q_ASSERT(!name.isEmpty());
1262 name = isoOffsetFormat(offsetSeconds, QTimeZone::OffsetName);
1265 init(id, offsetSeconds, name, name, QLocale::AnyTerritory, name);
1347QString QUtcTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
1348 QTimeZone::NameType nameType,
1349 const QLocale &locale)
const
1351#if QT_CONFIG(timezone_locale)
1358 m_offsetFromUtc != 0 ? QString() :
1360 QTimeZonePrivate::displayName(timeType, nameType, locale);
1367 const auto matchesFallback = [](
int offset, QStringView name) {
1369 int seconds = offset % 60;
1370 int rounded = offset
1371 + (seconds > 30 || (seconds == 30 && (offset / 60) % 2)
1373 : (seconds < -30 || (seconds == -30 && (offset / 60) % 2)
1376 const QString avoid = isoOffsetFormat(rounded);
1379 Q_ASSERT(avoid.startsWith(
"UTC"_L1));
1380 Q_ASSERT(avoid.size() == 9);
1383 if (!(name.startsWith(
"GMT"_L1) || name.startsWith(
"UTC"_L1)) || name.size() < 5)
1386 QStringView tail{avoid};
1387 tail = tail.sliced(3);
1388 if (name.sliced(3) == tail)
1390 while (tail.endsWith(
":00"_L1))
1391 tail = tail.chopped(3);
1392 while (name.endsWith(
":00"_L1))
1393 name = name.chopped(3);
1397 const QChar sign = name[3] == u'\u2212' ? u'-' : name[3];
1399 return sign == tail[0] && tail.sliced(tail[1] == u'0' ? 2 : 1) == name.sliced(4);
1401 if (!name.isEmpty() && (m_name.isEmpty() || !matchesFallback(m_offsetFromUtc, name)))
1407 if (nameType == QTimeZone::ShortName)
1408 return m_abbreviation;
1409 if (nameType == QTimeZone::OffsetName)
1410 return isoOffsetFormat(m_offsetFromUtc);
1490QList<QByteArray> QUtcTimeZonePrivate::availableTimeZoneIds(qint32 offsetSeconds)
const
1494 QList<QByteArray> result;
1495 const auto data = std::lower_bound(std::begin(utcDataTable), std::end(utcDataTable),
1496 offsetSeconds, atLowerUtcOffset);
1497 if (data != std::end(utcDataTable) && data->offsetFromUtc == offsetSeconds) {
1498 QByteArrayView id = data->id();
1500 while ((cut = id.indexOf(
' ')) >= 0) {
1501 result << id.first(cut).toByteArray();
1502 id = id.sliced(cut + 1);
1504 result << id.toByteArray();
1509 QByteArray isoName = isoOffsetFormat(offsetSeconds, QTimeZone::ShortName).toUtf8();
1510 if (offsetFromUtcString(isoName) == qint64(offsetSeconds) && !result.contains(isoName))
1513 std::sort(result.begin(), result.end());