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>
This commit is contained in:
parent
9b7e6cb83d
commit
f767d3a1b2
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user