From 4408d077acb4bf785ff8327fb63898685166735c Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 30 Sep 2024 15:03:20 +0200 Subject: [PATCH] Address problems with date-time parsing near bounds Document the limited range of datetimes that can be parsed from strings and the (awkward) consequence of the range bounds being specified in local time. Add tests for UTC hitting those bounds and QEXPECT_FAIL() the ones that (as the new docs say they may) fail. Technically the fact that the UTC:min one *doesn't* fail is evidence of a bug in the existing date-time parser. Its replacement should support unbounded parsing. Pick to 6.8 required removing a QEXPECT_FAIL() specific (on MS) to the std::chrono::tzdb backend, which isn't present before 6.9. Fixes: QTBUG-129287 Task-number: QTBUG-77948 Pick-to: 6.5 Change-Id: Ic90558f1a06a62f7d0a947e37802370d1ab4e373 Reviewed-by: Thiago Macieira (cherry picked from commit 3e25203d4d5ba428309e972367dbdf99c4e70327) --- src/corelib/time/qdatetime.cpp | 6 +++++- .../corelib/time/qdatetime/tst_qdatetime.cpp | 21 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 89aba9a53de..23c9ddedb25 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -5883,7 +5883,11 @@ QDateTime QDateTime::fromString(QStringView string, Qt::DateFormat format) two digits. Incorrectly specified fields of the \a string will cause an invalid - QDateTime to be returned. + QDateTime to be returned. Only datetimes between the local time start of + year 100 and end of year 9999 are supported. Note that datetimes near the + ends of this range in other time-zones, notably including UTC, may fall + outside the range (and thust be treated as invalid) depending on local time + zone. \note Day and month names as well as AM/PM indicators must be given in English (C locale). If localized month and day names or localized forms of diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp index bad8837ed24..6975a4d9ced 100644 --- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp @@ -3334,6 +3334,17 @@ void tst_QDateTime::fromStringStringFormat_data() QTest::newRow("ASN.1:UTC-end") << u"491231235959Z"_s << u"yyMMddHHmmsst"_s << 1950 << QDate(2049, 12, 31).endOfDay(QTimeZone::UTC).addMSecs(-999); + // Reproducer for QTBUG-129287 + QTest::newRow("ASN.1:max") + << u"99991231235959Z"_s << u"yyyyMMddHHmmsst"_s << 1950 + << QDate(9999, 12, 31).endOfDay(QTimeZone::UTC).addMSecs(-999); + // Can also be reproduced with more legible formats: + QTest::newRow("UTC:max") + << u"9999 Dec 31 23:59:59 +00:00"_s << u"yyyy MMM dd HH:mm:ss t"_s << 9900 + << QDate(9999, 12, 31).endOfDay(QTimeZone::UTC).addMSecs(-999); + QTest::newRow("UTC:min") + << u"0100 Jan 1 00:00:00 +00:00"_s << u"yyyy MMM d HH:mm:ss t"_s << 100 + << QDate(100, 1, 1).startOfDay(QTimeZone::UTC); // fuzzer test QTest::newRow("integer overflow found by fuzzer") @@ -3359,9 +3370,17 @@ void tst_QDateTime::fromStringStringFormat() QFETCH(int, baseYear); QFETCH(QDateTime, expected); - QDateTime dt = QDateTime::fromString(string, format, baseYear); + if (futureTimeType == LocalTimeAheadOfUtc) { + // The new parser should remove the bounds, removing this limitation. + QEXPECT_FAIL("ASN.1:max", "QTBUG-77948: min/max are local times", Abort); + QEXPECT_FAIL("UTC:max", "QTBUG-77948: min/max are local times", Abort); + } + // For contrast, UTC::min gets away with it, which probably means there are + // genuinely out-of-range cases the old parser gets wrong. + QDateTime dt = QDateTime::fromString(string, format, baseYear); QCOMPARE(dt, expected); + if (expected.isValid()) { QCOMPARE(dt.timeSpec(), expected.timeSpec()); QCOMPARE(dt.timeRepresentation(), dt.timeRepresentation());