summaryrefslogtreecommitdiffstats
path: root/src/corelib/time
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2018-06-13 21:19:18 +0200
committerEdward Welbourne <edward.welbourne@qt.io>2020-10-09 01:09:42 +0200
commitcb0ecd6b6dfaea372d7973e4d78e661deb441540 (patch)
tree0c40156d09605f5901136d5727bf71278fd09267 /src/corelib/time
parent83bff8951a6129cb9e6eaa0286328ae4953f0e8c (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.cpp21
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: {