MS TZ data: avoid calculating a date in year 0
There is no year 0 in the proleptic Gregorian calendar, so QDate() won't be happy if asked for a date in it. Tweak scanning of the data we get from MS-Win so as to avoid a date calculation that could otherwise happen in year 0 when constructing QDateTime(QDate(1, 1, 1), QTime(0, 0, 0), QTimeZone("Australia/Sydney")). Added a test for this case, which Oliver Wolff has kindly verified does reproduce the assertion failure. However, Coin is unable to reproduce, as all its MS builds are configured with -release, so Q_ASSERT() does nothing. (The relevant code then skips over year 0, albeit for the wrong reasons, and gets the right results, albeit inefficiently, leaving no other symptom by which to detect the problem.) Fixes: QTBUG-78051 Change-Id: Ife8a7470e5bd450bc421e89b3f1e1211756fc889 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Oliver Wolff <oliver.wolff@qt.io> Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
9864d2c6f3
commit
8286daba03
@ -371,6 +371,7 @@ QDate calculateTransitionLocalDate(const SYSTEMTIME &rule, int year)
|
||||
// Otherwise, the rule date is annual and relative:
|
||||
const int dayOfWeek = rule.wDayOfWeek == 0 ? 7 : rule.wDayOfWeek;
|
||||
QDate date(year, rule.wMonth, 1);
|
||||
Q_ASSERT(date.isValid());
|
||||
// How many days before was last dayOfWeek before target month ?
|
||||
int adjust = dayOfWeek - date.dayOfWeek(); // -6 <= adjust < 7
|
||||
if (adjust >= 0) // Ensure -7 <= adjust < 0:
|
||||
@ -401,6 +402,7 @@ qint64 calculateTransitionForYear(const SYSTEMTIME &rule, int year, int bias)
|
||||
{
|
||||
// TODO Consider caching the calculated values - i.e. replace SYSTEMTIME in
|
||||
// WinTransitionRule; do this in init() once and store the results.
|
||||
Q_ASSERT(year);
|
||||
const QDate date = calculateTransitionLocalDate(rule, year);
|
||||
const QTime time = QTime(rule.wHour, rule.wMinute, rule.wSecond);
|
||||
if (date.isValid() && time.isValid())
|
||||
@ -479,6 +481,7 @@ struct TransitionTimePair
|
||||
|
||||
int yearEndOffset(const QWinTimeZonePrivate::QWinTransitionRule &rule, int year)
|
||||
{
|
||||
Q_ASSERT(year);
|
||||
int offset = rule.standardTimeBias;
|
||||
// Only needed to help another TransitionTimePair work out year + 1's start
|
||||
// offset; and the oldYearOffset we use only affects an alleged transition
|
||||
@ -743,11 +746,12 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) cons
|
||||
const QWinTransitionRule &rule = m_tranRules.at(ruleIndex);
|
||||
// Does this rule's period include any transition at all ?
|
||||
if (rule.standardTimeRule.wMonth > 0 || rule.daylightTimeRule.wMonth > 0) {
|
||||
const int endYear = qMax(rule.startYear, year - 1);
|
||||
int prior = year == 1 ? -1 : year - 1; // No year 0.
|
||||
const int endYear = qMax(rule.startYear, prior);
|
||||
while (year >= endYear) {
|
||||
const int newYearOffset = (year <= rule.startYear && ruleIndex > 0)
|
||||
? yearEndOffset(m_tranRules.at(ruleIndex - 1), year - 1)
|
||||
: yearEndOffset(rule, year - 1);
|
||||
? yearEndOffset(m_tranRules.at(ruleIndex - 1), prior)
|
||||
: yearEndOffset(rule, prior);
|
||||
const TransitionTimePair pair(rule, year, newYearOffset);
|
||||
bool isDst = false;
|
||||
if (pair.std != invalidMSecs() && pair.std <= forMSecsSinceEpoch) {
|
||||
@ -755,7 +759,8 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) cons
|
||||
} else if (pair.dst != invalidMSecs() && pair.dst <= forMSecsSinceEpoch) {
|
||||
isDst = true;
|
||||
} else {
|
||||
--year; // Try an earlier year for this rule (once).
|
||||
year = prior; // Try an earlier year for this rule (once).
|
||||
prior = year == 1 ? -1 : year - 1; // No year 0.
|
||||
continue;
|
||||
}
|
||||
return ruleToData(rule, forMSecsSinceEpoch,
|
||||
@ -767,8 +772,11 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) cons
|
||||
// No transition, no DST, use the year's standard time.
|
||||
return ruleToData(rule, forMSecsSinceEpoch, QTimeZone::StandardTime);
|
||||
}
|
||||
if (year >= rule.startYear)
|
||||
if (year >= rule.startYear) {
|
||||
year = rule.startYear - 1; // Seek last transition in new rule.
|
||||
if (!year)
|
||||
--year;
|
||||
}
|
||||
}
|
||||
// We don't have relevant data :-(
|
||||
return invalidData();
|
||||
@ -795,9 +803,10 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::nextTransition(qint64 afterMSecsSinc
|
||||
year = rule.startYear; // Seek first transition in this rule.
|
||||
const int endYear = ruleIndex + 1 < m_tranRules.count()
|
||||
? qMin(m_tranRules.at(ruleIndex + 1).startYear, year + 2) : (year + 2);
|
||||
int prior = year == 1 ? -1 : year - 1; // No year 0.
|
||||
int newYearOffset = (year <= rule.startYear && ruleIndex > 0)
|
||||
? yearEndOffset(m_tranRules.at(ruleIndex - 1), year - 1)
|
||||
: yearEndOffset(rule, year - 1);
|
||||
? yearEndOffset(m_tranRules.at(ruleIndex - 1), prior)
|
||||
: yearEndOffset(rule, prior);
|
||||
while (year < endYear) {
|
||||
const TransitionTimePair pair(rule, year, newYearOffset);
|
||||
bool isDst = false;
|
||||
@ -810,7 +819,9 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::nextTransition(qint64 afterMSecsSinc
|
||||
newYearOffset = rule.standardTimeBias;
|
||||
if (pair.dst > pair.std)
|
||||
newYearOffset += rule.daylightTimeBias;
|
||||
++year; // Try a later year for this rule (once).
|
||||
// Try a later year for this rule (once).
|
||||
prior = year;
|
||||
year = year == -1 ? 1 : year + 1; // No year 0
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -837,11 +848,12 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSec
|
||||
const QWinTransitionRule &rule = m_tranRules.at(ruleIndex);
|
||||
// Does this rule's period include any transition at all ?
|
||||
if (rule.standardTimeRule.wMonth > 0 || rule.daylightTimeRule.wMonth > 0) {
|
||||
const int endYear = qMax(rule.startYear, year - 1);
|
||||
int prior = year == 1 ? -1 : year - 1; // No year 0.
|
||||
const int endYear = qMax(rule.startYear, prior);
|
||||
while (year >= endYear) {
|
||||
const int newYearOffset = (year <= rule.startYear && ruleIndex > 0)
|
||||
? yearEndOffset(m_tranRules.at(ruleIndex - 1), year - 1)
|
||||
: yearEndOffset(rule, year - 1);
|
||||
? yearEndOffset(m_tranRules.at(ruleIndex - 1), prior)
|
||||
: yearEndOffset(rule, prior);
|
||||
const TransitionTimePair pair(rule, year, newYearOffset);
|
||||
bool isDst = false;
|
||||
if (pair.std != invalidMSecs() && pair.std < beforeMSecsSinceEpoch) {
|
||||
@ -849,7 +861,8 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSec
|
||||
} else if (pair.dst != invalidMSecs() && pair.dst < beforeMSecsSinceEpoch) {
|
||||
isDst = true;
|
||||
} else {
|
||||
--year; // Try an earlier year for this rule (once).
|
||||
year = prior; // Try an earlier year for this rule (once).
|
||||
prior = year == 1 ? -1 : year - 1; // No year 0.
|
||||
continue;
|
||||
}
|
||||
if (isDst)
|
||||
@ -863,8 +876,11 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSec
|
||||
// rule:
|
||||
return ruleToData(rule, startOfTime, QTimeZone::StandardTime, false);
|
||||
} // else: no transition during rule's period
|
||||
if (year >= rule.startYear)
|
||||
if (year >= rule.startYear) {
|
||||
year = rule.startYear - 1; // Seek last transition in new rule
|
||||
if (!year)
|
||||
--year;
|
||||
}
|
||||
}
|
||||
// Apparently no transition before the given time:
|
||||
return invalidData();
|
||||
|
@ -3352,6 +3352,14 @@ void tst_QDateTime::timeZones() const
|
||||
QCOMPARE(dt3.timeSpec(), dt1.timeSpec());
|
||||
QCOMPARE(dt3.timeZone(), dt1.timeZone());
|
||||
|
||||
// The start of year 1 should be *describable* in any zone (QTBUG-78051)
|
||||
dt3 = QDateTime(QDate(1, 1, 1), QTime(0, 0, 0), ausTz);
|
||||
QVERIFY(dt3.isValid());
|
||||
// Likewise the end of year -1 (a.k.a. 1 BCE).
|
||||
dt3 = dt3.addMSecs(-1);
|
||||
QVERIFY(dt3.isValid());
|
||||
QCOMPARE(dt3, QDateTime(QDate(-1, 12, 31), QTime(23, 59, 59, 999), ausTz));
|
||||
|
||||
// Check datastream serialises the time zone
|
||||
QByteArray tmp;
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user