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>
This commit is contained in:
Edward Welbourne 2023-03-24 13:24:51 +01:00
parent 06e2719f73
commit 13e8609fc9

View File

@ -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