From 49063c34d6314c59ec9529550e0639832796372e Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 18 Nov 2019 13:51:01 +0100 Subject: 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 --- .../auto/corelib/time/qdatetime/tst_qdatetime.cpp | 52 ++++++++++++++++------ 1 file changed, 38 insertions(+), 14 deletions(-) (limited to 'tests/auto/corelib/time') diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp index 7f13fd0aa5..c628d2b241 100644 --- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp @@ -150,6 +150,7 @@ private slots: void timeZones() const; void systemTimeZoneChange() const; + void invalid_data() const; void invalid() const; void range() const; @@ -2458,6 +2459,7 @@ void tst_QDateTime::fromStringStringFormat_data() if (southBrazil.isValid()) { QTest::newRow("spring-forward-midnight") << QString("2008-10-19 23:45.678 America/Sao_Paulo") << QString("yyyy-MM-dd mm:ss.zzz t") + // That's in the hour skipped - expect the matching time after the spring-forward, in DST: << QDateTime(QDate(2008, 10, 19), QTime(1, 23, 45, 678), southBrazil); } #endif @@ -3359,6 +3361,9 @@ void tst_QDateTime::timeZones() const QCOMPARE(utc.date(), utcDst.date()); QCOMPARE(utc.time(), utcDst.time()); + // Crash test, QTBUG-80146: + QVERIFY(!nzStd.toTimeZone(QTimeZone()).isValid()); + // Sydney is 2 hours behind New Zealand QTimeZone ausTz = QTimeZone("Australia/Sydney"); QDateTime aus = nzStd.toTimeZone(ausTz); @@ -3528,23 +3533,42 @@ void tst_QDateTime::systemTimeZoneChange() const QCOMPARE(tzDate.toMSecsSinceEpoch(), tzMsecs); } -void tst_QDateTime::invalid() const +void tst_QDateTime::invalid_data() const { - QDateTime invalidDate = QDateTime(QDate(0, 0, 0), QTime(-1, -1, -1)); - QCOMPARE(invalidDate.isValid(), false); - QCOMPARE(invalidDate.timeSpec(), Qt::LocalTime); - - QDateTime utcDate = invalidDate.toUTC(); - QCOMPARE(utcDate.isValid(), false); - QCOMPARE(utcDate.timeSpec(), Qt::UTC); + QTest::addColumn("when"); + QTest::addColumn("spec"); + QTest::addColumn("goodZone"); + QTest::newRow("default") << QDateTime() << Qt::LocalTime << true; - QDateTime offsetDate = invalidDate.toOffsetFromUtc(3600); - QCOMPARE(offsetDate.isValid(), false); - QCOMPARE(offsetDate.timeSpec(), Qt::OffsetFromUTC); + QDateTime invalidDate = QDateTime(QDate(0, 0, 0), QTime(-1, -1, -1)); + QTest::newRow("simple") << invalidDate << Qt::LocalTime << true; + QTest::newRow("UTC") << invalidDate.toUTC() << Qt::UTC << true; + QTest::newRow("offset") + << invalidDate.toOffsetFromUtc(3600) << Qt::OffsetFromUTC << true; + QTest::newRow("CET") + << invalidDate.toTimeZone(QTimeZone("Europe/Oslo")) << Qt::TimeZone << true; + + // Crash tests, QTBUG-80146: + QTest::newRow("nozone+construct") + << QDateTime(QDate(1970, 1, 1), QTime(12, 0), QTimeZone()) << Qt::TimeZone << false; + QTest::newRow("nozone+fromMSecs") + << QDateTime::fromMSecsSinceEpoch(42, QTimeZone()) << Qt::TimeZone << false; + QDateTime valid(QDate(1970, 1, 1), QTime(12, 0), Qt::UTC); + QTest::newRow("tonozone") << valid.toTimeZone(QTimeZone()) << Qt::TimeZone << false; +} - QDateTime tzDate = invalidDate.toTimeZone(QTimeZone("Europe/Oslo")); - QCOMPARE(tzDate.isValid(), false); - QCOMPARE(tzDate.timeSpec(), Qt::TimeZone); +void tst_QDateTime::invalid() const +{ + QFETCH(QDateTime, when); + QFETCH(Qt::TimeSpec, spec); + QFETCH(bool, goodZone); + QVERIFY(!when.isValid()); + QCOMPARE(when.timeSpec(), spec); + QCOMPARE(when.timeZoneAbbreviation(), QString()); + if (!goodZone) + QCOMPARE(when.toMSecsSinceEpoch(), 0); + QVERIFY(!when.isDaylightTime()); + QCOMPARE(when.timeZone().isValid(), goodZone); } void tst_QDateTime::range() const -- cgit v1.2.3 From 653c1aab185fe59a523f16ffe0cf7b9173c2ff68 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 2 Dec 2019 12:55:31 +0100 Subject: Fix handling of trailing space at the end of an ISO date-time If milliseconds were followed by a space, the space was included in the count of "digits" read as the fractional part; since we read (up to) four digits (so that we round correctly if extras are given), a harmless apce could cause scaling down by too large a power of ten. Since QString::toInt() ignores leading space, we were also allowing interior space at the start of the milliseconds, which we should not, so catch that at the same time. Added tests, including one for the rounding that's the reason for reading the extra digit, when present. Fixes: QTBUG-80445 Change-Id: I606b29a94818a101f45c8b59a0f5d1f78893d78f Reviewed-by: Thiago Macieira --- tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'tests/auto/corelib/time') diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp index c628d2b241..029ca0aabd 100644 --- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp @@ -2210,6 +2210,13 @@ void tst_QDateTime::fromStringDateFormat_data() << Qt::TextDate << QDateTime(QDate(2013, 5, 6), QTime(1, 2, 3, 456)); // Test Qt::ISODate format. + QTest::newRow("trailing space") // QTBUG-80445 + << QString("2000-01-02 03:04:05.678 ") + << Qt::ISODate << QDateTime(QDate(2000, 1, 2), QTime(3, 4, 5, 678)); + QTest::newRow("space before millis") + << QString("2000-01-02 03:04:05. 678") << Qt::ISODate << QDateTime(); + + // Normal usage: QTest::newRow("ISO +01:00") << QString::fromLatin1("1987-02-13T13:24:51+01:00") << Qt::ISODate << QDateTime(QDate(1987, 2, 13), QTime(12, 24, 51), Qt::UTC); QTest::newRow("ISO +00:01") << QString::fromLatin1("1987-02-13T13:24:51+00:01") @@ -2233,8 +2240,12 @@ void tst_QDateTime::fromStringDateFormat_data() // No time specified - defaults to Qt::LocalTime. QTest::newRow("ISO data3") << QString::fromLatin1("2002-10-01") << Qt::ISODate << QDateTime(QDate(2002, 10, 1), QTime(0, 0, 0, 0), Qt::LocalTime); + // Excess digits in milliseconds, round correctly: QTest::newRow("ISO") << QString::fromLatin1("2005-06-28T07:57:30.0010000000Z") << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 1), Qt::UTC); + QTest::newRow("ISO rounding") << QString::fromLatin1("2005-06-28T07:57:30.0015Z") + << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 2), Qt::UTC); + // ... and accept comma as separator: QTest::newRow("ISO with comma 1") << QString::fromLatin1("2005-06-28T07:57:30,0040000000Z") << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 4), Qt::UTC); QTest::newRow("ISO with comma 2") << QString::fromLatin1("2005-06-28T07:57:30,0015Z") -- cgit v1.2.3 From 6b5f848ebd007e71f0ae5d81e834f5a2a5765aac Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Tue, 3 Dec 2019 11:17:31 +0100 Subject: Allow lower-case for the T and Z in ISO 8601 date format Cite RFC 3339 as basis for allowing a space in place of the T, too. The RFC mentions that ISO 8601 accepts t and z for T and Z, so test for them case-insensitively. Add a test for this. Change-Id: Iba700c8d74d485df154d27300aab7b1958e1ccef Reviewed-by: Thiago Macieira --- tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tests/auto/corelib/time') diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp index 029ca0aabd..7778542736 100644 --- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp @@ -2237,6 +2237,8 @@ void tst_QDateTime::fromStringDateFormat_data() << Qt::ISODate << QDateTime(QDate(2014, 12, 15), QTime(15, 37, 9), Qt::UTC); QTest::newRow("ISO zzz-3") << QString::fromLatin1("2014-12-15T12:37:09.745-3") << Qt::ISODate << QDateTime(QDate(2014, 12, 15), QTime(15, 37, 9, 745), Qt::UTC); + QTest::newRow("ISO lower-case") << QString::fromLatin1("2005-06-28T07:57:30.002z") + << Qt::ISODate << QDateTime(QDate(2005, 6, 28), QTime(7, 57, 30, 2), Qt::UTC); // No time specified - defaults to Qt::LocalTime. QTest::newRow("ISO data3") << QString::fromLatin1("2002-10-01") << Qt::ISODate << QDateTime(QDate(2002, 10, 1), QTime(0, 0, 0, 0), Qt::LocalTime); -- cgit v1.2.3