From b44df9937e4b15596b994f8e20822b83ac4bed29 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Tue, 30 Jan 2018 21:02:10 +0100 Subject: Clean-up in QDateTime's parsing of ISODate{,WithMs} Actually check that there's a T where ISO 8601 wants it (instead of just skipping over whatever's there), with something after it; move some declarations later; add some comments; and use the QStringRef API more cleanly (so that it's easier to see what's going on). Simplify a loop condition to avoid the need for a post-loop fix-up. This incidentally prevents an assertion failure (which brought the mess to my attention) parsing a short string as an ISO date-time; if there's a T with nothing after it, we won't try to read at index -1 in the following text. (The actual fail seen had a Z where the T should have been, with nothing after it.) Add tests for invalid ISOdate cases that triggered the assertion. Task-number: QTBUG-66076 Change-Id: Ided9adf62a56d98f144bdf91b40f918e22bd82cd Reviewed-by: Israel Lins Albuquerque Reviewed-by: Thiago Macieira (cherry picked from commit a9c111ed8c30a5a8fec3f02244f0d5a4bd08e931) --- src/corelib/tools/qdatetime.cpp | 23 +++++++++++++++------- .../auto/corelib/tools/qdatetime/tst_qdatetime.cpp | 4 ++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index 9d26207a0f..045ad10755 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -4720,25 +4720,35 @@ QDateTime QDateTime::fromString(const QString& string, Qt::DateFormat format) if (size < 10) return QDateTime(); - QStringRef isoString(&string); - Qt::TimeSpec spec = Qt::LocalTime; - QDate date = QDate::fromString(string.left(10), Qt::ISODate); if (!date.isValid()) return QDateTime(); if (size == 10) return QDateTime(date); - isoString = isoString.right(isoString.length() - 11); + Qt::TimeSpec spec = Qt::LocalTime; + QStringRef isoString(&string); + isoString = isoString.mid(10); // trim "yyyy-MM-dd" + + // Must be left with T and at least one digit for the hour: + if (isoString.size() < 2 + || !(isoString.startsWith(QLatin1Char('T')) + // FIXME: QSql relies on QVariant::toDateTime() accepting a space here: + || isoString.startsWith(QLatin1Char(' ')))) { + return QDateTime(); + } + isoString = isoString.mid(1); // trim 'T' (or space) + int offset = 0; // Check end of string for Time Zone definition, either Z for UTC or [+-]HH:mm for Offset if (isoString.endsWith(QLatin1Char('Z'))) { spec = Qt::UTC; - isoString = isoString.left(isoString.size() - 1); + isoString.chop(1); // trim 'Z' } else { // the loop below is faster but functionally equal to: // const int signIndex = isoString.indexOf(QRegExp(QStringLiteral("[+-]"))); int signIndex = isoString.size() - 1; + Q_ASSERT(signIndex >= 0); bool found = false; { const QChar plus = QLatin1Char('+'); @@ -4746,8 +4756,7 @@ QDateTime QDateTime::fromString(const QString& string, Qt::DateFormat format) do { QChar character(isoString.at(signIndex)); found = character == plus || character == minus; - } while (--signIndex >= 0 && !found); - ++signIndex; + } while (!found && --signIndex >= 0); } if (found) { diff --git a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp index 28ad2d193c..91b5966fa8 100644 --- a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp @@ -2277,6 +2277,10 @@ void tst_QDateTime::fromStringDateFormat_data() QTest::newRow("ISO .99999 of a minute (comma)") << QString::fromLatin1("2012-01-01T08:00,99999") << Qt::ISODate << QDateTime(QDate(2012, 1, 1), QTime(8, 0, 59, 999), Qt::LocalTime); QTest::newRow("ISO empty") << QString::fromLatin1("") << Qt::ISODate << invalidDateTime(); + QTest::newRow("ISO short") << QString::fromLatin1("2017-07-01T") << Qt::ISODate << invalidDateTime(); + QTest::newRow("ISO zoned date") << QString::fromLatin1("2017-07-01Z") << Qt::ISODate << invalidDateTime(); + QTest::newRow("ISO zoned empty time") << QString::fromLatin1("2017-07-01TZ") << Qt::ISODate << invalidDateTime(); + QTest::newRow("ISO mis-punctuated") << QString::fromLatin1("2018/01/30 ") << Qt::ISODate << invalidDateTime(); // Test Qt::RFC2822Date format (RFC 2822). QTest::newRow("RFC 2822 +0100") << QString::fromLatin1("13 Feb 1987 13:24:51 +0100") -- cgit v1.2.3