Check against {und,ov}erflow in more QDateTime methods
QDateTime's range of possible values is wider than anyone generally needs, but let's not do confusing things when someone does overflow it. Change-Id: Ifbaf7a0f02cd3afe7d3d13c829bf0887eba29f7f Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
This commit is contained in:
parent
cb0ecd6b6d
commit
2a6f2fe9ef
@ -3959,7 +3959,15 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs)
|
||||
*/
|
||||
void QDateTime::setSecsSinceEpoch(qint64 secs)
|
||||
{
|
||||
setMSecsSinceEpoch(secs * 1000);
|
||||
qint64 msecs;
|
||||
if (!mul_overflow(secs, std::integral_constant<qint64, 1000>(), &msecs)) {
|
||||
setMSecsSinceEpoch(msecs);
|
||||
} else if (d.isShort()) {
|
||||
d.data.status &= ~QDateTimePrivate::ValidWhenMask;
|
||||
} else {
|
||||
d.detach();
|
||||
d->m_status &= ~QDateTimePrivate::ValidWhenMask;
|
||||
}
|
||||
}
|
||||
|
||||
#if QT_CONFIG(datestring) // depends on, so implies, textdate
|
||||
@ -4208,7 +4216,10 @@ QDateTime QDateTime::addYears(int nyears) const
|
||||
|
||||
QDateTime QDateTime::addSecs(qint64 s) const
|
||||
{
|
||||
return addMSecs(s * 1000);
|
||||
qint64 msecs;
|
||||
if (mul_overflow(s, std::integral_constant<qint64, 1000>(), &msecs))
|
||||
return QDateTime();
|
||||
return addMSecs(msecs);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -4226,15 +4237,31 @@ QDateTime QDateTime::addMSecs(qint64 msecs) const
|
||||
return QDateTime();
|
||||
|
||||
QDateTime dt(*this);
|
||||
auto spec = getSpec(d);
|
||||
if (spec == Qt::LocalTime || spec == Qt::TimeZone) {
|
||||
// Convert to real UTC first in case crosses DST transition
|
||||
dt.setMSecsSinceEpoch(toMSecsSinceEpoch() + msecs);
|
||||
} else {
|
||||
switch (getSpec(d)) {
|
||||
case Qt::LocalTime:
|
||||
case Qt::TimeZone:
|
||||
// Convert to real UTC first in case this crosses a DST transition:
|
||||
if (!add_overflow(toMSecsSinceEpoch(), msecs, &msecs)) {
|
||||
dt.setMSecsSinceEpoch(msecs);
|
||||
} else if (dt.d.isShort()) {
|
||||
dt.d.data.status &= ~QDateTimePrivate::ValidWhenMask;
|
||||
} else {
|
||||
dt.d.detach();
|
||||
dt.d->m_status &= ~QDateTimePrivate::ValidWhenMask;
|
||||
}
|
||||
break;
|
||||
case Qt::UTC:
|
||||
case Qt::OffsetFromUTC:
|
||||
// No need to convert, just add on
|
||||
if (d.isShort()) {
|
||||
if (add_overflow(getMSecs(d), msecs, &msecs)) {
|
||||
if (dt.d.isShort()) {
|
||||
dt.d.data.status &= ~QDateTimePrivate::ValidWhenMask;
|
||||
} else {
|
||||
dt.d.detach();
|
||||
dt.d->m_status &= ~QDateTimePrivate::ValidWhenMask;
|
||||
}
|
||||
} else if (d.isShort()) {
|
||||
// need to check if we need to enlarge first
|
||||
msecs += dt.d.data.msecs;
|
||||
if (msecsCanBeSmall(msecs)) {
|
||||
dt.d.data.msecs = qintptr(msecs);
|
||||
} else {
|
||||
@ -4243,8 +4270,9 @@ QDateTime QDateTime::addMSecs(qint64 msecs) const
|
||||
}
|
||||
} else {
|
||||
dt.d.detach();
|
||||
dt.d->m_msecs += msecs;
|
||||
dt.d->m_msecs = msecs;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return dt;
|
||||
}
|
||||
@ -4289,7 +4317,7 @@ qint64 QDateTime::daysTo(const QDateTime &other) const
|
||||
|
||||
qint64 QDateTime::secsTo(const QDateTime &other) const
|
||||
{
|
||||
return (msecsTo(other) / 1000);
|
||||
return msecsTo(other) / 1000;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -564,6 +564,19 @@ void tst_QDateTime::setSecsSinceEpoch()
|
||||
QCOMPARE(dt1, QDateTime(QDate(1970, 1, 2), QTime(10, 17, 36), Qt::UTC));
|
||||
QCOMPARE(dt1.timeSpec(), Qt::OffsetFromUTC);
|
||||
QCOMPARE(dt1.offsetFromUtc(), 60 * 60);
|
||||
|
||||
// Only testing UTC; see fromSecsSinceEpoch() for fuller test.
|
||||
const qint64 maxSeconds = std::numeric_limits<qint64>::max() / 1000;
|
||||
dt1.setSecsSinceEpoch(maxSeconds);
|
||||
QVERIFY(dt1.isValid());
|
||||
dt1.setSecsSinceEpoch(-maxSeconds);
|
||||
QVERIFY(dt1.isValid());
|
||||
dt1.setSecsSinceEpoch(maxSeconds + 1);
|
||||
QVERIFY(!dt1.isValid());
|
||||
dt1.setSecsSinceEpoch(0);
|
||||
QVERIFY(dt1.isValid());
|
||||
dt1.setSecsSinceEpoch(-maxSeconds - 1);
|
||||
QVERIFY(!dt1.isValid());
|
||||
}
|
||||
|
||||
void tst_QDateTime::setMSecsSinceEpoch_data()
|
||||
@ -754,6 +767,7 @@ void tst_QDateTime::fromMSecsSinceEpoch()
|
||||
|
||||
void tst_QDateTime::fromSecsSinceEpoch()
|
||||
{
|
||||
// Compare setSecsSinceEpoch()
|
||||
const qint64 maxSeconds = std::numeric_limits<qint64>::max() / 1000;
|
||||
const QDateTime early = QDateTime::fromSecsSinceEpoch(-maxSeconds, Qt::UTC);
|
||||
const QDateTime late = QDateTime::fromSecsSinceEpoch(maxSeconds, Qt::UTC);
|
||||
@ -1204,7 +1218,7 @@ void tst_QDateTime::addYears()
|
||||
QCOMPARE(end.offsetFromUtc(), 60 * 60);
|
||||
}
|
||||
|
||||
void tst_QDateTime::addSecs_data()
|
||||
void tst_QDateTime::addMSecs_data()
|
||||
{
|
||||
QTest::addColumn<QDateTime>("dt");
|
||||
QTest::addColumn<qint64>("nsecs");
|
||||
@ -1300,6 +1314,31 @@ void tst_QDateTime::addSecs_data()
|
||||
QTest::newRow("epoch-1s-local")
|
||||
<< QDateTime(QDate(1970, 1, 1), QTime(0, 0)) << qint64(-1)
|
||||
<< QDateTime(QDate(1969, 12, 31), QTime(23, 59, 59));
|
||||
|
||||
// Overflow and Underflow
|
||||
const qint64 maxSeconds = std::numeric_limits<qint64>::max() / 1000;
|
||||
QTest::newRow("after-last")
|
||||
<< QDateTime::fromSecsSinceEpoch(maxSeconds, Qt::UTC) << qint64(1) << QDateTime();
|
||||
QTest::newRow("to-last")
|
||||
<< QDateTime::fromSecsSinceEpoch(maxSeconds - 1, Qt::UTC) << qint64(1)
|
||||
<< QDateTime::fromSecsSinceEpoch(maxSeconds, Qt::UTC);
|
||||
QTest::newRow("before-first")
|
||||
<< QDateTime::fromSecsSinceEpoch(-maxSeconds, Qt::UTC) << qint64(-1) << QDateTime();
|
||||
QTest::newRow("to-first")
|
||||
<< QDateTime::fromSecsSinceEpoch(1 - maxSeconds, Qt::UTC) << qint64(-1)
|
||||
<< QDateTime::fromSecsSinceEpoch(-maxSeconds, Qt::UTC);
|
||||
}
|
||||
|
||||
void tst_QDateTime::addSecs_data()
|
||||
{
|
||||
addMSecs_data();
|
||||
|
||||
const qint64 maxSeconds = std::numeric_limits<qint64>::max() / 1000;
|
||||
// Results would be representable, but the step isn't
|
||||
QTest::newRow("leap-up")
|
||||
<< QDateTime::fromSecsSinceEpoch(-1, Qt::UTC) << 1 + maxSeconds << QDateTime();
|
||||
QTest::newRow("leap-down")
|
||||
<< QDateTime::fromSecsSinceEpoch(1, Qt::UTC) << -1 - maxSeconds << QDateTime();
|
||||
}
|
||||
|
||||
void tst_QDateTime::addSecs()
|
||||
@ -1308,16 +1347,15 @@ void tst_QDateTime::addSecs()
|
||||
QFETCH(const qint64, nsecs);
|
||||
QFETCH(const QDateTime, result);
|
||||
QDateTime test = dt.addSecs(nsecs);
|
||||
QCOMPARE(test, result);
|
||||
QCOMPARE(test.timeSpec(), dt.timeSpec());
|
||||
if (test.timeSpec() == Qt::OffsetFromUTC)
|
||||
QCOMPARE(test.offsetFromUtc(), dt.offsetFromUtc());
|
||||
QCOMPARE(result.addSecs(-nsecs), dt);
|
||||
}
|
||||
|
||||
void tst_QDateTime::addMSecs_data()
|
||||
{
|
||||
addSecs_data();
|
||||
if (!result.isValid()) {
|
||||
QVERIFY(!test.isValid());
|
||||
} else {
|
||||
QCOMPARE(test, result);
|
||||
QCOMPARE(test.timeSpec(), dt.timeSpec());
|
||||
if (test.timeSpec() == Qt::OffsetFromUTC)
|
||||
QCOMPARE(test.offsetFromUtc(), dt.offsetFromUtc());
|
||||
QCOMPARE(result.addSecs(-nsecs), dt);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::addMSecs()
|
||||
@ -1327,11 +1365,15 @@ void tst_QDateTime::addMSecs()
|
||||
QFETCH(const QDateTime, result);
|
||||
|
||||
QDateTime test = dt.addMSecs(qint64(nsecs) * 1000);
|
||||
QCOMPARE(test, result);
|
||||
QCOMPARE(test.timeSpec(), dt.timeSpec());
|
||||
if (test.timeSpec() == Qt::OffsetFromUTC)
|
||||
QCOMPARE(test.offsetFromUtc(), dt.offsetFromUtc());
|
||||
QCOMPARE(result.addMSecs(qint64(-nsecs) * 1000), dt);
|
||||
if (!result.isValid()) {
|
||||
QVERIFY(!test.isValid());
|
||||
} else {
|
||||
QCOMPARE(test, result);
|
||||
QCOMPARE(test.timeSpec(), dt.timeSpec());
|
||||
if (test.timeSpec() == Qt::OffsetFromUTC)
|
||||
QCOMPARE(test.offsetFromUtc(), dt.offsetFromUtc());
|
||||
QCOMPARE(result.addMSecs(qint64(-nsecs) * 1000), dt);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::toTimeSpec_data()
|
||||
@ -1540,7 +1582,7 @@ void tst_QDateTime::secsTo()
|
||||
QFETCH(const qint64, nsecs);
|
||||
QFETCH(const QDateTime, result);
|
||||
|
||||
if (dt.isValid()) {
|
||||
if (result.isValid()) {
|
||||
QCOMPARE(dt.secsTo(result), (qint64)nsecs);
|
||||
QCOMPARE(result.secsTo(dt), (qint64)-nsecs);
|
||||
QVERIFY((dt == result) == (0 == nsecs));
|
||||
@ -1566,7 +1608,7 @@ void tst_QDateTime::msecsTo()
|
||||
QFETCH(const qint64, nsecs);
|
||||
QFETCH(const QDateTime, result);
|
||||
|
||||
if (dt.isValid()) {
|
||||
if (result.isValid()) {
|
||||
QCOMPARE(dt.msecsTo(result), qint64(nsecs) * 1000);
|
||||
QCOMPARE(result.msecsTo(dt), -qint64(nsecs) * 1000);
|
||||
QVERIFY((dt == result) == (0 == (qint64(nsecs) * 1000)));
|
||||
|
Loading…
Reference in New Issue
Block a user