diff --git a/src/corelib/time/qdatetimeparser.cpp b/src/corelib/time/qdatetimeparser.cpp index 82389613ee7..79f055c8083 100644 --- a/src/corelib/time/qdatetimeparser.cpp +++ b/src/corelib/time/qdatetimeparser.cpp @@ -1281,18 +1281,14 @@ QDateTimeParser::scanString(const QDateTime &defaultValue, bool fixup) const if (isUtc || isUtcOffset) { timeZone = QTimeZone::fromSecondsAheadOfUtc(sect.value); - } else { #if QT_CONFIG(timezone) + } else if (startsWithLocalTimeZone(zoneName, usedDateTime) != sect.used) { QTimeZone namedZone = QTimeZone(zoneName.toLatin1()); - if (namedZone.isValid()) { - timeZone = namedZone; - } else { - Q_ASSERT(startsWithLocalTimeZone(zoneName, usedDateTime)); - timeZone = QTimeZone::LocalTime; - } -#else - timeZone = QTimeZone::LocalTime; + Q_ASSERT(namedZone.isValid()); + timeZone = namedZone; #endif + } else { + timeZone = QTimeZone::LocalTime; } } break; diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp index 7659c9722e0..1054e5f9680 100644 --- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp @@ -7,7 +7,7 @@ #include #include -#include // for qTzSet() +#include // for qTzSet(), qTzName() #ifdef Q_OS_WIN # include @@ -3255,10 +3255,15 @@ void tst_QDateTime::fromStringStringFormat_localTimeZone_data() QTimeZone gmt("GMT"); if (gmt.isValid()) { lacksRows = false; + const bool fullyLocal = ([]() { + TimeZoneRollback useZone("GMT"); + return qTzName(0) == u"GMT"_s; + })(); QTest::newRow("local-timezone-with-offset:GMT") << QByteArrayLiteral("GMT") << QString("2008-10-13 GMT 11.50") << QString("yyyy-MM-dd t hh.mm") - << QDateTime(QDate(2008, 10, 13), QTime(11, 50), gmt); + << QDateTime(QDate(2008, 10, 13), QTime(11, 50), + fullyLocal ? QTimeZone(QTimeZone::LocalTime) : gmt); } QTimeZone helsinki("Europe/Helsinki"); if (helsinki.isValid()) { diff --git a/tests/auto/corelib/time/qdatetimeparser/tst_qdatetimeparser.cpp b/tests/auto/corelib/time/qdatetimeparser/tst_qdatetimeparser.cpp index 32753d895f6..7f97e551a6b 100644 --- a/tests/auto/corelib/time/qdatetimeparser/tst_qdatetimeparser.cpp +++ b/tests/auto/corelib/time/qdatetimeparser/tst_qdatetimeparser.cpp @@ -46,6 +46,7 @@ class tst_QDateTimeParser : public QObject Q_OBJECT private Q_SLOTS: + void reparse(); void parseSection_data(); void parseSection(); @@ -53,6 +54,37 @@ private Q_SLOTS: void intermediateYear(); }; +void tst_QDateTimeParser::reparse() +{ + const QDateTime when = QDate(2023, 6, 15).startOfDay(); + // QTBUG-114575: 6.2 through 6.5 got back a bogus Qt::TimeZone (with zero offset): + const Qt::TimeSpec spec = ([](QStringView name) { + // When local time is UTC or a fixed offset from it, the parser prefers + // to interpret a UTC or offset suffix as such, rather than as local + // time (thereby avoiding DST-ness checks). We have to match that here. + if (name == QLatin1StringView("UTC")) + return Qt::UTC; + if (name.startsWith(u'+') || name.startsWith(u'-')) { + if (std::all_of(name.begin() + 1, name.end(), [](QChar ch) { return ch == u'0'; })) + return Qt::UTC; + if (std::all_of(name.begin() + 1, name.end(), [](QChar ch) { return ch.isDigit(); })) + return Qt::OffsetFromUTC; + // Potential hh:mm offset ? Not yet seen as local tzname[] entry. + } + return Qt::LocalTime; + })(when.timeZoneAbbreviation()); + + const QStringView format = u"dd/MM/yyyy HH:mm t"; + QDateTimeParser who(QMetaType::QDateTime, QDateTimeParser::DateTimeEdit); + QVERIFY(who.parseFormat(format)); + const auto state = who.parse(when.toString(format), -1, when, false); + QCOMPARE(state.state, QDateTimeParser::Acceptable); + QVERIFY(!state.conflicts); + QCOMPARE(state.padded, 0); + QCOMPARE(state.value.timeSpec(), spec); + QCOMPARE(state.value, when); +} + void tst_QDateTimeParser::parseSection_data() { QTest::addColumn("format");