summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools')
-rw-r--r--src/corelib/tools/qtimezone.cpp6
-rw-r--r--src/corelib/tools/qtimezoneprivate.cpp5
-rw-r--r--src/corelib/tools/qtimezoneprivate_p.h1
-rw-r--r--src/corelib/tools/qtimezoneprivate_tz.cpp259
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