You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
9.3 KiB
272 lines
9.3 KiB
From c337f6fae51b987ce7dbed1fd9bea41e6073efbb Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Andreas=20K=2E=20H=C3=BCttel?= <dilfridge@gentoo.org>
|
|
Date: Sat, 10 Oct 2020 19:26:13 +0200
|
|
Subject: [PATCH 1/2] Revert "Cache system zone ID when fetched from the
|
|
file-system"
|
|
|
|
This reverts commit c70ce3d042025c858faffe661f85d2482a2a0d8c.
|
|
---
|
|
src/corelib/time/qtimezoneprivate_tz.cpp | 205 +++++++----------------
|
|
1 file changed, 64 insertions(+), 141 deletions(-)
|
|
|
|
diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp
|
|
index c5c70b7364..01f9a6cce0 100644
|
|
--- a/src/corelib/time/qtimezoneprivate_tz.cpp
|
|
+++ b/src/corelib/time/qtimezoneprivate_tz.cpp
|
|
@@ -1,6 +1,5 @@
|
|
/****************************************************************************
|
|
**
|
|
-** Copyright (C) 2020 The Qt Company Ltd.
|
|
** Copyright (C) 2019 Crimson AS <info@crimson.no>
|
|
** Copyright (C) 2013 John Layt <jlayt@kde.org>
|
|
** Contact: https://www.qt.io/licensing/
|
|
@@ -43,19 +42,18 @@
|
|
#include "qtimezoneprivate_p.h"
|
|
#include "private/qlocale_tools_p.h"
|
|
|
|
-#include <QtCore/QDataStream>
|
|
-#include <QtCore/QDateTime>
|
|
#include <QtCore/QFile>
|
|
-#include <QtCore/QHash>
|
|
#include <QtCore/QMutex>
|
|
+#include <QtCore/QHash>
|
|
+#include <QtCore/QDataStream>
|
|
+#include <QtCore/QDateTime>
|
|
|
|
#include <qdebug.h>
|
|
-#include <qplatformdefs.h>
|
|
|
|
#include <algorithm>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
-#ifndef Q_OS_INTEGRITY
|
|
+#if !defined(Q_OS_INTEGRITY)
|
|
#include <sys/param.h> // to use MAXSYMLINKS constant
|
|
#endif
|
|
#include <unistd.h> // to use _SC_SYMLOOP_MAX constant
|
|
@@ -1102,146 +1100,28 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecs
|
|
return last > tranCache().cbegin() ? dataForTzTransition(*--last) : invalidData();
|
|
}
|
|
|
|
-bool QTzTimeZonePrivate::isTimeZoneIdAvailable(const QByteArray &ianaId) const
|
|
-{
|
|
- return tzZones->contains(ianaId);
|
|
-}
|
|
-
|
|
-QList<QByteArray> QTzTimeZonePrivate::availableTimeZoneIds() const
|
|
-{
|
|
- QList<QByteArray> result = tzZones->keys();
|
|
- std::sort(result.begin(), result.end());
|
|
- return result;
|
|
-}
|
|
-
|
|
-QList<QByteArray> QTzTimeZonePrivate::availableTimeZoneIds(QLocale::Country country) const
|
|
+static long getSymloopMax()
|
|
{
|
|
- // TODO AnyCountry
|
|
- QList<QByteArray> result;
|
|
- for (auto it = tzZones->cbegin(), end = tzZones->cend(); it != end; ++it) {
|
|
- if (it.value().country == country)
|
|
- result << it.key();
|
|
- }
|
|
- std::sort(result.begin(), result.end());
|
|
- return result;
|
|
-}
|
|
-
|
|
-// Getting the system zone's ID:
|
|
-
|
|
-namespace {
|
|
-class ZoneNameReader : public QObject
|
|
-{
|
|
-public:
|
|
- QByteArray name()
|
|
- {
|
|
- /* Assumptions:
|
|
- a) Systems don't change which of localtime and TZ they use without a
|
|
- reboot.
|
|
- b) When they change, they use atomic renames, hence a new device and
|
|
- inode for the new file.
|
|
- c) If we change which *name* is used for a zone, while referencing
|
|
- the same final zoneinfo file, we don't care about the change of
|
|
- name (e.g. if Europe/Oslo and Europe/Berlin are both symlinks to
|
|
- the same CET file, continuing to use the old name, after
|
|
- /etc/localtime changes which of the two it points to, is
|
|
- harmless).
|
|
-
|
|
- The alternative would be to use a file-system watcher, but they are a
|
|
- scarce resource.
|
|
- */
|
|
- const StatIdent local = identify("/etc/localtime");
|
|
- const StatIdent tz = identify("/etc/TZ");
|
|
- if (!m_name.isEmpty() && m_last.isValid() && (m_last == local || m_last == tz))
|
|
- return m_name;
|
|
-
|
|
- m_name = etcLocalTime();
|
|
- if (!m_name.isEmpty()) {
|
|
- m_last = local;
|
|
- return m_name;
|
|
- }
|
|
-
|
|
- m_name = etcTZ();
|
|
- m_last = m_name.isEmpty() ? StatIdent() : tz;
|
|
- return m_name;
|
|
- }
|
|
-
|
|
-
|
|
-private:
|
|
- QByteArray m_name;
|
|
- struct StatIdent
|
|
- {
|
|
- static constexpr unsigned long bad = ~0ul;
|
|
- unsigned long m_dev, m_ino;
|
|
- StatIdent() : m_dev(bad), m_ino(bad) {}
|
|
- StatIdent(const QT_STATBUF &data) : m_dev(data.st_dev), m_ino(data.st_ino) {}
|
|
- bool isValid() { return m_dev != bad || m_ino != bad; }
|
|
- bool operator==(const StatIdent &other)
|
|
- { return other.m_dev == m_dev && other.m_ino == m_ino; }
|
|
- };
|
|
- StatIdent m_last;
|
|
-
|
|
- static StatIdent identify(const char *path)
|
|
- {
|
|
- QT_STATBUF data;
|
|
- return QT_STAT(path, &data) == -1 ? StatIdent() : StatIdent(data);
|
|
- }
|
|
-
|
|
- static QByteArray etcLocalTime()
|
|
- {
|
|
- // On most distros /etc/localtime is a symlink to a real file so extract
|
|
- // name from the path
|
|
- const QLatin1String zoneinfo("/zoneinfo/");
|
|
- QString path = QStringLiteral("/etc/localtime");
|
|
- long iteration = getSymloopMax();
|
|
- // Symlink may point to another symlink etc. before being under zoneinfo/
|
|
- // We stop on the first path under /zoneinfo/, even if it is itself a
|
|
- // 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:
|
|
- return path.midRef(index + zoneinfo.size()).toUtf8();
|
|
- } while (!path.isEmpty() && --iteration > 0);
|
|
-
|
|
- return QByteArray();
|
|
- }
|
|
-
|
|
- static QByteArray etcTZ()
|
|
- {
|
|
- // Some systems (e.g. uClibc) have a default value for $TZ in /etc/TZ:
|
|
- const QString path = QStringLiteral("/etc/TZ");
|
|
- QFile zone(path);
|
|
- if (zone.open(QIODevice::ReadOnly))
|
|
- return zone.readAll().trimmed();
|
|
-
|
|
- return QByteArray();
|
|
- }
|
|
-
|
|
- // Any chain of symlinks longer than this is assumed to be a loop:
|
|
- static long getSymloopMax()
|
|
- {
|
|
-#ifdef SYMLOOP_MAX
|
|
- // If defined, at runtime it can only be greater than this, so this is a safe bet:
|
|
- return SYMLOOP_MAX;
|
|
+#if defined(SYMLOOP_MAX)
|
|
+ return SYMLOOP_MAX; // if defined, at runtime it can only be greater than this, so this is a safe bet
|
|
#else
|
|
- errno = 0;
|
|
- long result = sysconf(_SC_SYMLOOP_MAX);
|
|
- if (result >= 0)
|
|
- return result;
|
|
- // result is -1, meaning either error or no limit
|
|
- Q_ASSERT(!errno); // ... but it can't be an error, POSIX mandates _SC_SYMLOOP_MAX
|
|
-
|
|
- // therefore we can make up our own limit
|
|
-# ifdef MAXSYMLINKS
|
|
- return MAXSYMLINKS;
|
|
+ errno = 0;
|
|
+ long result = sysconf(_SC_SYMLOOP_MAX);
|
|
+ if (result >= 0)
|
|
+ return result;
|
|
+ // result is -1, meaning either error or no limit
|
|
+ Q_ASSERT(!errno); // ... but it can't be an error, POSIX mandates _SC_SYMLOOP_MAX
|
|
+
|
|
+ // therefore we can make up our own limit
|
|
+# if defined(MAXSYMLINKS)
|
|
+ return MAXSYMLINKS;
|
|
# else
|
|
- return 8;
|
|
+ return 8;
|
|
# endif
|
|
#endif
|
|
- }
|
|
-};
|
|
}
|
|
|
|
+// TODO Could cache the value and monitor the required files for any changes
|
|
QByteArray QTzTimeZonePrivate::systemTimeZoneId() const
|
|
{
|
|
// Check TZ env var first, if not populated try find it
|
|
@@ -1256,9 +1136,28 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const
|
|
else if (ianaId.startsWith(':'))
|
|
ianaId = ianaId.mid(1);
|
|
|
|
+ // On most distros /etc/localtime is a symlink to a real file so extract name from the path
|
|
+ if (ianaId.isEmpty()) {
|
|
+ const QLatin1String zoneinfo("/zoneinfo/");
|
|
+ QString path = QFile::symLinkTarget(QStringLiteral("/etc/localtime"));
|
|
+ int index = -1;
|
|
+ long iteration = getSymloopMax();
|
|
+ // Symlink may point to another symlink etc. before being under zoneinfo/
|
|
+ // We stop on the first path under /zoneinfo/, even if it is itself a
|
|
+ // symlink, like America/Montreal pointing to America/Toronto
|
|
+ while (iteration-- > 0 && !path.isEmpty() && (index = path.indexOf(zoneinfo)) < 0)
|
|
+ path = QFile::symLinkTarget(path);
|
|
+ if (index >= 0) {
|
|
+ // /etc/localtime is a symlink to the current TZ file, so extract from path
|
|
+ ianaId = path.midRef(index + zoneinfo.size()).toUtf8();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Some systems (e.g. uClibc) have a default value for $TZ in /etc/TZ:
|
|
if (ianaId.isEmpty()) {
|
|
- thread_local static ZoneNameReader reader;
|
|
- ianaId = reader.name();
|
|
+ QFile zone(QStringLiteral("/etc/TZ"));
|
|
+ if (zone.open(QIODevice::ReadOnly))
|
|
+ ianaId = zone.readAll().trimmed();
|
|
}
|
|
|
|
// Give up for now and return UTC
|
|
@@ -1268,4 +1167,28 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const
|
|
return ianaId;
|
|
}
|
|
|
|
+bool QTzTimeZonePrivate::isTimeZoneIdAvailable(const QByteArray &ianaId) const
|
|
+{
|
|
+ return tzZones->contains(ianaId);
|
|
+}
|
|
+
|
|
+QList<QByteArray> QTzTimeZonePrivate::availableTimeZoneIds() const
|
|
+{
|
|
+ QList<QByteArray> result = tzZones->keys();
|
|
+ std::sort(result.begin(), result.end());
|
|
+ return result;
|
|
+}
|
|
+
|
|
+QList<QByteArray> QTzTimeZonePrivate::availableTimeZoneIds(QLocale::Country country) const
|
|
+{
|
|
+ // TODO AnyCountry
|
|
+ QList<QByteArray> result;
|
|
+ for (auto it = tzZones->cbegin(), end = tzZones->cend(); it != end; ++it) {
|
|
+ if (it.value().country == country)
|
|
+ result << it.key();
|
|
+ }
|
|
+ std::sort(result.begin(), result.end());
|
|
+ return result;
|
|
+}
|
|
+
|
|
QT_END_NAMESPACE
|
|
--
|
|
2.28.0
|
|
|