From ab2655c559f42e382c13e21a3bea65fb21af95eb Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Fri, 3 May 2019 16:24:51 +0200 Subject: [PATCH] Support POSIX rules as $TZ values The TZ environment variable can validly contain a POSIX rule, rather than an IANA ID, as described here: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 However, if TZ were set to such a value, leading to it being used as systemTimeZoneId(), it would be passed to QTzTimeZonePrivate::init(), which is a no-op unless it manages to open a zoneinfo/ file with the given ianaId as name. When the environment variable doesn't name a zoneinfo/ file, we would thus get an invalid time-zone. We can, instead, check whether the ianaId looks like a valid POSIX rule and, if it does, use it as m_posixRule, enabling us to correctly handle this case. Tweak parsing of POSIX rules so that a zone using name "UTC" or "GMT" with an offset other than 0 will be rejected as invalid. This avoids parsing a zone name such as "GMT+17" or "UTC+00:01" as a POSIX rule, where it should be understood as an offset from UTC (and only certain well-established offsets are supported). Added two test-cases to tst_QTimeZone::tzTest() for validity of a POSIX zone value - a simple one constructed during discussion of the bug, the other taken from an example in: http://leaf.sourceforge.net/doc/buci-tz3.html Task-number: QTBUG-75565 Change-Id: Ia5cb1cc56b13b0f6b56258e48be98d04d909e32a Reviewed-by: Thiago Macieira --- src/corelib/tools/qtimezoneprivate_tz.cpp | 15 ++++++++++++++- .../corelib/tools/qtimezone/tst_qtimezone.cpp | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/corelib/tools/qtimezoneprivate_tz.cpp b/src/corelib/tools/qtimezoneprivate_tz.cpp index fab0b2cbf1..d9e87212d3 100644 --- a/src/corelib/tools/qtimezoneprivate_tz.cpp +++ b/src/corelib/tools/qtimezoneprivate_tz.cpp @@ -516,6 +516,10 @@ PosixZone PosixZone::parse(const char *&pos, const char *end) QString name = QString::fromUtf8(nameBegin, nameEnd - nameBegin); const int offset = zoneEnd > zoneBegin ? parsePosixOffset(zoneBegin, zoneEnd) : InvalidOffset; pos = zoneEnd; + // UTC+hh:mm:ss or GMT+hh:mm:ss should be read as offsets from UTC, not as a + // POSIX rule naming a zone as UTC or GMT and specifying a non-zero offset. + if (offset != 0 && (name == QLatin1String("UTC") || name == QLatin1String("GMT"))) + return invalid(); return {std::move(name), offset}; } @@ -654,8 +658,17 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId) tzif.setFileName(QLatin1String("/usr/share/zoneinfo/") + QString::fromLocal8Bit(ianaId)); if (!tzif.open(QIODevice::ReadOnly)) { tzif.setFileName(QLatin1String("/usr/lib/zoneinfo/") + QString::fromLocal8Bit(ianaId)); - if (!tzif.open(QIODevice::ReadOnly)) + if (!tzif.open(QIODevice::ReadOnly)) { + // ianaId may be a POSIX rule, taken from $TZ + const QByteArray zoneInfo = ianaId.split(',').at(0); + const char *begin = zoneInfo.constBegin(); + if (PosixZone::parse(begin, zoneInfo.constEnd()).hasValidOffset() + && (begin == zoneInfo.constEnd() + || PosixZone::parse(begin, zoneInfo.constEnd()).hasValidOffset())) { + m_id = m_posixRule = ianaId; + } return; + } } } diff --git a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp index 4160a00f71..9904719f7c 100644 --- a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp @@ -915,6 +915,14 @@ void tst_QTimeZone::tzTest() QTzTimeZonePrivate tzp("Europe/Berlin"); QVERIFY(tzp.isValid()); + // Test POSIX-format value for $TZ: + QTzTimeZonePrivate tzposix("MET-1METDST-2,M3.5.0/02:00:00,M10.5.0/03:00:00"); + QVERIFY(tzposix.isValid()); + + QTimeZone tzBrazil("BRT+3"); // parts of Northern Brazil, as a POSIX rule + QVERIFY(tzBrazil.isValid()); + QCOMPARE(tzBrazil.offsetFromUtc(QDateTime(QDate(1111, 11, 11).startOfDay())), -10800); + // Test display names by type, either ICU or abbreviation only QLocale enUS("en_US"); // Only test names in debug mode, names used can vary by ICU version installed