QTimeZone: Add back-end based on std::chrono::tzdb

Implement QTimeZone back-end based on C++20
std::chrono::tzdb; requires GCC libstdc++ 14,
Clang libc++ 19 or MSVC STL 19.29.

Fixes: QTBUG-68812
Change-Id: I6581b3fe908c2318befa8f421c4ca8bf7c51a760
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Magdalena Stojek 2024-05-22 14:46:09 +02:00
parent d7af695feb
commit 755733f591
8 changed files with 348 additions and 32 deletions

View File

@ -923,24 +923,40 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_timezone
time/qtimezoneprivate_data_p.h
)
qt_internal_extend_target(Core CONDITION APPLE AND QT_FEATURE_timezone
qt_internal_extend_target(Core
CONDITION
QT_FEATURE_timezone_tzdb
SOURCES
time/qtimezoneprivate_chrono.cpp
)
qt_internal_extend_target(Core
CONDITION
QT_FEATURE_timezone AND APPLE AND NOT QT_FEATURE_timezone_tzdb
SOURCES
time/qtimezoneprivate_mac.mm
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_timezone AND ANDROID AND NOT APPLE
qt_internal_extend_target(Core
CONDITION
QT_FEATURE_timezone AND ANDROID
AND NOT APPLE AND NOT QT_FEATURE_timezone_tzdb
SOURCES
time/qtimezoneprivate_android.cpp
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_timezone AND UNIX AND NOT ANDROID AND NOT APPLE
qt_internal_extend_target(Core
CONDITION
QT_FEATURE_timezone AND UNIX
AND NOT ANDROID AND NOT APPLE AND NOT QT_FEATURE_timezone_tzdb
SOURCES
time/qtimezoneprivate_tz.cpp
)
qt_internal_extend_target(Core
CONDITION
QT_FEATURE_icu AND QT_FEATURE_timezone AND NOT UNIX
QT_FEATURE_icu AND QT_FEATURE_timezone
AND NOT UNIX AND NOT QT_FEATURE_timezone_tzdb
SOURCES
time/qtimezoneprivate_icu.cpp
)
@ -949,18 +965,21 @@ qt_internal_extend_target(Core
qt_internal_extend_target(Core
CONDITION
QT_FEATURE_timezone AND WIN32 AND NOT QT_FEATURE_icu
AND NOT QT_FEATURE_timezone_tzdb
SOURCES
time/qtimezoneprivate_win.cpp
)
qt_internal_extend_target(Core
CONDITION QT_FEATURE_timezone_locale
CONDITION
QT_FEATURE_timezone_locale
SOURCES
time/qtimezonelocale.cpp time/qtimezonelocale_p.h
)
qt_internal_extend_target(Core
CONDITION QT_FEATURE_timezone_locale AND NOT QT_FEATURE_icu
CONDITION
QT_FEATURE_timezone_locale AND NOT QT_FEATURE_icu
SOURCES
time/qtimezonelocale_data_p.h
)

View File

@ -495,6 +495,28 @@ int main(int argc, char** argv) {
}
")
# <chrono>
qt_config_compile_test(chrono_tzdb
LABEL "Support for timezones in C++20 <chrono>"
CODE
"#include <chrono>
#if __cpp_lib_chrono < 201907L
#error
#endif
int main(void)
{
/* BEGIN TEST: */
const std::chrono::tzdb &tzdb = std::chrono::get_tzdb();
auto when = std::chrono::system_clock::now();
const std::chrono::time_zone *currentZone = tzdb.current_zone();
auto zoneInfo = currentZone->get_info(when);
/* END TEST: */
return 0;
}
"
)
#### Features
qt_feature("clock-gettime" PRIVATE
@ -908,6 +930,13 @@ qt_feature("timezone_locale" PRIVATE
CONDITION
QT_FEATURE_timezone AND NOT APPLE AND NOT ANDROID
)
qt_feature("timezone_tzdb" PUBLIC
SECTION "Utilities"
LABEL "std::chrono::tzdb QTZ backend"
PURPOSE "Provides support for a timezone backend using std::chrono."
CONDITION TEST_chrono_tzdb
AUTODETECT OFF
)
qt_feature("datetimeparser" PRIVATE
SECTION "Utilities"
LABEL "QDateTimeParser"
@ -979,6 +1008,7 @@ qt_configure_add_summary_entry(ARGS "system-doubleconversion")
qt_configure_add_summary_entry(ARGS "forkfd_pidfd" CONDITION LINUX)
qt_configure_add_summary_entry(ARGS "glib")
qt_configure_add_summary_entry(ARGS "icu")
qt_configure_add_summary_entry(ARGS "timezone_tzdb")
qt_configure_add_summary_entry(ARGS "system-libb2")
qt_configure_add_summary_entry(ARGS "mimetype-database")
qt_configure_add_summary_entry(ARGS "permissions")

View File

@ -22,7 +22,9 @@ using namespace Qt::StringLiterals;
// Create default time zone using appropriate backend
static QTimeZonePrivate *newBackendTimeZone()
{
#if defined(Q_OS_DARWIN)
#if QT_CONFIG(timezone_tzdb)
return new QChronoTimeZonePrivate();
#elif defined(Q_OS_DARWIN)
return new QMacTimeZonePrivate();
#elif defined(Q_OS_ANDROID)
return new QAndroidTimeZonePrivate();
@ -41,7 +43,9 @@ static QTimeZonePrivate *newBackendTimeZone()
static QTimeZonePrivate *newBackendTimeZone(const QByteArray &ianaId)
{
Q_ASSERT(!ianaId.isEmpty());
#if defined(Q_OS_DARWIN)
#if QT_CONFIG(timezone_tzdb)
return new QChronoTimeZonePrivate(ianaId);
#elif defined(Q_OS_DARWIN)
return new QMacTimeZonePrivate(ianaId);
#elif defined(Q_OS_ANDROID)
return new QAndroidTimeZonePrivate(ianaId);

View File

@ -0,0 +1,146 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qtimezoneprivate_p.h"
#include <chrono>
QT_BEGIN_NAMESPACE
using namespace std::chrono;
using namespace std::chrono_literals;
#define EXCEPTION_CHECKED(expression, fallback) \
QT_TRY { \
expression; \
} QT_CATCH (const std::runtime_error &) { \
fallback; \
}
static std::chrono::sys_time<std::chrono::milliseconds>
chronoForEpochMillis(qint64 millis)
{
return sys_time<milliseconds>(milliseconds(millis));
}
static std::optional<std::chrono::sys_info>
infoAtEpochMillis(const std::chrono::time_zone *zone, qint64 millis)
{
EXCEPTION_CHECKED(return zone->get_info(chronoForEpochMillis(millis)), return std::nullopt);
}
static const std::chrono::time_zone *idToZone(std::string_view id)
{
EXCEPTION_CHECKED(return get_tzdb().locate_zone(id), return nullptr);
}
static QChronoTimeZonePrivate::Data
fromSysInfo(std::chrono::sys_info info, qint64 atMSecsSinceEpoch)
{
QString abbreviation = QString::fromLatin1(info.abbrev);
int offsetFromUtc = info.offset.count();
// offset is in seconds, save is in minutes
int standardTimeOffset = offsetFromUtc - seconds(info.save).count();
return QChronoTimeZonePrivate::Data(abbreviation, atMSecsSinceEpoch,
offsetFromUtc, standardTimeOffset);
}
QChronoTimeZonePrivate::QChronoTimeZonePrivate()
: m_timeZone(std::chrono::current_zone())
{
if (m_timeZone)
m_id.assign(m_timeZone->name());
}
QChronoTimeZonePrivate::QChronoTimeZonePrivate(QByteArrayView id)
: m_timeZone(idToZone(std::string_view(id.data(), id.size())))
{
if (m_timeZone)
m_id.assign(m_timeZone->name());
}
QChronoTimeZonePrivate::~QChronoTimeZonePrivate()
= default;
QString QChronoTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
{
if (auto info = infoAtEpochMillis(m_timeZone, atMSecsSinceEpoch))
return fromSysInfo(*info, atMSecsSinceEpoch).abbreviation;
return {};
}
int QChronoTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const
{
if (auto info = infoAtEpochMillis(m_timeZone, atMSecsSinceEpoch))
return fromSysInfo(*info, atMSecsSinceEpoch).offsetFromUtc;
return invalidSeconds();
}
int QChronoTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const
{
// Subtracting minutes from seconds will convert the minutes to seconds.
if (auto info = infoAtEpochMillis(m_timeZone, atMSecsSinceEpoch))
return int((info->offset - info->save).count());
return invalidSeconds();
}
int QChronoTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const
{
if (auto info = infoAtEpochMillis(m_timeZone, atMSecsSinceEpoch))
return int(std::chrono::seconds(info->save).count());
return invalidSeconds();
}
bool QChronoTimeZonePrivate::hasDaylightTime() const
{
Data data = QTimeZonePrivate::data(QTimeZone::DaylightTime);
return data.daylightTimeOffset != 0
&& data.daylightTimeOffset != invalidSeconds();
}
bool QChronoTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
{
if (auto info = infoAtEpochMillis(m_timeZone, atMSecsSinceEpoch))
return info->save != 0min;
return false;
}
QChronoTimeZonePrivate::Data
QChronoTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
{
if (auto info = infoAtEpochMillis(m_timeZone, forMSecsSinceEpoch))
return fromSysInfo(*info, forMSecsSinceEpoch);
return {};
}
bool QChronoTimeZonePrivate::hasTransitions() const
{
return true;
}
QChronoTimeZonePrivate::Data
QChronoTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const
{
if (const auto info = infoAtEpochMillis(m_timeZone, afterMSecsSinceEpoch)) {
const auto tran = info->end;
qint64 when = milliseconds(tran.time_since_epoch()).count();
if (when > afterMSecsSinceEpoch) {
return fromSysInfo(*info, afterMSecsSinceEpoch);
} // else we were already at (or after) the end-of-time "transition"
}
return {};
}
QChronoTimeZonePrivate::Data
QChronoTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
{
if (const auto info = infoAtEpochMillis(m_timeZone, beforeMSecsSinceEpoch - 1)) {
qint64 when = milliseconds(info->begin.time_since_epoch()).count();
if (when < beforeMSecsSinceEpoch) {
return fromSysInfo(*info, beforeMSecsSinceEpoch);
} // else we were already at (or before) the start-of-time "transition"
}
return {};
}
QT_END_NAMESPACE

View File

@ -22,6 +22,10 @@
#include "private/qlocale_p.h"
#include "private/qdatetime_p.h"
#if QT_CONFIG(timezone_tzdb)
#include <chrono>
#endif
#if QT_CONFIG(icu)
#include <unicode/ucal.h>
#endif
@ -229,7 +233,33 @@ private:
};
// Platform backend cascade: match newBackendTimeZone() in qtimezone.cpp
#ifdef Q_OS_DARWIN
#if QT_CONFIG(timezone_tzdb)
class QChronoTimeZonePrivate final : public QTimeZonePrivate
{
public:
QChronoTimeZonePrivate();
QChronoTimeZonePrivate(QByteArrayView id);
~QChronoTimeZonePrivate() override;
QString abbreviation(qint64 atMSecsSinceEpoch) const override;
int offsetFromUtc(qint64 atMSecsSinceEpoch) const override;
int standardTimeOffset(qint64 atMSecsSinceEpoch) const override;
int daylightTimeOffset(qint64 atMSecsSinceEpoch) const override;
bool hasDaylightTime() const override;
bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
Data data(qint64 forMSecsSinceEpoch) const override;
bool hasTransitions() const override;
Data nextTransition(qint64 afterMSecsSinceEpoch) const override;
Data previousTransition(qint64 beforeMSecsSinceEpoch) const override;
private:
const std::chrono::time_zone *const m_timeZone;
Q_DISABLE_COPY_MOVE(QChronoTimeZonePrivate)
};
#elif defined(Q_OS_DARWIN)
class Q_AUTOTEST_EXPORT QMacTimeZonePrivate final : public QTimeZonePrivate
{
public:
@ -495,7 +525,7 @@ private:
QString m_daylightName;
QList<QWinTransitionRule> m_tranRules;
};
#endif // Darwin, Android, Unix, ICU, Win.
#endif // C++20, Darwin, Android, Unix, ICU, Win.
QT_END_NAMESPACE

View File

@ -19,8 +19,19 @@
using namespace QtPrivate::DateTimeConstants;
using namespace Qt::StringLiterals;
#if defined(Q_OS_WIN) && !QT_CONFIG(icu)
# define USING_WIN_TZ
#undef USING_MS_TZDB
#undef USING_WIN_TZ
#ifdef Q_OS_WIN
# if QT_CONFIG(timezone_tzdb)
# define USING_MS_TZDB
# elif !QT_CONFIG(icu)
# define USING_WIN_TZ
# endif
#endif
#undef GLIBC_TZDB_MISPARSE
#if QT_CONFIG(timezone_tzdb) && defined(__GLIBCXX__) // && _GLIBCXX_RELEASE <= 14
# define GLIBC_TZDB_MISPARSE // QTBUG-127598
#endif
class tst_QDate : public QObject
@ -509,6 +520,7 @@ void tst_QDate::weekNumber_invalid()
enum BackendKludge { IgnoreStart = 1, IgnoreEnd = 2, };
Q_DECLARE_FLAGS(BackendKludges, BackendKludge)
Q_DECLARE_OPERATORS_FOR_FLAGS(BackendKludges)
#undef KLUDGING
void tst_QDate::startOfDay_endOfDay_data()
{
@ -524,13 +536,28 @@ void tst_QDate::startOfDay_endOfDay_data()
const QTime early(0, 0), late(23, 59, 59, 999), invalid(QDateTime().time());
constexpr BackendKludges Clean = {};
constexpr BackendKludges IgnoreBoth = IgnoreStart | IgnoreEnd;
// Use IgnoreBoth directly for the one transition Android lacks; the other
// two that need kludges also fail this one.
#ifdef Q_OS_ANDROID
#define KLUDGING
#endif
#ifdef USING_WIN_TZ
constexpr BackendKludges MsNoStart = IgnoreStart;
constexpr BackendKludges MsNoBoth = IgnoreBoth;
#define KLUDGING
#else
constexpr BackendKludges MsNoStart = Clean;
constexpr BackendKludges MsNoBoth = Clean;
// And use IgnoreBoth directly for the one transition Android lacks.
#endif
#if QT_CONFIG(timezone) && QT_CONFIG(timezone_tzdb) && defined(__GLIBCXX__)
// The IANA-DB parser in libstdc++ (at least up to _GLIBCXX_RELEASE == 14) gets
// a lot of zone-transitions wrong in C++20's tzdb :-(
constexpr BackendKludges GlibCxxNoStart = IgnoreStart;
constexpr BackendKludges GlibCxxNoBoth = IgnoreBoth;
#define KLUDGING
#else
constexpr BackendKludges GlibCxxNoStart = Clean;
constexpr BackendKludges GlibCxxNoBoth = Clean;
#endif
const QTimeZone UTC(QTimeZone::UTC);
@ -557,7 +584,8 @@ void tst_QDate::startOfDay_endOfDay_data()
const BackendKludges msOpt;
} transitions[] = {
// The western Mexico time-zones skipped the first hour of 1970.
{ "BajaMexico", "America/Hermosillo", QDate(1970, 1, 1), QTime(1, 0), late, MsNoStart },
{ "BajaMexico", "America/Hermosillo", QDate(1970, 1, 1), QTime(1, 0), late,
MsNoStart | GlibCxxNoStart },
// Compare tst_QDateTime::fromStringDateFormat(ISO 24:00 in DST).
{ "Brazil", "America/Sao_Paulo", QDate(2008, 10, 19), QTime(1, 0), late, Clean },
@ -569,7 +597,8 @@ void tst_QDate::startOfDay_endOfDay_data()
// Two Pacific zones skipped days to get on the west of the
// International Date Line; those days have neither start nor end.
{ "Kiritimati", "Pacific/Kiritimati", QDate(1994, 12, 31), invalid, invalid, IgnoreBoth },
{ "Samoa", "Pacific/Apia", QDate(2011, 12, 30), invalid, invalid, MsNoBoth },
{ "Samoa", "Pacific/Apia", QDate(2011, 12, 30), invalid, invalid,
MsNoBoth | GlibCxxNoBoth },
// TODO: find other zones with transitions at/crossing midnight.
};
@ -612,7 +641,7 @@ void tst_QDate::startOfDay_endOfDay()
QFETCH(const QTimeZone, zone);
QFETCH(const QTime, start);
QFETCH(const QTime, end);
#if defined(USING_WIN_TZ) || defined(Q_OS_ANDROID) // Coping with backend limitations.
#ifdef KLUDGING // Cope with backend limitations:
QFETCH(const BackendKludges, kludge);
#define UNLESSKLUDGE(flag) if (!kludge.testFlag(flag))
#else
@ -629,6 +658,9 @@ void tst_QDate::startOfDay_endOfDay()
if (start.isValid()) {
QVERIFY(front.isValid());
QCOMPARE(front.date(), date);
#ifdef USING_MS_TZDB
QEXPECT_FAIL("Brazil", "MS misreads the IANA DB", Continue);
#endif
UNLESSKLUDGE(IgnoreStart) QCOMPARE(front.time(), start);
} else UNLESSKLUDGE(IgnoreStart) {
auto report = qScopeGuard([front]() { qDebug() << "Start of day:" << front; });
@ -646,6 +678,7 @@ void tst_QDate::startOfDay_endOfDay()
}
#undef UNLESSKLUDGE
}
#undef KLUDGING
void tst_QDate::startOfDay_endOfDay_fixed_data()
{
@ -700,7 +733,7 @@ void tst_QDate::startOfDay_endOfDay_fixed()
// Minimal testing for LocalTime and TimeZone
QCOMPARE(date.startOfDay().date(), date);
QCOMPARE(date.endOfDay().date(), date);
#if QT_CONFIG(timezone)
#if QT_CONFIG(timezone) && !defined(GLIBC_TZDB_MISPARSE)
const QTimeZone cet("Europe/Oslo");
if (cet.isValid()) {
QCOMPARE(date.startOfDay(cet).date(), date);

View File

@ -14,6 +14,22 @@
# define USING_WIN_TZ
#endif
#undef USING_MS_TZDB
#undef USING_WIN_TZ
#ifdef Q_OS_WIN
# if QT_CONFIG(timezone_tzdb)
# define USING_MS_TZDB
# elif !QT_CONFIG(icu)
# define USING_WIN_TZ
# endif
#endif
#undef GLIBC_TZDB_MISPARSE
#if QT_CONFIG(timezone_tzdb) && defined(__GLIBCXX__) // && _GLIBCXX_RELEASE <= 14
# define GLIBC_TZDB_MISPARSE // QTBUG-127598
#endif
#undef INADEQUATE_TZ_DATA
#ifdef Q_OS_WIN
# include <qt_windows.h>
# ifdef USING_WIN_TZ
@ -820,6 +836,10 @@ void tst_QDateTime::setMSecsSinceEpoch()
QVERIFY(!cet.isValid()); // overflows
} else if (zoneIsCET) {
QVERIFY(cet.isValid());
#ifdef USING_MS_TZDB
QEXPECT_FAIL("old max (Tue Jun 3 21:59:59 5874898)",
"MS doesn't handle the distant future", Continue);
#endif
QCOMPARE(dt.toLocalTime(), cet);
// Test converting from LocalTime to UTC back to LocalTime.
@ -837,6 +857,10 @@ void tst_QDateTime::setMSecsSinceEpoch()
dt2.setTimeZone(europe);
#endif
dt2.setMSecsSinceEpoch(msecs);
#ifdef GLIBC_TZDB_MISPARSE
QEXPECT_FAIL("old max (Tue Jun 3 21:59:59 5874898)",
"QTBUG-127598 Bad libstdc++ data", Continue);
#endif
if (cet.date().year() >= 1970 || cet.date() == utc.date())
QCOMPARE(dt2.date(), cet.date());
@ -913,11 +937,18 @@ void tst_QDateTime::fromMSecsSinceEpoch()
QCOMPARE(dtOffset.time(), utc.time().addMSecs(60*60*1000));
}
#ifdef USING_MS_TZDB
if (zoneIsCET && QTest::currentDataTag() == "old max (Tue Jun 3 21:59:59 5874898)"_ba) {
qInfo("Distant future of CET unhandled");
} else
#endif
if (zoneIsCET) {
QCOMPARE(dtLocal.toLocalTime(), cet);
QCOMPARE(dtUtc.toLocalTime(), cet);
if (msecs != Bound::max())
QCOMPARE(dtOffset.toLocalTime(), cet);
} else {
qInfo("CET-specific test skipped");
}
if (!localOverflow)
@ -969,6 +1000,9 @@ void tst_QDateTime::fromSecsSinceEpoch()
#endif
const qint64 last = maxSeconds - qMax(lateZone, 0);
#ifdef GLIBC_TZDB_MISPARSE
QEXPECT_FAIL("", "QTBUG-127598 Bad libstdc++ data", Continue);
#endif
QVERIFY(QDateTime::fromSecsSinceEpoch(last).isValid());
QVERIFY(!QDateTime::fromSecsSinceEpoch(last + 1).isValid());
const qint64 first = -maxSeconds - qMin(early.addYears(1).toLocalTime().offsetFromUtc(), 0);
@ -3361,6 +3395,9 @@ void tst_QDateTime::fromStringStringFormat()
QDateTime dt = QDateTime::fromString(string, format, baseYear);
#ifdef USING_MS_TZDB
QEXPECT_FAIL("spring-forward-midnight", "MS misreads the IANA DB", Continue);
#endif
QCOMPARE(dt, expected);
if (expected.isValid()) {
QCOMPARE(dt.timeSpec(), expected.timeSpec());
@ -3661,7 +3698,18 @@ void tst_QDateTime::zoneAtTime()
const QTime noon(12, 0);
QTimeZone zone(ianaID);
#ifdef USING_MS_TZDB
QEXPECT_FAIL("after:NPT", "MS lacks NPT", Abort);
QEXPECT_FAIL("before:NPT", "MS lacks NPT", Abort);
#endif
QVERIFY(zone.isValid());
#ifdef GLIBC_TZDB_MISPARSE
QEXPECT_FAIL("after:WST", "QTBUG-127598 Bad libstdc++ data", Abort);
QEXPECT_FAIL("before:WST", "QTBUG-127598 Bad libstdc++ data", Abort);
#endif
#ifdef USING_MS_TZDB
QEXPECT_FAIL("after:ACWST", "MS gets ACWST wrong", Abort);
#endif
QCOMPARE(QDateTime(date, noon, zone).offsetFromUtc(), offset);
QCOMPARE(zone.offsetFromUtc(QDateTime(date, noon, zone)), offset);
#else
@ -4401,8 +4449,10 @@ void tst_QDateTime::timeZones() const
// kicked the habit by the end of 2100.
constexpr int longYear = 1'143'678;
constexpr qint64 millisInWeek = qint64(7) * 24 * 60 * 60 * 1000;
if (QDateTime(QDate(longYear, 3, 24), QTime(12, 0), cet).msecsTo(
QDateTime(QDate(longYear, 3, 31), QTime(12, 0), cet)) < millisInWeek) {
const QDateTime longYearEarly(QDate(longYear, 3, 24), QTime(12, 0), cet);
const QDateTime longYearLate(QDate(longYear, 3, 31), QTime(12, 0), cet);
if (longYearEarly.isValid() && longYearLate.isValid()
&& longYearEarly.msecsTo(longYearLate) < millisInWeek) {
inGap = QDateTime(QDate(longYear, 3, 27), QTime(2, 30), cet);
QVERIFY(inGap.isValid());
QCOMPARE(inGap.date(), QDate(longYear, 3, 27));

View File

@ -13,7 +13,7 @@
#include <QOperatingSystemVersion>
#endif
#if defined(Q_OS_WIN) && !QT_CONFIG(icu)
#if defined(Q_OS_WIN) && !QT_CONFIG(icu) && !QT_CONFIG(timezone_tzdb)
# define USING_WIN_TZ
#endif
@ -762,6 +762,13 @@ void tst_QTimeZone::hasAlternativeName()
void tst_QTimeZone::specificTransition_data()
{
#if QT_CONFIG(timezone) && QT_CONFIG(timezone_tzdb) && defined(__GLIBCXX__)
QSKIP("libstdc++'s C++20 misreads the IANA DB for Moscow's transitions (among others).");
#endif
#if defined Q_OS_ANDROID && !QT_CONFIG(timezone_tzdb)
if (!QTimeZone("Europe/Moscow").hasTransitions())
QSKIP("Android time-zone back-end has no transition data");
#endif
QTest::addColumn<QByteArray>("zone");
QTest::addColumn<QDate>("start");
QTest::addColumn<QDate>("stop");
@ -771,10 +778,6 @@ void tst_QTimeZone::specificTransition_data()
QTest::addColumn<int>("offset");
QTest::addColumn<int>("stdoff");
QTest::addColumn<int>("dstoff");
#ifdef Q_OS_ANDROID
if (!QTimeZone("Europe/Moscow").hasTransitions())
QSKIP("Android time-zone back-end has no transition data");
#endif
// Moscow ditched DST on 2010-10-31 but has since changed standard offset twice.
#ifdef USING_WIN_TZ
@ -1128,7 +1131,7 @@ void tst_QTimeZone::isValidId_data()
// Parts separated by '/', each part min 1 and max of 14 chars
TESTSET("empty", "", false);
TESTSET("minimal", "m", true);
#if defined(Q_OS_ANDROID) || QT_CONFIG(icu)
#if (defined(Q_OS_ANDROID) || QT_CONFIG(icu)) && !QT_CONFIG(timezone_tzdb)
TESTSET("maximal", "East-Saskatchewan", true); // Android actually uses this
TESTSET("too long", "North-Saskatchewan", false); // ... but thankfully not this.
#else
@ -1199,7 +1202,7 @@ void tst_QTimeZone::isValidId_data()
QTest::newRow("a,z alone") << QByteArray("a,z") << false;
QTest::newRow("/z alone") << QByteArray("/z") << false;
QTest::newRow("-z alone") << QByteArray("-z") << false;
#if defined(Q_OS_ANDROID) || QT_CONFIG(icu)
#if (defined(Q_OS_ANDROID) || QT_CONFIG(icu)) && !QT_CONFIG(timezone_tzdb)
QTest::newRow("long alone") << QByteArray("12345678901234567") << true;
QTest::newRow("over-long alone") << QByteArray("123456789012345678") << false;
#else
@ -1340,7 +1343,7 @@ void tst_QTimeZone::utcTest()
void tst_QTimeZone::icuTest()
{
#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(icu) && !defined(Q_OS_UNIX)
#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(icu) && !QT_CONFIG(timezone_tzdb) && !defined(Q_OS_UNIX)
// Known datetimes
qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
@ -1383,12 +1386,13 @@ void tst_QTimeZone::icuTest()
if (QTest::currentTestFailed())
return;
testEpochTranPrivate(QIcuTimeZonePrivate("America/Toronto"));
#endif // ICU not on Unix
#endif // ICU not on Unix, without tzdb
}
void tst_QTimeZone::tzTest()
{
#if defined QT_BUILD_INTERNAL && defined Q_OS_UNIX && !defined Q_OS_DARWIN && !defined Q_OS_ANDROID
#if defined QT_BUILD_INTERNAL && defined Q_OS_UNIX \
&& !QT_CONFIG(timezone_tzdb) && !defined Q_OS_DARWIN && !defined Q_OS_ANDROID
const auto UTC = QTimeZone::UTC;
// Known datetimes
qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), UTC).toMSecsSinceEpoch();
@ -1587,12 +1591,12 @@ void tst_QTimeZone::tzTest()
QDateTime dt(QDate(2016, 3, 28), QTime(0, 0), UTC);
QCOMPARE(tzBarnaul.data(dt.toMSecsSinceEpoch()).abbreviation, QString("+07"));
}
#endif // QT_BUILD_INTERNAL && Q_OS_UNIX && !Q_OS_DARWIN && !Q_OS_ANDROID
#endif // QT_BUILD_INTERNAL && Q_OS_UNIX && !timezone_tzdb && !Q_OS_DARWIN && !Q_OS_ANDROID
}
void tst_QTimeZone::macTest()
{
#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_DARWIN)
#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_DARWIN) && !QT_CONFIG(timezone_tzdb)
// Known datetimes
qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
@ -1633,7 +1637,7 @@ void tst_QTimeZone::macTest()
if (QTest::currentTestFailed())
return;
testEpochTranPrivate(QMacTimeZonePrivate("America/Toronto"));
#endif // QT_BUILD_INTERNAL && Q_OS_DARWIN
#endif // QT_BUILD_INTERNAL && Q_OS_DARWIN without tzdb
}
void tst_QTimeZone::darwinTypes()