summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
authorJohn Layt <jlayt@kde.org>2013-09-20 18:35:08 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-11-21 09:25:23 +0100
commit8e6258f059140ce735391b6438d5976dc9469e95 (patch)
tree463f8f17ac333f0e035ee5f1779355b0294a51de /src/corelib
parentc03ea9be38a3c23b54e56bb567205a02ad4d1e19 (diff)
QTimeZone - Fix TZ file abbreviations
Fix parsing of TZ file abbreviations, to correctly return cases where POSIX rule doesn't have separate DST rules, and where abbreviation is a sub-string of another abbreviation, otherwise any toString() call will crash. Add test to exercise all available time zones, especially useful for TZ file to confirm all file format variations dealt with. Fix parsing of Version 3 of TZ file, and ICU display name, to allow all files generated from release 2013f to pass, otherwise isValid() call will crash. Task-number: QTBUG-34061 Change-Id: Ie0b6abc218adff1c8967eb33fdb0762041d2305f Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib')
-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