summaryrefslogtreecommitdiffstats
path: root/src/corelib/time/qtimezoneprivate_tz.cpp
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2023-09-11 11:41:39 +0200
committerEdward Welbourne <edward.welbourne@qt.io>2023-09-13 19:51:47 +0200
commit569dc0de50a46b64763921199a2706ab34d151a9 (patch)
treedb43c0fe9f62dcbdbea74d829c2a626d6bcf7266 /src/corelib/time/qtimezoneprivate_tz.cpp
parentc0cb616c1d62a90e612bd64d427f37ac11a5a473 (diff)
Support the TZDIR environment variable
On Linux / glibc, this overrides the default system location for the zone info. So check for files there first. Break out a function to manage the trying of (now three) zoneinfo directories when opening a file by name relative to there. Pick-to: 6.6 6.5 Task-number: QTBUG-116017 Change-Id: I1f97107aabd9015c0a5543639870f1d70654ca67 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib/time/qtimezoneprivate_tz.cpp')
-rw-r--r--src/corelib/time/qtimezoneprivate_tz.cpp71
1 files changed, 48 insertions, 23 deletions
diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp
index e702a5d6b4..2b54cf7a5e 100644
--- a/src/corelib/time/qtimezoneprivate_tz.cpp
+++ b/src/corelib/time/qtimezoneprivate_tz.cpp
@@ -51,17 +51,41 @@ typedef QHash<QByteArray, QTzTimeZone> QTzTimeZoneHash;
static bool isTzFile(const QString &name);
+// Open a named file under the zone info directory:
+static bool openZoneInfo(QString name, QFile *file)
+{
+ // At least on Linux / glibc (see man 3 tzset), $TZDIR overrides the system
+ // default location for zone info:
+ const QString tzdir = qEnvironmentVariable("TZDIR");
+ if (!tzdir.isEmpty()) {
+ file->setFileName(QDir(tzdir).filePath(name));
+ if (file->open(QIODevice::ReadOnly))
+ return true;
+ }
+ // Try modern system path first:
+ constexpr auto zoneShare = "/usr/share/zoneinfo/"_L1;
+ if (tzdir != zoneShare && tzdir != zoneShare.chopped(1)) {
+ file->setFileName(zoneShare + name);
+ if (file->open(QIODevice::ReadOnly))
+ return true;
+ }
+ // Fall back to legacy system path:
+ constexpr auto zoneLib = "/usr/lib/zoneinfo/"_L1;
+ if (tzdir != zoneLib && tzdir != zoneLib.chopped(1)) {
+ file->setFileName(zoneShare + name);
+ if (file->open(QIODevice::ReadOnly))
+ return true;
+ }
+ return false;
+}
+
// Parse zone.tab table for territory information, read directories to ensure we
// find all installed zones (many are omitted from zone.tab; even more from
// zone1970.tab).
static QTzTimeZoneHash loadTzTimeZones()
{
- QString path = QStringLiteral("/usr/share/zoneinfo/zone.tab");
- if (!QFile::exists(path))
- path = QStringLiteral("/usr/lib/zoneinfo/zone.tab");
-
- QFile tzif(path);
- if (!tzif.open(QIODevice::ReadOnly))
+ QFile tzif;
+ if (!openZoneInfo("zone.tab"_L1, &tzif))
return QTzTimeZoneHash();
QTzTimeZoneHash zonesHash;
@@ -91,6 +115,7 @@ static QTzTimeZoneHash loadTzTimeZones()
}
}
+ const QString path = tzif.fileName();
const qsizetype cut = path.lastIndexOf(u'/');
Q_ASSERT(cut > 0);
const QDir zoneDir = QDir(path.first(cut));
@@ -772,20 +797,13 @@ QTzTimeZoneCacheEntry QTzTimeZoneCache::findEntry(const QByteArray &ianaId)
tzif.setFileName(QStringLiteral("/etc/localtime"));
if (!tzif.open(QIODevice::ReadOnly))
return ret;
- } else {
- // Open named tz, try modern path first, if fails try legacy path
- tzif.setFileName("/usr/share/zoneinfo/"_L1 + QString::fromLocal8Bit(ianaId));
- if (!tzif.open(QIODevice::ReadOnly)) {
- tzif.setFileName("/usr/lib/zoneinfo/"_L1 + QString::fromLocal8Bit(ianaId));
- if (!tzif.open(QIODevice::ReadOnly)) {
- // ianaId may be a POSIX rule, taken from $TZ or /etc/TZ
- auto check = validatePosixRule(ianaId);
- if (check.isValid) {
- ret.m_hasDst = check.hasDst;
- ret.m_posixRule = ianaId;
- }
- return ret;
- }
+ } else if (!openZoneInfo(QString::fromLocal8Bit(ianaId), &tzif)) {
+ // ianaId may be a POSIX rule, taken from $TZ or /etc/TZ
+ auto check = validatePosixRule(ianaId);
+ if (check.isValid) {
+ ret.m_hasDst = check.hasDst;
+ ret.m_posixRule = ianaId;
+ return ret;
}
}
@@ -1332,7 +1350,8 @@ private:
{
// On most distros /etc/localtime is a symlink to a real file so extract
// name from the path
- const auto zoneinfo = "/zoneinfo/"_L1;
+ const QString tzdir = qEnvironmentVariable("TZDIR");
+ constexpr auto zoneinfo = "/zoneinfo/"_L1;
QString path = QStringLiteral("/etc/localtime");
long iteration = getSymloopMax();
// Symlink may point to another symlink etc. before being under zoneinfo/
@@ -1340,8 +1359,14 @@ private:
// symlink, like America/Montreal pointing to America/Toronto
do {
path = QFile::symLinkTarget(path);
- int index = path.indexOf(zoneinfo);
- if (index >= 0) // Found zoneinfo file; extract zone name from path:
+ // If it's a zoneinfo file, extract the zone name from its path:
+ int index = tzdir.isEmpty() ? -1 : path.indexOf(tzdir);
+ if (index >= 0) {
+ const auto tail = QStringView{ path }.sliced(index + tzdir.size()).toUtf8();
+ return tail.startsWith(u'/') ? tail.sliced(1) : tail;
+ }
+ index = path.indexOf(zoneinfo);
+ if (index >= 0)
return QStringView{ path }.sliced(index + zoneinfo.size()).toUtf8();
} while (!path.isEmpty() && --iteration > 0);