Adapt QTimeZone to handle Qt::TimeSpec machinery

[ChangeLog][QtCore][QTimeZone] QTimeZone is now always defined;
feature timezone now controls most of its prior API and some new API
is added, most of it always present, to enable QTimeZone to package a
Qt::TimeSpec and, for Qt::OffsetFromUTC, its offset. Prior to this
change, APIs using Qt::TimeSpec had to provide a separate function
taking a QTimeZone alongside a function taking a Qt::TimeSpec and
optional offset; it will now be possible to unify these into a single
function taking a QTimeZone. Adaptation of other Qt classes to do so
shall follow.

Task-number: QTBUG-108199
Change-Id: If5ec3cc63920af882ebb333bf69cde266b1f6ad7
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Edward Welbourne 2022-11-10 18:52:01 +01:00
parent ab15c02991
commit ae6186c7e8
9 changed files with 997 additions and 123 deletions

View File

@ -256,12 +256,13 @@ qt_internal_add_module(Core
time/qcalendarbackend_p.h
time/qcalendarmath_p.h
time/qdatetime.cpp time/qdatetime.h time/qdatetime_p.h
time/qlocaltime.cpp time/qlocaltime_p.h
time/qgregoriancalendar.cpp time/qgregoriancalendar_p.h
time/qjuliancalendar.cpp time/qjuliancalendar_p.h
time/qlocaltime.cpp time/qlocaltime_p.h
time/qmilankoviccalendar.cpp time/qmilankoviccalendar_p.h
time/qromancalendar.cpp time/qromancalendar_p.h
time/qromancalendar_data_p.h
time/qtimezone.cpp time/qtimezone.h
tools/qalgorithms.h
tools/qarraydata.cpp tools/qarraydata.h
tools/qarraydataops.h
@ -872,7 +873,6 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_jalalicalendar
qt_internal_extend_target(Core CONDITION QT_FEATURE_timezone
SOURCES
time/qtimezone.cpp time/qtimezone.h
time/qtimezoneprivate.cpp time/qtimezoneprivate_p.h
time/qtimezoneprivate_data_p.h
)

View File

@ -728,15 +728,25 @@
Mean Time has zero offset from it. Neither UTC nor OffsetFromUTC
has any transitions.
When specifying a datetime using OffsetFromUTC, the offset from UTC must
also be supplied (it is measured in seconds). To specify a datetime using
TimeZone, a QTimeZone must be supplied. From Qt 6.5, a QTimeZone can now
package a timespec with, where needed, an offset as a lightweight time
description, so that passing a QTimeZone now provides a uniform way to use
datetime APIs, saving the need to call them differently for different
timespecs.
\note After a change to the system time-zone setting, the behavior
of LocalTime-based QDateTime objects created before the change is
undefined: QDateTime may have cached data that the change
invalidates. (This is not triggered by transitions of the system
invalidates. (This is not triggered by \e transitions of the system
time-zone.) In long-running processes, updates to the system's
time-zone data (e.g. when politicians change the rules for a zone)
may likewise lead to conflicts between the updated time-zone
information and data cached by QDateTime objects created before
the update, using either LocalTime or TimeZone.
\sa QTimeZone, QDateTime
*/
/*!

View File

@ -495,9 +495,9 @@ QTimeZone QTimeZone::fromCFTimeZone(CFTimeZoneRef timeZone)
CFTimeZoneRef QTimeZone::toCFTimeZone() const
{
#ifndef QT_NO_DYNAMIC_CAST
Q_ASSERT(dynamic_cast<const QMacTimeZonePrivate *>(d.data()));
Q_ASSERT(dynamic_cast<const QMacTimeZonePrivate *>(d.d));
#endif
const QMacTimeZonePrivate *p = static_cast<const QMacTimeZonePrivate *>(d.data());
const QMacTimeZonePrivate *p = static_cast<const QMacTimeZonePrivate *>(d.d);
return reinterpret_cast<CFTimeZoneRef>([p->nsTimeZone() copy]);
}

View File

@ -2704,6 +2704,7 @@ QDateTimePrivate::ZoneState QDateTimePrivate::zoneStateAtMillis(const QTimeZone
qint64 millis, DaylightStatus dst)
{
Q_ASSERT(zone.isValid());
Q_ASSERT(zone.timeSpec() == Qt::TimeZone);
// Get the effective data from QTimeZone
QTimeZonePrivate::Data data = zone.d->dataForLocalTime(millis, int(dst));
if (data.offsetFromUtc == QTimeZonePrivate::invalidSeconds())
@ -3170,6 +3171,7 @@ QDateTime::Data QDateTimePrivate::create(QDate toDate, QTime toTime, Qt::TimeSpe
inline QDateTime::Data QDateTimePrivate::create(QDate toDate, QTime toTime,
const QTimeZone &toTimeZone)
{
Q_ASSERT(toTimeZone.timeSpec() == Qt::TimeZone);
QDateTime::Data result(Qt::TimeZone);
Q_ASSERT(!result.isShort());
@ -3396,7 +3398,10 @@ QDateTime::QDateTime(QDate date, QTime time, Qt::TimeSpec spec, int offsetSecond
*/
QDateTime::QDateTime(QDate date, QTime time, const QTimeZone &timeZone)
: d(QDateTimePrivate::create(date, time, timeZone))
: d(timeZone.timeSpec() == Qt::TimeZone
? QDateTimePrivate::create(date, time, timeZone)
: QDateTimePrivate::create(date, time, timeZone.timeSpec(),
timeZone.fixedSecondsAheadOfUtc()))
{
}
#endif // timezone
@ -3757,10 +3762,11 @@ void QDateTime::setOffsetFromUtc(int offsetSeconds)
void QDateTime::setTimeZone(const QTimeZone &toZone)
{
d.detach(); // always detach
d->m_status = mergeSpec(d->m_status, Qt::TimeZone);
d->m_offsetFromUtc = 0;
d->m_timeZone = toZone;
refreshZonedDateTime(d, Qt::TimeZone);
d->m_status = mergeSpec(d->m_status, toZone.timeSpec());
d->m_offsetFromUtc = toZone.fixedSecondsAheadOfUtc();
if (toZone.timeSpec() == Qt::TimeZone)
d->m_timeZone = toZone;
checkValidDateTime(d);
}
#endif // timezone
@ -4527,6 +4533,16 @@ QDateTime QDateTime::toUTC() const
QDateTime QDateTime::toTimeZone(const QTimeZone &timeZone) const
{
switch (timeZone.timeSpec()) {
case Qt::OffsetFromUTC:
return toOffsetFromUtc(timeZone.fixedSecondsAheadOfUtc());
case Qt::LocalTime:
return toLocalTime();
case Qt::UTC:
return toUTC();
case Qt::TimeZone:
break;
}
if (getSpec(d) == Qt::TimeZone && d->m_timeZone == timeZone)
return *this;
@ -4939,6 +4955,9 @@ QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec, int offs
*/
QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone)
{
if (timeZone.timeSpec() != Qt::TimeZone)
return fromMSecsSinceEpoch(msecs, timeZone.timeSpec(), timeZone.fixedSecondsAheadOfUtc());
QDateTime dt;
dt.setTimeZone(timeZone);
if (timeZone.isValid())
@ -4962,6 +4981,9 @@ QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone
*/
QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, const QTimeZone &timeZone)
{
if (timeZone.timeSpec() != Qt::TimeZone)
return fromSecsSinceEpoch(secs, timeZone.timeSpec(), timeZone.fixedSecondsAheadOfUtc());
QDateTime dt;
dt.setTimeZone(timeZone);
if (timeZone.isValid())

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,17 @@
// Copyright (C) 2013 John Layt <jlayt@kde.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTIMEZONE_H
#define QTIMEZONE_H
#include <QtCore/qshareddata.h>
#include <QtCore/qlocale.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qlocale.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qswap.h>
#include <QtCore/qtclasshelpermacros.h>
#include <chrono>
QT_REQUIRE_CONFIG(timezone);
#if (defined(Q_OS_DARWIN) || defined(Q_QDOC)) && !defined(QT_NO_SYSTEMLOCALE)
Q_FORWARD_DECLARE_CF_TYPE(CFTimeZone);
Q_FORWARD_DECLARE_OBJC_CLASS(NSTimeZone);
@ -24,6 +23,64 @@ class QTimeZonePrivate;
class Q_CORE_EXPORT QTimeZone
{
struct ShortData
{
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
quintptr mode : 2;
#endif
qintptr offset : sizeof(void *) * 8 - 2;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
quintptr mode : 2;
#endif
// mode is a cycled Qt::TimeSpec, (int(spec) + 1) % 4, so that zero
// (lowest bits of a pointer) matches spec being Qt::TimeZone, for which
// Data holds a QTZP pointer instead of ShortData.
// Passing Qt::TimeZone gets the equivalent of a null QTZP; it is not short.
constexpr ShortData(Qt::TimeSpec spec, int secondsAhead = 0)
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
: offset(spec == Qt::OffsetFromUTC ? secondsAhead : 0),
mode((int(spec) + 1) & 3)
#else
: mode((int(spec) + 1) & 3),
offset(spec == Qt::OffsetFromUTC ? secondsAhead : 0)
#endif
{
}
friend constexpr bool operator==(const ShortData &lhs, const ShortData &rhs)
{ return lhs.mode == rhs.mode && lhs.offset == rhs.offset; }
constexpr Qt::TimeSpec spec() const { return Qt::TimeSpec((mode + 3) & 3); }
};
union Data
{
Data() noexcept;
Data(ShortData &&sd) : s(std::move(sd)) {}
Data(const Data &other) noexcept;
Data(Data &&other) noexcept;
Data &operator=(const Data &other) noexcept;
Data &operator=(Data &&other) noexcept { swap(other); return *this; }
~Data();
void swap(Data &other) noexcept { qt_ptr_swap(d, other.d); }
// isShort() is equivalent to s.spec() != Qt::TimeZone
bool isShort() const { return s.mode; } // a.k.a. quintptr(d) & 3
// Typse must support: out << wrap("C-strings");
template <typename Stream, typename Wrap>
void serialize(Stream &out, const Wrap &wrap) const;
Data(QTimeZonePrivate *dptr) noexcept;
Data &operator=(QTimeZonePrivate *dptr) noexcept;
const QTimeZonePrivate *operator->() const { Q_ASSERT(!isShort()); return d; }
QTimeZonePrivate *operator->() { Q_ASSERT(!isShort()); return d; }
QTimeZonePrivate *d = nullptr;
ShortData s;
};
QTimeZone(ShortData &&sd) : d(std::move(sd)) {}
public:
// Sane UTC offsets range from -14 to +14 hours:
enum {
@ -33,13 +90,20 @@ public:
MaxUtcOffsetSecs = +14 * 3600
};
QTimeZone() noexcept;
explicit QTimeZone(int offsetSeconds);
enum Initialization { LocalTime, UTC };
QTimeZone() noexcept;
Q_IMPLICIT QTimeZone(Initialization spec) noexcept
: d(ShortData(spec == UTC ? Qt::UTC : Qt::LocalTime)) {}
#if QT_CONFIG(timezone)
explicit QTimeZone(int offsetSeconds);
explicit QTimeZone(const QByteArray &ianaId);
QTimeZone(const QByteArray &zoneId, int offsetSeconds, const QString &name,
const QString &abbreviation, QLocale::Territory territory = QLocale::AnyTerritory,
const QString &comment = QString());
#endif // timezone backends
QTimeZone(const QTimeZone &other) noexcept;
QTimeZone(QTimeZone &&other) noexcept;
~QTimeZone();
@ -55,6 +119,27 @@ public:
bool isValid() const;
static QTimeZone fromDurationAheadOfUtc(std::chrono::seconds offset)
{
return fromSecondsAheadOfUtc(int(offset.count()));
}
static QTimeZone fromSecondsAheadOfUtc(int offset)
{
return QTimeZone((offset >= MinUtcOffsetSecs && offset <= MaxUtcOffsetSecs)
? ShortData(offset ? Qt::OffsetFromUTC : Qt::UTC, offset)
: ShortData(Qt::TimeZone));
}
constexpr Qt::TimeSpec timeSpec() const noexcept { return d.s.spec(); }
constexpr int fixedSecondsAheadOfUtc() const noexcept
{ return timeSpec() == Qt::OffsetFromUTC ? int(d.s.offset) : 0; }
static constexpr bool isUtcOrFixedOffset(Qt::TimeSpec spec) noexcept
{ return spec == Qt::UTC || spec == Qt::OffsetFromUTC; }
constexpr bool isUtcOrFixedOffset() const noexcept { return isUtcOrFixedOffset(timeSpec()); }
#if QT_CONFIG(timezone)
QTimeZone asBackendZone() const;
enum TimeType {
StandardTime = 0,
DaylightTime = 1,
@ -79,10 +164,10 @@ public:
QByteArray id() const;
QLocale::Territory territory() const;
#if QT_DEPRECATED_SINCE(6, 6)
# if QT_DEPRECATED_SINCE(6, 6)
QT_DEPRECATED_VERSION_X_6_6("Use territory() instead")
QLocale::Country country() const;
#endif
# endif
QString comment() const;
QString displayName(const QDateTime &atDateTime,
@ -125,14 +210,14 @@ public:
static QList<QByteArray> windowsIdToIanaIds(const QByteArray &windowsId,
QLocale::Territory territory);
#if (defined(Q_OS_DARWIN) || defined(Q_QDOC)) && !defined(QT_NO_SYSTEMLOCALE)
# if (defined(Q_OS_DARWIN) || defined(Q_QDOC)) && !defined(QT_NO_SYSTEMLOCALE)
static QTimeZone fromCFTimeZone(CFTimeZoneRef timeZone);
CFTimeZoneRef toCFTimeZone() const Q_DECL_CF_RETURNS_RETAINED;
static QTimeZone fromNSTimeZone(const NSTimeZone *timeZone);
NSTimeZone *toNSTimeZone() const Q_DECL_NS_RETURNS_AUTORELEASED;
#endif
# endif
#if __cpp_lib_chrono >= 201907L || defined(Q_QDOC)
# if __cpp_lib_chrono >= 201907L || defined(Q_QDOC)
QT_POST_CXX17_API_IN_EXPORTED_CLASS
static QTimeZone fromStdTimeZonePtr(const std::chrono::time_zone *timeZone)
{
@ -141,20 +226,25 @@ public:
const std::string_view timeZoneName = timeZone->name();
return QTimeZone(QByteArrayView(timeZoneName).toByteArray());
}
#endif
# endif
#endif // feature timezone
private:
#ifndef QT_NO_DATASTREAM
friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz);
#endif
#ifndef QT_NO_DEBUG_STREAM
friend Q_CORE_EXPORT QDebug operator<<(QDebug dbg, const QTimeZone &tz);
#endif
QTimeZone(QTimeZonePrivate &dd);
friend class QTimeZonePrivate;
friend class QDateTime;
friend class QDateTimePrivate;
QSharedDataPointer<QTimeZonePrivate> d;
Data d;
};
#if QT_CONFIG(timezone)
Q_DECLARE_TYPEINFO(QTimeZone::OffsetData, Q_RELOCATABLE_TYPE);
#endif
Q_DECLARE_SHARED(QTimeZone)
#ifndef QT_NO_DATASTREAM

View File

@ -82,6 +82,7 @@ qt_internal_extend_target(Bootstrap
../../corelib/time/qgregoriancalendar.cpp
../../corelib/time/qlocaltime.cpp
../../corelib/time/qromancalendar.cpp
../../corelib/time/qtimezone.cpp
../../corelib/tools/qarraydata.cpp
../../corelib/tools/qbitarray.cpp
../../corelib/tools/qcommandlineoption.cpp

View File

@ -8,6 +8,4 @@ add_subdirectory(qdate)
add_subdirectory(qdatetime)
add_subdirectory(qdatetimeparser)
add_subdirectory(qtime)
if(QT_FEATURE_timezone AND NOT INTEGRITY)
add_subdirectory(qtimezone)
endif()
add_subdirectory(qtimezone)

View File

@ -22,8 +22,14 @@ private Q_SLOTS:
// Public class default system tests
void createTest();
void nullTest();
void systemZone();
void assign();
void compare();
void timespec();
void offset();
void dataStreamTest();
#if QT_CONFIG(timezone)
void asBackendZone();
void systemZone();
void isTimeZoneIdAvailable();
void availableTimeZoneIds();
void utcOffsetId_data();
@ -51,16 +57,17 @@ private Q_SLOTS:
void localeSpecificDisplayName();
void stdCompatibility_data();
void stdCompatibility();
#endif // timezone backends
private:
void printTimeZone(const QTimeZone &tz);
#ifdef QT_BUILD_INTERNAL
#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(timezone)
// Generic tests of privates, called by implementation-specific private tests:
void testCetPrivate(const QTimeZonePrivate &tzp);
void testEpochTranPrivate(const QTimeZonePrivate &tzp);
#endif // QT_BUILD_INTERNAL
#endif // QT_BUILD_INTERNAL && timezone backends
// Set to true to print debug output, test Display Names and run long stress tests
const bool debug = false;
static constexpr bool debug = false;
};
void tst_QTimeZone::printTimeZone(const QTimeZone &tz)
@ -292,33 +299,138 @@ void tst_QTimeZone::nullTest()
QCOMPARE(data.daylightTimeOffset, invalidOffset);
}
void tst_QTimeZone::systemZone()
void tst_QTimeZone::assign()
{
const QTimeZone zone = QTimeZone::systemTimeZone();
QVERIFY(zone.isValid());
QCOMPARE(zone.id(), QTimeZone::systemTimeZoneId());
QCOMPARE(zone, QTimeZone(QTimeZone::systemTimeZoneId()));
// Check it behaves the same as local-time:
const QDate dates[] = {
QDate::fromJulianDay(0), // far in the distant past (LMT)
QDate(1625, 6, 8), // Before time-zones (date of Cassini's birth)
QDate(1901, 12, 13), // Last day before 32-bit time_t's range
QDate(1969, 12, 31), // Last day before the epoch
QDate(1970, 0, 0), // Start of epoch
QDate(2000, 2, 29), // An anomalous leap day
QDate(2038, 1, 20) // First day after 32-bit time_t's range
};
for (const auto &date : dates)
QCOMPARE(date.startOfDay(Qt::LocalTime), date.startOfDay(zone));
QTimeZone assignee;
QCOMPARE(assignee.timeSpec(), Qt::TimeZone);
assignee = QTimeZone();
QCOMPARE(assignee.timeSpec(), Qt::TimeZone);
assignee = QTimeZone::UTC;
QCOMPARE(assignee.timeSpec(), Qt::UTC);
assignee = QTimeZone::LocalTime;
QCOMPARE(assignee.timeSpec(), Qt::LocalTime);
assignee = QTimeZone();
QCOMPARE(assignee.timeSpec(), Qt::TimeZone);
assignee = QTimeZone::fromSecondsAheadOfUtc(1);
QCOMPARE(assignee.timeSpec(), Qt::OffsetFromUTC);
assignee = QTimeZone::fromSecondsAheadOfUtc(0);
QCOMPARE(assignee.timeSpec(), Qt::UTC);
#if QT_CONFIG(timezone)
{
const QTimeZone cet("Europe/Oslo");
assignee = cet;
QCOMPARE(assignee.timeSpec(), Qt::TimeZone);
}
#endif
}
#if __cpp_lib_chrono >= 201907L
const std::chrono::time_zone *currentTimeZone = std::chrono::current_zone();
QCOMPARE(QByteArrayView(currentTimeZone->name()), QByteArrayView(zone.id()));
void tst_QTimeZone::compare()
{
const QTimeZone local;
const QTimeZone utc(QTimeZone::UTC);
const auto secondEast = QTimeZone::fromSecondsAheadOfUtc(1);
QCOMPARE_NE(local, utc);
QCOMPARE_NE(utc, secondEast);
QCOMPARE_NE(secondEast, local);
QCOMPARE(local, QTimeZone());
QCOMPARE(utc, QTimeZone::fromSecondsAheadOfUtc(0));
QCOMPARE(secondEast, QTimeZone::fromDurationAheadOfUtc(std::chrono::seconds{1}));
}
void tst_QTimeZone::timespec()
{
using namespace std::chrono_literals;
QCOMPARE(QTimeZone().timeSpec(), Qt::TimeZone);
QCOMPARE(QTimeZone(QTimeZone::UTC).timeSpec(), Qt::UTC);
QCOMPARE(QTimeZone(QTimeZone::LocalTime).timeSpec(), Qt::LocalTime);
QCOMPARE(QTimeZone::fromSecondsAheadOfUtc(0).timeSpec(), Qt::UTC);
QCOMPARE(QTimeZone::fromDurationAheadOfUtc(0s).timeSpec(), Qt::UTC);
QCOMPARE(QTimeZone::fromDurationAheadOfUtc(0min).timeSpec(), Qt::UTC);
QCOMPARE(QTimeZone::fromDurationAheadOfUtc(0h).timeSpec(), Qt::UTC);
QCOMPARE(QTimeZone::fromSecondsAheadOfUtc(1).timeSpec(), Qt::OffsetFromUTC);
QCOMPARE(QTimeZone::fromSecondsAheadOfUtc(-1).timeSpec(), Qt::OffsetFromUTC);
QCOMPARE(QTimeZone::fromSecondsAheadOfUtc(36000).timeSpec(), Qt::OffsetFromUTC);
QCOMPARE(QTimeZone::fromSecondsAheadOfUtc(-36000).timeSpec(), Qt::OffsetFromUTC);
QCOMPARE(QTimeZone::fromDurationAheadOfUtc(3h - 20min +17s).timeSpec(), Qt::OffsetFromUTC);
{
const QTimeZone zone;
QCOMPARE(zone.timeSpec(), Qt::TimeZone);
}
{
const QTimeZone zone = { QTimeZone::UTC };
QCOMPARE(zone.timeSpec(), Qt::UTC);
}
{
const QTimeZone zone = { QTimeZone::LocalTime };
QCOMPARE(zone.timeSpec(), Qt::LocalTime);
}
{
const auto zone = QTimeZone::fromSecondsAheadOfUtc(0);
QCOMPARE(zone.timeSpec(), Qt::UTC);
}
{
const auto zone = QTimeZone::fromDurationAheadOfUtc(0s);
QCOMPARE(zone.timeSpec(), Qt::UTC);
}
{
const auto zone = QTimeZone::fromSecondsAheadOfUtc(1);
QCOMPARE(zone.timeSpec(), Qt::OffsetFromUTC);
}
{
const auto zone = QTimeZone::fromDurationAheadOfUtc(1s);
QCOMPARE(zone.timeSpec(), Qt::OffsetFromUTC);
}
#if QT_CONFIG(timezone)
QCOMPARE(QTimeZone("Europe/Oslo").timeSpec(), Qt::TimeZone);
#endif
}
void tst_QTimeZone::offset()
{
QCOMPARE(QTimeZone().fixedSecondsAheadOfUtc(), 0);
QCOMPARE(QTimeZone(QTimeZone::UTC).fixedSecondsAheadOfUtc(), 0);
QCOMPARE(QTimeZone::fromSecondsAheadOfUtc(0).fixedSecondsAheadOfUtc(), 0);
QCOMPARE(QTimeZone::fromDurationAheadOfUtc(std::chrono::seconds{}).fixedSecondsAheadOfUtc(), 0);
QCOMPARE(QTimeZone::fromDurationAheadOfUtc(std::chrono::minutes{}).fixedSecondsAheadOfUtc(), 0);
QCOMPARE(QTimeZone::fromDurationAheadOfUtc(std::chrono::hours{}).fixedSecondsAheadOfUtc(), 0);
QCOMPARE(QTimeZone::fromSecondsAheadOfUtc(1).fixedSecondsAheadOfUtc(), 1);
QCOMPARE(QTimeZone::fromSecondsAheadOfUtc(-1).fixedSecondsAheadOfUtc(), -1);
QCOMPARE(QTimeZone::fromSecondsAheadOfUtc(36000).fixedSecondsAheadOfUtc(), 36000);
QCOMPARE(QTimeZone::fromSecondsAheadOfUtc(-36000).fixedSecondsAheadOfUtc(), -36000);
{
const QTimeZone zone;
QCOMPARE(zone.fixedSecondsAheadOfUtc(), 0);
}
{
const QTimeZone zone = { QTimeZone::UTC };
QCOMPARE(zone.fixedSecondsAheadOfUtc(), 0);
}
{
const auto zone = QTimeZone::fromSecondsAheadOfUtc(0);
QCOMPARE(zone.fixedSecondsAheadOfUtc(), 0);
}
{
const auto zone = QTimeZone::fromDurationAheadOfUtc(std::chrono::seconds{});
QCOMPARE(zone.fixedSecondsAheadOfUtc(), 0);
}
{
const auto zone = QTimeZone::fromSecondsAheadOfUtc(1);
QCOMPARE(zone.fixedSecondsAheadOfUtc(), 1);
}
{
const auto zone = QTimeZone::fromDurationAheadOfUtc(std::chrono::seconds{1});
QCOMPARE(zone.fixedSecondsAheadOfUtc(), 1);
}
#if QT_CONFIG(timezone)
QCOMPARE(QTimeZone("Europe/Oslo").fixedSecondsAheadOfUtc(), 0);
#endif
}
void tst_QTimeZone::dataStreamTest()
{
#ifndef QT_NO_DATASTREAM
// Test the OffsetFromUtc backend serialization. First with a custom timezone:
QTimeZone tz1("QST", 123456, "Qt Standard Time", "QST", QLocale::Norway, "Qt Testing");
QByteArray tmp;
@ -372,6 +484,42 @@ void tst_QTimeZone::dataStreamTest()
ds >> tz2;
}
QCOMPARE(tz2.id(), tz1.id());
#endif
}
#if QT_CONFIG(timezone)
void tst_QTimeZone::asBackendZone()
{
QCOMPARE(QTimeZone(QTimeZone::LocalTime).asBackendZone(), QTimeZone::systemTimeZone());
QCOMPARE(QTimeZone(QTimeZone::UTC).asBackendZone(), QTimeZone::utc());
QCOMPARE(QTimeZone::fromSecondsAheadOfUtc(-300).asBackendZone(), QTimeZone(-300));
QTimeZone cet("Europe/Oslo");
QCOMPARE(cet.asBackendZone(), cet);
}
void tst_QTimeZone::systemZone()
{
const QTimeZone zone = QTimeZone::systemTimeZone();
QVERIFY(zone.isValid());
QCOMPARE(zone.id(), QTimeZone::systemTimeZoneId());
QCOMPARE(zone, QTimeZone(QTimeZone::systemTimeZoneId()));
// Check it behaves the same as local-time:
const QDate dates[] = {
QDate::fromJulianDay(0), // far in the distant past (LMT)
QDate(1625, 6, 8), // Before time-zones (date of Cassini's birth)
QDate(1901, 12, 13), // Last day before 32-bit time_t's range
QDate(1969, 12, 31), // Last day before the epoch
QDate(1970, 0, 0), // Start of epoch
QDate(2000, 2, 29), // An anomalous leap day
QDate(2038, 1, 20) // First day after 32-bit time_t's range
};
for (const auto &date : dates)
QCOMPARE(date.startOfDay(Qt::LocalTime), date.startOfDay(zone));
#if __cpp_lib_chrono >= 201907L
const std::chrono::time_zone *currentTimeZone = std::chrono::current_zone();
QCOMPARE(QByteArrayView(currentTimeZone->name()), QByteArrayView(zone.id()));
#endif
}
void tst_QTimeZone::isTimeZoneIdAvailable()
@ -1624,6 +1772,7 @@ void tst_QTimeZone::stdCompatibility()
QSKIP("This test requires C++20's <chrono>.");
#endif
}
#endif // timezone backends
QTEST_APPLESS_MAIN(tst_QTimeZone)
#include "tst_qtimezone.moc"