summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorunknown <jlayt@kde.org>2013-10-31 11:07:47 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-11-09 01:11:07 +0100
commitf767d3a1b20b16c5e19456d3839651ffe14dd442 (patch)
tree028deba58b6dcf5b7fc6e088d7758cf645b32c46
parent9b7e6cb83d99642977ef88a1426d3f79751c6869 (diff)
QTimeZone - Fix Windows Transitions
The Windows tz transition routines were not checking for a number of invalid scenarios, in particular where there are no next transitions able to be calcualted, leading to infinite loops. Change-Id: I262b4321a95be1df4228774ada3908f8d3ed6c1a Reviewed-by: Mitch Curtis <mitch.curtis@digia.com> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r--src/corelib/tools/qtimezoneprivate_win.cpp152
1 files changed, 89 insertions, 63 deletions
diff --git a/src/corelib/tools/qtimezoneprivate_win.cpp b/src/corelib/tools/qtimezoneprivate_win.cpp
index efdd6676fb..f411d35b3d 100644
--- a/src/corelib/tools/qtimezoneprivate_win.cpp
+++ b/src/corelib/tools/qtimezoneprivate_win.cpp
@@ -254,6 +254,10 @@ static QByteArray windowsSystemZoneId()
static QDate calculateTransitionLocalDate(const SYSTEMTIME &rule, int year)
{
+ // If month is 0 then there is no date
+ if (rule.wMonth == 0)
+ return QDate();
+
SYSTEMTIME time = rule;
// If the year isn't set, then the rule date is relative
if (time.wYear == 0) {
@@ -269,9 +273,10 @@ static QDate calculateTransitionLocalDate(const SYSTEMTIME &rule, int year)
while (date.month() != time.wMonth)
date = date.addDays(-7);
return date;
- } else {
- return QDate(time.wYear, time.wMonth, time.wDay);
}
+
+ // If the year is set then is an absolute date
+ return QDate(time.wYear, time.wMonth, time.wDay);
}
// Converts a date/time value into msecs
@@ -285,19 +290,25 @@ static void calculateTransitionsForYear(const QWinTimeZonePrivate::QWinTransitio
qint64 *stdMSecs, qint64 *dstMSecs)
{
// TODO Consider caching the calculated values
-
- // The local time in Daylight Time when switches to Standard TIme
+ // 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);
- // The local time in Standard Time when switches to Daylight TIme
+ 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);
-
- *stdMSecs = timeToMSecs(standardDate, standardTime)
- + ((rule.standardTimeBias + rule.daylightTimeBias) * 60000);
- *dstMSecs = timeToMSecs(daylightDate, daylightTime) + (rule.standardTimeBias * 60000);
+ if (standardDate.isValid() && standardTime.isValid())
+ *dstMSecs = timeToMSecs(daylightDate, daylightTime) + (rule.standardTimeBias * 60000);
+ else
+ *dstMSecs = QTimeZonePrivate::invalidMSecs();
}
static QLocale::Country userCountry()
@@ -465,15 +476,8 @@ bool QWinTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
{
- // Convert MSecs to year to get transitions for, but around 31 Dec/1 Jan may not be right year
- // So get the year after we think we want transitions for, to be safe
- QDate date = msecsToDate(forMSecsSinceEpoch);
- int year;
- int month;
- int day;
- date.getDate(&year, &month, &day);
- if ((month == 12 && day == 31) || (month == 1 && day == 1))
- ++year;
+ // Convert MSecs to year to get transitions for, assumes no transitions around 31 Dec/1 Jan
+ int year = msecsToDate(forMSecsSinceEpoch).year();
qint64 first;
qint64 second;
@@ -485,20 +489,22 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) cons
// Convert the transition rules into msecs for the year we want to try
rule = ruleForYear(year);
calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
- first = qMin(stdMSecs, dstMSecs);
- second = qMax(stdMSecs, dstMSecs);
- if (forMSecsSinceEpoch >= second)
+ if (stdMSecs < dstMSecs) {
+ first = stdMSecs;
+ second = dstMSecs;
+ } else {
+ first = dstMSecs;
+ second = stdMSecs;
+ }
+ if (forMSecsSinceEpoch >= second && second != invalidMSecs())
next = second;
- else if (forMSecsSinceEpoch >= first)
+ else if (forMSecsSinceEpoch >= first && first != invalidMSecs())
next = first;
// If didn't fall in this year, try the previous
--year;
- } while (forMSecsSinceEpoch < first && year >= MIN_YEAR);
+ } while (next == maxMSecs() && year >= MIN_YEAR);
- if (next == dstMSecs)
- return ruleToData(rule, forMSecsSinceEpoch, QTimeZone::DaylightTime);
- else
- return ruleToData(rule, forMSecsSinceEpoch, QTimeZone::StandardTime);
+ return ruleToData(rule, forMSecsSinceEpoch, (next == dstMSecs) ? QTimeZone::DaylightTime : QTimeZone::StandardTime);
}
bool QWinTimeZonePrivate::hasTransitions() const
@@ -512,79 +518,99 @@ bool QWinTimeZonePrivate::hasTransitions() const
QTimeZonePrivate::Data QWinTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const
{
- // Convert MSecs to year to get transitions for, but around 31 Dec/1 Jan may not be right year
- // Get the year before we think we want transitions for, to be safe
- QDate date = msecsToDate(afterMSecsSinceEpoch);
- int year;
- int month;
- int day;
- date.getDate(&year, &month, &day);
- if ((month == 12 && day == 31) || (month == 1 && day == 1))
- --year;
+ // Convert MSecs to year to get transitions for, assumes no transitions around 31 Dec/1 Jan
+ int year = msecsToDate(afterMSecsSinceEpoch).year();
+
+ QWinTransitionRule rule;
+ // 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);
+ // 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) {
+ return invalidData();
+ }
+ }
+ // Otherwise we have a valid rule for the required year that can be used
+ // to calculate this year or next
qint64 first;
qint64 second;
qint64 next = minMSecs();
qint64 stdMSecs;
qint64 dstMSecs;
- QWinTransitionRule rule;
do {
// Convert the transition rules into msecs for the year we want to try
rule = ruleForYear(year);
calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
// Find the first and second transition for the year
- first = qMin(stdMSecs, dstMSecs);
- second = qMax(stdMSecs, dstMSecs);
+ if (stdMSecs < dstMSecs) {
+ first = stdMSecs;
+ second = dstMSecs;
+ } else {
+ first = dstMSecs;
+ second = stdMSecs;
+ }
if (afterMSecsSinceEpoch < first)
next = first;
else if (afterMSecsSinceEpoch < second)
next = second;
// If didn't fall in this year, try the next
++year;
- } while (afterMSecsSinceEpoch >= second && year <= MAX_YEAR);
+ } while (next == minMSecs() && year <= MAX_YEAR);
- if (next == dstMSecs)
- return ruleToData(rule, next, QTimeZone::DaylightTime);
- else
- return ruleToData(rule, next, QTimeZone::StandardTime);
+ if (next == minMSecs() || next == invalidMSecs())
+ return invalidData();
+
+ return ruleToData(rule, next, (next == dstMSecs) ? QTimeZone::DaylightTime : QTimeZone::StandardTime);
}
QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
{
- // Convert MSecs to year to get transitions for, but around 31 Dec/1 Jan may not be right year
- // So get the year after we think we want transitions for, to be safe
- QDate date = msecsToDate(beforeMSecsSinceEpoch);
- int year;
- int month;
- int day;
- date.getDate(&year, &month, &day);
- if ((month == 12 && day == 31) || (month == 1 && day == 1))
- ++year;
+ // Convert MSecs to year to get transitions for, assumes no transitions around 31 Dec/1 Jan
+ int year = msecsToDate(beforeMSecsSinceEpoch).year();
+
+ QWinTransitionRule rule;
+ // 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);
+ // 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) {
+ return invalidData();
+ }
+ }
qint64 first;
qint64 second;
qint64 next = maxMSecs();
qint64 stdMSecs;
qint64 dstMSecs;
- QWinTransitionRule rule;
do {
// Convert the transition rules into msecs for the year we want to try
rule = ruleForYear(year);
calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
- first = qMin(stdMSecs, dstMSecs);
- second = qMax(stdMSecs, dstMSecs);
- if (beforeMSecsSinceEpoch > second)
+ if (stdMSecs < dstMSecs) {
+ first = stdMSecs;
+ second = dstMSecs;
+ } else {
+ first = dstMSecs;
+ second = stdMSecs;
+ }
+ if (beforeMSecsSinceEpoch > second && second != invalidMSecs())
next = second;
- else if (beforeMSecsSinceEpoch > first)
+ else if (beforeMSecsSinceEpoch > first && first != invalidMSecs())
next = first;
// If didn't fall in this year, try the previous
--year;
- } while (beforeMSecsSinceEpoch < first && year >= MIN_YEAR);
+ } while (next == maxMSecs() && year >= MIN_YEAR);
- if (next == dstMSecs)
- return ruleToData(rule, next, QTimeZone::DaylightTime);
- else
- return ruleToData(rule, next, QTimeZone::StandardTime);
+ if (next == maxMSecs())
+ return invalidData();
+
+ return ruleToData(rule, next, (next == dstMSecs) ? QTimeZone::DaylightTime : QTimeZone::StandardTime);
}
QByteArray QWinTimeZonePrivate::systemTimeZoneId() const