diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2019-11-18 13:51:01 +0100 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2019-12-06 14:44:18 +0100 |
commit | 49063c34d6314c59ec9529550e0639832796372e (patch) | |
tree | 3e784ab3619656a7ad312aa4857ba17d710df46f /src/corelib | |
parent | da94625f38692365217b4647dd721fa5ea14c6ff (diff) |
Fix crash when a date-time has an invalid time-zone
QDateTime is a friend of QTimeZone, so can access its internals; but
it must check the zone is valid before doing so.
Expanded tst_QDateTime::invalid() and made it data-driven to catch the
failure cases.
Commented on a test-case that caught a mistake in my first attempt at
this, and on QDateTimeParser's surprising reliance on a quirk of
QDateTime::toMSecsSinceEpoch()'s behavior.
Fixes: QTBUG-80146
Change-Id: I24856e19ff9bf402152d17d71f83be84e366faad
Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
Diffstat (limited to 'src/corelib')
-rw-r--r-- | src/corelib/time/qdatetime.cpp | 32 | ||||
-rw-r--r-- | src/corelib/time/qdatetimeparser.cpp | 2 |
2 files changed, 23 insertions, 11 deletions
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 9b0bc688c9..8de4f7caf8 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -3410,6 +3410,7 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT DaylightStatus hint, QDate *zoneDate, QTime *zoneTime) { + Q_ASSERT(zone.isValid()); // Get the effective data from QTimeZone QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs, int(hint)); // Docs state any time before 1970-01-01 will *not* have any DST applied @@ -3799,8 +3800,9 @@ QTimeZone QDateTime::timeZone() const case Qt::OffsetFromUTC: return QTimeZone(d->m_offsetFromUtc); case Qt::TimeZone: - Q_ASSERT(d->m_timeZone.isValid()); - return d->m_timeZone; + if (d->m_timeZone.isValid()) + return d->m_timeZone; + break; case Qt::LocalTime: return QTimeZone::systemTimeZone(); } @@ -3884,6 +3886,7 @@ QString QDateTime::timeZoneAbbreviation() const #if !QT_CONFIG(timezone) break; #else + Q_ASSERT(d->m_timeZone.isValid()); return d->m_timeZone.d->abbreviation(toMSecsSinceEpoch()); #endif // timezone case Qt::LocalTime: { @@ -3920,6 +3923,7 @@ bool QDateTime::isDaylightTime() const #if !QT_CONFIG(timezone) break; #else + Q_ASSERT(d->m_timeZone.isValid()); return d->m_timeZone.d->isDaylightTime(toMSecsSinceEpoch()); #endif // timezone case Qt::LocalTime: { @@ -4044,6 +4048,10 @@ void QDateTime::setTimeZone(const QTimeZone &toZone) */ qint64 QDateTime::toMSecsSinceEpoch() const { + // Note: QDateTimeParser relies on this producing a useful result, even when + // !isValid(), at least when the invalidity is a time in a fall-back (that + // we'll have adjusted to lie outside it, but marked invalid because it's + // not what was asked for). Other things may be doing similar. switch (getSpec(d)) { case Qt::UTC: return getMSecs(d); @@ -4058,12 +4066,13 @@ qint64 QDateTime::toMSecsSinceEpoch() const } case Qt::TimeZone: -#if !QT_CONFIG(timezone) - return 0; -#else - return QDateTimePrivate::zoneMSecsToEpochMSecs(d->m_msecs, d->m_timeZone, - extractDaylightStatus(getStatus(d))); +#if QT_CONFIG(timezone) + if (d->m_timeZone.isValid()) { + return QDateTimePrivate::zoneMSecsToEpochMSecs(d->m_msecs, d->m_timeZone, + extractDaylightStatus(getStatus(d))); + } #endif + return 0; } Q_UNREACHABLE(); return 0; @@ -4158,9 +4167,11 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs) case Qt::TimeZone: Q_ASSERT(!d.isShort()); #if QT_CONFIG(timezone) + d.detach(); + if (!d->m_timeZone.isValid()) + break; // Docs state any LocalTime before 1970-01-01 will *not* have any DST applied // but all affected times afterwards will have DST applied. - d.detach(); if (msecs >= 0) { status = mergeDaylightStatus(status, d->m_timeZone.d->isDaylightTime(msecs) @@ -4433,7 +4444,7 @@ static inline void massageAdjustedDateTime(const QDateTimeData &d, QDate *date, QDateTimePrivate::DaylightStatus status = QDateTimePrivate::UnknownDaylightTime; localMSecsToEpochMSecs(timeToMSecs(*date, *time), &status, date, time); #if QT_CONFIG(timezone) - } else if (spec == Qt::TimeZone) { + } else if (spec == Qt::TimeZone && d->m_timeZone.isValid()) { QDateTimePrivate::zoneMSecsToEpochMSecs(timeToMSecs(*date, *time), d->m_timeZone, QDateTimePrivate::UnknownDaylightTime, @@ -5094,7 +5105,8 @@ QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone { QDateTime dt; dt.setTimeZone(timeZone); - dt.setMSecsSinceEpoch(msecs); + if (timeZone.isValid()) + dt.setMSecsSinceEpoch(msecs); return dt; } diff --git a/src/corelib/time/qdatetimeparser.cpp b/src/corelib/time/qdatetimeparser.cpp index 74db2bdfd5..61214b0c7e 100644 --- a/src/corelib/time/qdatetimeparser.cpp +++ b/src/corelib/time/qdatetimeparser.cpp @@ -1363,7 +1363,7 @@ QDateTimeParser::scanString(const QDateTime &defaultValue, // given date (which might be a spring-forward, skipping an hour). if (parserType == QVariant::DateTime && !(isSet & HourSectionMask) && !when.isValid()) { qint64 msecs = when.toMSecsSinceEpoch(); - // Fortunately, that gets a useful answer ... + // Fortunately, that gets a useful answer, even though when is invalid ... const QDateTime replace = #if QT_CONFIG(timezone) tspec == Qt::TimeZone |