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)
|
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;
|
SYSTEMTIME time = rule;
|
||||||
// If the year isn't set, then the rule date is relative
|
// If the year isn't set, then the rule date is relative
|
||||||
if (time.wYear == 0) {
|
if (time.wYear == 0) {
|
||||||
@ -269,9 +273,10 @@ static QDate calculateTransitionLocalDate(const SYSTEMTIME &rule, int year)
|
|||||||
while (date.month() != time.wMonth)
|
while (date.month() != time.wMonth)
|
||||||
date = date.addDays(-7);
|
date = date.addDays(-7);
|
||||||
return date;
|
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
|
// Converts a date/time value into msecs
|
||||||
@ -285,19 +290,25 @@ static void calculateTransitionsForYear(const QWinTimeZonePrivate::QWinTransitio
|
|||||||
qint64 *stdMSecs, qint64 *dstMSecs)
|
qint64 *stdMSecs, qint64 *dstMSecs)
|
||||||
{
|
{
|
||||||
// TODO Consider caching the calculated values
|
// 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);
|
QDate standardDate = calculateTransitionLocalDate(rule.standardTimeRule, year);
|
||||||
QTime standardTime = QTime(rule.standardTimeRule.wHour, rule.standardTimeRule.wMinute,
|
QTime standardTime = QTime(rule.standardTimeRule.wHour, rule.standardTimeRule.wMinute,
|
||||||
rule.standardTimeRule.wSecond);
|
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);
|
QDate daylightDate = calculateTransitionLocalDate(rule.daylightTimeRule, year);
|
||||||
QTime daylightTime = QTime(rule.daylightTimeRule.wHour, rule.daylightTimeRule.wMinute,
|
QTime daylightTime = QTime(rule.daylightTimeRule.wHour, rule.daylightTimeRule.wMinute,
|
||||||
rule.daylightTimeRule.wSecond);
|
rule.daylightTimeRule.wSecond);
|
||||||
|
if (standardDate.isValid() && standardTime.isValid())
|
||||||
*stdMSecs = timeToMSecs(standardDate, standardTime)
|
|
||||||
+ ((rule.standardTimeBias + rule.daylightTimeBias) * 60000);
|
|
||||||
*dstMSecs = timeToMSecs(daylightDate, daylightTime) + (rule.standardTimeBias * 60000);
|
*dstMSecs = timeToMSecs(daylightDate, daylightTime) + (rule.standardTimeBias * 60000);
|
||||||
|
else
|
||||||
|
*dstMSecs = QTimeZonePrivate::invalidMSecs();
|
||||||
}
|
}
|
||||||
|
|
||||||
static QLocale::Country userCountry()
|
static QLocale::Country userCountry()
|
||||||
@ -465,15 +476,8 @@ bool QWinTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
|
|||||||
|
|
||||||
QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) 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
|
// Convert MSecs to year to get transitions for, assumes no transitions around 31 Dec/1 Jan
|
||||||
// So get the year after we think we want transitions for, to be safe
|
int year = msecsToDate(forMSecsSinceEpoch).year();
|
||||||
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;
|
|
||||||
|
|
||||||
qint64 first;
|
qint64 first;
|
||||||
qint64 second;
|
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
|
// Convert the transition rules into msecs for the year we want to try
|
||||||
rule = ruleForYear(year);
|
rule = ruleForYear(year);
|
||||||
calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
|
calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
|
||||||
first = qMin(stdMSecs, dstMSecs);
|
if (stdMSecs < dstMSecs) {
|
||||||
second = qMax(stdMSecs, dstMSecs);
|
first = stdMSecs;
|
||||||
if (forMSecsSinceEpoch >= second)
|
second = dstMSecs;
|
||||||
|
} else {
|
||||||
|
first = dstMSecs;
|
||||||
|
second = stdMSecs;
|
||||||
|
}
|
||||||
|
if (forMSecsSinceEpoch >= second && second != invalidMSecs())
|
||||||
next = second;
|
next = second;
|
||||||
else if (forMSecsSinceEpoch >= first)
|
else if (forMSecsSinceEpoch >= first && first != invalidMSecs())
|
||||||
next = first;
|
next = first;
|
||||||
// If didn't fall in this year, try the previous
|
// If didn't fall in this year, try the previous
|
||||||
--year;
|
--year;
|
||||||
} while (forMSecsSinceEpoch < first && year >= MIN_YEAR);
|
} while (next == maxMSecs() && year >= MIN_YEAR);
|
||||||
|
|
||||||
if (next == dstMSecs)
|
return ruleToData(rule, forMSecsSinceEpoch, (next == dstMSecs) ? QTimeZone::DaylightTime : QTimeZone::StandardTime);
|
||||||
return ruleToData(rule, forMSecsSinceEpoch, QTimeZone::DaylightTime);
|
|
||||||
else
|
|
||||||
return ruleToData(rule, forMSecsSinceEpoch, QTimeZone::StandardTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QWinTimeZonePrivate::hasTransitions() const
|
bool QWinTimeZonePrivate::hasTransitions() const
|
||||||
@ -512,79 +518,99 @@ bool QWinTimeZonePrivate::hasTransitions() const
|
|||||||
|
|
||||||
QTimeZonePrivate::Data QWinTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) 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
|
// Convert MSecs to year to get transitions for, assumes no transitions around 31 Dec/1 Jan
|
||||||
// Get the year before we think we want transitions for, to be safe
|
int year = msecsToDate(afterMSecsSinceEpoch).year();
|
||||||
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;
|
|
||||||
|
|
||||||
|
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 first;
|
||||||
qint64 second;
|
qint64 second;
|
||||||
qint64 next = minMSecs();
|
qint64 next = minMSecs();
|
||||||
qint64 stdMSecs;
|
qint64 stdMSecs;
|
||||||
qint64 dstMSecs;
|
qint64 dstMSecs;
|
||||||
QWinTransitionRule rule;
|
|
||||||
do {
|
do {
|
||||||
// Convert the transition rules into msecs for the year we want to try
|
// Convert the transition rules into msecs for the year we want to try
|
||||||
rule = ruleForYear(year);
|
rule = ruleForYear(year);
|
||||||
calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
|
calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
|
||||||
// Find the first and second transition for the year
|
// Find the first and second transition for the year
|
||||||
first = qMin(stdMSecs, dstMSecs);
|
if (stdMSecs < dstMSecs) {
|
||||||
second = qMax(stdMSecs, dstMSecs);
|
first = stdMSecs;
|
||||||
|
second = dstMSecs;
|
||||||
|
} else {
|
||||||
|
first = dstMSecs;
|
||||||
|
second = stdMSecs;
|
||||||
|
}
|
||||||
if (afterMSecsSinceEpoch < first)
|
if (afterMSecsSinceEpoch < first)
|
||||||
next = first;
|
next = first;
|
||||||
else if (afterMSecsSinceEpoch < second)
|
else if (afterMSecsSinceEpoch < second)
|
||||||
next = second;
|
next = second;
|
||||||
// If didn't fall in this year, try the next
|
// If didn't fall in this year, try the next
|
||||||
++year;
|
++year;
|
||||||
} while (afterMSecsSinceEpoch >= second && year <= MAX_YEAR);
|
} while (next == minMSecs() && year <= MAX_YEAR);
|
||||||
|
|
||||||
if (next == dstMSecs)
|
if (next == minMSecs() || next == invalidMSecs())
|
||||||
return ruleToData(rule, next, QTimeZone::DaylightTime);
|
return invalidData();
|
||||||
else
|
|
||||||
return ruleToData(rule, next, QTimeZone::StandardTime);
|
return ruleToData(rule, next, (next == dstMSecs) ? QTimeZone::DaylightTime : QTimeZone::StandardTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
|
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
|
// Convert MSecs to year to get transitions for, assumes no transitions around 31 Dec/1 Jan
|
||||||
// So get the year after we think we want transitions for, to be safe
|
int year = msecsToDate(beforeMSecsSinceEpoch).year();
|
||||||
QDate date = msecsToDate(beforeMSecsSinceEpoch);
|
|
||||||
int year;
|
QWinTransitionRule rule;
|
||||||
int month;
|
// If the required year falls before the first rule start year and the first rule has no
|
||||||
int day;
|
// valid transition calculations then there is no previous transition
|
||||||
date.getDate(&year, &month, &day);
|
if (year < m_tranRules.first().startYear) {
|
||||||
if ((month == 12 && day == 31) || (month == 1 && day == 1))
|
rule = ruleForYear(year);
|
||||||
++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 first;
|
||||||
qint64 second;
|
qint64 second;
|
||||||
qint64 next = maxMSecs();
|
qint64 next = maxMSecs();
|
||||||
qint64 stdMSecs;
|
qint64 stdMSecs;
|
||||||
qint64 dstMSecs;
|
qint64 dstMSecs;
|
||||||
QWinTransitionRule rule;
|
|
||||||
do {
|
do {
|
||||||
// Convert the transition rules into msecs for the year we want to try
|
// Convert the transition rules into msecs for the year we want to try
|
||||||
rule = ruleForYear(year);
|
rule = ruleForYear(year);
|
||||||
calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
|
calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
|
||||||
first = qMin(stdMSecs, dstMSecs);
|
if (stdMSecs < dstMSecs) {
|
||||||
second = qMax(stdMSecs, dstMSecs);
|
first = stdMSecs;
|
||||||
if (beforeMSecsSinceEpoch > second)
|
second = dstMSecs;
|
||||||
|
} else {
|
||||||
|
first = dstMSecs;
|
||||||
|
second = stdMSecs;
|
||||||
|
}
|
||||||
|
if (beforeMSecsSinceEpoch > second && second != invalidMSecs())
|
||||||
next = second;
|
next = second;
|
||||||
else if (beforeMSecsSinceEpoch > first)
|
else if (beforeMSecsSinceEpoch > first && first != invalidMSecs())
|
||||||
next = first;
|
next = first;
|
||||||
// If didn't fall in this year, try the previous
|
// If didn't fall in this year, try the previous
|
||||||
--year;
|
--year;
|
||||||
} while (beforeMSecsSinceEpoch < first && year >= MIN_YEAR);
|
} while (next == maxMSecs() && year >= MIN_YEAR);
|
||||||
|
|
||||||
if (next == dstMSecs)
|
if (next == maxMSecs())
|
||||||
return ruleToData(rule, next, QTimeZone::DaylightTime);
|
return invalidData();
|
||||||
else
|
|
||||||
return ruleToData(rule, next, QTimeZone::StandardTime);
|
return ruleToData(rule, next, (next == dstMSecs) ? QTimeZone::DaylightTime : QTimeZone::StandardTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray QWinTimeZonePrivate::systemTimeZoneId() const
|
QByteArray QWinTimeZonePrivate::systemTimeZoneId() const
|
||||||
|
Loading…
x
Reference in New Issue
Block a user