diff options
Diffstat (limited to 'src/corelib/tools/qtimezoneprivate_tz.cpp')
-rw-r--r-- | src/corelib/tools/qtimezoneprivate_tz.cpp | 139 |
1 files changed, 91 insertions, 48 deletions
diff --git a/src/corelib/tools/qtimezoneprivate_tz.cpp b/src/corelib/tools/qtimezoneprivate_tz.cpp index 10b61c3a40..1714c9802f 100644 --- a/src/corelib/tools/qtimezoneprivate_tz.cpp +++ b/src/corelib/tools/qtimezoneprivate_tz.cpp @@ -199,7 +199,7 @@ static QVector<QTzTransition> parseTzTransitions(QDataStream &ds, int tzh_timecn } } else { // Parse tzh_timecnt x 4-byte transition times - int val; + qint32 val; for (int i = 0; i < tzh_timecnt && ds.status() == QDataStream::Ok; ++i) { ds >> val; transitions[i].tz_time = val; @@ -452,13 +452,31 @@ static inline bool asciiIsLetter(char ch) return ch >= 'a' && ch <= 'z'; } +namespace { + +struct PosixZone +{ + enum { + InvalidOffset = INT_MIN, + }; + + QString name; + int offset; + + static PosixZone invalid() { return {QString(), InvalidOffset}; } + static PosixZone parse(const char *&pos, const char *end); + + bool hasValidOffset() const Q_DECL_NOTHROW { return offset != InvalidOffset; } +}; + +} // unnamed namespace + // Returns the zone name, the offset (in seconds) and advances \a begin to // where the parsing ended. Returns a zone of INT_MIN in case an offset // couldn't be read. -static QPair<QString, int> parsePosixZoneNameAndOffset(const char *&pos, const char *end) +PosixZone PosixZone::parse(const char *&pos, const char *end) { static const char offsetChars[] = "0123456789:"; - QPair<QString, int> result = qMakePair(QString(), INT_MIN); const char *nameBegin = pos; const char *nameEnd; @@ -480,7 +498,7 @@ static QPair<QString, int> parsePosixZoneNameAndOffset(const char *&pos, const c pos = nameEnd; } if (nameEnd - nameBegin < 3) - return result; // name must be at least 3 characters long + return invalid(); // name must be at least 3 characters long // zone offset, form [+-]hh:mm:ss const char *zoneBegin = pos; @@ -493,11 +511,10 @@ static QPair<QString, int> parsePosixZoneNameAndOffset(const char *&pos, const c ++zoneEnd; } - result.first = QString::fromUtf8(nameBegin, nameEnd - nameBegin); - if (zoneEnd > zoneBegin) - result.second = parsePosixOffset(zoneBegin, zoneEnd); + QString name = QString::fromUtf8(nameBegin, nameEnd - nameBegin); + const int offset = zoneEnd > zoneBegin ? parsePosixOffset(zoneBegin, zoneEnd) : InvalidOffset; pos = zoneEnd; - return result; + return {std::move(name), offset}; } static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArray &posixRule, @@ -517,19 +534,19 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra // See the section about TZ at http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html QList<QByteArray> parts = posixRule.split(','); - QPair<QString, int> stdZone, dstZone; + PosixZone stdZone, dstZone; { const QByteArray &zoneinfo = parts.at(0); const char *begin = zoneinfo.constBegin(); - stdZone = parsePosixZoneNameAndOffset(begin, zoneinfo.constEnd()); - if (stdZone.second == INT_MIN) { - stdZone.second = 0; // reset to UTC if we failed to parse + stdZone = PosixZone::parse(begin, zoneinfo.constEnd()); + if (!stdZone.hasValidOffset()) { + stdZone.offset = 0; // reset to UTC if we failed to parse } else if (begin < zoneinfo.constEnd()) { - dstZone = parsePosixZoneNameAndOffset(begin, zoneinfo.constEnd()); - if (dstZone.second == INT_MIN) { + dstZone = PosixZone::parse(begin, zoneinfo.constEnd()); + if (!dstZone.hasValidOffset()) { // if the dst offset isn't provided, it is 1 hour ahead of the standard offset - dstZone.second = stdZone.second + (60 * 60); + dstZone.offset = stdZone.offset + (60 * 60); } } } @@ -538,10 +555,10 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra if (parts.count() == 1) { QTimeZonePrivate::Data data; data.atMSecsSinceEpoch = lastTranMSecs; - data.offsetFromUtc = stdZone.second; - data.standardTimeOffset = stdZone.second; + data.offsetFromUtc = stdZone.offset; + data.standardTimeOffset = stdZone.offset; data.daylightTimeOffset = 0; - data.abbreviation = stdZone.first; + data.abbreviation = stdZone.name; result << data; return result; } @@ -568,18 +585,18 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra for (int year = startYear; year <= endYear; ++year) { QTimeZonePrivate::Data dstData; QDateTime dst(calculatePosixDate(dstDateRule, year), dstTime, Qt::UTC); - dstData.atMSecsSinceEpoch = dst.toMSecsSinceEpoch() - (stdZone.second * 1000); - dstData.offsetFromUtc = dstZone.second; - dstData.standardTimeOffset = stdZone.second; - dstData.daylightTimeOffset = dstZone.second - stdZone.second; - dstData.abbreviation = dstZone.first; + 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; QDateTime std(calculatePosixDate(stdDateRule, year), stdTime, Qt::UTC); - stdData.atMSecsSinceEpoch = std.toMSecsSinceEpoch() - (dstZone.second * 1000); - stdData.offsetFromUtc = stdZone.second; - stdData.standardTimeOffset = stdZone.second; + stdData.atMSecsSinceEpoch = std.toMSecsSinceEpoch() - (dstZone.offset * 1000); + stdData.offsetFromUtc = stdZone.offset; + stdData.standardTimeOffset = stdZone.offset; stdData.daylightTimeOffset = 0; - stdData.abbreviation = stdZone.first; + stdData.abbreviation = stdZone.name; // Part of the high year will overflow if (year == 292278994 && (dstData.atMSecsSinceEpoch < 0 || stdData.atMSecsSinceEpoch < 0)) { if (dstData.atMSecsSinceEpoch > 0) { @@ -598,37 +615,21 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra // Create the system default time zone QTzTimeZonePrivate::QTzTimeZonePrivate() -#if QT_CONFIG(icu) - : m_icu(0) -#endif { init(systemTimeZoneId()); } // Create a named time zone QTzTimeZonePrivate::QTzTimeZonePrivate(const QByteArray &ianaId) -#if QT_CONFIG(icu) - : m_icu(0) -#endif { init(ianaId); } -QTzTimeZonePrivate::QTzTimeZonePrivate(const QTzTimeZonePrivate &other) - : QTimeZonePrivate(other), m_tranTimes(other.m_tranTimes), - m_tranRules(other.m_tranRules), m_abbreviations(other.m_abbreviations), -#if QT_CONFIG(icu) - m_icu(other.m_icu), -#endif - m_posixRule(other.m_posixRule) -{ -} - QTzTimeZonePrivate::~QTzTimeZonePrivate() { } -QTimeZonePrivate *QTzTimeZonePrivate::clone() +QTzTimeZonePrivate *QTzTimeZonePrivate::clone() const { return new QTzTimeZonePrivate(*this); } @@ -725,19 +726,61 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId) } } - // Now for each transition time calculate our rule and save them - m_tranTimes.reserve(tranList.count()); - for (const QTzTransition &tz_tran : qAsConst(tranList)) { + // Now for each transition time calculate and store our rule: + const int tranCount = tranList.count();; + m_tranTimes.reserve(tranCount); + // The DST offset when in effect: usually stable, usually an hour: + int lastDstOff = 3600; + for (int i = 0; i < tranCount; i++) { + const QTzTransition &tz_tran = tranList.at(i); QTzTransitionTime tran; QTzTransitionRule rule; const QTzType tz_type = typeList.at(tz_tran.tz_typeind); // Calculate the associated Rule - if (!tz_type.tz_isdst) + if (!tz_type.tz_isdst) { utcOffset = tz_type.tz_gmtoff; + } else if (Q_UNLIKELY(tz_type.tz_gmtoff != utcOffset + lastDstOff)) { + /* + This might be a genuine change in DST offset, but could also be + DST starting at the same time as the standard offset changed. See + if DST's end gives a more plausible utcOffset (i.e. one closer to + the last we saw, or a simple whole hour): + */ + // Standard offset inferred from net offset and expected DST offset: + const int inferStd = tz_type.tz_gmtoff - lastDstOff; // != utcOffset + for (int j = i + 1; j < tranCount; j++) { + const QTzType new_type = typeList.at(tranList.at(j).tz_typeind); + if (!new_type.tz_isdst) { + const int newUtc = new_type.tz_gmtoff; + if (newUtc == utcOffset) { + // DST-end can't help us, avoid lots of messy checks. + // else: See if the end matches the familiar DST offset: + } else if (newUtc == inferStd) { + utcOffset = newUtc; + // else: let either end shift us to one hour as DST offset: + } else if (tz_type.tz_gmtoff - 3600 == utcOffset) { + // Start does it + } else if (tz_type.tz_gmtoff - 3600 == newUtc) { + utcOffset = newUtc; // End does it + // else: prefer whichever end gives DST offset closer to + // last, but consider any offset > 0 "closer" than any <= 0: + } else if (newUtc < tz_type.tz_gmtoff + ? (utcOffset >= tz_type.tz_gmtoff + || qAbs(newUtc - inferStd) < qAbs(utcOffset - inferStd)) + : (utcOffset >= tz_type.tz_gmtoff + && qAbs(newUtc - inferStd) < qAbs(utcOffset - inferStd))) { + utcOffset = newUtc; + } + break; + } + } + lastDstOff = tz_type.tz_gmtoff - utcOffset; + } rule.stdOffset = utcOffset; rule.dstOffset = tz_type.tz_gmtoff - utcOffset; rule.abbreviationIndex = tz_type.tz_abbrind; + // If the rule already exist then use that, otherwise add it int ruleIndex = m_tranRules.indexOf(rule); if (ruleIndex == -1) { |