From 76c8a988d28ae3173df8e591f5d022f4e3fc7600 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Fri, 26 Feb 2021 14:27:34 +0100 Subject: [PATCH] Rework massageAdjusted and make the most of its DST knowledge Since massageAdjustedDateTime() has to split LocalTime from TimeZone and call {local,zone}MSecsToEpochMSecs() in any case, its call to refreshZonedDateTime(), via checkValidDateTime(), was duplicating that work uselessly. So handle UTC and offset-from-UTC separately with a call to refreshSimple and handle local/zone time by inlining what remains of refreshZonedDateTime() aside from the duplication. At the same time, recent reworking of qt_mktime() make localMSecsToEpochMSecs() handle its DST-hint the same way zoneMSecsToEpochMSecs() has long done so, to take care of the intended handling of DST gaps and repeats. Change-Id: Id6bef7dd0f8983c3e923f4580a62a76aa6fcb810 Reviewed-by: Thiago Macieira Reviewed-by: Andrei Golubev --- src/corelib/time/qdatetime.cpp | 60 ++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 564eeccf6f..c776e3bf69 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -4323,27 +4323,51 @@ static inline void massageAdjustedDateTime(QDateTimeData &d, QDate date, QTime t If we have just adjusted to a day with a DST transition, our given time may lie in the transition hour (either missing or duplicated). For any other time, telling mktime (deep in the bowels of localMSecsToEpochMSecs) - we don't know its DST-ness will produce no adjustment (just a decision as - to its DST-ness); but for a time in spring's missing hour it'll adjust the - time while picking a DST-ness. (Handling of autumn is trickier, as either - DST-ness is valid, without adjusting the time. We might want to propagate - the daylight status in that case, but it's hard to do so without breaking - (far more common) other cases; and it makes little difference, as the two - answers do then differ only in DST-ness.) + or QTimeZone (via zoneMSecsToEpochMSecs) what we know about DST-ness, of + the time we adjusted from, will make no difference; it'll just tell us the + actual DST-ness of the given time. When landing in a transition that + repeats an hour, passing the prior DST-ness - when known - will get us the + indicated side of the duplicate (either local or zone). When landing in a + gap, the zone gives us the other side of the gap but (for now) local time + gets us a platform-dependent side of the gap (e.g. DST-side for glibc). */ - auto spec = getSpec(d); - if (spec == Qt::LocalTime) { - QDateTimePrivate::DaylightStatus status = QDateTimePrivate::UnknownDaylightTime; - QDateTimePrivate::localMSecsToEpochMSecs(timeToMSecs(date, time), &status, &date, &time); -#if QT_CONFIG(timezone) - } else if (spec == Qt::TimeZone && d->m_timeZone.isValid()) { - QDateTimePrivate::DaylightStatus status = QDateTimePrivate::UnknownDaylightTime; - QDateTimePrivate::zoneMSecsToEpochMSecs(timeToMSecs(date, time), - d->m_timeZone, &status, &date, &time); -#endif // timezone + auto status = getStatus(d); + Q_ASSERT((status & QDateTimePrivate::ValidDate) && (status & QDateTimePrivate::ValidTime) + && (status & QDateTimePrivate::ValidDateTime)); + auto spec = extractSpec(status); + if (spec == Qt::OffsetFromUTC || spec == Qt::UTC) { + setDateTime(d, date, time); + refreshSimpleDateTime(d); + return; } + auto dst = extractDaylightStatus(status); + qint64 utc = 0, local = timeToMSecs(date, time); + if (spec == Qt::LocalTime) + utc = QDateTimePrivate::localMSecsToEpochMSecs(local, &dst, &date, &time); +#if QT_CONFIG(timezone) + else if (spec == Qt::TimeZone && d->m_timeZone.isValid()) + utc = QDateTimePrivate::zoneMSecsToEpochMSecs(local, d->m_timeZone, &dst, &date, &time); +#endif // timezone + else + dst = QDateTimePrivate::UnknownDaylightTime; + setDateTime(d, date, time); - checkValidDateTime(d); + status = getStatus(d); // Updated by setDateTime() + const bool ok = (dst != QDateTimePrivate::UnknownDaylightTime + && (status & QDateTimePrivate::ValidDate) + && (status & QDateTimePrivate::ValidTime)); + if (ok) + status = mergeDaylightStatus(status | QDateTimePrivate::ValidDateTime, dst); + else + status &= ~QDateTimePrivate::ValidDateTime; + + if (status & QDateTimePrivate::ShortData) { + d.data.status = status.toInt(); + } else { + d->m_status = status; + if (ok) + d->m_offsetFromUtc = (local - utc) / MSECS_PER_SEC; + } } /*!