Catch overflow in QDateTime::fromSecsSinceEpoch()

It's documented to be undefined if the number of seconds is outside
the allowed range, but it doesn't hurt for that undefined behavior to
happen to be that the result is invalid. Added a simple test.

Change-Id: I20c3f680c7948b3904f213452272133be77e4d62
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Edward Welbourne 2020-04-23 16:03:59 +02:00
parent bd5fe6f385
commit e325bd68fd
2 changed files with 39 additions and 0 deletions

View File

@ -4608,6 +4608,10 @@ QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec, int of
*/
QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec, int offsetSeconds)
{
constexpr qint64 maxSeconds = std::numeric_limits<qint64>::max() / 1000;
constexpr qint64 minSeconds = std::numeric_limits<qint64>::min() / 1000;
if (secs > maxSeconds || secs < minSeconds)
return QDateTime(); // Would {und,ov}erflow
return fromMSecsSinceEpoch(secs * 1000, spec, offsetSeconds);
}
@ -4641,6 +4645,10 @@ QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone
*/
QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, const QTimeZone &timeZone)
{
constexpr qint64 maxSeconds = std::numeric_limits<qint64>::max() / 1000;
constexpr qint64 minSeconds = std::numeric_limits<qint64>::min() / 1000;
if (secs > maxSeconds || secs < minSeconds)
return QDateTime(); // Would {und,ov}erflow
return fromMSecsSinceEpoch(secs * 1000, timeZone);
}
#endif

View File

@ -73,6 +73,7 @@ private slots:
void setSecsSinceEpoch();
void setMSecsSinceEpoch_data();
void setMSecsSinceEpoch();
void fromSecsSinceEpoch();
void fromMSecsSinceEpoch_data();
void fromMSecsSinceEpoch();
void toString_isoDate_data();
@ -790,6 +791,36 @@ void tst_QDateTime::fromMSecsSinceEpoch()
QCOMPARE(dtOffset, reference.addMSecs(msecs));
}
void tst_QDateTime::fromSecsSinceEpoch()
{
const qint64 maxSeconds = std::numeric_limits<qint64>::max() / 1000;
QVERIFY(QDateTime::fromSecsSinceEpoch(maxSeconds).isValid());
QVERIFY(!QDateTime::fromSecsSinceEpoch(maxSeconds + 1).isValid());
QVERIFY(QDateTime::fromSecsSinceEpoch(-maxSeconds).isValid());
QVERIFY(!QDateTime::fromSecsSinceEpoch(-maxSeconds - 1).isValid());
QVERIFY(QDateTime::fromSecsSinceEpoch(maxSeconds, Qt::UTC).isValid());
QVERIFY(!QDateTime::fromSecsSinceEpoch(maxSeconds + 1, Qt::UTC).isValid());
QVERIFY(QDateTime::fromSecsSinceEpoch(-maxSeconds, Qt::UTC).isValid());
QVERIFY(!QDateTime::fromSecsSinceEpoch(-maxSeconds - 1, Qt::UTC).isValid());
// Use an offset for which .toUTC()'s return would flip the validity:
QVERIFY(QDateTime::fromSecsSinceEpoch(maxSeconds, Qt::OffsetFromUTC, 7200).isValid());
QVERIFY(!QDateTime::fromSecsSinceEpoch(maxSeconds + 1, Qt::OffsetFromUTC, -7200).isValid());
QVERIFY(QDateTime::fromSecsSinceEpoch(-maxSeconds, Qt::OffsetFromUTC, -7200).isValid());
QVERIFY(!QDateTime::fromSecsSinceEpoch(-maxSeconds - 1, Qt::OffsetFromUTC, 7200).isValid());
#if QT_CONFIG(timezone)
// As for offset, use zones each side of UTC:
const QTimeZone west("UTC-02:00"), east("UTC+02:00");
QVERIFY(QDateTime::fromSecsSinceEpoch(maxSeconds, east).isValid());
QVERIFY(!QDateTime::fromSecsSinceEpoch(maxSeconds + 1, west).isValid());
QVERIFY(QDateTime::fromSecsSinceEpoch(-maxSeconds, west).isValid());
QVERIFY(!QDateTime::fromSecsSinceEpoch(-maxSeconds - 1, east).isValid());
#endif // timezone
}
void tst_QDateTime::toString_isoDate_data()
{
QTest::addColumn<QDateTime>("datetime");