From 74eddd5bf0672db4edd190aefd813bcf7eb26df4 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 1 Mar 2021 12:08:17 +0100 Subject: [PATCH] Rework setMSecsSinceEpoch() to avoid local->UTC conversions The function takes a UTC time, which it converts to local time when needed, but its (two) calls to refresh a local time instance were doing the (more expensive) reverse conversion, which we don't need (because we knew UTC to start with) and, in this case, it can't land on an invalid time, so we don't need to cope with that. Change-Id: I49b42bfa9f6a5fde12810f5a0da9ff4466ca86a4 Reviewed-by: Qt CI Bot Reviewed-by: Thiago Macieira Reviewed-by: Andrei Golubev --- src/corelib/time/qdatetime.cpp | 87 ++++++++++++++++------------------ 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 94ea0112d4..b84f450097 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -4005,63 +4005,60 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs) { auto status = getStatus(d); const auto spec = extractSpec(status); + Q_ASSERT(spec == Qt::UTC || spec == Qt::LocalTime || !d.isShort()); + qint64 local = msecs; + int offsetFromUtc = 0; status &= ~QDateTimePrivate::ValidityMask; - switch (spec) { - case Qt::UTC: - status |= QDateTimePrivate::ValidWhenMask; - break; - case Qt::OffsetFromUTC: - if (!add_overflow(msecs, d->m_offsetFromUtc * MSECS_PER_SEC, &msecs)) + if (spec == Qt::UTC || spec == Qt::OffsetFromUTC) { + if (spec == Qt::OffsetFromUTC) + offsetFromUtc = d->m_offsetFromUtc; + if (!offsetFromUtc || !add_overflow(msecs, offsetFromUtc * MSECS_PER_SEC, &local)) status |= QDateTimePrivate::ValidWhenMask; - break; - case Qt::TimeZone: - Q_ASSERT(!d.isShort()); -#if QT_CONFIG(timezone) - d.detach(); - if (!d->m_timeZone.isValid()) - break; - status = mergeDaylightStatus(status, - d->m_timeZone.d->isDaylightTime(msecs) - ? QDateTimePrivate::DaylightTime - : QDateTimePrivate::StandardTime); - d->m_offsetFromUtc = d->m_timeZone.d->offsetFromUtc(msecs); - // NB: cast to qint64 here is important to make sure a matching - // add_overflow is found, GCC 7.5.0 fails without this cast - if (!add_overflow(msecs, qint64(d->m_offsetFromUtc * MSECS_PER_SEC), &msecs)) - status |= QDateTimePrivate::ValidWhenMask; -#endif // timezone - break; - case Qt::LocalTime: { - QDate dt; - QTime tm; - QDateTimePrivate::DaylightStatus dstStatus; - if (QDateTimePrivate::epochMSecsToLocalTime(msecs, &dt, &tm, &dstStatus)) { - setDateTime(d, dt, tm); + } else { + auto dst = extractDaylightStatus(status); + if (spec == Qt::LocalTime) { + QDate dt; + QTime tm; + if (QDateTimePrivate::epochMSecsToLocalTime(msecs, &dt, &tm, &dst)) + setDateTime(d, dt, tm); status = getStatus(d); + if ((status & QDateTimePrivate::ValidDate) && (status & QDateTimePrivate::ValidTime)) { + local = getMSecs(d); + offsetFromUtc = (local - msecs) / MSECS_PER_SEC; + status |= QDateTimePrivate::ValidWhenMask; + } +#if QT_CONFIG(timezone) + } else if (spec == Qt::TimeZone && (d.detach(), d->m_timeZone.isValid())) { + const auto data = d->m_timeZone.d->data(msecs); + if (data.offsetFromUtc != QTimeZonePrivate::invalidSeconds()) { + dst = data.daylightTimeOffset + ? QDateTimePrivate::DaylightTime + : QDateTimePrivate::StandardTime; + offsetFromUtc = data.offsetFromUtc; + if (!offsetFromUtc + // NB: cast to qint64 here is important to make sure a matching + // add_overflow is found, GCC 7.5.0 fails without this cast + || !add_overflow(msecs, qint64(offsetFromUtc * MSECS_PER_SEC), &local)) { + status |= QDateTimePrivate::ValidWhenMask; + } + } +#endif // timezone } - if ((status & QDateTimePrivate::ValidDate) && (status & QDateTimePrivate::ValidTime)) { - refreshZonedDateTime(d, spec); // FIXME: we do this again, below - msecs = getMSecs(d); - status = mergeDaylightStatus(getStatus(d), dstStatus); - } - break; - } + status = mergeDaylightStatus(status, dst); } + Q_ASSERT(!(status & QDateTimePrivate::ValidDateTime) + || (offsetFromUtc >= -SECS_PER_DAY && offsetFromUtc <= SECS_PER_DAY)); - if (msecsCanBeSmall(msecs) && d.isShort()) { + if (msecsCanBeSmall(local) && d.isShort()) { // we can keep short - d.data.msecs = qintptr(msecs); + d.data.msecs = qintptr(local); d.data.status = status.toInt(); } else { d.detach(); d->m_status = status & ~QDateTimePrivate::ShortData; - d->m_msecs = msecs; - } - - if (spec == Qt::LocalTime || spec == Qt::TimeZone) { - refreshZonedDateTime(d, spec); - Q_ASSERT((d.isShort() ? d.data.msecs : d->m_msecs) == msecs); + d->m_msecs = local; + d->m_offsetFromUtc = offsetFromUtc; } }