From 1ec67b5e266864fb9d37f04e0239277b8e8978e2 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 21 Sep 2020 13:46:02 +0200 Subject: Make more consistent use of m_offsetFromUtc() In QDateTime::toMSecsSinceEpoch(), all non-short date-times know their offsets from UTC, so can use that, saving the TimeZone ones the need to call (costly) zoneMSecsToEpochMSecs() and, when not short, the LocalTime ones the need to call localMSecsToEpochMSecs(). Moved the setting of offset clear of refreshZonedDateTime()'s check that the date and time match the milliseconds being handled, so that a sensible offset value is set even when the date-time comes back invalid due to being in a spring forward. As refreshZonedDateTime() now sets d's m_offsetFromUtc for both specs, it no longer needs QDateTimePrivate::setUtcOffsetByTZ(). In any case, the private time-zone access that provided is equivalent to the difference we already have between msecs and epochMSecs, except in a zone-gap. Add assertions to confirm this in zoneMSecsToEpochMSecs(). This brought to light a problem with whole-day transitions, where QDate::startOfDay() assumed the gap it was hitting would end by end of day. Search backwards from noon tomorrow, instead; likewise, for endOfDay(), search forward from yesterday. Task-number: QTBUG-75585 Change-Id: I6151d1dfccab321605753c98e01152281461bfe2 Reviewed-by: Thiago Macieira --- src/corelib/time/qdatetime.cpp | 41 +++++++++++++++++++++++++---------------- src/corelib/time/qdatetime_p.h | 3 --- 2 files changed, 25 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index c6a41855b0..af77a0ee26 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -874,7 +874,10 @@ QDateTime QDate::startOfDay(const QTimeZone &zone) const // The start of the day must have fallen in a spring-forward's gap; find the spring-forward: if (zone.hasTransitions()) { - QTimeZone::OffsetData tran = zone.previousTransition(QDateTime(*this, QTime(23, 59, 59, 999), zone)); + QTimeZone::OffsetData tran + // There's unlikely to be another transition before noon tomorrow. + // However, the whole of today may have been skipped ! + = zone.previousTransition(QDateTime(addDays(1), QTime(12, 0), zone)); const QDateTime &at = tran.atUtc.toTimeZone(zone); if (at.isValid() && at.date() == *this) return at; @@ -997,7 +1000,10 @@ QDateTime QDate::endOfDay(const QTimeZone &zone) const // The end of the day must have fallen in a spring-forward's gap; find the spring-forward: if (zone.hasTransitions()) { - QTimeZone::OffsetData tran = zone.nextTransition(QDateTime(*this, QTime(0, 0), zone)); + QTimeZone::OffsetData tran + // It's unlikely there's been another transition since yesterday noon. + // However, the whole of today may have been skipped ! + = zone.nextTransition(QDateTime(addDays(-1), QTime(12, 0), zone)); const QDateTime &at = tran.atUtc.toTimeZone(zone); if (at.isValid() && at.date() == *this) return at; @@ -2794,13 +2800,6 @@ static inline bool usesSameOffset(const QDateTimeData &a, const QDateTimeData &b return false; } -#if QT_CONFIG(timezone) -void QDateTimePrivate::setUtcOffsetByTZ(qint64 atMSecsSinceEpoch) -{ - m_offsetFromUtc = m_timeZone.d->offsetFromUtc(atMSecsSinceEpoch); -} -#endif - // Refresh the LocalTime or TimeZone validity and offset static void refreshZonedDateTime(QDateTimeData &d, Qt::TimeSpec spec) { @@ -2829,14 +2828,13 @@ static void refreshZonedDateTime(QDateTimeData &d, Qt::TimeSpec spec) } else if (d->m_timeZone.isValid()) { epochMSecs = QDateTimePrivate::zoneMSecsToEpochMSecs( msecs, d->m_timeZone, extractDaylightStatus(status), &testDate, &testTime); - d->setUtcOffsetByTZ(epochMSecs); #endif // timezone } // else: testDate, testTime haven't been set, so are invalid. + // Cache the offset to use in offsetFromUtc() &c. + offsetFromUtc = (msecs - epochMSecs) / 1000; if (testDate.isValid() && testTime.isValid() && timeToMSecs(testDate, testTime) == msecs) { status |= QDateTimePrivate::ValidDateTime; - // Cache the offset to use in offsetFromUtc() - offsetFromUtc = (msecs - epochMSecs) / 1000; } else { status &= ~QDateTimePrivate::ValidDateTime; } @@ -3155,6 +3153,12 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT Q_ASSERT(zone.isValid()); // Get the effective data from QTimeZone QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs, int(hint)); + Q_ASSERT(zone.d->offsetFromUtc(data.atMSecsSinceEpoch) == data.offsetFromUtc); + Q_ASSERT((zoneMSecs - data.atMSecsSinceEpoch) / 1000 == data.offsetFromUtc + // If zoneMSecs fell in a spring-forward's gap, we get this instead: + || (zoneMSecs - data.atMSecsSinceEpoch) / 1000 == data.standardTimeOffset + // If it fell in a skipped day (Pacific date-line crossings), this happens: + || (data.offsetFromUtc - (zoneMSecs - data.atMSecsSinceEpoch) / 1000) % 86400 == 0); // Docs state any time before 1970-01-01 will *not* have any DST applied // but all affected times afterwards will have DST applied. if (data.atMSecsSinceEpoch < 0) { @@ -3772,20 +3776,25 @@ qint64 QDateTime::toMSecsSinceEpoch() const return getMSecs(d); case Qt::OffsetFromUTC: + Q_ASSERT(!d.isShort()); return d->m_msecs - (d->m_offsetFromUtc * 1000); case Qt::LocalTime: { // recalculate the local timezone auto status = extractDaylightStatus(getStatus(d)); + // If short, use offset saved by refreshZonedDateTime() on creation: + if (!d.isShort()) + return d->m_msecs - d->m_offsetFromUtc * 1000; + // Offset from UTC not recorded: need to recompute. return localMSecsToEpochMSecs(getMSecs(d), &status); } case Qt::TimeZone: + Q_ASSERT(!d.isShort()); #if QT_CONFIG(timezone) - if (d->m_timeZone.isValid()) { - return QDateTimePrivate::zoneMSecsToEpochMSecs(d->m_msecs, d->m_timeZone, - extractDaylightStatus(getStatus(d))); - } + // Use offset refreshZonedDateTime() saved creation: + if (d->m_timeZone.isValid()) + return d->m_msecs - d->m_offsetFromUtc * 1000; #endif return 0; } diff --git a/src/corelib/time/qdatetime_p.h b/src/corelib/time/qdatetime_p.h index c500b023c4..2b690841ee 100644 --- a/src/corelib/time/qdatetime_p.h +++ b/src/corelib/time/qdatetime_p.h @@ -128,9 +128,6 @@ public: static qint64 zoneMSecsToEpochMSecs(qint64 msecs, const QTimeZone &zone, DaylightStatus hint = UnknownDaylightTime, QDate *localDate = nullptr, QTime *localTime = nullptr); - - // Inlined for its one caller in qdatetime.cpp - inline void setUtcOffsetByTZ(qint64 atMSecsSinceEpoch); #endif // timezone }; -- cgit v1.2.3