Fix some out-of-range issues with time-zone support
The MS-Windows back-end neglected to check for overflow when mapping date and time to milliseconds from the epoch. Add the checks for that and take care not to return qint64-min as a transition time - that's the invalidMSecs() value used as a special marker. QTimeZonePrivate::dataForLocalTime() neglected to handle the case of the backend being unable to answer offsetFromUtc() for one of the times requested, which the MS backend might. Change-Id: I6d7ee2cbf9aaf6678abb24a20e18b5cdac7f5a23 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
71e490f0ba
commit
2b26dea51b
@ -395,9 +395,12 @@ QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs,
|
|||||||
|
|
||||||
int early = offsetFromUtc(recent);
|
int early = offsetFromUtc(recent);
|
||||||
int late = offsetFromUtc(imminent);
|
int late = offsetFromUtc(imminent);
|
||||||
if (early == late) { // > 99% of the time
|
if (early == late // > 99% of the time
|
||||||
if (sub_overflow(forLocalMSecs, early * qint64(1000), &utcEpochMSecs))
|
|| late == invalidSeconds()) {
|
||||||
|
if (early == invalidSeconds()
|
||||||
|
|| sub_overflow(forLocalMSecs, early * qint64(1000), &utcEpochMSecs)) {
|
||||||
return invalidData(); // Outside representable range
|
return invalidData(); // Outside representable range
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Close to a DST transition: early > late is near a fall-back,
|
// Close to a DST transition: early > late is near a fall-back,
|
||||||
// early < late is near a spring-forward.
|
// early < late is near a spring-forward.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
|
** Copyright (C) 2021 The Qt Company Ltd.
|
||||||
** Copyright (C) 2013 John Layt <jlayt@kde.org>
|
** Copyright (C) 2013 John Layt <jlayt@kde.org>
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
@ -41,8 +42,8 @@
|
|||||||
#include "qtimezoneprivate_p.h"
|
#include "qtimezoneprivate_p.h"
|
||||||
|
|
||||||
#include "qdatetime.h"
|
#include "qdatetime.h"
|
||||||
|
|
||||||
#include "qdebug.h"
|
#include "qdebug.h"
|
||||||
|
#include <private/qnumeric_p.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@ -95,9 +96,9 @@ namespace {
|
|||||||
QDate msecsToDate(qint64 msecs)
|
QDate msecsToDate(qint64 msecs)
|
||||||
{
|
{
|
||||||
qint64 jd = JULIAN_DAY_FOR_EPOCH;
|
qint64 jd = JULIAN_DAY_FOR_EPOCH;
|
||||||
|
// Corner case: don't use qAbs() because msecs may be numeric_limits<qint64>::min()
|
||||||
if (qAbs(msecs) >= MSECS_PER_DAY) {
|
if (msecs >= MSECS_PER_DAY || msecs <= -MSECS_PER_DAY) {
|
||||||
jd += (msecs / MSECS_PER_DAY);
|
jd += msecs / MSECS_PER_DAY;
|
||||||
msecs %= MSECS_PER_DAY;
|
msecs %= MSECS_PER_DAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,11 +276,12 @@ QDate calculateTransitionLocalDate(const SYSTEMTIME &rule, int year)
|
|||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts a date/time value into msecs
|
// Converts a date/time value into msecs, returns true on overflow:
|
||||||
inline qint64 timeToMSecs(QDate date, QTime time)
|
inline bool timeToMSecs(QDate date, QTime time, qint64 *msecs)
|
||||||
{
|
{
|
||||||
return ((date.toJulianDay() - JULIAN_DAY_FOR_EPOCH) * MSECS_PER_DAY)
|
qint64 dayms = 0;
|
||||||
+ time.msecsSinceStartOfDay();
|
return mul_overflow(date.toJulianDay() - JULIAN_DAY_FOR_EPOCH, qint64(MSECS_PER_DAY), &dayms)
|
||||||
|
|| add_overflow(dayms, qint64(time.msecsSinceStartOfDay()), msecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 calculateTransitionForYear(const SYSTEMTIME &rule, int year, int bias)
|
qint64 calculateTransitionForYear(const SYSTEMTIME &rule, int year, int bias)
|
||||||
@ -289,8 +291,15 @@ qint64 calculateTransitionForYear(const SYSTEMTIME &rule, int year, int bias)
|
|||||||
Q_ASSERT(year);
|
Q_ASSERT(year);
|
||||||
const QDate date = calculateTransitionLocalDate(rule, year);
|
const QDate date = calculateTransitionLocalDate(rule, year);
|
||||||
const QTime time = QTime(rule.wHour, rule.wMinute, rule.wSecond);
|
const QTime time = QTime(rule.wHour, rule.wMinute, rule.wSecond);
|
||||||
if (date.isValid() && time.isValid())
|
qint64 msecs = 0;
|
||||||
return timeToMSecs(date, time) + bias * 60000;
|
using Bound = std::numeric_limits<qint64>;
|
||||||
|
if (date.isValid() && time.isValid() && !timeToMSecs(date, time, &msecs)) {
|
||||||
|
// If bias pushes us outside representable range, clip to range - and
|
||||||
|
// exclude min() from range as it's invalidMSecs():
|
||||||
|
return bias && add_overflow(msecs, qint64(bias) * 60000, &msecs)
|
||||||
|
? (bias < 0 ? Bound::min() + 1 : Bound::max())
|
||||||
|
: (msecs == Bound::min() ? msecs + 1 : msecs);
|
||||||
|
}
|
||||||
return QTimeZonePrivate::invalidMSecs();
|
return QTimeZonePrivate::invalidMSecs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user