diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2019-05-03 16:24:51 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2019-05-10 14:17:01 +0000 |
commit | ab2655c559f42e382c13e21a3bea65fb21af95eb (patch) | |
tree | bf4f2ae93bbb52db3f07ecce1200322ccddbfca9 | |
parent | b29cc512f8062d69d573fd95907b3d05298a8c23 (diff) |
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 <thiago.macieira@intel.com>
-rw-r--r-- | src/corelib/tools/qtimezoneprivate_tz.cpp | 15 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp | 8 |
2 files changed, 22 insertions, 1 deletions
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 |