Fix broken data for time-zones with no transitions
While an invalid time-zone shall have no transitions, so may various constant zones, like UTC. The TZ data may include only the POSIX rule for such a zone, in which case we should use it, even if there are no transitions. Broke out a piece of repeated code as a common method, in the process, since I was complicating it further. Added test for the case that revealed this; and made sure we see a warning if any of the checkOffset() tests gets skipped because its zone is unsupported. Fixes: QTBUG-74614 Change-Id: Ic8e039a2a9b3f4e0f567585682a94f4b494b558d Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
1119cd4ece
commit
03fadc26e7
@ -331,6 +331,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void init(const QByteArray &ianaId);
|
void init(const QByteArray &ianaId);
|
||||||
|
QVector<QTimeZonePrivate::Data> getPosixTransitions(qint64 msNear) const;
|
||||||
|
|
||||||
Data dataForTzTransition(QTzTransitionTime tran) const;
|
Data dataForTzTransition(QTzTransitionTime tran) const;
|
||||||
QVector<QTzTransitionTime> m_tranTimes;
|
QVector<QTzTransitionTime> m_tranTimes;
|
||||||
|
@ -943,19 +943,21 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::dataForTzTransition(QTzTransitionTime
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<QTimeZonePrivate::Data> QTzTimeZonePrivate::getPosixTransitions(qint64 msNear) const
|
||||||
|
{
|
||||||
|
const int year = QDateTime::fromMSecsSinceEpoch(msNear, Qt::UTC).date().year();
|
||||||
|
// The Data::atMSecsSinceEpoch of the single entry if zone is constant:
|
||||||
|
qint64 atTime = m_tranTimes.isEmpty() ? msNear : m_tranTimes.last().atMSecsSinceEpoch;
|
||||||
|
return calculatePosixTransitions(m_posixRule, year - 1, year + 1, atTime);
|
||||||
|
}
|
||||||
|
|
||||||
QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
|
QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
|
||||||
{
|
{
|
||||||
// If we have no rules (so probably an invalid tz), return invalid data:
|
// If the required time is after the last transition (or there were none)
|
||||||
if (!m_tranTimes.size())
|
// and we have a POSIX rule then use it:
|
||||||
return invalidData();
|
if ((m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < forMSecsSinceEpoch)
|
||||||
|
|
||||||
// If the required time is after the last transition and we have a POSIX rule then use it
|
|
||||||
if (m_tranTimes.last().atMSecsSinceEpoch < forMSecsSinceEpoch
|
|
||||||
&& !m_posixRule.isEmpty() && forMSecsSinceEpoch >= 0) {
|
&& !m_posixRule.isEmpty() && forMSecsSinceEpoch >= 0) {
|
||||||
const int year = QDateTime::fromMSecsSinceEpoch(forMSecsSinceEpoch, Qt::UTC).date().year();
|
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(forMSecsSinceEpoch);
|
||||||
QVector<QTimeZonePrivate::Data> posixTrans =
|
|
||||||
calculatePosixTransitions(m_posixRule, year - 1, year + 1,
|
|
||||||
m_tranTimes.last().atMSecsSinceEpoch);
|
|
||||||
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
|
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
|
||||||
[forMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
|
[forMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
|
||||||
return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
|
return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
|
||||||
@ -986,13 +988,11 @@ bool QTzTimeZonePrivate::hasTransitions() const
|
|||||||
|
|
||||||
QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const
|
QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const
|
||||||
{
|
{
|
||||||
// If the required time is after the last transition and we have a POSIX rule then use it
|
// If the required time is after the last transition (or there were none)
|
||||||
if (m_tranTimes.size() > 0 && m_tranTimes.last().atMSecsSinceEpoch < afterMSecsSinceEpoch
|
// and we have a POSIX rule then use it:
|
||||||
|
if ((m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < afterMSecsSinceEpoch)
|
||||||
&& !m_posixRule.isEmpty() && afterMSecsSinceEpoch >= 0) {
|
&& !m_posixRule.isEmpty() && afterMSecsSinceEpoch >= 0) {
|
||||||
const int year = QDateTime::fromMSecsSinceEpoch(afterMSecsSinceEpoch, Qt::UTC).date().year();
|
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(afterMSecsSinceEpoch);
|
||||||
QVector<QTimeZonePrivate::Data> posixTrans =
|
|
||||||
calculatePosixTransitions(m_posixRule, year - 1, year + 1,
|
|
||||||
m_tranTimes.last().atMSecsSinceEpoch);
|
|
||||||
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
|
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
|
||||||
[afterMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
|
[afterMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
|
||||||
return at.atMSecsSinceEpoch <= afterMSecsSinceEpoch;
|
return at.atMSecsSinceEpoch <= afterMSecsSinceEpoch;
|
||||||
@ -1011,21 +1011,19 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSince
|
|||||||
|
|
||||||
QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
|
QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
|
||||||
{
|
{
|
||||||
// If the required time is after the last transition and we have a POSIX rule then use it
|
// If the required time is after the last transition (or there were none)
|
||||||
if (m_tranTimes.size() > 0 && m_tranTimes.last().atMSecsSinceEpoch < beforeMSecsSinceEpoch
|
// and we have a POSIX rule then use it:
|
||||||
|
if ((m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < beforeMSecsSinceEpoch)
|
||||||
&& !m_posixRule.isEmpty() && beforeMSecsSinceEpoch > 0) {
|
&& !m_posixRule.isEmpty() && beforeMSecsSinceEpoch > 0) {
|
||||||
const int year = QDateTime::fromMSecsSinceEpoch(beforeMSecsSinceEpoch, Qt::UTC).date().year();
|
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(beforeMSecsSinceEpoch);
|
||||||
QVector<QTimeZonePrivate::Data> posixTrans =
|
|
||||||
calculatePosixTransitions(m_posixRule, year - 1, year + 1,
|
|
||||||
m_tranTimes.last().atMSecsSinceEpoch);
|
|
||||||
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
|
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
|
||||||
[beforeMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
|
[beforeMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
|
||||||
return at.atMSecsSinceEpoch < beforeMSecsSinceEpoch;
|
return at.atMSecsSinceEpoch < beforeMSecsSinceEpoch;
|
||||||
});
|
});
|
||||||
if (it > posixTrans.cbegin())
|
if (it > posixTrans.cbegin())
|
||||||
return *--it;
|
return *--it;
|
||||||
// else: it fell between the last transition and the first of the POSIX rule.
|
// It fell between the last transition (if any) and the first of the POSIX rule:
|
||||||
return dataForTzTransition(m_tranTimes.last());
|
return m_tranTimes.isEmpty() ? invalidData() : dataForTzTransition(m_tranTimes.last());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise if we can find a valid tran then use its rule
|
// Otherwise if we can find a valid tran then use its rule
|
||||||
|
@ -539,6 +539,8 @@ void tst_QTimeZone::checkOffset_data()
|
|||||||
int year, month, day, hour, min, sec;
|
int year, month, day, hour, min, sec;
|
||||||
int std, dst;
|
int std, dst;
|
||||||
} table[] = {
|
} table[] = {
|
||||||
|
// Zone with no transitions (QTBUG-74614, when TZ backend uses minimalist data)
|
||||||
|
{ "Etc/UTC", "epoch", 1970, 1, 1, 0, 0, 0, 0, 0 },
|
||||||
// Kiev: regression test for QTBUG-64122 (on MS):
|
// Kiev: regression test for QTBUG-64122 (on MS):
|
||||||
{ "Europe/Kiev", "summer", 2017, 10, 27, 12, 0, 0, 2 * 3600, 3600 },
|
{ "Europe/Kiev", "summer", 2017, 10, 27, 12, 0, 0, 2 * 3600, 3600 },
|
||||||
{ "Europe/Kiev", "winter", 2017, 10, 29, 12, 0, 0, 2 * 3600, 0 }
|
{ "Europe/Kiev", "winter", 2017, 10, 29, 12, 0, 0, 2 * 3600, 0 }
|
||||||
@ -551,6 +553,8 @@ void tst_QTimeZone::checkOffset_data()
|
|||||||
<< QDateTime(QDate(entry.year, entry.month, entry.day),
|
<< QDateTime(QDate(entry.year, entry.month, entry.day),
|
||||||
QTime(entry.hour, entry.min, entry.sec), zone)
|
QTime(entry.hour, entry.min, entry.sec), zone)
|
||||||
<< entry.dst + entry.std << entry.std << entry.dst;
|
<< entry.dst + entry.std << entry.std << entry.dst;
|
||||||
|
} else {
|
||||||
|
qWarning("Skipping %s@%s test as zone is invalid", entry.zone, entry.nick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user