summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qtimezoneprivate_win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools/qtimezoneprivate_win.cpp')
-rw-r--r--src/corelib/tools/qtimezoneprivate_win.cpp227
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) {