Use std::partition_point for faster searches among transitions

QTzTimeZonePrivate's methods were iterating transitions from first to
last, or last to first, to find where .atMSecsSinceEpoch crossed some
threshold; but the transitions are sorted in order of increasing
.atMSecsSinceEpoch, so binary chop would be more efficient than such
linear searches.  So use std::partition_point() instead.

Change-Id: I65c43cb20fca6685a22ea52a4ca2f1089c128ebf
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Edward Welbourne 2018-08-17 14:49:10 +02:00
parent 98b030fc95
commit 1acafb1207

View File

@ -878,14 +878,17 @@ QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
} }
// Otherwise is strange sequence, so work backwards through trans looking for first match, if any // 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) { auto it = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
if (m_tranTimes.at(i).atMSecsSinceEpoch <= currentMSecs) { [currentMSecs](const QTzTransitionTime &at) {
tran = dataForTzTransition(m_tranTimes.at(i)); return at.atMSecsSinceEpoch <= currentMSecs;
if ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0) });
|| (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0)) {
return tran.abbreviation; while (it != m_tranTimes.cbegin()) {
} --it;
} tran = dataForTzTransition(*it);
int offset = tran.daylightTimeOffset;
if ((timeType == QTimeZone::DaylightTime) != (offset == 0))
return tran.abbreviation;
} }
// Otherwise if no match use current data // Otherwise if no match use current data
@ -900,7 +903,7 @@ QString QTzTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
int QTzTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const int QTzTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const
{ {
const QTimeZonePrivate::Data tran = data(atMSecsSinceEpoch); const QTimeZonePrivate::Data tran = data(atMSecsSinceEpoch);
return tran.standardTimeOffset + tran.daylightTimeOffset; return tran.offsetFromUtc; // == tran.standardTimeOffset + tran.daylightTimeOffset
} }
int QTzTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const int QTzTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const
@ -942,40 +945,38 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::dataForTzTransition(QTzTransitionTime
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 (!m_tranTimes.size())
return invalidData();
// 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 and we have a POSIX rule then use it
if (m_tranTimes.size() > 0 && m_tranTimes.last().atMSecsSinceEpoch < forMSecsSinceEpoch 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(); const int year = QDateTime::fromMSecsSinceEpoch(forMSecsSinceEpoch, Qt::UTC).date().year();
QVector<QTimeZonePrivate::Data> posixTrans = QVector<QTimeZonePrivate::Data> posixTrans =
calculatePosixTransitions(m_posixRule, year - 1, year + 1, calculatePosixTransitions(m_posixRule, year - 1, year + 1,
m_tranTimes.last().atMSecsSinceEpoch); m_tranTimes.last().atMSecsSinceEpoch);
for (int i = posixTrans.size() - 1; i >= 0; --i) { auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
if (posixTrans.at(i).atMSecsSinceEpoch <= forMSecsSinceEpoch) { [forMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
QTimeZonePrivate::Data data = posixTrans.at(i); return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
data.atMSecsSinceEpoch = forMSecsSinceEpoch; });
return data; if (it > posixTrans.cbegin()) {
} QTimeZonePrivate::Data data = *--it;
}
}
// 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; data.atMSecsSinceEpoch = forMSecsSinceEpoch;
return data; return data;
} }
} }
// Otherwise use the earliest transition we have // Otherwise, if we can find a valid tran, then use its rule:
if (m_tranTimes.size() > 0) { auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
Data data = dataForTzTransition(m_tranTimes.at(0)); [forMSecsSinceEpoch] (const QTzTransitionTime &at) {
data.atMSecsSinceEpoch = forMSecsSinceEpoch; return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
return data; });
} if (last > m_tranTimes.cbegin())
--last;
// Otherwise we have no rules, so probably an invalid tz, so return invalid data Data data = dataForTzTransition(*last);
return invalidData(); data.atMSecsSinceEpoch = forMSecsSinceEpoch;
return data;
} }
bool QTzTimeZonePrivate::hasTransitions() const bool QTzTimeZonePrivate::hasTransitions() const
@ -992,21 +993,20 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSince
QVector<QTimeZonePrivate::Data> posixTrans = QVector<QTimeZonePrivate::Data> posixTrans =
calculatePosixTransitions(m_posixRule, year - 1, year + 1, calculatePosixTransitions(m_posixRule, year - 1, year + 1,
m_tranTimes.last().atMSecsSinceEpoch); m_tranTimes.last().atMSecsSinceEpoch);
for (int i = 0; i < posixTrans.size(); ++i) { auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
if (posixTrans.at(i).atMSecsSinceEpoch > afterMSecsSinceEpoch) [afterMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
return posixTrans.at(i); return at.atMSecsSinceEpoch <= afterMSecsSinceEpoch;
} });
return it == posixTrans.cend() ? invalidData() : *it;
} }
// Otherwise if we can find a valid tran then use its rule // Otherwise, if we can find a valid tran, use its rule:
for (int i = 0; i < m_tranTimes.size(); ++i) { auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
if (m_tranTimes.at(i).atMSecsSinceEpoch > afterMSecsSinceEpoch) { [afterMSecsSinceEpoch] (const QTzTransitionTime &at) {
return dataForTzTransition(m_tranTimes.at(i)); return at.atMSecsSinceEpoch <= afterMSecsSinceEpoch;
} });
} return last != m_tranTimes.cend() ? dataForTzTransition(*last) : invalidData();
// Otherwise we have no rule, or there is no next transition, so return invalid data
return invalidData();
} }
QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
@ -1018,21 +1018,20 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecs
QVector<QTimeZonePrivate::Data> posixTrans = QVector<QTimeZonePrivate::Data> posixTrans =
calculatePosixTransitions(m_posixRule, year - 1, year + 1, calculatePosixTransitions(m_posixRule, year - 1, year + 1,
m_tranTimes.last().atMSecsSinceEpoch); m_tranTimes.last().atMSecsSinceEpoch);
for (int i = posixTrans.size() - 1; i >= 0; --i) { auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
if (posixTrans.at(i).atMSecsSinceEpoch < beforeMSecsSinceEpoch) [beforeMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
return posixTrans.at(i); return at.atMSecsSinceEpoch < beforeMSecsSinceEpoch;
} });
Q_ASSERT(it > posixTrans.cbegin());
return *--it;
} }
// Otherwise if we can find a valid tran then use its rule // Otherwise if we can find a valid tran then use its rule
for (int i = m_tranTimes.size() - 1; i >= 0; --i) { auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
if (m_tranTimes.at(i).atMSecsSinceEpoch < beforeMSecsSinceEpoch) { [beforeMSecsSinceEpoch] (const QTzTransitionTime &at) {
return dataForTzTransition(m_tranTimes.at(i)); return at.atMSecsSinceEpoch < beforeMSecsSinceEpoch;
} });
} return last > m_tranTimes.cbegin() ? dataForTzTransition(*--last) : invalidData();
// Otherwise we have no rule, so return invalid data
return invalidData();
} }
// TODO Could cache the value and monitor the required files for any changes // TODO Could cache the value and monitor the required files for any changes