diff options
Diffstat (limited to 'src/corelib/tools/qtimezoneprivate_win.cpp')
-rw-r--r-- | src/corelib/tools/qtimezoneprivate_win.cpp | 927 |
1 files changed, 0 insertions, 927 deletions
diff --git a/src/corelib/tools/qtimezoneprivate_win.cpp b/src/corelib/tools/qtimezoneprivate_win.cpp deleted file mode 100644 index 1bf2366748..0000000000 --- a/src/corelib/tools/qtimezoneprivate_win.cpp +++ /dev/null @@ -1,927 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 John Layt <jlayt@kde.org> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qtimezone.h" -#include "qtimezoneprivate_p.h" - -#include "qdatetime.h" - -#include "qdebug.h" - -#include <algorithm> - -QT_BEGIN_NAMESPACE - -#ifndef Q_OS_WINRT -// The registry-based timezone backend is not available on WinRT, which falls back to equivalent APIs. -#define QT_USE_REGISTRY_TIMEZONE 1 -#endif - -/* - Private - - Windows system implementation -*/ - -#define MAX_KEY_LENGTH 255 -#define FILETIME_UNIX_EPOCH Q_UINT64_C(116444736000000000) - -// MSDN home page for Time support -// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724962%28v=vs.85%29.aspx - -// For Windows XP and later refer to MSDN docs on TIME_ZONE_INFORMATION structure -// http://msdn.microsoft.com/en-gb/library/windows/desktop/ms725481%28v=vs.85%29.aspx - -// Vista introduced support for historic data, see MSDN docs on DYNAMIC_TIME_ZONE_INFORMATION -// http://msdn.microsoft.com/en-gb/library/windows/desktop/ms724253%28v=vs.85%29.aspx -#ifdef QT_USE_REGISTRY_TIMEZONE -static const char tzRegPath[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"; -static const char currTzRegPath[] = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation"; -#endif - -enum { - MIN_YEAR = -292275056, - MAX_YEAR = 292278994, - MSECS_PER_DAY = 86400000, - TIME_T_MAX = 2145916799, // int maximum 2037-12-31T23:59:59 UTC - JULIAN_DAY_FOR_EPOCH = 2440588 // result of julianDayFromDate(1970, 1, 1) -}; - -// Copied from MSDN, see above for link -typedef struct _REG_TZI_FORMAT -{ - LONG Bias; - LONG StandardBias; - LONG DaylightBias; - SYSTEMTIME StandardDate; - SYSTEMTIME DaylightDate; -} REG_TZI_FORMAT; - -namespace { - -// Fast and reliable conversion from msecs to date for all values -// Adapted from QDateTime msecsToDate -QDate msecsToDate(qint64 msecs) -{ - qint64 jd = JULIAN_DAY_FOR_EPOCH; - - if (qAbs(msecs) >= MSECS_PER_DAY) { - jd += (msecs / MSECS_PER_DAY); - msecs %= MSECS_PER_DAY; - } - - if (msecs < 0) { - qint64 ds = MSECS_PER_DAY - msecs - 1; - jd -= ds / MSECS_PER_DAY; - } - - return QDate::fromJulianDay(jd); -} - -bool equalSystemtime(const SYSTEMTIME &t1, const SYSTEMTIME &t2) -{ - return (t1.wYear == t2.wYear - && t1.wMonth == t2.wMonth - && t1.wDay == t2.wDay - && t1.wDayOfWeek == t2.wDayOfWeek - && t1.wHour == t2.wHour - && t1.wMinute == t2.wMinute - && t1.wSecond == t2.wSecond - && t1.wMilliseconds == t2.wMilliseconds); -} - -bool equalTzi(const TIME_ZONE_INFORMATION &tzi1, const TIME_ZONE_INFORMATION &tzi2) -{ - return(tzi1.Bias == tzi2.Bias - && tzi1.StandardBias == tzi2.StandardBias - && equalSystemtime(tzi1.StandardDate, tzi2.StandardDate) - && wcscmp(tzi1.StandardName, tzi2.StandardName) == 0 - && tzi1.DaylightBias == tzi2.DaylightBias - && equalSystemtime(tzi1.DaylightDate, tzi2.DaylightDate) - && wcscmp(tzi1.DaylightName, tzi2.DaylightName) == 0); -} - -#ifdef QT_USE_REGISTRY_TIMEZONE -bool openRegistryKey(const QString &keyPath, HKEY *key) -{ - return RegOpenKeyEx(HKEY_LOCAL_MACHINE, reinterpret_cast<const wchar_t*>(keyPath.utf16()), - 0, KEY_READ, key) == ERROR_SUCCESS; -} - -QString readRegistryString(const HKEY &key, const wchar_t *value) -{ - wchar_t buffer[MAX_PATH] = {0}; - DWORD size = sizeof(wchar_t) * MAX_PATH; - RegQueryValueEx(key, value, nullptr, nullptr, reinterpret_cast<LPBYTE>(buffer), &size); - return QString::fromWCharArray(buffer); -} - -int readRegistryValue(const HKEY &key, const wchar_t *value) -{ - DWORD buffer; - DWORD size = sizeof(buffer); - RegQueryValueEx(key, value, nullptr, nullptr, reinterpret_cast<LPBYTE>(&buffer), &size); - return buffer; -} - -QWinTimeZonePrivate::QWinTransitionRule readRegistryRule(const HKEY &key, - const wchar_t *value, bool *ok) -{ - *ok = false; - QWinTimeZonePrivate::QWinTransitionRule rule; - REG_TZI_FORMAT tzi; - DWORD tziSize = sizeof(tzi); - if (RegQueryValueEx(key, value, nullptr, nullptr, reinterpret_cast<BYTE *>(&tzi), &tziSize) - == ERROR_SUCCESS) { - rule.startYear = 0; - rule.standardTimeBias = tzi.Bias + tzi.StandardBias; - rule.daylightTimeBias = tzi.Bias + tzi.DaylightBias - rule.standardTimeBias; - rule.standardTimeRule = tzi.StandardDate; - rule.daylightTimeRule = tzi.DaylightDate; - *ok = true; - } - return rule; -} - -TIME_ZONE_INFORMATION getRegistryTzi(const QByteArray &windowsId, bool *ok) -{ - *ok = false; - TIME_ZONE_INFORMATION tzi; - REG_TZI_FORMAT regTzi; - DWORD regTziSize = sizeof(regTzi); - HKEY key = NULL; - const QString tziKeyPath = QString::fromUtf8(tzRegPath) + QLatin1Char('\\') - + QString::fromUtf8(windowsId); - - if (openRegistryKey(tziKeyPath, &key)) { - - DWORD size = sizeof(tzi.DaylightName); - RegQueryValueEx(key, L"Dlt", nullptr, nullptr, reinterpret_cast<LPBYTE>(tzi.DaylightName), &size); - - size = sizeof(tzi.StandardName); - RegQueryValueEx(key, L"Std", nullptr, nullptr, reinterpret_cast<LPBYTE>(tzi.StandardName), &size); - - if (RegQueryValueEx(key, L"TZI", nullptr, nullptr, reinterpret_cast<BYTE *>(®Tzi), ®TziSize) - == ERROR_SUCCESS) { - tzi.Bias = regTzi.Bias; - tzi.StandardBias = regTzi.StandardBias; - tzi.DaylightBias = regTzi.DaylightBias; - tzi.StandardDate = regTzi.StandardDate; - tzi.DaylightDate = regTzi.DaylightDate; - *ok = true; - } - - RegCloseKey(key); - } - - return tzi; -} -#else // QT_USE_REGISTRY_TIMEZONE -struct QWinDynamicTimeZone -{ - QString standardName; - QString daylightName; - QString timezoneName; - qint32 bias; - bool daylightTime; -}; - -typedef QHash<QByteArray, QWinDynamicTimeZone> QWinRTTimeZoneHash; - -Q_GLOBAL_STATIC(QWinRTTimeZoneHash, gTimeZones) - -void enumerateTimeZones() -{ - DYNAMIC_TIME_ZONE_INFORMATION dtzInfo; - quint32 index = 0; - QString prevTimeZoneKeyName; - while (SUCCEEDED(EnumDynamicTimeZoneInformation(index++, &dtzInfo))) { - QWinDynamicTimeZone item; - item.timezoneName = QString::fromWCharArray(dtzInfo.TimeZoneKeyName); - // As soon as key name repeats, break. Some systems continue to always - // return the last item independent of index being out of range - if (item.timezoneName == prevTimeZoneKeyName) - break; - item.standardName = QString::fromWCharArray(dtzInfo.StandardName); - item.daylightName = QString::fromWCharArray(dtzInfo.DaylightName); - item.daylightTime = !dtzInfo.DynamicDaylightTimeDisabled; - item.bias = dtzInfo.Bias; - gTimeZones->insert(item.timezoneName.toUtf8(), item); - prevTimeZoneKeyName = item.timezoneName; - } -} - -DYNAMIC_TIME_ZONE_INFORMATION dynamicInfoForId(const QByteArray &windowsId) -{ - DYNAMIC_TIME_ZONE_INFORMATION dtzInfo; - quint32 index = 0; - QString prevTimeZoneKeyName; - while (SUCCEEDED(EnumDynamicTimeZoneInformation(index++, &dtzInfo))) { - const QString timeZoneName = QString::fromWCharArray(dtzInfo.TimeZoneKeyName); - if (timeZoneName == QLatin1String(windowsId)) - break; - if (timeZoneName == prevTimeZoneKeyName) - break; - prevTimeZoneKeyName = timeZoneName; - } - return dtzInfo; -} - -QWinTimeZonePrivate::QWinTransitionRule -readDynamicRule(DYNAMIC_TIME_ZONE_INFORMATION &dtzi, int year, bool *ok) -{ - TIME_ZONE_INFORMATION tzi; - QWinTimeZonePrivate::QWinTransitionRule rule; - *ok = GetTimeZoneInformationForYear(year, &dtzi, &tzi); - if (*ok) { - rule.startYear = 0; - rule.standardTimeBias = tzi.Bias + tzi.StandardBias; - rule.daylightTimeBias = tzi.Bias + tzi.DaylightBias - rule.standardTimeBias; - rule.standardTimeRule = tzi.StandardDate; - rule.daylightTimeRule = tzi.DaylightDate; - } - return rule; -} -#endif // QT_USE_REGISTRY_TIMEZONE - -bool isSameRule(const QWinTimeZonePrivate::QWinTransitionRule &last, - const QWinTimeZonePrivate::QWinTransitionRule &rule) -{ - // In particular, when this is true and either wYear is 0, so is the other; - // so if one rule is recurrent and they're equal, so is the other. If - // either rule *isn't* recurrent, it has non-0 wYear which shall be - // different from the other's. Note that we don't compare .startYear, since - // that will always be different. - return equalSystemtime(last.standardTimeRule, rule.standardTimeRule) - && equalSystemtime(last.daylightTimeRule, rule.daylightTimeRule) - && last.standardTimeBias == rule.standardTimeBias - && last.daylightTimeBias == rule.daylightTimeBias; -} - -QList<QByteArray> availableWindowsIds() -{ -#ifdef QT_USE_REGISTRY_TIMEZONE - // TODO Consider caching results in a global static, very unlikely to change. - QList<QByteArray> list; - HKEY key = NULL; - if (openRegistryKey(QString::fromUtf8(tzRegPath), &key)) { - DWORD idCount = 0; - if (RegQueryInfoKey(key, 0, 0, 0, &idCount, 0, 0, 0, 0, 0, 0, 0) == ERROR_SUCCESS - && idCount > 0) { - for (DWORD i = 0; i < idCount; ++i) { - DWORD maxLen = MAX_KEY_LENGTH; - TCHAR buffer[MAX_KEY_LENGTH]; - if (RegEnumKeyEx(key, i, buffer, &maxLen, 0, 0, 0, 0) == ERROR_SUCCESS) - list.append(QString::fromWCharArray(buffer).toUtf8()); - } - } - RegCloseKey(key); - } - return list; -#else // QT_USE_REGISTRY_TIMEZONE - if (gTimeZones->isEmpty()) - enumerateTimeZones(); - return gTimeZones->keys(); -#endif // QT_USE_REGISTRY_TIMEZONE -} - -QByteArray windowsSystemZoneId() -{ -#ifdef QT_USE_REGISTRY_TIMEZONE - // On Vista and later is held in the value TimeZoneKeyName in key currTzRegPath - QString id; - HKEY key = NULL; - QString tziKeyPath = QString::fromUtf8(currTzRegPath); - if (openRegistryKey(tziKeyPath, &key)) { - id = readRegistryString(key, L"TimeZoneKeyName"); - RegCloseKey(key); - if (!id.isEmpty()) - return std::move(id).toUtf8(); - } - - // On XP we have to iterate over the zones until we find a match on - // names/offsets with the current data - TIME_ZONE_INFORMATION sysTzi; - GetTimeZoneInformation(&sysTzi); - bool ok = false; - const auto winIds = availableWindowsIds(); - for (const QByteArray &winId : winIds) { - if (equalTzi(getRegistryTzi(winId, &ok), sysTzi)) - return winId; - } -#else // QT_USE_REGISTRY_TIMEZONE - DYNAMIC_TIME_ZONE_INFORMATION dtzi; - if (SUCCEEDED(GetDynamicTimeZoneInformation(&dtzi))) - return QString::fromWCharArray(dtzi.TimeZoneKeyName).toLocal8Bit(); -#endif // QT_USE_REGISTRY_TIMEZONE - - // If we can't determine the current ID use UTC - return QTimeZonePrivate::utcQByteArray(); -} - -QDate calculateTransitionLocalDate(const SYSTEMTIME &rule, int year) -{ - // If month is 0 then there is no date - if (rule.wMonth == 0) - return QDate(); - - // 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); - } - return date; -} - -// Converts a date/time value into msecs -inline qint64 timeToMSecs(const QDate &date, const QTime &time) -{ - return ((date.toJulianDay() - JULIAN_DAY_FOR_EPOCH) * MSECS_PER_DAY) - + time.msecsSinceStartOfDay(); -} - -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. - const QDate date = calculateTransitionLocalDate(rule, year); - const QTime time = QTime(rule.wHour, rule.wMinute, rule.wSecond); - if (date.isValid() && time.isValid()) - return timeToMSecs(date, time) + bias * 60000; - return QTimeZonePrivate::invalidMSecs(); -} - -struct TransitionTimePair -{ - // Transition times after the epoch, in ms: - qint64 std, dst; - // If either is invalidMSecs(), which shall then be < the other, there is no - // DST and the other describes a change in actual standard offset. - - TransitionTimePair(const QWinTimeZonePrivate::QWinTransitionRule &rule, - int year, int oldYearOffset) - // The local time in Daylight Time of the switch to Standard Time - : std(calculateTransitionForYear(rule.standardTimeRule, year, - rule.standardTimeBias + rule.daylightTimeBias)), - // The local time in Standard Time of the switch to Daylight Time - dst(calculateTransitionForYear(rule.daylightTimeRule, year, rule.standardTimeBias)) - { - /* - Check for potential "fake DST", used by MS's APIs because the - TIME_ZONE_INFORMATION spec either expresses no transitions in the - year, or expresses a transition of each kind, even if standard time - did change in a year with no DST. We've seen year-start fake-DST - (whose offset matches prior standard offset, in which the previous - year ended); and conjecture that similar might be used at a year-end. - (This might be used for a southern-hemisphere zone, where the start of - the year usually is in DST, when applicable.) Note that, here, wDay - identifies an instance of a given day-of-week in the month, with 5 - meaning last. - - Either the alleged standardTimeRule or the alleged daylightTimeRule - may be faked; either way, the transition is actually a change to the - current standard offset; but the unfaked half of the rule contains the - useful bias data, so we have to go along with its lies. - - Example: Russia/Moscow - Format: -bias +( -stdBias, stdDate | -dstBias, dstDate ) notes - Last year of DST, 2010: 180 +( 0, 0-10-5 3:0 | 60, 0-3-5 2:0 ) normal DST - Zone change in 2011: 180 +( 0, 0-1-1 0:0 | 60 0-3-5 2:0 ) fake DST at transition - Fixed standard in 2012: 240 +( 0, 0-0-0 0:0 | 60, 0-0-0 0:0 ) standard time years - Zone change in 2014: 180 +( 0, 0-10-5 2:0 | 60, 0-1-1 0:0 ) fake DST at year-start - The last of these is missing on Win7 VMs (too old to know about it). - */ - if (rule.daylightTimeRule.wMonth == 1 && rule.daylightTimeRule.wDay == 1) { - // Fake "DST transition" at start of year producing the same offset as - // previous year ended in. - if (rule.standardTimeBias + rule.daylightTimeBias == oldYearOffset) - dst = QTimeZonePrivate::invalidMSecs(); - } else if (rule.daylightTimeRule.wMonth == 12 && rule.daylightTimeRule.wDay > 3) { - // Similar, conjectured, for end of year, not changing offset. - if (rule.daylightTimeBias == 0) - dst = QTimeZonePrivate::invalidMSecs(); - } - if (rule.standardTimeRule.wMonth == 1 && rule.standardTimeRule.wDay == 1) { - // Fake "transition out of DST" at start of year producing the same - // offset as previous year ended in. - if (rule.standardTimeBias == oldYearOffset) - std = QTimeZonePrivate::invalidMSecs(); - } else if (rule.standardTimeRule.wMonth == 12 && rule.standardTimeRule.wDay > 3) { - // Similar, conjectured, for end of year, not changing offset. - if (rule.daylightTimeBias == 0) - std = QTimeZonePrivate::invalidMSecs(); - } - } - - bool fakesDst() const - { - return std == QTimeZonePrivate::invalidMSecs() - || dst == QTimeZonePrivate::invalidMSecs(); - } -}; - -int yearEndOffset(const QWinTimeZonePrivate::QWinTransitionRule &rule, int 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 - // at the *start* of this year, so it doesn't matter if we guess wrong here: - TransitionTimePair pair(rule, year, offset); - if (pair.dst > pair.std) - offset += rule.daylightTimeBias; - return offset; -} - -QLocale::Country userCountry() -{ - const GEOID id = GetUserGeoID(GEOCLASS_NATION); - wchar_t code[3]; - const int size = GetGeoInfo(id, GEO_ISO2, code, 3, 0); - return (size == 3) ? QLocalePrivate::codeToCountry(QStringView(code, size)) - : QLocale::AnyCountry; -} - -// Index of last rule in rules with .startYear <= year: -int ruleIndexForYear(const QList<QWinTimeZonePrivate::QWinTransitionRule> &rules, int year) -{ - if (rules.last().startYear <= year) - return rules.count() - 1; - // We don't have a rule for before the first, but the first is the best we can offer: - if (rules.first().startYear > year) - return 0; - - // Otherwise, use binary chop: - int lo = 0, hi = rules.count(); - // invariant: rules[i].startYear <= year < rules[hi].startYear - // subject to treating rules[rules.count()] as "off the end of time" - while (lo + 1 < hi) { - const int mid = (lo + hi) / 2; - // lo + 2 <= hi, so lo + 1 <= mid <= hi - 1, so lo < mid < hi - // In particular, mid < rules.count() - const int midYear = rules.at(mid).startYear; - if (midYear > year) - hi = mid; - else if (midYear < year) - lo = mid; - else // No two rules have the same startYear: - return mid; - } - return lo; -} - -} // anonymous namespace - -// Create the system default time zone -QWinTimeZonePrivate::QWinTimeZonePrivate() - : QTimeZonePrivate() -{ - init(QByteArray()); -} - -// Create a named time zone -QWinTimeZonePrivate::QWinTimeZonePrivate(const QByteArray &ianaId) - : QTimeZonePrivate() -{ - init(ianaId); -} - -QWinTimeZonePrivate::QWinTimeZonePrivate(const QWinTimeZonePrivate &other) - : QTimeZonePrivate(other), m_windowsId(other.m_windowsId), - m_displayName(other.m_displayName), m_standardName(other.m_standardName), - m_daylightName(other.m_daylightName), m_tranRules(other.m_tranRules) -{ -} - -QWinTimeZonePrivate::~QWinTimeZonePrivate() -{ -} - -QWinTimeZonePrivate *QWinTimeZonePrivate::clone() const -{ - return new QWinTimeZonePrivate(*this); -} - -void QWinTimeZonePrivate::init(const QByteArray &ianaId) -{ - if (ianaId.isEmpty()) { - m_windowsId = windowsSystemZoneId(); - m_id = systemTimeZoneId(); - } else { - m_windowsId = ianaIdToWindowsId(ianaId); - m_id = ianaId; - } - - bool badMonth = false; // Only warn once per zone, if at all. - if (!m_windowsId.isEmpty()) { -#ifdef QT_USE_REGISTRY_TIMEZONE - // Open the base TZI for the time zone - HKEY baseKey = NULL; - const QString baseKeyPath = QString::fromUtf8(tzRegPath) + QLatin1Char('\\') - + QString::fromUtf8(m_windowsId); - if (openRegistryKey(baseKeyPath, &baseKey)) { - // Load the localized names - m_displayName = readRegistryString(baseKey, L"Display"); - m_standardName = readRegistryString(baseKey, L"Std"); - m_daylightName = readRegistryString(baseKey, L"Dlt"); - // On Vista and later the optional dynamic key holds historic data - const QString dynamicKeyPath = baseKeyPath + QLatin1String("\\Dynamic DST"); - HKEY dynamicKey = NULL; - if (openRegistryKey(dynamicKeyPath, &dynamicKey)) { - // Find out the start and end years stored, then iterate over them - int startYear = readRegistryValue(dynamicKey, L"FirstEntry"); - int endYear = readRegistryValue(dynamicKey, L"LastEntry"); - for (int year = startYear; year <= endYear; ++year) { - bool ruleOk; - QWinTransitionRule rule = readRegistryRule(dynamicKey, - reinterpret_cast<LPCWSTR>(QString::number(year).utf16()), - &ruleOk); - if (ruleOk - // Don't repeat a recurrent rule: - && (m_tranRules.isEmpty() - || !isSameRule(m_tranRules.last(), rule))) { - if (!badMonth - && (rule.standardTimeRule.wMonth == 0) - != (rule.daylightTimeRule.wMonth == 0)) { - badMonth = true; - qWarning("MS registry TZ API violated its wMonth constraint;" - "this may cause mistakes for %s from %d", - ianaId.constData(), year); - } - rule.startYear = m_tranRules.isEmpty() ? MIN_YEAR : year; - m_tranRules.append(rule); - } - } - RegCloseKey(dynamicKey); - } else { - // No dynamic data so use the base data - bool ruleOk; - QWinTransitionRule rule = readRegistryRule(baseKey, L"TZI", &ruleOk); - rule.startYear = MIN_YEAR; - if (ruleOk) - m_tranRules.append(rule); - } - RegCloseKey(baseKey); - } -#else // QT_USE_REGISTRY_TIMEZONE - if (gTimeZones->isEmpty()) - enumerateTimeZones(); - QWinRTTimeZoneHash::const_iterator it = gTimeZones->find(m_windowsId); - if (it != gTimeZones->constEnd()) { - m_displayName = it->timezoneName; - m_standardName = it->standardName; - m_daylightName = it->daylightName; - DWORD firstYear = 0; - DWORD lastYear = 0; - DYNAMIC_TIME_ZONE_INFORMATION dtzi = dynamicInfoForId(m_windowsId); - if (GetDynamicTimeZoneInformationEffectiveYears(&dtzi, &firstYear, &lastYear) - == ERROR_SUCCESS && firstYear < lastYear) { - for (DWORD year = firstYear; year <= lastYear; ++year) { - bool ok = false; - QWinTransitionRule rule = readDynamicRule(dtzi, year, &ok); - if (ok - // Don't repeat a recurrent rule - && (m_tranRules.isEmpty() - || !isSameRule(m_tranRules.last(), rule))) { - if (!badMonth - && (rule.standardTimeRule.wMonth == 0) - != (rule.daylightTimeRule.wMonth == 0)) { - badMonth = true; - qWarning("MS dynamic TZ API violated its wMonth constraint;" - "this may cause mistakes for %s from %d", - ianaId.constData(), year); - } - rule.startYear = m_tranRules.isEmpty() ? MIN_YEAR : year; - m_tranRules.append(rule); - } - } - } else { - // At least try to get the non-dynamic data: - dtzi.DynamicDaylightTimeDisabled = false; - bool ok = false; - QWinTransitionRule rule = readDynamicRule(dtzi, 1970, &ok); - if (ok) { - rule.startYear = MIN_YEAR; - m_tranRules.append(rule); - } - } - } -#endif // QT_USE_REGISTRY_TIMEZONE - } - - // If there are no rules then we failed to find a windowsId or any tzi info - if (m_tranRules.size() == 0) { - m_id.clear(); - m_windowsId.clear(); - m_displayName.clear(); - } -} - -QString QWinTimeZonePrivate::comment() const -{ - return m_displayName; -} - -QString QWinTimeZonePrivate::displayName(QTimeZone::TimeType timeType, - QTimeZone::NameType nameType, - const QLocale &locale) const -{ - // TODO Registry holds MUI keys, should be able to look up translations? - Q_UNUSED(locale); - - if (nameType == QTimeZone::OffsetName) { - const QWinTransitionRule &rule = - m_tranRules.at(ruleIndexForYear(m_tranRules, QDate::currentDate().year())); - int offset = rule.standardTimeBias; - if (timeType == QTimeZone::DaylightTime) - offset += rule.daylightTimeBias; - return isoOffsetFormat(offset * -60); - } - - switch (timeType) { - case QTimeZone::DaylightTime : - return m_daylightName; - case QTimeZone::GenericTime : - return m_displayName; - case QTimeZone::StandardTime : - return m_standardName; - } - return m_standardName; -} - -QString QWinTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const -{ - return data(atMSecsSinceEpoch).abbreviation; -} - -int QWinTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const -{ - return data(atMSecsSinceEpoch).offsetFromUtc; -} - -int QWinTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const -{ - return data(atMSecsSinceEpoch).standardTimeOffset; -} - -int QWinTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const -{ - return data(atMSecsSinceEpoch).daylightTimeOffset; -} - -bool QWinTimeZonePrivate::hasDaylightTime() const -{ - return hasTransitions(); -} - -bool QWinTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const -{ - return (data(atMSecsSinceEpoch).daylightTimeOffset != 0); -} - -QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const -{ - int year = msecsToDate(forMSecsSinceEpoch).year(); - for (int ruleIndex = ruleIndexForYear(m_tranRules, year); - ruleIndex >= 0; --ruleIndex) { - 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); - while (year >= endYear) { - const int newYearOffset = (year <= rule.startYear && ruleIndex > 0) - ? yearEndOffset(m_tranRules.at(ruleIndex - 1), year - 1) - : yearEndOffset(rule, year - 1); - const TransitionTimePair pair(rule, year, newYearOffset); - bool isDst = false; - if (pair.std != invalidMSecs() && pair.std <= forMSecsSinceEpoch) { - isDst = pair.std < pair.dst && pair.dst <= forMSecsSinceEpoch; - } else if (pair.dst != invalidMSecs() && pair.dst <= forMSecsSinceEpoch) { - isDst = true; - } else { - --year; // Try an earlier year for this rule (once). - continue; - } - return ruleToData(rule, forMSecsSinceEpoch, - isDst ? QTimeZone::DaylightTime : QTimeZone::StandardTime, - pair.fakesDst()); - } - // Fell off start of rule, try previous rule. - } else { - // No transition, no DST, use the year's standard time. - return ruleToData(rule, forMSecsSinceEpoch, QTimeZone::StandardTime); - } - if (year >= rule.startYear) - year = rule.startYear - 1; // Seek last transition in new rule. - } - // We don't have relevant data :-( - return invalidData(); -} - -bool QWinTimeZonePrivate::hasTransitions() const -{ - for (const QWinTransitionRule &rule : m_tranRules) { - if (rule.standardTimeRule.wMonth > 0 && rule.daylightTimeRule.wMonth > 0) - return true; - } - return false; -} - -QTimeZonePrivate::Data QWinTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const -{ - int year = msecsToDate(afterMSecsSinceEpoch).year(); - for (int ruleIndex = ruleIndexForYear(m_tranRules, year); - ruleIndex < m_tranRules.count(); ++ruleIndex) { - 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) { - if (year < rule.startYear) - 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 newYearOffset = (year <= rule.startYear && ruleIndex > 0) - ? yearEndOffset(m_tranRules.at(ruleIndex - 1), year - 1) - : yearEndOffset(rule, year - 1); - while (year < endYear) { - const TransitionTimePair pair(rule, year, newYearOffset); - bool isDst = false; - Q_ASSERT(invalidMSecs() <= afterMSecsSinceEpoch); // invalid is min qint64 - if (pair.std > afterMSecsSinceEpoch) { - isDst = pair.std > pair.dst && pair.dst > afterMSecsSinceEpoch; - } else if (pair.dst > afterMSecsSinceEpoch) { - isDst = true; - } else { - newYearOffset = rule.standardTimeBias; - if (pair.dst > pair.std) - newYearOffset += rule.daylightTimeBias; - ++year; // Try a later year for this rule (once). - continue; - } - - if (isDst) - return ruleToData(rule, pair.dst, QTimeZone::DaylightTime, pair.fakesDst()); - return ruleToData(rule, pair.std, QTimeZone::StandardTime, pair.fakesDst()); - } - // Fell off end of rule, try next rule. - } // else: no transition during rule's period - } - // Apparently no transition after the given time: - return invalidData(); -} - -QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const -{ - const qint64 startOfTime = invalidMSecs() + 1; - if (beforeMSecsSinceEpoch <= startOfTime) - return invalidData(); - - int year = msecsToDate(beforeMSecsSinceEpoch).year(); - for (int ruleIndex = ruleIndexForYear(m_tranRules, year); - ruleIndex >= 0; --ruleIndex) { - 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); - while (year >= endYear) { - const int newYearOffset = (year <= rule.startYear && ruleIndex > 0) - ? yearEndOffset(m_tranRules.at(ruleIndex - 1), year - 1) - : yearEndOffset(rule, year - 1); - const TransitionTimePair pair(rule, year, newYearOffset); - bool isDst = false; - if (pair.std != invalidMSecs() && pair.std < beforeMSecsSinceEpoch) { - isDst = pair.std < pair.dst && pair.dst < beforeMSecsSinceEpoch; - } else if (pair.dst != invalidMSecs() && pair.dst < beforeMSecsSinceEpoch) { - isDst = true; - } else { - --year; // Try an earlier year for this rule (once). - continue; - } - if (isDst) - return ruleToData(rule, pair.dst, QTimeZone::DaylightTime, pair.fakesDst()); - return ruleToData(rule, pair.std, QTimeZone::StandardTime, pair.fakesDst()); - } - // Fell off start of rule, try previous rule. - } else if (ruleIndex == 0) { - // Treat a no-transition first rule as a transition at the start of - // time, so that a scan through all rules *does* see it as the first - // rule: - return ruleToData(rule, startOfTime, QTimeZone::StandardTime, false); - } // else: no transition during rule's period - if (year >= rule.startYear) - year = rule.startYear - 1; // Seek last transition in new rule - } - // Apparently no transition before the given time: - return invalidData(); -} - -QByteArray QWinTimeZonePrivate::systemTimeZoneId() const -{ - const QLocale::Country country = userCountry(); - const QByteArray windowsId = windowsSystemZoneId(); - QByteArray ianaId; - // If we have a real country, then try get a specific match for that country - if (country != QLocale::AnyCountry) - ianaId = windowsIdToDefaultIanaId(windowsId, country); - // If we don't have a real country, or there wasn't a specific match, try the global default - if (ianaId.isEmpty()) { - ianaId = windowsIdToDefaultIanaId(windowsId); - // If no global default then probably an unknown Windows ID so return UTC - if (ianaId.isEmpty()) - return utcQByteArray(); - } - return ianaId; -} - -QList<QByteArray> QWinTimeZonePrivate::availableTimeZoneIds() const -{ - QList<QByteArray> result; - const auto winIds = availableWindowsIds(); - for (const QByteArray &winId : winIds) - result += windowsIdToIanaIds(winId); - std::sort(result.begin(), result.end()); - result.erase(std::unique(result.begin(), result.end()), result.end()); - return result; -} - -QTimeZonePrivate::Data QWinTimeZonePrivate::ruleToData(const QWinTransitionRule &rule, - qint64 atMSecsSinceEpoch, - QTimeZone::TimeType type, - bool fakeDst) const -{ - Data tran = invalidData(); - tran.atMSecsSinceEpoch = atMSecsSinceEpoch; - tran.standardTimeOffset = rule.standardTimeBias * -60; - if (fakeDst) { - tran.daylightTimeOffset = 0; - tran.abbreviation = m_standardName; - // Rule may claim we're in DST when it's actually a standard time change: - if (type == QTimeZone::DaylightTime) - tran.standardTimeOffset += rule.daylightTimeBias * -60; - } else if (type == QTimeZone::DaylightTime) { - tran.daylightTimeOffset = rule.daylightTimeBias * -60; - tran.abbreviation = m_daylightName; - } else { - tran.daylightTimeOffset = 0; - tran.abbreviation = m_standardName; - } - tran.offsetFromUtc = tran.standardTimeOffset + tran.daylightTimeOffset; - return tran; -} - -QT_END_NAMESPACE |