From 9c0def81e4e5f473e6abe4a61c94df51a78beb63 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Thu, 14 Sep 2023 11:15:24 +0200 Subject: [PATCH] Warn on failure to construct valid system time zone object Also move some docs from asBackendZone() to systemTimeZone(), making clear that the system zone object is current at the time of creation and won't be updated if the system is reconfigured. Adapt some tests to fail and make clear that the system is misconfigured if no valid system zone is found. [ChangeLog][QtCore][QTimeZone] If systemTimeZone() is unable to identify a valid system time zone, it now produces a warning the first time it encounters the problem. Task-number: QTBUG-116017 Change-Id: Ia437d8a03ff3cbf2b2cd98e8a8c3aebe50c1ee32 Reviewed-by: Thiago Macieira --- src/corelib/time/qtimezone.cpp | 26 ++++++++++++++----- tests/auto/corelib/time/qdate/tst_qdate.cpp | 5 +++- .../corelib/time/qtimezone/tst_qtimezone.cpp | 4 ++- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/corelib/time/qtimezone.cpp b/src/corelib/time/qtimezone.cpp index 44b6662b5b7..81b0a5efe7c 100644 --- a/src/corelib/time/qtimezone.cpp +++ b/src/corelib/time/qtimezone.cpp @@ -556,16 +556,12 @@ QTimeZone::QTimeZone(QTimeZonePrivate &dd) In all cases, the result's \l timeSpec() is Qt::TimeZone. When this QTimeZone's timeSpec() is Qt::TimeZone, this QTimeZone itself is returned. + If timeSpec() is Qt::LocalTime then systemTimeZone() is returned. If timeSpec() is Qt::UTC, QTimeZone::utc() is returned. If it is Qt::OffsetFromUTC then QTimeZone(int) is passed its offset and the result is returned. - If timeSpec() is Qt::LocalTime then an instance of the current system time - zone will be returned. This will not change to reflect any subsequent change - to the system time zone. It represents the local time that was in effect - when asBackendZone() was called. - When using a lightweight time representation - local time, UTC time or time at a fixed offset from UTC - using methods only supported when feature \c timezone is enabled may be more expensive than using a corresponding time @@ -1353,11 +1349,29 @@ QByteArray QTimeZone::systemTimeZoneId() representation \c {QTimeZone(QTimeZone::LocalTime)}, albeit implemented as a time zone. + The returned object will not change to reflect any subsequent change to the + system time zone. It represents the local time that was in effect when + asBackendZone() was called. On misconfigured systems, such as those that + lack the timezone data relied on by the backend for which Qt was compiled, + it may be invalid. In such a case, a warning is output. + \sa utc(), Initialization, asBackendZone() */ QTimeZone QTimeZone::systemTimeZone() { - return QTimeZone(global_tz->backend->systemTimeZoneId()); + // Use ID even if empty, as default constructor is invalid but empty-ID + // constructor goes to backend's default constructor, which may succeed. + const auto sys = QTimeZone(global_tz->backend->systemTimeZoneId()); + if (!sys.isValid()) { + static bool neverWarned = true; + if (neverWarned) { + // Racey but, at worst, merely repeats the warning. + neverWarned = false; + qWarning("Unable to determine system time zone: " + "please check your system configuration."); + } + } + return sys; } /*! diff --git a/tests/auto/corelib/time/qdate/tst_qdate.cpp b/tests/auto/corelib/time/qdate/tst_qdate.cpp index 7c1577c39a7..ab27af0dd81 100644 --- a/tests/auto/corelib/time/qdate/tst_qdate.cpp +++ b/tests/auto/corelib/time/qdate/tst_qdate.cpp @@ -573,6 +573,7 @@ void tst_QDate::startOfDay_endOfDay_data() #if QT_CONFIG(timezone) const QTimeZone sys = QTimeZone::systemTimeZone(); + QVERIFY2(sys.isValid(), "Test depends on properly configured system"); for (const auto &tran : transitions) { if (QTimeZone zone(tran.zone); zone.isValid()) { QTest::newRow(tran.test) @@ -728,7 +729,9 @@ void tst_QDate::startOfDay_endOfDay_bounds() QCOMPARE(qdteMin.startOfDay(UTC).date(), qdteMin); QCOMPARE(qdteMin.startOfDay().date(), qdteMin); #if QT_CONFIG(timezone) - QCOMPARE(qdteMin.startOfDay(QTimeZone::systemTimeZone()).date(), qdteMin); + const QTimeZone sys = QTimeZone::systemTimeZone(); + QVERIFY2(sys.isValid(), "Test depends on properly configured system"); + QCOMPARE(qdteMin.startOfDay(sys).date(), qdteMin); QTimeZone berlin("Europe/Berlin"); if (berlin.isValid()) QCOMPARE(qdteMin.startOfDay(berlin).date(), qdteMin); diff --git a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp index 7d60f5d0ff6..ff6f96c1a05 100644 --- a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp @@ -507,7 +507,9 @@ void tst_QTimeZone::asBackendZone() void tst_QTimeZone::systemZone() { const QTimeZone zone = QTimeZone::systemTimeZone(); - QVERIFY2(zone.isValid(), "Invalid system zone setting, tests are doomed."); + QVERIFY2(zone.isValid(), + "Invalid system zone setting, tests are doomed on misconfigured system."); + // This may fail on Windows if CLDR data doesn't map system MS ID to IANA ID: QCOMPARE(zone.id(), QTimeZone::systemTimeZoneId()); QCOMPARE(zone, QTimeZone(QTimeZone::systemTimeZoneId())); // Check it behaves the same as local-time: