Rearrange mapLocalTime() so its millis always have the right sign

Doing a simple division to get seconds, before using rounding-down
division to get days and positive seconds, saves the need to check for
seconds and millis in opposite directions that might cause a needless
overflow when computing the final result.

Change-Id: Ia4f95bb0510eb4f2c1f9131a34d317bd41bbed2a
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Edward Welbourne 2022-08-10 16:29:39 +02:00
parent 854cb55987
commit 0acb56518d

View File

@ -348,11 +348,13 @@ QString localTimeAbbbreviationAt(qint64 local, QDateTimePrivate::DaylightStatus
QDateTimePrivate::ZoneState mapLocalTime(qint64 local, QDateTimePrivate::DaylightStatus dst) QDateTimePrivate::ZoneState mapLocalTime(qint64 local, QDateTimePrivate::DaylightStatus dst)
{ {
const qint64 localDays = QRoundingDown::qDiv(local, MSECS_PER_DAY); qint64 localSecs = local / MSECS_PER_SEC;
qint64 millis = local - localDays * MSECS_PER_DAY; qint64 millis = local - localSecs * MSECS_PER_SEC; // 0 or with same sign as local
Q_ASSERT(0 <= millis && millis < MSECS_PER_DAY); // Definition of QRD::qDiv. const qint64 localDays = QRoundingDown::qDiv(localSecs, SECS_PER_DAY);
struct tm tmLocal = timeToTm(localDays, int(millis / MSECS_PER_SEC), dst); qint64 daySecs = localSecs - localDays * SECS_PER_DAY;
millis %= MSECS_PER_SEC; Q_ASSERT(0 <= daySecs && daySecs < SECS_PER_DAY); // Definition of QRD::qDiv.
struct tm tmLocal = timeToTm(localDays, daySecs, dst);
time_t utcSecs; time_t utcSecs;
if (!callMkTime(&tmLocal, &utcSecs)) if (!callMkTime(&tmLocal, &utcSecs))
return {local}; return {local};
@ -368,22 +370,21 @@ QDateTimePrivate::ZoneState mapLocalTime(qint64 local, QDateTimePrivate::Dayligh
&jd))) { &jd))) {
return {local, offset, dst, false}; return {local, offset, dst, false};
} }
qint64 daySecs = tmSecsWithinDay(tmLocal); daySecs = tmSecsWithinDay(tmLocal);
Q_ASSERT(0 <= daySecs && daySecs < SECS_PER_DAY); Q_ASSERT(0 <= daySecs && daySecs < SECS_PER_DAY);
if (daySecs > 0 && jd < JULIAN_DAY_FOR_EPOCH) { if (daySecs > 0 && jd < JULIAN_DAY_FOR_EPOCH) {
++jd; ++jd;
daySecs -= SECS_PER_DAY; daySecs -= SECS_PER_DAY;
} }
qint64 localSecs;
if (Q_UNLIKELY(daysAndSecondsOverflow(jd, daySecs, &localSecs))) if (Q_UNLIKELY(daysAndSecondsOverflow(jd, daySecs, &localSecs)))
return {local, offset, dst, false}; return {local, offset, dst, false};
offset = localSecs - utcSecs; offset = localSecs - utcSecs;
if (localSecs < 0 && millis > 0) { // The only way localSecs and millis can now have opposite sign is for
++localSecs; // resolution of the local time to have kicked us across the epoch, in which
millis -= MSECS_PER_SEC; // case there's no danger of overflow. So if overflow is in danger of
} // happening, we're already doing the best we can to avoid it.
qint64 revised; qint64 revised;
const bool overflow = secondsAndMillisOverflow(localSecs, millis, &revised); const bool overflow = secondsAndMillisOverflow(localSecs, millis, &revised);
return {overflow ? local : revised, offset, dst, !overflow}; return {overflow ? local : revised, offset, dst, !overflow};