QTzTimeZonePrivate: provide correct data for before the first transition

We previously used the data for after the first transition; but the
Olson database knows about local mean time for each zone, and it does
get used by the system libraries, so systemZone will conflict with
LocalTime once we use the time_t functions outside their 32-bit range
(coming shortly). Record the pre-zone data during parsing and use it
in the (fortunately only one) place that needs it.

Discovered the issue in the course of debugging other issues while
purging QDateTime of its wilful ignorance of pre-1970 DST.

Change-Id: Icf957460fa3ccbaf5165e79f38ac68b450ecf98f
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Edward Welbourne 2021-02-22 18:44:41 +01:00
parent 9d3a7c72ad
commit b6544dc36c
2 changed files with 24 additions and 14 deletions

View File

@ -300,6 +300,7 @@ struct QTzTimeZoneCacheEntry
QList<QTzTransitionRule> m_tranRules;
QList<QByteArray> m_abbreviations;
QByteArray m_posixRule;
QTzTransitionRule m_preZoneRule;
};
class Q_AUTOTEST_EXPORT QTzTimeZonePrivate final : public QTimeZonePrivate
@ -349,6 +350,7 @@ private:
QList<QTimeZonePrivate::Data> getPosixTransitions(qint64 msNear) const;
Data dataForTzTransition(QTzTransitionTime tran) const;
Data dataFromRule(QTzTransitionRule rule, qint64 msecsSinceEpoch) const;
#if QT_CONFIG(icu)
mutable QSharedDataPointer<QTimeZonePrivate> m_icu;
#endif

View File

@ -768,12 +768,20 @@ QTzTimeZoneCacheEntry QTzTimeZoneCache::findEntry(const QByteArray &ianaId)
ret.m_abbreviations.append(it.value());
abbrindList.append(it.key());
}
// Map tz_abbrind from map's keys (as initially read) to abbrindList's
// indices (used hereafter):
for (int i = 0; i < typeList.size(); ++i)
typeList[i].tz_abbrind = abbrindList.indexOf(typeList.at(i).tz_abbrind);
// TODO: is typeList[0] always the "before zones" data ? It seems to be ...
if (typeList.size())
ret.m_preZoneRule = { typeList.at(0).tz_gmtoff, 0, typeList.at(0).tz_abbrind };
else
ret.m_preZoneRule = { 0, 0, 0 };
// Offsets are stored as total offset, want to know separate UTC and DST offsets
// so find the first non-dst transition to use as base UTC Offset
int utcOffset = 0;
int utcOffset = ret.m_preZoneRule.stdOffset;
for (const QTzTransition &tran : qAsConst(tranList)) {
if (!typeList.at(tran.tz_typeind).tz_isdst) {
utcOffset = typeList.at(tran.tz_typeind).tz_gmtoff;
@ -1026,14 +1034,14 @@ bool QTzTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
QTimeZonePrivate::Data QTzTimeZonePrivate::dataForTzTransition(QTzTransitionTime tran) const
{
QTimeZonePrivate::Data data;
data.atMSecsSinceEpoch = tran.atMSecsSinceEpoch;
QTzTransitionRule rule = cached_data.m_tranRules.at(tran.ruleIndex);
data.standardTimeOffset = rule.stdOffset;
data.daylightTimeOffset = rule.dstOffset;
data.offsetFromUtc = rule.stdOffset + rule.dstOffset;
data.abbreviation = QString::fromUtf8(cached_data.m_abbreviations.at(rule.abbreviationIndex));
return data;
return dataFromRule(cached_data.m_tranRules.at(tran.ruleIndex), tran.atMSecsSinceEpoch);
}
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 };
}
QList<QTimeZonePrivate::Data> QTzTimeZonePrivate::getPosixTransitions(qint64 msNear) const
@ -1070,11 +1078,11 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
[forMSecsSinceEpoch] (const QTzTransitionTime &at) {
return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
});
if (last > tranCache().cbegin())
--last;
Data data = dataForTzTransition(*last);
data.atMSecsSinceEpoch = forMSecsSinceEpoch;
return data;
if (last == tranCache().cbegin())
return dataFromRule(cached_data.m_preZoneRule, forMSecsSinceEpoch);
--last;
return dataFromRule(cached_data.m_tranRules.at(last->ruleIndex), forMSecsSinceEpoch);
}
bool QTzTimeZonePrivate::hasTransitions() const