Fix misguided winding backwards past start of start of MS TZ data

QWinTimeZonePrivate::data()'s search for a rule applicable to a given
time, in the first year whose milliseconds qint64 can represent, tries
to look at the preceding year to get start-of-year data, which leads
to misleading results. It does so to determine whether to use a rule's
standard or daylight-saving details, but this year is long before the
invention of daylight-saving time, so we can bypass the whole mess.

Unfortunately, MS's data does pretend (in some zones) that DST has
always been in effect, so cutting off that claim at some historical
point will actually get better results for before the cut-off than for
the period after it, until the relevant zone actually adopted DST.
Conservatively put the cut-off at 1900, before any actual zone used
DST, albeit after the idea was originally floated.

This fixes a failure found by some QDate::{start,end}OfDay() tests
that I want to introduce.

Pick-to: 6.3
Task-number: QTBUG-99747
Change-Id: I15cf9dd092b946191e8863c7e85fbeb4ba6c106d
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Edward Welbourne 2022-01-25 15:53:48 +01:00
parent 2e4db2596a
commit 9a83706046

View File

@ -73,6 +73,22 @@ static const wchar_t currTzRegPath[] = LR"(SYSTEM\CurrentControlSet\Control\Time
constexpr qint64 MSECS_PER_DAY = 86400000LL; constexpr qint64 MSECS_PER_DAY = 86400000LL;
constexpr qint64 JULIAN_DAY_FOR_EPOCH = 2440588LL; // result of julianDayFromDate(1970, 1, 1) constexpr qint64 JULIAN_DAY_FOR_EPOCH = 2440588LL; // result of julianDayFromDate(1970, 1, 1)
/* Ignore any claims of DST before 1900.
Daylight-Saving time adjustments were first proposed in 1895 (George Vernon
Hudson in New Zealand) and 1905 (William Willett in the UK) and first adopted
in 1908 (one town in Ontario, Canada) and 1916 (Germany). Since MS's data
tends to pretend the rules in force in 1970ish (or later) had always been in
effect, which presents difficulties for the code that selects correct data
(for a time close to the earliest we can represent), always ignore any claims
a first rule may make of DST before 1900.
See:
* https://www.timeanddate.com/time/dst/history.html
* https://en.wikipedia.org/wiki/Daylight_saving_time#History
*/
constexpr int FIRST_DST_YEAR = 1900;
// Copied from MSDN, see above for link // Copied from MSDN, see above for link
typedef struct _REG_TZI_FORMAT typedef struct _REG_TZI_FORMAT
{ {
@ -606,7 +622,11 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) cons
: yearEndOffset(rule, prior); : yearEndOffset(rule, prior);
const TransitionTimePair pair(rule, year, newYearOffset); const TransitionTimePair pair(rule, year, newYearOffset);
bool isDst = false; bool isDst = false;
if (pair.std != invalidMSecs() && pair.std <= forMSecsSinceEpoch) { if (!ruleIndex && year < FIRST_DST_YEAR) {
// We're before the invention of DST and have no earlier
// rule that might give better data on this year, so just
// extrapolate standard time (modulo fakery) backwards.
} else if (pair.std != invalidMSecs() && pair.std <= forMSecsSinceEpoch) {
isDst = pair.std < pair.dst && pair.dst <= forMSecsSinceEpoch; isDst = pair.std < pair.dst && pair.dst <= forMSecsSinceEpoch;
} else if (pair.dst != invalidMSecs() && pair.dst <= forMSecsSinceEpoch) { } else if (pair.dst != invalidMSecs() && pair.dst <= forMSecsSinceEpoch) {
isDst = true; isDst = true;