From 454bbbe787003be8ba41999ad5e3237b02deafd7 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Thu, 26 Oct 2017 19:18:09 +0200 Subject: Clean up in TZP-Win's calculateTransitionLocalDate() It's decoding a SYSTEMTIME in the slightly quirky manner of MS's timezone APIs (year 0 means annual, with wDay as 1 through 4 for the first through fourth, or 5 for the last, of a specified week-day within a month) and the calculations to go with it were a little opaque. So clean it up, document what it's doing (and why) and assert some things that should be true. Also, only copy one int, instead of a whole structure, to change from their day-numbering to ours. Expand on a related TODO comment in its caller, at the same time. Change-Id: Iffd95c094c37fc1081b73b2a267cfdcd29aeb4ae Reviewed-by: Thiago Macieira --- src/corelib/tools/qtimezoneprivate_win.cpp | 47 ++++++++++++++++++------------ 1 file changed, 28 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qtimezoneprivate_win.cpp b/src/corelib/tools/qtimezoneprivate_win.cpp index 665e87daec..8bde07c710 100644 --- a/src/corelib/tools/qtimezoneprivate_win.cpp +++ b/src/corelib/tools/qtimezoneprivate_win.cpp @@ -360,25 +360,33 @@ QDate calculateTransitionLocalDate(const SYSTEMTIME &rule, int year) 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) { - if (time.wDayOfWeek == 0) - time.wDayOfWeek = 7; - QDate date(year, time.wMonth, 1); - int startDow = date.dayOfWeek(); - if (startDow <= time.wDayOfWeek) - date = date.addDays(time.wDayOfWeek - startDow - 7); - else - date = date.addDays(time.wDayOfWeek - startDow); - date = date.addDays(time.wDay * 7); - while (date.month() != time.wMonth) - date = date.addDays(-7); - return date; + // Interpret SYSTEMTIME according to the slightly quirky rules in: + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx + + // If the year is set, the rule gives an absolute date: + if (rule.wYear) + return QDate(rule.wYear, rule.wMonth, rule.wDay); + + // Otherwise, the rule date is annual and relative: + const int dayOfWeek = rule.wDayOfWeek == 0 ? 7 : rule.wDayOfWeek; + QDate date(year, rule.wMonth, 1); + // 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: + adjust -= 7; + // Normally, wDay is day-within-month; but here it is 1 for the first + // of the given dayOfWeek in the month, through 4 for the fourth or ... + adjust += (rule.wDay < 1 ? 1 : rule.wDay > 4 ? 5 : rule.wDay) * 7; + date = date.addDays(adjust); + // ... 5 for the last; so back up by weeks to get within the month: + if (date.month() != rule.wMonth) { + Q_ASSERT(rule.wDay > 4); + // (Note that, with adjust < 0, date <= 28th of our target month + // is guaranteed when wDay <= 4, or after our first -7 here.) + date = date.addDays(-7); + Q_ASSERT(date.month() == rule.wMonth); } - - // If the year is set then is an absolute date - return QDate(time.wYear, time.wMonth, time.wDay); + return date; } // Converts a date/time value into msecs @@ -390,7 +398,8 @@ inline qint64 timeToMSecs(const QDate &date, const QTime &time) qint64 calculateTransitionForYear(const SYSTEMTIME &rule, int year, int bias) { - // TODO Consider caching the calculated values + // TODO Consider caching the calculated values - i.e. replace SYSTEMTIME in + // WinTransitionRule; do this in init() once and store the results. const QDate date = calculateTransitionLocalDate(rule, year); const QTime time = QTime(rule.wHour, rule.wMinute, rule.wSecond); if (date.isValid() && time.isValid()) -- cgit v1.2.3