diff options
Diffstat (limited to 'src/corelib')
-rw-r--r-- | src/corelib/tools/qtimezone.cpp | 6 | ||||
-rw-r--r-- | src/corelib/tools/qtimezoneprivate.cpp | 5 | ||||
-rw-r--r-- | src/corelib/tools/qtimezoneprivate_p.h | 1 | ||||
-rw-r--r-- | src/corelib/tools/qtimezoneprivate_tz.cpp | 259 |
4 files changed, 171 insertions, 100 deletions
diff --git a/src/corelib/tools/qtimezone.cpp b/src/corelib/tools/qtimezone.cpp index 37d6e183a7..5fec06b30a 100644 --- a/src/corelib/tools/qtimezone.cpp +++ b/src/corelib/tools/qtimezone.cpp @@ -708,6 +708,9 @@ bool QTimeZone::hasTransitions() const This is most useful when you have a Transition time and wish to find the Transition after it. + If there is no transition after the given \a afterDateTime then an invalid + OffsetData will be returned with an invalid QDateTime. + The given \a afterDateTime is exclusive. \sa hasTransitions(), previousTransition(), transitions() @@ -726,6 +729,9 @@ QTimeZone::OffsetData QTimeZone::nextTransition(const QDateTime &afterDateTime) This is most useful when you have a Transition time and wish to find the Transition before it. + If there is no transition before the given \a beforeDateTime then an invalid + OffsetData will be returned with an invalid QDateTime. + The given \a beforeDateTime is exclusive. \sa hasTransitions(), nextTransition(), transitions() diff --git a/src/corelib/tools/qtimezoneprivate.cpp b/src/corelib/tools/qtimezoneprivate.cpp index cbf731a19e..dd4e18e01f 100644 --- a/src/corelib/tools/qtimezoneprivate.cpp +++ b/src/corelib/tools/qtimezoneprivate.cpp @@ -328,10 +328,11 @@ QTimeZonePrivate::DataList QTimeZonePrivate::transitions(qint64 fromMSecsSinceEp qint64 toMSecsSinceEpoch) const { DataList list; - if (toMSecsSinceEpoch > fromMSecsSinceEpoch) { + if (toMSecsSinceEpoch >= fromMSecsSinceEpoch) { // fromMSecsSinceEpoch is inclusive but nextTransitionTime() is exclusive so go back 1 msec Data next = nextTransition(fromMSecsSinceEpoch - 1); - while (next.atMSecsSinceEpoch <= toMSecsSinceEpoch) { + while (next.atMSecsSinceEpoch != invalidMSecs() + && next.atMSecsSinceEpoch <= toMSecsSinceEpoch) { list.append(next); next = nextTransition(next.atMSecsSinceEpoch); } diff --git a/src/corelib/tools/qtimezoneprivate_p.h b/src/corelib/tools/qtimezoneprivate_p.h index 186a352a2f..108aec2654 100644 --- a/src/corelib/tools/qtimezoneprivate_p.h +++ b/src/corelib/tools/qtimezoneprivate_p.h @@ -309,6 +309,7 @@ private: bool operator==(const QTzTransitionRule &other) { return (stdOffset == other.stdOffset && dstOffset == other.dstOffset && abbreviationIndex == other.abbreviationIndex); } }; + Data dataForTzTransition(QTzTransitionTime tran) const; QList<QTzTransitionTime> m_tranTimes; QList<QTzTransitionRule> m_tranRules; QList<QByteArray> m_abbreviations; diff --git a/src/corelib/tools/qtimezoneprivate_tz.cpp b/src/corelib/tools/qtimezoneprivate_tz.cpp index 4bf19178fa..1fb6bb1b5a 100644 --- a/src/corelib/tools/qtimezoneprivate_tz.cpp +++ b/src/corelib/tools/qtimezoneprivate_tz.cpp @@ -149,11 +149,13 @@ static QTzHeader parseTzHeader(QDataStream &ds, bool *ok) if (memcmp(hdr.tzh_magic, TZ_MAGIC, 4) != 0 || ds.status() != QDataStream::Ok) return hdr; - // Parse Version, 1 byte, before 2005 was '\0', since 2005 a '2' + // Parse Version, 1 byte, before 2005 was '\0', since 2005 a '2', since 2013 a '3' ds >> ch; hdr.tzh_version = ch; - if (ds.status() != QDataStream::Ok || (hdr.tzh_version != '2' && hdr.tzh_version != '\0')) + if (ds.status() != QDataStream::Ok + || (hdr.tzh_version != '2' && hdr.tzh_version != '\0' && hdr.tzh_version != '3')) { return hdr; + } // Parse reserved space, 15 bytes ds.readRawData(hdr.tzh_reserved, 15); @@ -238,30 +240,31 @@ static QList<QTzType> parseTzTypes(QDataStream &ds, int tzh_typecnt) return typeList; } -static QMap<int, QByteArray> parseTzAbbreviations(QDataStream &ds, int tzh_charcnt) +static QMap<int, QByteArray> parseTzAbbreviations(QDataStream &ds, int tzh_charcnt, QList<QTzType> typeList) { // Parse the abbreviation list which is tzh_charcnt long with '\0' separated strings. The - // tz_abbrind index points to the first char of the abbreviation in the array, not the - // occurrence in the list. By parsing char at a time we can track the char index and convert - // to an occurrence index. By using a map with tz_abbrind as ordered key we get both index + // QTzType.tz_abbrind index points to the first char of the abbreviation in the array, not the + // occurrence in the list. It can also point to a partial string so we need to use the actual typeList + // index values when parsing. By using a map with tz_abbrind as ordered key we get both index // methods in one data structure and can convert the types afterwards. QMap<int, QByteArray> map; quint8 ch; - QByteArray abbrev; - // Track the start position of each abbreviation - int tz_abbrind = 0; + QByteArray input; + // First parse the full abbrev string for (int i = 0; i < tzh_charcnt && ds.status() == QDataStream::Ok; ++i) { ds >> ch; - if (ds.status() == QDataStream::Ok) { - if (ch == '\0') { - // Have reached end of an abbreviation, so add to map - map[tz_abbrind] = abbrev; - tz_abbrind = i + 1; - abbrev.clear(); - } else { - abbrev.append((char)ch); - } - } + if (ds.status() == QDataStream::Ok) + input.append(char(ch)); + else + return map; + } + // Then extract all the substrings pointed to by typeList + foreach (const QTzType type, typeList) { + QByteArray abbrev; + for (int i = type.tz_abbrind; input.at(i) != '\0'; ++i) + abbrev.append(input.at(i)); + // Have reached end of an abbreviation, so add to map + map[type.tz_abbrind] = abbrev; } return map; } @@ -371,7 +374,7 @@ static QDate calculatePosixDate(const QByteArray dateRule, int year) } } -static QTime parsePosixTime(const QByteArray timeRule) +static QTime parsePosixTime(const QByteArray &timeRule) { // Format "HH:mm:ss", put check parts count just in case QList<QByteArray> parts = timeRule.split(':'); @@ -385,7 +388,7 @@ static QTime parsePosixTime(const QByteArray timeRule) return QTime(2, 0, 0); } -static int parsePosixOffset(const QByteArray timeRule) +static int parsePosixOffset(const QByteArray &timeRule) { // Format "[+|-]hh[:mm[:ss]]" QList<QByteArray> parts = timeRule.split(':'); @@ -399,7 +402,9 @@ static int parsePosixOffset(const QByteArray timeRule) return 0; } -static QList<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArray &posixRule, int startYear, int endYear) +static QList<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArray &posixRule, + int startYear, int endYear, + int lastTranMSecs) { QList<QTimeZonePrivate::Data> list; @@ -443,11 +448,13 @@ static QList<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArray // If only the name part then no transitions if (parts.count() == 1) { QTimeZonePrivate::Data data; - data.atMSecsSinceEpoch = 0; + data.atMSecsSinceEpoch = lastTranMSecs; data.offsetFromUtc = utcOffset; data.standardTimeOffset = utcOffset; data.daylightTimeOffset = 0; data.abbreviation = stdName; + list << data; + return list; } // If not populated the total dst offset is 1 hour @@ -572,7 +579,7 @@ void QTzTimeZonePrivate::init(const QByteArray &olsenId) QList<QTzType> typeList = parseTzTypes(ds, hdr.tzh_typecnt); if (ds.status() != QDataStream::Ok) return; - QMap<int, QByteArray> abbrevMap = parseTzAbbreviations(ds, hdr.tzh_charcnt); + QMap<int, QByteArray> abbrevMap = parseTzAbbreviations(ds, hdr.tzh_charcnt, typeList); if (ds.status() != QDataStream::Ok) return; parseTzLeapSeconds(ds, hdr.tzh_leapcnt, false); @@ -583,7 +590,7 @@ void QTzTimeZonePrivate::init(const QByteArray &olsenId) return; // If version 2 then parse the second block of data - if (hdr.tzh_version == '2') { + if (hdr.tzh_version == '2' || hdr.tzh_version == '3') { ok = false; QTzHeader hdr2 = parseTzHeader(ds, &ok); if (!ok || ds.status() != QDataStream::Ok) @@ -594,7 +601,7 @@ void QTzTimeZonePrivate::init(const QByteArray &olsenId) typeList = parseTzTypes(ds, hdr2.tzh_typecnt); if (ds.status() != QDataStream::Ok) return; - abbrevMap = parseTzAbbreviations(ds, hdr2.tzh_charcnt); + abbrevMap = parseTzAbbreviations(ds, hdr2.tzh_charcnt, typeList); if (ds.status() != QDataStream::Ok) return; parseTzLeapSeconds(ds, hdr2.tzh_leapcnt, true); @@ -682,12 +689,14 @@ QString QTzTimeZonePrivate::displayName(qint64 atMSecsSinceEpoch, if (!m_icu) m_icu = new QIcuTimeZonePrivate(m_id); // TODO small risk may not match if tran times differ due to outdated files - return m_icu->displayName(atMSecsSinceEpoch, nameType, locale); + // TODO Some valid TZ names are not valid ICU names, use translation table? + if (m_icu->isValid()) + return m_icu->displayName(atMSecsSinceEpoch, nameType, locale); #else Q_UNUSED(nameType) Q_UNUSED(locale) - return abbreviation(atMSecsSinceEpoch); #endif // QT_USE_ICU + return abbreviation(atMSecsSinceEpoch); } QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType, @@ -698,19 +707,59 @@ QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType, if (!m_icu) m_icu = new QIcuTimeZonePrivate(m_id); // TODO small risk may not match if tran times differ due to outdated files - return m_icu->displayName(timeType, nameType, locale); + // TODO Some valid TZ names are not valid ICU names, use translation table? + if (m_icu->isValid()) + return m_icu->displayName(timeType, nameType, locale); #else Q_UNUSED(timeType) Q_UNUSED(nameType) Q_UNUSED(locale) - const int atMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); - QTimeZonePrivate::Data tran = data(atMSecsSinceEpoch); - while ((timeType == QTimeZone::StandardTime && tran.daylightTimeOffset != 0) - || (timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset == 0)) { - tran = nextTransition(tran.atMSecsSinceEpoch); - } - return tran.abbreviation; #endif // QT_USE_ICU + // If no ICU available then have to use abbreviations instead + // Abbreviations don't have GenericTime + if (timeType == QTimeZone::GenericTime) + timeType = QTimeZone::StandardTime; + + // Get current tran, if valid and is what we want, then use it + const qint64 currentMSecs = QDateTime::currentMSecsSinceEpoch(); + QTimeZonePrivate::Data tran = data(currentMSecs); + if (tran.atMSecsSinceEpoch != invalidMSecs() + && ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0) + || (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) { + return tran.abbreviation; + } + + // Otherwise get next tran and if valid and is what we want, then use it + tran = nextTransition(currentMSecs); + if (tran.atMSecsSinceEpoch != invalidMSecs() + && ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0) + || (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) { + return tran.abbreviation; + } + + // Otherwise get prev tran and if valid and is what we want, then use it + tran = previousTransition(currentMSecs); + if (tran.atMSecsSinceEpoch != invalidMSecs()) + tran = previousTransition(tran.atMSecsSinceEpoch); + if (tran.atMSecsSinceEpoch != invalidMSecs() + && ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0) + || (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) { + return tran.abbreviation; + } + + // Otherwise is strange sequence, so work backwards through trans looking for first match, if any + for (int i = m_tranTimes.size() - 1; i >= 0; --i) { + if (m_tranTimes.at(i).atMSecsSinceEpoch <= currentMSecs) { + tran = dataForTzTransition(m_tranTimes.at(i)); + if ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0) + || (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0)) { + return tran.abbreviation; + } + } + } + + // Otherwise if no match use current data + return data(currentMSecs).abbreviation; } QString QTzTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const @@ -749,35 +798,55 @@ bool QTzTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const return (daylightTimeOffset(atMSecsSinceEpoch) != 0); } +QTimeZonePrivate::Data QTzTimeZonePrivate::dataForTzTransition(QTzTransitionTime tran) const +{ + QTimeZonePrivate::Data data; + data.atMSecsSinceEpoch = tran.atMSecsSinceEpoch; + QTzTransitionRule rule = m_tranRules.at(tran.ruleIndex); + data.standardTimeOffset = rule.stdOffset; + data.daylightTimeOffset = rule.dstOffset; + data.offsetFromUtc = rule.stdOffset + rule.dstOffset; + data.abbreviation = QString::fromUtf8(m_abbreviations.at(rule.abbreviationIndex)); + return data; +} + QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const { - QTimeZonePrivate::Data data = invalidData(); - int lastTran = m_tranTimes.size() - 1; - int tran; - for (tran = lastTran; tran > 0; --tran) { - if (m_tranTimes.at(tran).atMSecsSinceEpoch <= forMSecsSinceEpoch) - break; - } - // If after the last transition time then we need to use the posix rule if available - if (tran >= lastTran && !m_posixRule.isEmpty()) { - QDateTime dt = QDateTime::fromMSecsSinceEpoch(forMSecsSinceEpoch); - int year = dt.date().year(); - QList<QTimeZonePrivate::Data> posixTrans = calculatePosixTransitions(m_posixRule, year - 1, year + 1); - for (int i = posixTrans.size() - 1; i > 0; --i) { + // If the required time is after the last transition and we have a POSIX rule then use it + if (m_tranTimes.size() > 0 && m_tranTimes.last().atMSecsSinceEpoch < forMSecsSinceEpoch + &&!m_posixRule.isEmpty() && forMSecsSinceEpoch >= 0) { + const int year = QDateTime::fromMSecsSinceEpoch(forMSecsSinceEpoch, Qt::UTC).date().year(); + const int lastMSecs = (m_tranTimes.size() > 0) ? m_tranTimes.last().atMSecsSinceEpoch : 0; + QList<QTimeZonePrivate::Data> posixTrans = calculatePosixTransitions(m_posixRule, year - 1, + year + 1, lastMSecs); + for (int i = posixTrans.size() - 1; i >= 0; --i) { if (posixTrans.at(i).atMSecsSinceEpoch <= forMSecsSinceEpoch) { + QTimeZonePrivate::Data data; data = posixTrans.at(i); data.atMSecsSinceEpoch = forMSecsSinceEpoch; return data; } } } - data.atMSecsSinceEpoch = forMSecsSinceEpoch; - QTzTransitionRule rule = m_tranRules.at(m_tranTimes.at(tran).ruleIndex); - data.standardTimeOffset = rule.stdOffset; - data.daylightTimeOffset = rule.dstOffset; - data.offsetFromUtc = rule.stdOffset + rule.dstOffset; - data.abbreviation = QString::fromUtf8(m_abbreviations.at(rule.abbreviationIndex)); - return data; + + // Otherwise if we can find a valid tran then use its rule + for (int i = m_tranTimes.size() - 1; i >= 0; --i) { + if (m_tranTimes.at(i).atMSecsSinceEpoch <= forMSecsSinceEpoch) { + Data data = dataForTzTransition(m_tranTimes.at(i)); + data.atMSecsSinceEpoch = forMSecsSinceEpoch; + return data; + } + } + + // Otherwise use the earliest transition we have + if (m_tranTimes.size() > 0) { + Data data = dataForTzTransition(m_tranTimes.at(0)); + data.atMSecsSinceEpoch = forMSecsSinceEpoch; + return data; + } + + // Otherwise we have no rules, so probably an invalid tz, so return invalid data + return invalidData(); } bool QTzTimeZonePrivate::hasTransitions() const @@ -787,60 +856,54 @@ bool QTzTimeZonePrivate::hasTransitions() const QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const { - int lastTran = m_tranTimes.size() - 1; - int tran; - for (tran = 0; tran < lastTran; ++tran) { - if (m_tranTimes.at(tran).atMSecsSinceEpoch > afterMSecsSinceEpoch) - break; - } - // If after the last transition time then we need to use the posix rule if available - if (tran >= lastTran && !m_posixRule.isEmpty()) { - QDateTime dt = QDateTime::fromMSecsSinceEpoch(afterMSecsSinceEpoch); - int year = dt.date().year(); - QList<QTimeZonePrivate::Data> posixTrans = calculatePosixTransitions(m_posixRule, year - 1, year + 1); - for (int i = 0; i < posixTrans.size() - 1; ++i) { + // If the required time is after the last transition and we have a POSIX rule then use it + if (m_tranTimes.size() > 0 && m_tranTimes.last().atMSecsSinceEpoch < afterMSecsSinceEpoch + &&!m_posixRule.isEmpty() && afterMSecsSinceEpoch >= 0) { + const int year = QDateTime::fromMSecsSinceEpoch(afterMSecsSinceEpoch, Qt::UTC).date().year(); + const int lastMSecs = (m_tranTimes.size() > 0) ? m_tranTimes.last().atMSecsSinceEpoch : 0; + QList<QTimeZonePrivate::Data> posixTrans = calculatePosixTransitions(m_posixRule, year - 1, + year + 1, lastMSecs); + for (int i = 0; i < posixTrans.size(); ++i) { if (posixTrans.at(i).atMSecsSinceEpoch > afterMSecsSinceEpoch) return posixTrans.at(i); } } - // Otherwise use the transition we found - QTimeZonePrivate::Data data; - data.atMSecsSinceEpoch = m_tranTimes.at(tran).atMSecsSinceEpoch; - QTzTransitionRule rule = m_tranRules.at(m_tranTimes.at(tran).ruleIndex); - data.standardTimeOffset = rule.stdOffset; - data.daylightTimeOffset = rule.dstOffset; - data.offsetFromUtc = rule.stdOffset + rule.dstOffset; - data.abbreviation = QString::fromUtf8(m_abbreviations.at(rule.abbreviationIndex)); - return data; + + // Otherwise if we can find a valid tran then use its rule + for (int i = 0; i < m_tranTimes.size(); ++i) { + if (m_tranTimes.at(i).atMSecsSinceEpoch > afterMSecsSinceEpoch) { + return dataForTzTransition(m_tranTimes.at(i)); + } + } + + // Otherwise we have no rule, or there is no next transition, so return invalid data + return invalidData(); } QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const { - int lastTran = m_tranTimes.size() - 1; - int tran; - for (tran = lastTran; tran > 0; --tran) { - if (m_tranTimes.at(tran).atMSecsSinceEpoch < beforeMSecsSinceEpoch) - break; - } - // If after the last transition time then we need to use the posix rule if available - if (tran >= lastTran && !m_posixRule.isEmpty()) { - QDateTime dt = QDateTime::fromMSecsSinceEpoch(beforeMSecsSinceEpoch); - int year = dt.date().year(); - QList<QTimeZonePrivate::Data> posixTrans = calculatePosixTransitions(m_posixRule, year - 1, year + 1); - for (int i = posixTrans.size() - 1; i > 0; --i) { + // If the required time is after the last transition and we have a POSIX rule then use it + if (m_tranTimes.size() > 0 && m_tranTimes.last().atMSecsSinceEpoch < beforeMSecsSinceEpoch + &&!m_posixRule.isEmpty() && beforeMSecsSinceEpoch > 0) { + const int year = QDateTime::fromMSecsSinceEpoch(beforeMSecsSinceEpoch, Qt::UTC).date().year(); + const int lastMSecs = (m_tranTimes.size() > 0) ? m_tranTimes.last().atMSecsSinceEpoch : 0; + QList<QTimeZonePrivate::Data> posixTrans = calculatePosixTransitions(m_posixRule, year - 1, + year + 1, lastMSecs); + for (int i = posixTrans.size() - 1; i >= 0; --i) { if (posixTrans.at(i).atMSecsSinceEpoch < beforeMSecsSinceEpoch) return posixTrans.at(i); } } - // Otherwise use the transition we found - QTimeZonePrivate::Data data; - data.atMSecsSinceEpoch = m_tranTimes.at(tran).atMSecsSinceEpoch; - QTzTransitionRule rule = m_tranRules.at(m_tranTimes.at(tran).ruleIndex); - data.standardTimeOffset = rule.stdOffset; - data.daylightTimeOffset = rule.dstOffset; - data.offsetFromUtc = rule.stdOffset + rule.dstOffset; - data.abbreviation = QString::fromUtf8(m_abbreviations.at(rule.abbreviationIndex)); - return data; + + // Otherwise if we can find a valid tran then use its rule + for (int i = m_tranTimes.size() - 1; i >= 0; --i) { + if (m_tranTimes.at(i).atMSecsSinceEpoch < beforeMSecsSinceEpoch) { + return dataForTzTransition(m_tranTimes.at(i)); + } + } + + // Otherwise we have no rule, so return invalid data + return invalidData(); } // TODO Could cache the value and monitor the required files for any changes |