diff options
Diffstat (limited to 'src/corelib/tools/qtimezoneprivate_win.cpp')
-rw-r--r-- | src/corelib/tools/qtimezoneprivate_win.cpp | 227 |
1 files changed, 145 insertions, 82 deletions
diff --git a/src/corelib/tools/qtimezoneprivate_win.cpp b/src/corelib/tools/qtimezoneprivate_win.cpp index a50227d3cc..3bce55925f 100644 --- a/src/corelib/tools/qtimezoneprivate_win.cpp +++ b/src/corelib/tools/qtimezoneprivate_win.cpp @@ -259,8 +259,38 @@ static DYNAMIC_TIME_ZONE_INFORMATION dynamicInfoForId(const QByteArray &windowsI } return dtzInfo; } + +static QWinTimeZonePrivate::QWinTransitionRule +readDynamicRule(DYNAMIC_TIME_ZONE_INFORMATION &dtzi, int year, bool *ok) +{ + TIME_ZONE_INFORMATION tzi; + QWinTimeZonePrivate::QWinTransitionRule rule; + *ok = GetTimeZoneInformationForYear(year, &dtzi, &tzi); + if (*ok) { + rule.startYear = 0; + rule.standardTimeBias = tzi.Bias + tzi.StandardBias; + rule.daylightTimeBias = tzi.Bias + tzi.DaylightBias - rule.standardTimeBias; + rule.standardTimeRule = tzi.StandardDate; + rule.daylightTimeRule = tzi.DaylightDate; + } + return rule; +} #endif // QT_USE_REGISTRY_TIMEZONE +static bool isSameRule(const QWinTimeZonePrivate::QWinTransitionRule &last, + const QWinTimeZonePrivate::QWinTransitionRule &rule) +{ + // In particular, when this is true and either wYear is 0, so is the other; + // so if one rule is recurrent and they're equal, so is the other. If + // either rule *isn't* recurrent, it has non-0 wYear which shall be + // different from the other's. Note that we don't compare .startYear, since + // that will always be different. + return equalSystemtime(last.standardTimeRule, rule.standardTimeRule) + && equalSystemtime(last.daylightTimeRule, rule.daylightTimeRule) + && last.standardTimeBias == rule.standardTimeBias + && last.daylightTimeBias == rule.daylightTimeBias; +} + static QList<QByteArray> availableWindowsIds() { #ifdef QT_USE_REGISTRY_TIMEZONE @@ -356,30 +386,33 @@ static inline qint64 timeToMSecs(const QDate &date, const QTime &time) + time.msecsSinceStartOfDay(); } -static void calculateTransitionsForYear(const QWinTimeZonePrivate::QWinTransitionRule &rule, int year, - qint64 *stdMSecs, qint64 *dstMSecs) +static qint64 calculateTransitionForYear(const SYSTEMTIME &rule, int year, int bias) { // TODO Consider caching the calculated values - // The local time in Daylight Time when switches to Standard Time - QDate standardDate = calculateTransitionLocalDate(rule.standardTimeRule, year); - QTime standardTime = QTime(rule.standardTimeRule.wHour, rule.standardTimeRule.wMinute, - rule.standardTimeRule.wSecond); - if (standardDate.isValid() && standardTime.isValid()) { - *stdMSecs = timeToMSecs(standardDate, standardTime) - + ((rule.standardTimeBias + rule.daylightTimeBias) * 60000); - } else { - *stdMSecs = QTimeZonePrivate::invalidMSecs(); - } - - // The local time in Standard Time when switches to Daylight Time - QDate daylightDate = calculateTransitionLocalDate(rule.daylightTimeRule, year); - QTime daylightTime = QTime(rule.daylightTimeRule.wHour, rule.daylightTimeRule.wMinute, - rule.daylightTimeRule.wSecond); - if (daylightDate.isValid() && daylightTime.isValid()) - *dstMSecs = timeToMSecs(daylightDate, daylightTime) + (rule.standardTimeBias * 60000); - else - *dstMSecs = QTimeZonePrivate::invalidMSecs(); -} + const QDate date = calculateTransitionLocalDate(rule, year); + const QTime time = QTime(rule.wHour, rule.wMinute, rule.wSecond); + if (date.isValid() && time.isValid()) + return timeToMSecs(date, time) + bias * 60000; + return QTimeZonePrivate::invalidMSecs(); +} + +namespace { +struct TransitionTimePair +{ + // Transition times after the epoch, in ms: + qint64 std, dst; + TransitionTimePair() + : std(QTimeZonePrivate::invalidMSecs()), dst(QTimeZonePrivate::invalidMSecs()) + {} + TransitionTimePair(const QWinTimeZonePrivate::QWinTransitionRule &rule, int year) + // The local time in Daylight Time of the switch to Standard Time + : std(calculateTransitionForYear(rule.standardTimeRule, year, + rule.standardTimeBias + rule.daylightTimeBias)), + // The local time in Standard Time of the switch to Daylight Time + dst(calculateTransitionForYear(rule.daylightTimeRule, year, rule.standardTimeBias)) + {} +}; +} // anonymous namespace static QLocale::Country userCountry() { @@ -390,6 +423,34 @@ static QLocale::Country userCountry() : QLocale::AnyCountry; } +// Index of last rule in rules with .startYear <= year: +static int ruleIndexForYear(const QList<QWinTimeZonePrivate::QWinTransitionRule> &rules, int year) +{ + if (rules.last().startYear <= year) + return rules.count() - 1; + // We don't have a rule for before the first, but the first is the best we can offer: + if (rules.first().startYear > year) + return 0; + + // Otherwise, use binary chop: + int lo = 0, hi = rules.count(); + // invariant: rules[i].startYear <= year < rules[hi].startYear + // subject to treating rules[rules.count()] as "off the end of time" + while (lo + 1 < hi) { + const int mid = (lo + hi) / 2; + // lo + 2 <= hi, so lo + 1 <= mid <= hi - 1, so lo < mid < hi + // In particular, mid < rules.count() + const int midYear = rules.at(mid).startYear; + if (midYear > year) + hi = mid; + else if (midYear < year) + lo = mid; + else // No two rules have the same startYear: + return mid; + } + return lo; +} + // Create the system default time zone QWinTimeZonePrivate::QWinTimeZonePrivate() : QTimeZonePrivate() @@ -453,16 +514,20 @@ void QWinTimeZonePrivate::init(const QByteArray &ianaId) QWinTransitionRule rule = readRegistryRule(dynamicKey, (LPCWSTR)QString::number(year).utf16(), &ruleOk); - rule.startYear = year; - if (ruleOk) + if (ruleOk + // Don't repeat a recurrent rule: + && (m_tranRules.isEmpty() + || !isSameRule(m_tranRules.last(), rule))) { + rule.startYear = m_tranRules.isEmpty() ? MIN_YEAR : year; m_tranRules.append(rule); + } } RegCloseKey(dynamicKey); } else { // No dynamic data so use the base data bool ruleOk; QWinTransitionRule rule = readRegistryRule(baseKey, L"TZI", &ruleOk); - rule.startYear = 1970; + rule.startYear = MIN_YEAR; if (ruleOk) m_tranRules.append(rule); } @@ -479,20 +544,28 @@ void QWinTimeZonePrivate::init(const QByteArray &ianaId) DWORD firstYear = 0; DWORD lastYear = 0; DYNAMIC_TIME_ZONE_INFORMATION dtzi = dynamicInfoForId(m_windowsId); - GetDynamicTimeZoneInformationEffectiveYears(&dtzi, &firstYear, &lastYear); - // If there is no dynamic information, you can still query for - // year 0, which helps simplifying following part - for (DWORD year = firstYear; year <= lastYear; ++year) { - TIME_ZONE_INFORMATION tzi; - if (!GetTimeZoneInformationForYear(year, &dtzi, &tzi)) - continue; - QWinTransitionRule rule; - rule.standardTimeBias = tzi.Bias + tzi.StandardBias; - rule.daylightTimeBias = tzi.Bias + tzi.DaylightBias - rule.standardTimeBias; - rule.standardTimeRule = tzi.StandardDate; - rule.daylightTimeRule = tzi.DaylightDate; - rule.startYear = year; - m_tranRules.append(rule); + if (GetDynamicTimeZoneInformationEffectiveYears(&dtzi, &firstYear, &lastYear) + == ERROR_SUCCESS && firstYear < lastYear) { + for (DWORD year = firstYear; year <= lastYear; ++year) { + bool ok = false; + QWinTransitionRule rule = readDynamicRule(dtzi, year, &ok); + if (ok + // Don't repeat a recurrent rule + && (m_tranRules.isEmpty() + || !isSameRule(m_tranRules.last(), rule))) { + rule.startYear = m_tranRules.isEmpty() ? MIN_YEAR : year; + m_tranRules.append(rule); + } + } + } else { + // At least try to get the non-dynamic data: + dtzi.DynamicDaylightTimeDisabled = false; + bool ok = false; + QWinTransitionRule rule = readDynamicRule(dtzi, 1970, &ok); + if (ok) { + rule.startYear = MIN_YEAR; + m_tranRules.append(rule); + } } } #endif // QT_USE_REGISTRY_TIMEZONE @@ -519,7 +592,8 @@ QString QWinTimeZonePrivate::displayName(QTimeZone::TimeType timeType, Q_UNUSED(locale); if (nameType == QTimeZone::OffsetName) { - QWinTransitionRule rule = ruleForYear(QDate::currentDate().year()); + const QWinTransitionRule &rule = + m_tranRules.at(ruleIndexForYear(m_tranRules, QDate::currentDate().year())); if (timeType == QTimeZone::DaylightTime) return isoOffsetFormat((rule.standardTimeBias + rule.daylightTimeBias) * -60); else @@ -575,22 +649,22 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) cons qint64 first; qint64 second; qint64 next = maxMSecs(); - qint64 stdMSecs; - qint64 dstMSecs; + TransitionTimePair pair; QWinTransitionRule rule; do { // Convert the transition rules into msecs for the year we want to try - rule = ruleForYear(year); + rule = m_tranRules.at(ruleIndexForYear(m_tranRules, year)); // If no transition rules to calculate then no DST, so just use rule for std if (rule.standardTimeRule.wMonth == 0 && rule.daylightTimeRule.wMonth == 0) break; - calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs); - if (stdMSecs < dstMSecs) { - first = stdMSecs; - second = dstMSecs; + + pair = TransitionTimePair(rule, year); + if (pair.std < pair.dst) { + first = pair.std; + second = pair.dst; } else { - first = dstMSecs; - second = stdMSecs; + first = pair.dst; + second = pair.std; } if (forMSecsSinceEpoch >= second && second != invalidMSecs()) next = second; @@ -600,7 +674,7 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) cons --year; } while (next == maxMSecs() && year >= MIN_YEAR); - return ruleToData(rule, forMSecsSinceEpoch, (next == dstMSecs) ? QTimeZone::DaylightTime : QTimeZone::StandardTime); + return ruleToData(rule, forMSecsSinceEpoch, (next == pair.dst) ? QTimeZone::DaylightTime : QTimeZone::StandardTime); } bool QWinTimeZonePrivate::hasTransitions() const @@ -621,7 +695,7 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::nextTransition(qint64 afterMSecsSinc // If the required year falls after the last rule start year and the last rule has no // valid future transition calculations then there is no next transition if (year > m_tranRules.last().startYear) { - rule = ruleForYear(year); + rule = m_tranRules.at(ruleIndexForYear(m_tranRules, year)); // If the rules have either a fixed year, or no month, then no future trans if (rule.standardTimeRule.wYear != 0 || rule.daylightTimeRule.wYear != 0 || rule.standardTimeRule.wMonth == 0 || rule.daylightTimeRule.wMonth == 0) { @@ -634,22 +708,21 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::nextTransition(qint64 afterMSecsSinc qint64 first; qint64 second; qint64 next = minMSecs(); - qint64 stdMSecs; - qint64 dstMSecs; + TransitionTimePair pair; do { // Convert the transition rules into msecs for the year we want to try - rule = ruleForYear(year); + rule = m_tranRules.at(ruleIndexForYear(m_tranRules, year)); // If no transition rules to calculate then no next transition if (rule.standardTimeRule.wMonth == 0 && rule.daylightTimeRule.wMonth == 0) return invalidData(); - calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs); + pair = TransitionTimePair(rule, year); // Find the first and second transition for the year - if (stdMSecs < dstMSecs) { - first = stdMSecs; - second = dstMSecs; + if (pair.std < pair.dst) { + first = pair.std; + second = pair.dst; } else { - first = dstMSecs; - second = stdMSecs; + first = pair.dst; + second = pair.std; } if (afterMSecsSinceEpoch < first) next = first; @@ -662,7 +735,7 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::nextTransition(qint64 afterMSecsSinc if (next == minMSecs() || next == invalidMSecs()) return invalidData(); - return ruleToData(rule, next, (next == dstMSecs) ? QTimeZone::DaylightTime : QTimeZone::StandardTime); + return ruleToData(rule, next, (next == pair.dst) ? QTimeZone::DaylightTime : QTimeZone::StandardTime); } QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const @@ -674,7 +747,7 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSec // If the required year falls before the first rule start year and the first rule has no // valid transition calculations then there is no previous transition if (year < m_tranRules.first().startYear) { - rule = ruleForYear(year); + rule = m_tranRules.at(ruleIndexForYear(m_tranRules, year)); // If the rules have either a fixed year, or no month, then no previous trans if (rule.standardTimeRule.wYear != 0 || rule.daylightTimeRule.wYear != 0 || rule.standardTimeRule.wMonth == 0 || rule.daylightTimeRule.wMonth == 0) { @@ -685,21 +758,20 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSec qint64 first; qint64 second; qint64 next = maxMSecs(); - qint64 stdMSecs; - qint64 dstMSecs; + TransitionTimePair pair; do { // Convert the transition rules into msecs for the year we want to try - rule = ruleForYear(year); + rule = m_tranRules.at(ruleIndexForYear(m_tranRules, year)); // If no transition rules to calculate then no previous transition if (rule.standardTimeRule.wMonth == 0 && rule.daylightTimeRule.wMonth == 0) return invalidData(); - calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs); - if (stdMSecs < dstMSecs) { - first = stdMSecs; - second = dstMSecs; + pair = TransitionTimePair(rule, year); + if (pair.std < pair.dst) { + first = pair.std; + second = pair.dst; } else { - first = dstMSecs; - second = stdMSecs; + first = pair.dst; + second = pair.std; } if (beforeMSecsSinceEpoch > second && second != invalidMSecs()) next = second; @@ -712,7 +784,7 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSec if (next == maxMSecs()) return invalidData(); - return ruleToData(rule, next, (next == dstMSecs) ? QTimeZone::DaylightTime : QTimeZone::StandardTime); + return ruleToData(rule, next, (next == pair.dst) ? QTimeZone::DaylightTime : QTimeZone::StandardTime); } QByteArray QWinTimeZonePrivate::systemTimeZoneId() const @@ -744,20 +816,11 @@ QList<QByteArray> QWinTimeZonePrivate::availableTimeZoneIds() const return result; } -QWinTimeZonePrivate::QWinTransitionRule QWinTimeZonePrivate::ruleForYear(int year) const -{ - for (int i = m_tranRules.size() - 1; i >= 0; --i) { - if (m_tranRules.at(i).startYear <= year) - return m_tranRules.at(i); - } - return m_tranRules.at(0); -} - QTimeZonePrivate::Data QWinTimeZonePrivate::ruleToData(const QWinTransitionRule &rule, qint64 atMSecsSinceEpoch, QTimeZone::TimeType type) const { - QTimeZonePrivate::Data tran = QTimeZonePrivate::invalidData(); + Data tran = invalidData(); tran.atMSecsSinceEpoch = atMSecsSinceEpoch; tran.standardTimeOffset = rule.standardTimeBias * -60; if (type == QTimeZone::DaylightTime) { |