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

View File

@ -768,12 +768,20 @@ QTzTimeZoneCacheEntry QTzTimeZoneCache::findEntry(const QByteArray &ianaId)
ret.m_abbreviations.append(it.value()); ret.m_abbreviations.append(it.value());
abbrindList.append(it.key()); 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) for (int i = 0; i < typeList.size(); ++i)
typeList[i].tz_abbrind = abbrindList.indexOf(typeList.at(i).tz_abbrind); 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 // 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 // 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)) { for (const QTzTransition &tran : qAsConst(tranList)) {
if (!typeList.at(tran.tz_typeind).tz_isdst) { if (!typeList.at(tran.tz_typeind).tz_isdst) {
utcOffset = typeList.at(tran.tz_typeind).tz_gmtoff; 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 QTzTimeZonePrivate::dataForTzTransition(QTzTransitionTime tran) const
{ {
QTimeZonePrivate::Data data; return dataFromRule(cached_data.m_tranRules.at(tran.ruleIndex), tran.atMSecsSinceEpoch);
data.atMSecsSinceEpoch = tran.atMSecsSinceEpoch; }
QTzTransitionRule rule = cached_data.m_tranRules.at(tran.ruleIndex);
data.standardTimeOffset = rule.stdOffset; QTimeZonePrivate::Data QTzTimeZonePrivate::dataFromRule(QTzTransitionRule rule,
data.daylightTimeOffset = rule.dstOffset; qint64 msecsSinceEpoch) const
data.offsetFromUtc = rule.stdOffset + rule.dstOffset; {
data.abbreviation = QString::fromUtf8(cached_data.m_abbreviations.at(rule.abbreviationIndex)); return { QString::fromUtf8(cached_data.m_abbreviations.at(rule.abbreviationIndex)),
return data; msecsSinceEpoch, rule.stdOffset + rule.dstOffset, rule.stdOffset, rule.dstOffset };
} }
QList<QTimeZonePrivate::Data> QTzTimeZonePrivate::getPosixTransitions(qint64 msNear) const QList<QTimeZonePrivate::Data> QTzTimeZonePrivate::getPosixTransitions(qint64 msNear) const
@ -1070,11 +1078,11 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
[forMSecsSinceEpoch] (const QTzTransitionTime &at) { [forMSecsSinceEpoch] (const QTzTransitionTime &at) {
return at.atMSecsSinceEpoch <= forMSecsSinceEpoch; return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
}); });
if (last > tranCache().cbegin()) if (last == tranCache().cbegin())
--last; return dataFromRule(cached_data.m_preZoneRule, forMSecsSinceEpoch);
Data data = dataForTzTransition(*last);
data.atMSecsSinceEpoch = forMSecsSinceEpoch; --last;
return data; return dataFromRule(cached_data.m_tranRules.at(last->ruleIndex), forMSecsSinceEpoch);
} }
bool QTzTimeZonePrivate::hasTransitions() const bool QTzTimeZonePrivate::hasTransitions() const