Make QTimeZonePrivate::Data construction easier

Turn QTZP::invalidData() into a default constructor. A second
constructor taking needed details avoids exercising that default
constructor elsewhere, when the invalid values aren't what we
need. All constructed instances now have all members initialized.

In the TZ backend, add some PosixZone helpers to create Data objects,
reducing the complexity of calculatePosixTransitions().

Task-number: QTBUG-122619
Change-Id: I56557a2f6249d60012355d5d72c662474be76e51
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
bb10
Edward Welbourne 2024-04-10 16:41:13 +02:00
parent 7aada633be
commit fccf6cfdd2
7 changed files with 71 additions and 72 deletions

View File

@ -230,7 +230,7 @@ bool QTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
QTimeZonePrivate::Data QTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
{
Q_UNUSED(forMSecsSinceEpoch);
return invalidData();
return {};
}
// Private only method for use by QDateTime to convert local msecs to epoch msecs
@ -489,13 +489,13 @@ bool QTimeZonePrivate::hasTransitions() const
QTimeZonePrivate::Data QTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const
{
Q_UNUSED(afterMSecsSinceEpoch);
return invalidData();
return {};
}
QTimeZonePrivate::Data QTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
{
Q_UNUSED(beforeMSecsSinceEpoch);
return invalidData();
return {};
}
QTimeZonePrivate::DataList QTimeZonePrivate::transitions(qint64 fromMSecsSinceEpoch,
@ -586,16 +586,6 @@ void QTimeZonePrivate::serialize(QDataStream &ds) const
// Static Utility Methods
QTimeZonePrivate::Data QTimeZonePrivate::invalidData()
{
Data data;
data.atMSecsSinceEpoch = invalidMSecs();
data.offsetFromUtc = invalidSeconds();
data.standardTimeOffset = invalidSeconds();
data.daylightTimeOffset = invalidSeconds();
return data;
}
QTimeZone::OffsetData QTimeZonePrivate::invalidOffsetData()
{
QTimeZone::OffsetData offsetData;

View File

@ -182,16 +182,11 @@ bool QAndroidTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
QTimeZonePrivate::Data QAndroidTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
{
if (androidTimeZone.isValid()) {
Data data;
data.atMSecsSinceEpoch = forMSecsSinceEpoch;
data.standardTimeOffset = standardTimeOffset(forMSecsSinceEpoch);
data.offsetFromUtc = offsetFromUtc(forMSecsSinceEpoch);
data.daylightTimeOffset = data.offsetFromUtc - data.standardTimeOffset;
data.abbreviation = abbreviation(forMSecsSinceEpoch);
return data;
} else {
return invalidData();
return Data(abbreviation(forMSecsSinceEpoch), forMSecsSinceEpoch,
offsetFromUtc(forMSecsSinceEpoch),
standardTimeOffset(forMSecsSinceEpoch));
}
return {};
}
// java.util.TimeZone does not directly provide transitions,

View File

@ -153,7 +153,7 @@ static QTimeZonePrivate::Data ucalTimeZoneTransition(UCalendar *m_ucal,
UTimeZoneTransitionType type,
qint64 atMSecsSinceEpoch)
{
QTimeZonePrivate::Data tran = QTimeZonePrivate::invalidData();
QTimeZonePrivate::Data tran;
// Clone the ucal so we don't change the shared object
UErrorCode status = U_ZERO_ERROR;
@ -384,7 +384,7 @@ bool QIcuTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
QTimeZonePrivate::Data QIcuTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
{
// Available in ICU C++ api, and draft C api in v50
QTimeZonePrivate::Data data = invalidData();
QTimeZonePrivate::Data data;
#if U_ICU_VERSION_MAJOR_NUM >= 50
data = ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE,
forMSecsSinceEpoch);
@ -417,7 +417,7 @@ QTimeZonePrivate::Data QIcuTimeZonePrivate::nextTransition(qint64 afterMSecsSinc
return ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_NEXT, afterMSecsSinceEpoch);
#else
Q_UNUSED(afterMSecsSinceEpoch);
return invalidData();
return {};
#endif
}
@ -428,7 +428,7 @@ QTimeZonePrivate::Data QIcuTimeZonePrivate::previousTransition(qint64 beforeMSec
return ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_PREVIOUS, beforeMSecsSinceEpoch);
#else
Q_UNUSED(beforeMSecsSinceEpoch);
return invalidData();
return {};
#endif
}

View File

@ -190,7 +190,7 @@ QTimeZonePrivate::Data QMacTimeZonePrivate::nextTransition(qint64 afterMSecsSinc
const NSTimeInterval nextSecs = nextDate.timeIntervalSince1970;
if (nextDate == nil || nextSecs <= seconds) {
[nextDate release];
return invalidData();
return {};
}
tran.atMSecsSinceEpoch = nextSecs * 1000;
tran.offsetFromUtc = [m_nstz secondsFromGMTForDate:nextDate];
@ -273,7 +273,7 @@ QTimeZonePrivate::Data QMacTimeZonePrivate::previousTransition(qint64 beforeMSec
return data(qint64(prevSecs * 1e3));
// No transition data; or first transition later than requested time.
return invalidData();
return {};
}
QByteArray QMacTimeZonePrivate::systemTimeZoneId() const

View File

@ -44,13 +44,26 @@ QT_BEGIN_NAMESPACE
class Q_AUTOTEST_EXPORT QTimeZonePrivate : public QSharedData
{
public:
//Version of QTimeZone::OffsetData struct using msecs for efficiency
// Version of QTimeZone::OffsetData struct using msecs for efficiency
struct Data {
QString abbreviation;
qint64 atMSecsSinceEpoch;
int offsetFromUtc;
int standardTimeOffset;
int daylightTimeOffset;
Data()
: atMSecsSinceEpoch(QTimeZonePrivate::invalidMSecs()),
offsetFromUtc(QTimeZonePrivate::invalidSeconds()),
standardTimeOffset(QTimeZonePrivate::invalidSeconds()),
daylightTimeOffset(QTimeZonePrivate::invalidSeconds())
{}
Data(const QString &name, qint64 when, int offset, int standard)
: abbreviation(name),
atMSecsSinceEpoch(when),
offsetFromUtc(offset),
standardTimeOffset(standard),
daylightTimeOffset(offset - standard)
{}
};
typedef QList<Data> DataList;
@ -112,7 +125,6 @@ public:
{ return (std::numeric_limits<qint64>::min)(); }
[[nodiscard]] static constexpr qint64 invalidSeconds()
{ return (std::numeric_limits<int>::min)(); }
static Data invalidData();
static QTimeZone::OffsetData invalidOffsetData();
static QTimeZone::OffsetData toOffsetData(const Data &data);
static bool isValidId(const QByteArray &ianaId);

View File

@ -523,6 +523,16 @@ struct PosixZone
QString name;
int offset = InvalidOffset;
bool hasValidOffset() const noexcept { return offset != InvalidOffset; }
QTimeZonePrivate::Data dataAt(qint64 when)
{
Q_ASSERT(hasValidOffset());
return QTimeZonePrivate::Data(name, when, offset, offset);
}
QTimeZonePrivate::Data dataAtOffset(qint64 when, int standard)
{
Q_ASSERT(hasValidOffset());
return QTimeZonePrivate::Data(name, when, offset, standard);
}
static PosixZone parse(const char *&pos, const char *end);
};
@ -663,13 +673,9 @@ static QList<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArray
// If only the name part, or no DST specified, then no transitions
if (parts.size() == 1 || !dstZone.hasValidOffset()) {
QTimeZonePrivate::Data data;
data.atMSecsSinceEpoch = lastTranMSecs;
data.offsetFromUtc = stdZone.offset;
data.standardTimeOffset = stdZone.offset;
data.daylightTimeOffset = 0;
data.abbreviation = stdZone.name.isEmpty() ? QString::fromUtf8(parts.at(0)) : stdZone.name;
result << data;
result.emplaceBack(
stdZone.name.isEmpty() ? QString::fromUtf8(parts.at(0)) : stdZone.name,
lastTranMSecs, stdZone.offset, stdZone.offset);
return result;
}
if (parts.size() < 3 || parts.at(1).isEmpty() || parts.at(2).isEmpty())
@ -702,40 +708,33 @@ static QList<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArray
// moments; the atMSecsSinceEpoch values computed from them are
// correctly offse to be UTC-based.
QTimeZonePrivate::Data dstData; // Transition to DST
// Transition to daylight-saving time:
QDateTime dst(calculatePosixDate(dstDateRule, year)
.startOfDay(QTimeZone::UTC).addSecs(dstTime));
dstData.atMSecsSinceEpoch = dst.toMSecsSinceEpoch() - stdZone.offset * 1000;
dstData.offsetFromUtc = dstZone.offset;
dstData.standardTimeOffset = stdZone.offset;
dstData.daylightTimeOffset = dstZone.offset - stdZone.offset;
dstData.abbreviation = dstZone.name;
QTimeZonePrivate::Data stdData; // Transition to standard time
auto saving = dstZone.dataAtOffset(dst.toMSecsSinceEpoch() - stdZone.offset * 1000,
stdZone.offset);
// Transition to standard time:
QDateTime std(calculatePosixDate(stdDateRule, year)
.startOfDay(QTimeZone::UTC).addSecs(stdTime));
stdData.atMSecsSinceEpoch = std.toMSecsSinceEpoch() - dstZone.offset * 1000;
stdData.offsetFromUtc = stdZone.offset;
stdData.standardTimeOffset = stdZone.offset;
stdData.daylightTimeOffset = 0;
stdData.abbreviation = stdZone.name;
auto standard = stdZone.dataAt(std.toMSecsSinceEpoch() - dstZone.offset * 1000);
if (year == startYear) {
// Handle the special case of fixed state, which may be represented
// by fake transitions at start and end of each year:
if (dstData.atMSecsSinceEpoch < stdData.atMSecsSinceEpoch) {
if (saving.atMSecsSinceEpoch < standard.atMSecsSinceEpoch) {
if (dst <= QDate(year, 1, 1).startOfDay(QTimeZone::UTC)
&& std >= QDate(year, 12, 31).endOfDay(QTimeZone::UTC)) {
// Permanent DST:
dstData.atMSecsSinceEpoch = lastTranMSecs;
result << dstData;
saving.atMSecsSinceEpoch = lastTranMSecs;
result.emplaceBack(std::move(saving));
return result;
}
} else {
if (std <= QDate(year, 1, 1).startOfDay(QTimeZone::UTC)
&& dst >= QDate(year, 12, 31).endOfDay(QTimeZone::UTC)) {
// Permanent Standard time, perversely described:
stdData.atMSecsSinceEpoch = lastTranMSecs;
result << stdData;
standard.atMSecsSinceEpoch = lastTranMSecs;
result.emplaceBack(std::move(standard));
return result;
}
}
@ -744,14 +743,17 @@ static QList<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArray
const bool useStd = std.isValid() && std.date().year() == year && !stdZone.name.isEmpty();
const bool useDst = dst.isValid() && dst.date().year() == year && !dstZone.name.isEmpty();
if (useStd && useDst) {
if (dst < std)
result << dstData << stdData;
else
result << stdData << dstData;
if (dst < std) {
result.emplaceBack(std::move(saving));
result.emplaceBack(std::move(standard));
} else {
result.emplaceBack(std::move(standard));
result.emplaceBack(std::move(saving));
}
} else if (useStd) {
result << stdData;
result.emplaceBack(std::move(standard));
} else if (useDst) {
result << dstData;
result.emplaceBack(std::move(saving));
}
}
return result;
@ -1137,8 +1139,8 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::dataForTzTransition(QTzTransitionTime
QTimeZonePrivate::Data QTzTimeZonePrivate::dataFromRule(QTzTransitionRule rule,
qint64 msecsSinceEpoch) const
{
return { QString::fromUtf8(cached_data.m_abbreviations.at(rule.abbreviationIndex)),
msecsSinceEpoch, rule.stdOffset + rule.dstOffset, rule.stdOffset, rule.dstOffset };
return Data(QString::fromUtf8(cached_data.m_abbreviations.at(rule.abbreviationIndex)),
msecsSinceEpoch, rule.stdOffset + rule.dstOffset, rule.stdOffset);
}
QList<QTimeZonePrivate::Data> QTzTimeZonePrivate::getPosixTransitions(qint64 msNear) const
@ -1168,7 +1170,7 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
}
}
if (tranCache().isEmpty()) // Only possible if !isValid()
return invalidData();
return {};
// Otherwise, use the rule for the most recent or first transition:
auto last = std::partition_point(tranCache().cbegin(), tranCache().cend(),
@ -1199,7 +1201,7 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSince
return at.atMSecsSinceEpoch <= afterMSecsSinceEpoch;
});
return it == posixTrans.cend() ? invalidData() : *it;
return it == posixTrans.cend() ? Data{} : *it;
}
// Otherwise, if we can find a valid tran, use its rule:
@ -1207,7 +1209,7 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSince
[afterMSecsSinceEpoch] (const QTzTransitionTime &at) {
return at.atMSecsSinceEpoch <= afterMSecsSinceEpoch;
});
return last != tranCache().cend() ? dataForTzTransition(*last) : invalidData();
return last != tranCache().cend() ? dataForTzTransition(*last) : Data{};
}
QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
@ -1224,7 +1226,7 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecs
if (it > posixTrans.cbegin())
return *--it;
// It fell between the last transition (if any) and the first of the POSIX rule:
return tranCache().isEmpty() ? invalidData() : dataForTzTransition(tranCache().last());
return tranCache().isEmpty() ? Data{} : dataForTzTransition(tranCache().last());
}
// Otherwise if we can find a valid tran then use its rule
@ -1232,7 +1234,7 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecs
[beforeMSecsSinceEpoch] (const QTzTransitionTime &at) {
return at.atMSecsSinceEpoch < beforeMSecsSinceEpoch;
});
return last > tranCache().cbegin() ? dataForTzTransition(*--last) : invalidData();
return last > tranCache().cbegin() ? dataForTzTransition(*--last) : Data{};
}
bool QTzTimeZonePrivate::isTimeZoneIdAvailable(const QByteArray &ianaId) const

View File

@ -692,7 +692,7 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) cons
// Fell off start of rule, try previous rule.
}
// We don't have relevant data :-(
return invalidData();
return {};
}
bool QWinTimeZonePrivate::hasTransitions() const
@ -774,13 +774,13 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::nextTransition(qint64 afterMSecsSinc
}
}
// Apparently no transition after the given time:
return invalidData();
return {};
}
QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
{
if (beforeMSecsSinceEpoch <= minMSecs())
return invalidData();
return {};
int year = msecsToDate(beforeMSecsSinceEpoch).year();
for (int ruleIndex = ruleIndexForYear(m_tranRules, year);
@ -830,7 +830,7 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSec
}
}
// Apparently no transition before the given time:
return invalidData();
return {};
}
QByteArray QWinTimeZonePrivate::systemTimeZoneId() const
@ -866,7 +866,7 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::ruleToData(const QWinTransitionRule
QTimeZone::TimeType type,
bool fakeDst) const
{
Data tran = invalidData();
Data tran;
tran.atMSecsSinceEpoch = atMSecsSinceEpoch;
tran.standardTimeOffset = rule.standardTimeBias * -60;
if (fakeDst) {