Make QDateTimeParser recognize local time offsets

Fixes: QTBUG-84209
Pick-to: 5.15
Change-Id: Iedbc7beafcaa55c72fec3ac5a5f519c6ed5f7770
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Andrei Golubev 2020-05-13 15:12:28 +03:00
parent 33fc622686
commit cfbb30decd
2 changed files with 49 additions and 14 deletions

View File

@ -1726,29 +1726,26 @@ QDateTimeParser::ParsedSection QDateTimeParser::findUtcOffset(QStringRef str) co
QDateTimeParser::ParsedSection
QDateTimeParser::findTimeZoneName(QStringRef str, const QDateTime &when) const
{
int index = startsWithLocalTimeZone(str);
if (index > 0) // won't actually use the offset, but need it to be valid
return ParsedSection(Acceptable, when.toLocalTime().offsetFromUtc(), index);
const int systemLength = startsWithLocalTimeZone(str);
#if QT_CONFIG(timezone)
const int size = str.length();
// Collect up plausibly-valid characters; let QTimeZone work out what's
// truly valid.
for (; index < size; ++index) {
const QChar here = str[index];
if (here >= 127 || (!here.isLetterOrNumber() && !QLatin1String("/-_.+:").contains(here)))
break;
}
const auto invalidZoneNameCharacter = [] (const QChar &c) {
return c.unicode() >= 127u
|| (!c.isLetterOrNumber() && !QLatin1String("+-./:_").contains(c));
};
int index = std::distance(str.cbegin(),
std::find_if(str.cbegin(), str.cend(), invalidZoneNameCharacter));
while (index > 0) {
for (; index > systemLength; --index) { // Find longest match
str.truncate(index);
QTimeZone zone(str.toLatin1());
if (zone.isValid())
return ParsedSection(Acceptable, zone.offsetFromUtc(when), index);
index--; // maybe we collected too much ...
}
#endif
if (systemLength > 0) // won't actually use the offset, but need it to be valid
return ParsedSection(Acceptable, when.toLocalTime().offsetFromUtc(), systemLength);
return ParsedSection();
}

View File

@ -119,6 +119,8 @@ private slots:
void fromStringDateFormat();
void fromStringStringFormat_data();
void fromStringStringFormat();
void fromStringStringFormat_localTimeZone_data();
void fromStringStringFormat_localTimeZone();
#if defined(Q_OS_WIN) && QT_CONFIG(textdate)
void fromString_LOCALE_ILDATE();
#endif
@ -2675,6 +2677,7 @@ void tst_QDateTime::fromStringStringFormat()
QDateTime dt = QDateTime::fromString(string, format);
QCOMPARE(dt, expected);
if (expected.isValid()) {
QCOMPARE(dt.timeSpec(), expected.timeSpec());
#if QT_CONFIG(timezone)
@ -2684,7 +2687,42 @@ void tst_QDateTime::fromStringStringFormat()
// OffsetFromUTC needs an offset check - we may as well do it for all:
QCOMPARE(dt.offsetFromUtc(), expected.offsetFromUtc());
}
QCOMPARE(dt, expected);
}
void tst_QDateTime::fromStringStringFormat_localTimeZone_data()
{
QTest::addColumn<QByteArray>("localTimeZone");
QTest::addColumn<QString>("string");
QTest::addColumn<QString>("format");
QTest::addColumn<QDateTime>("expected");
#if QT_CONFIG(timezone)
QTimeZone etcGmtWithOffset("Etc/GMT+3");
if (etcGmtWithOffset.isValid()) {
QTest::newRow("local-timezone-with-offset:Etc/GMT+3") << QByteArrayLiteral("GMT")
<< QString("2008-10-13 Etc/GMT+3 11.50") << QString("yyyy-MM-dd t hh.mm")
<< QDateTime(QDate(2008, 10, 13), QTime(11, 50), etcGmtWithOffset);
}
QTimeZone gmtWithOffset("GMT-2");
if (gmtWithOffset.isValid()) {
QTest::newRow("local-timezone-with-offset:GMT-2") << QByteArrayLiteral("GMT")
<< QString("2008-10-13 GMT-2 11.50") << QString("yyyy-MM-dd t hh.mm")
<< QDateTime(QDate(2008, 10, 13), QTime(11, 50), gmtWithOffset);
}
QTimeZone gmt("GMT");
if (gmt.isValid()) {
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);
}
#endif
}
void tst_QDateTime::fromStringStringFormat_localTimeZone()
{
QFETCH(QByteArray, localTimeZone);
TimeZoneRollback useZone(localTimeZone); // enforce test's time zone
fromStringStringFormat(); // call basic fromStringStringFormat test
}
#if defined(Q_OS_WIN) && QT_CONFIG(textdate)