diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2018-06-13 21:19:18 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2020-10-09 01:09:42 +0200 |
commit | cb0ecd6b6dfaea372d7973e4d78e661deb441540 (patch) | |
tree | 0c40156d09605f5901136d5727bf71278fd09267 /src/corelib/time | |
parent | 83bff8951a6129cb9e6eaa0286328ae4953f0e8c (diff) |
Check value is in range when setting a QDateTime
Previously, a QDate representing more than about 0.3 gigayears before
or after the epoch would overflow the millisecond count and produce a
"valid" date-time that didn't represent the date and time passed to
its constructor. Changed to detect such overflow and produce an
invalid date-time instead, if it happens.
Corrected some tests that wrongly expected to be able to represent
extreme date-time values with every time-spec. The (milli)seconds
since epoch are from UTC's epoch, so converting to another offset,
zone or local time may give a value outside the actual range. Added
some tests for the actual exact bounds.
Task-number: QTBUG-68855
Change-Id: I866a4974aeb54bba92dbe7eab0a440baf02124f0
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Diffstat (limited to 'src/corelib/time')
-rw-r--r-- | src/corelib/time/qdatetime.cpp | 21 |
1 files changed, 16 insertions, 5 deletions
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 96932ff129..559b3764a1 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -55,6 +55,7 @@ #include "private/qcore_mac_p.h" #endif #include "private/qgregoriancalendar_p.h" +#include "private/qnumeric_p.h" #include "private/qstringiterator_p.h" #if QT_CONFIG(timezone) #include "private/qtimezoneprivate_p.h" @@ -2977,10 +2978,20 @@ static void setDateTime(QDateTimeData &d, QDate date, QTime time) ds = useTime.msecsSinceStartOfDay(); newStatus |= QDateTimePrivate::ValidTime; } + Q_ASSERT(ds < MSECS_PER_DAY); + // Only the later parts of the very first day are representable - its start + // would overflow - so get ds the same side of 0 as days: + if (days < 0 && ds > 0) { + days++; + ds -= MSECS_PER_DAY; + } - // Set msecs serial value - qint64 msecs = (days * MSECS_PER_DAY) + ds; - if (d.isShort()) { + // Check in representable range: + qint64 msecs = 0; + if (mul_overflow(days, std::integral_constant<qint64, MSECS_PER_DAY>(), &msecs) + || add_overflow(msecs, qint64(ds), &msecs)) { + newStatus = QDateTimePrivate::StatusFlags{}; + } else if (d.isShort()) { // let's see if we can keep this short if (msecsCanBeSmall(msecs)) { // yes, we can @@ -3905,8 +3916,8 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs) status = mergeDaylightStatus(status, QDateTimePrivate::StandardTime); d->m_offsetFromUtc = d->m_timeZone.d->standardTimeOffset(msecs); } - msecs = msecs + (d->m_offsetFromUtc * 1000); - status |= QDateTimePrivate::ValidWhenMask; + if (!add_overflow(msecs, qint64(d->m_offsetFromUtc * 1000), &msecs)) + status |= QDateTimePrivate::ValidWhenMask; #endif // timezone break; case Qt::LocalTime: { |