summaryrefslogtreecommitdiffstats
path: root/src/corelib/time/qtimezoneprivate_tz.cpp
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2023-03-24 13:24:51 +0100
committerEdward Welbourne <edward.welbourne@qt.io>2023-05-15 14:02:41 +0200
commit13e8609fc957b9bdcc435a93e39aae4211fe4777 (patch)
tree58dbb884059644d06cf6ad81b6ecc95ef639535e /src/corelib/time/qtimezoneprivate_tz.cpp
parent06e2719f730e4545d370f7d7dabb32a24247e6c8 (diff)
Include all available IANA DB zones for the TZ backend
Previously, QTzTimeZonePrivate::availableTimeZoneIds() only reported the zones listed in the zone.tab file, which maps territories to zones. It thus omitted several zones that are provided by the IANA DB, but not the primary zones for any territory. This meant that it was possible to pass a zone name to the constructor successfully, despite isTimeZoneIdAvailable() claiming it isn't available. Pick-to: 6.5 Change-Id: I9e4eb7f4cfe578204951b995f0ad9ffd7eed5f9c 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.cpp36
1 files changed, 34 insertions, 2 deletions
diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp
index 2576fe7972..e702a5d6b4 100644
--- a/src/corelib/time/qtimezoneprivate_tz.cpp
+++ b/src/corelib/time/qtimezoneprivate_tz.cpp
@@ -10,6 +10,7 @@
#include <QtCore/QDataStream>
#include <QtCore/QDateTime>
+#include <QtCore/QDirIterator>
#include <QtCore/QFile>
#include <QtCore/QCache>
#include <QtCore/QMap>
@@ -41,14 +42,18 @@ Q_CONSTINIT static QBasicMutex s_icu_mutex;
*/
struct QTzTimeZone {
- QLocale::Territory territory;
+ QLocale::Territory territory = QLocale::AnyTerritory;
QByteArray comment;
};
// Define as a type as Q_GLOBAL_STATIC doesn't like it
typedef QHash<QByteArray, QTzTimeZone> QTzTimeZoneHash;
-// Parse zone.tab table, assume lists all installed zones, if not will need to read directories
+static bool isTzFile(const QString &name);
+
+// 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");
@@ -85,6 +90,28 @@ static QTzTimeZoneHash loadTzTimeZones()
}
}
}
+
+ const qsizetype cut = path.lastIndexOf(u'/');
+ Q_ASSERT(cut > 0);
+ const QDir zoneDir = QDir(path.first(cut));
+ QDirIterator zoneFiles(zoneDir, QDirIterator::Subdirectories);
+ while (zoneFiles.hasNext()) {
+ const QFileInfo info = zoneFiles.nextFileInfo();
+ if (!(info.isFile() || info.isSymLink()))
+ continue;
+ const QString name = zoneDir.relativeFilePath(info.filePath());
+ // Two sub-directories containing (more or less) copies of the zoneinfo tree.
+ if (info.isDir() ? name == "posix"_L1 || name == "right"_L1
+ : name.startsWith("posix/"_L1) || name.startsWith("right/"_L1)) {
+ continue;
+ }
+ // We could filter out *.* and leapseconds instead of doing the
+ // isTzFile() check; in practice current (2023) zoneinfo/ contains only
+ // actual zone files and matches to that filter.
+ const QByteArray id = QFile::encodeName(name);
+ if (!zonesHash.contains(id) && isTzFile(zoneDir.absoluteFilePath(name)))
+ zonesHash.insert(id, QTzTimeZone());
+ }
return zonesHash;
}
@@ -128,6 +155,11 @@ struct QTzType {
};
Q_DECLARE_TYPEINFO(QTzType, Q_PRIMITIVE_TYPE);
+static bool isTzFile(const QString &name)
+{
+ QFile file(name);
+ return file.open(QFile::ReadOnly) && file.read(strlen(TZ_MAGIC)) == TZ_MAGIC;
+}
// TZ File parsing