summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools')
-rw-r--r--src/corelib/tools/qtimezone.cpp4
-rw-r--r--src/corelib/tools/qtimezoneprivate_p.h62
-rw-r--r--src/corelib/tools/qtimezoneprivate_win.cpp664
-rw-r--r--src/corelib/tools/tools.pri2
4 files changed, 731 insertions, 1 deletions
diff --git a/src/corelib/tools/qtimezone.cpp b/src/corelib/tools/qtimezone.cpp
index ad13433583..f5f2b28b88 100644
--- a/src/corelib/tools/qtimezone.cpp
+++ b/src/corelib/tools/qtimezone.cpp
@@ -63,6 +63,8 @@ static QTimeZonePrivate *newBackendTimeZone()
return new QMacTimeZonePrivate();
#elif defined Q_OS_UNIX
return new QTzTimeZonePrivate();
+#elif defined Q_OS_WIN
+ return new QWinTimeZonePrivate();
#elif defined QT_USE_ICU
return new QIcuTimeZonePrivate();
#else
@@ -85,6 +87,8 @@ static QTimeZonePrivate *newBackendTimeZone(const QByteArray &olsenId)
return new QMacTimeZonePrivate(olsenId);
#elif defined Q_OS_UNIX
return new QTzTimeZonePrivate(olsenId);
+#elif defined Q_OS_WIN
+ return new QWinTimeZonePrivate(olsenId);
#elif defined QT_USE_ICU
return new QIcuTimeZonePrivate(olsenId);
#else
diff --git a/src/corelib/tools/qtimezoneprivate_p.h b/src/corelib/tools/qtimezoneprivate_p.h
index bc28e627a9..b4e4090c0b 100644
--- a/src/corelib/tools/qtimezoneprivate_p.h
+++ b/src/corelib/tools/qtimezoneprivate_p.h
@@ -69,6 +69,10 @@ class NSTimeZone;
#endif // __OBJC__
#endif // Q_OS_MAC
+#ifdef Q_OS_WIN
+#include <qt_windows.h>
+#endif // Q_OS_WIN
+
QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QTimeZonePrivate : public QSharedData
@@ -357,6 +361,64 @@ private:
};
#endif // Q_OS_MAC
+#ifdef Q_OS_WIN
+class Q_AUTOTEST_EXPORT QWinTimeZonePrivate Q_DECL_FINAL : public QTimeZonePrivate
+{
+public:
+ struct QWinTransitionRule {
+ int startYear;
+ int standardTimeBias;
+ int daylightTimeBias;
+ SYSTEMTIME standardTimeRule;
+ SYSTEMTIME daylightTimeRule;
+ };
+
+ // Create default time zone
+ QWinTimeZonePrivate();
+ // Create named time zone
+ QWinTimeZonePrivate(const QByteArray &olsenId);
+ QWinTimeZonePrivate(const QWinTimeZonePrivate &other);
+ ~QWinTimeZonePrivate();
+
+ QTimeZonePrivate *clone();
+
+ QString comment() const Q_DECL_OVERRIDE;
+
+ QString displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType,
+ const QLocale &locale) const Q_DECL_OVERRIDE;
+ QString abbreviation(qint64 atMSecsSinceEpoch) const Q_DECL_OVERRIDE;
+
+ int offsetFromUtc(qint64 atMSecsSinceEpoch) const Q_DECL_OVERRIDE;
+ int standardTimeOffset(qint64 atMSecsSinceEpoch) const Q_DECL_OVERRIDE;
+ int daylightTimeOffset(qint64 atMSecsSinceEpoch) const Q_DECL_OVERRIDE;
+
+ bool hasDaylightTime() const Q_DECL_OVERRIDE;
+ bool isDaylightTime(qint64 atMSecsSinceEpoch) const Q_DECL_OVERRIDE;
+
+ Data data(qint64 forMSecsSinceEpoch) const Q_DECL_OVERRIDE;
+
+ bool hasTransitions() const Q_DECL_OVERRIDE;
+ Data nextTransition(qint64 afterMSecsSinceEpoch) const Q_DECL_OVERRIDE;
+ Data previousTransition(qint64 beforeMSecsSinceEpoch) const Q_DECL_OVERRIDE;
+
+ QByteArray systemTimeZoneId() const Q_DECL_OVERRIDE;
+
+ QSet<QByteArray> availableTimeZoneIds() const Q_DECL_OVERRIDE;
+
+private:
+ void init(const QByteArray &olsenId);
+ QWinTransitionRule ruleForYear(int year) const;
+ QTimeZonePrivate::Data ruleToData(const QWinTransitionRule &rule, qint64 atMSecsSinceEpoch,
+ QTimeZone::TimeType type) const;
+
+ QByteArray m_windowsId;
+ QString m_displayName;
+ QString m_standardName;
+ QString m_daylightName;
+ QList<QWinTransitionRule> m_tranRules;
+};
+#endif // Q_OS_WIN
+
QT_END_NAMESPACE
#endif // QTIMEZONEPRIVATE_P_H
diff --git a/src/corelib/tools/qtimezoneprivate_win.cpp b/src/corelib/tools/qtimezoneprivate_win.cpp
new file mode 100644
index 0000000000..51300c0cad
--- /dev/null
+++ b/src/corelib/tools/qtimezoneprivate_win.cpp
@@ -0,0 +1,664 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 John Layt <jlayt@kde.org>
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtimezone.h"
+#include "qtimezoneprivate_p.h"
+
+#include "qdatetime.h"
+
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+/*
+ 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
+
+static const char tzRegPath[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones";
+static const char currTzRegPath[] = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation";
+
+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;
+
+// Fast and reliable conversion from msecs to date for all values
+// Adapted from QDateTime msecsToDate
+static 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);
+}
+
+static SYSTEMTIME msecsToSystemtime(qint64 forMSecsSinceEpoch)
+{
+ FILETIME utcFileTime;
+ ULONGLONG nsecs = (forMSecsSinceEpoch * 10000 ) + FILETIME_UNIX_EPOCH;
+ utcFileTime.dwLowDateTime = (DWORD) (nsecs & 0xFFFFFFFF);
+ utcFileTime.dwHighDateTime = (DWORD) (nsecs >> 32);
+ SYSTEMTIME utcTime;
+ FileTimeToSystemTime(&utcFileTime, &utcTime);
+ return utcTime;
+}
+
+static qint64 systemtimeToMsecs(const SYSTEMTIME &systemtime)
+{
+ FILETIME utcFileTime;
+ SystemTimeToFileTime(&systemtime, &utcFileTime);
+ ULONGLONG utcNSecs = (((ULONGLONG) utcFileTime.dwHighDateTime) << 32) + utcFileTime.dwLowDateTime;
+ return (utcNSecs - FILETIME_UNIX_EPOCH) / 10000;
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static bool openRegistryKey(const QString &keyPath, HKEY *key)
+{
+ return (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (const wchar_t*)keyPath.utf16(), 0, KEY_READ, key)
+ == ERROR_SUCCESS);
+}
+
+static QString readRegistryString(const HKEY &key, const wchar_t *value)
+{
+ wchar_t buffer[MAX_PATH] = {0};
+ DWORD size = sizeof(wchar_t) * MAX_PATH;
+ RegQueryValueEx(key, (LPCWSTR)value, NULL, NULL, (LPBYTE)buffer, &size);
+ return QString::fromWCharArray(buffer);
+}
+
+static int readRegistryValue(const HKEY &key, const wchar_t *value)
+{
+ DWORD buffer;
+ DWORD size = sizeof(buffer);
+ RegQueryValueEx(key, (LPCWSTR)value, NULL, NULL, (LPBYTE)&buffer, &size);
+ return buffer;
+}
+
+static 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, (LPCWSTR)value, NULL, NULL, (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;
+}
+
+static 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", NULL, NULL, (LPBYTE)tzi.DaylightName, &size);
+
+ size = sizeof(tzi.StandardName);
+ RegQueryValueEx(key, L"Std", NULL, NULL, (LPBYTE)tzi.StandardName, &size);
+
+ if (RegQueryValueEx(key, L"TZI", NULL, NULL, (BYTE *) &regTzi, &regTziSize)
+ == 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;
+}
+
+static QList<QByteArray> availableWindowsIds()
+{
+ // 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;
+}
+
+static QByteArray windowsSystemZoneId()
+{
+ // 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 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;
+ foreach (const QByteArray &winId, availableWindowsIds()) {
+ if (equalTzi(getRegistryTzi(winId, &ok), sysTzi))
+ return winId;
+ }
+
+ // If we can't determine the current ID use UTC
+ return QByteArrayLiteral("UTC");
+}
+
+static QDate calculateTransitionLocalDate(const SYSTEMTIME &rule, int year)
+{
+ 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;
+ } else {
+ return QDate(time.wYear, time.wMonth, time.wDay);
+ }
+}
+
+// Converts a date/time value into msecs
+static inline qint64 timeToMSecs(const QDate &date, const QTime &time)
+{
+ return ((date.toJulianDay() - JULIAN_DAY_FOR_EPOCH) * MSECS_PER_DAY)
+ + time.msecsSinceStartOfDay();
+}
+
+static void calculateTransitionsForYear(const QWinTimeZonePrivate::QWinTransitionRule &rule, int year,
+ qint64 *stdMSecs, qint64 *dstMSecs)
+{
+ // TODO Consider caching the calculated values
+
+ // The local time in Daylight Time when switches to Standard TIme
+ QDate standardDate = calculateTransitionLocalDate(rule.standardTimeRule, year);
+ QTime standardTime = QTime(rule.standardTimeRule.wHour, rule.standardTimeRule.wMinute,
+ rule.standardTimeRule.wSecond);
+ // The local time in Standard Time when switches to Daylight TIme
+ QDate daylightDate = calculateTransitionLocalDate(rule.daylightTimeRule, year);
+ QTime daylightTime = QTime(rule.daylightTimeRule.wHour, rule.daylightTimeRule.wMinute,
+ rule.daylightTimeRule.wSecond);
+
+ *stdMSecs = timeToMSecs(standardDate, standardTime)
+ + ((rule.standardTimeBias + rule.daylightTimeBias) * 60000);
+ *dstMSecs = timeToMSecs(daylightDate, daylightTime) + (rule.standardTimeBias * 60000);
+}
+
+static QLocale::Country userCountry()
+{
+#if defined(Q_OS_WINCE)
+ // Guess that the syslem locale country is the right one to use
+ // TODO Find if WinCE has equivalent api
+ return QLocale::system().country();
+#else
+ 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(QString::fromWCharArray(code))
+ : QLocale::AnyCountry;
+#endif // Q_OS_WINCE
+}
+
+// Create the system default time zone
+QWinTimeZonePrivate::QWinTimeZonePrivate()
+ : QTimeZonePrivate()
+{
+ init(QByteArray());
+}
+
+// Create a named time zone
+QWinTimeZonePrivate::QWinTimeZonePrivate(const QByteArray &olsenId)
+ : QTimeZonePrivate()
+{
+ init(olsenId);
+}
+
+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()
+{
+}
+
+QTimeZonePrivate *QWinTimeZonePrivate::clone()
+{
+ return new QWinTimeZonePrivate(*this);
+}
+
+void QWinTimeZonePrivate::init(const QByteArray &olsenId)
+{
+ if (olsenId.isEmpty()) {
+ m_windowsId = windowsSystemZoneId();
+ m_id = systemTimeZoneId();
+ } else {
+ m_windowsId = olsenIdToWindowsId(olsenId);
+ m_id = olsenId;
+ }
+
+ if (!m_windowsId.isEmpty()) {
+ // 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 + QStringLiteral("\\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,
+ (LPCWSTR)QString::number(year).utf16(),
+ &ruleOk);
+ rule.startYear = year;
+ if (ruleOk)
+ 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 = 1970;
+ if (ruleOk)
+ m_tranRules.append(rule);
+ }
+ RegCloseKey(baseKey);
+ }
+ }
+
+ // 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) {
+ QWinTransitionRule rule = ruleForYear(QDate::currentDate().year());
+ if (timeType == QTimeZone::DaylightTime)
+ return isoOffsetFormat((rule.standardTimeBias + rule.daylightTimeBias) * -60);
+ else
+ return isoOffsetFormat((rule.standardTimeBias) * -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
+{
+ // Convert MSecs to year to get transitions for, but around 31 Dec/1 Jan may not be right year
+ // So get the year after we think we want transitions for, to be safe
+ 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 second;
+ qint64 next = maxMSecs();
+ qint64 stdMSecs;
+ qint64 dstMSecs;
+ QWinTransitionRule rule;
+ do {
+ // Convert the transition rules into msecs for the year we want to try
+ rule = ruleForYear(year);
+ calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
+ first = qMin(stdMSecs, dstMSecs);
+ second = qMax(stdMSecs, dstMSecs);
+ if (forMSecsSinceEpoch >= second)
+ next = second;
+ else if (forMSecsSinceEpoch >= first)
+ next = first;
+ // If didn't fall in this year, try the previous
+ --year;
+ } while (forMSecsSinceEpoch < first && year >= MIN_YEAR);
+
+ if (next == dstMSecs)
+ return ruleToData(rule, forMSecsSinceEpoch, QTimeZone::DaylightTime);
+ else
+ return ruleToData(rule, forMSecsSinceEpoch, QTimeZone::StandardTime);
+}
+
+bool QWinTimeZonePrivate::hasTransitions() const
+{
+ foreach (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
+{
+ // Convert MSecs to year to get transitions for, but around 31 Dec/1 Jan may not be right year
+ // Get the year before we think we want transitions for, to be safe
+ 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;
+
+ qint64 first;
+ qint64 second;
+ qint64 next = minMSecs();
+ qint64 stdMSecs;
+ qint64 dstMSecs;
+ QWinTransitionRule rule;
+ do {
+ // Convert the transition rules into msecs for the year we want to try
+ rule = ruleForYear(year);
+ calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
+ // Find the first and second transition for the year
+ first = qMin(stdMSecs, dstMSecs);
+ second = qMax(stdMSecs, dstMSecs);
+ if (afterMSecsSinceEpoch < first)
+ next = first;
+ else if (afterMSecsSinceEpoch < second)
+ next = second;
+ // If didn't fall in this year, try the next
+ ++year;
+ } while (afterMSecsSinceEpoch >= second && year <= MAX_YEAR);
+
+ if (next == dstMSecs)
+ return ruleToData(rule, next, QTimeZone::DaylightTime);
+ else
+ return ruleToData(rule, next, QTimeZone::StandardTime);
+}
+
+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
+ // So get the year after we think we want transitions for, to be safe
+ QDate date = msecsToDate(beforeMSecsSinceEpoch);
+ int year;
+ int month;
+ int day;
+ date.getDate(&year, &month, &day);
+ if ((month == 12 && day == 31) || (month == 1 && day == 1))
+ ++year;
+
+ qint64 first;
+ qint64 second;
+ qint64 next = maxMSecs();
+ qint64 stdMSecs;
+ qint64 dstMSecs;
+ QWinTransitionRule rule;
+ do {
+ // Convert the transition rules into msecs for the year we want to try
+ rule = ruleForYear(year);
+ calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs);
+ first = qMin(stdMSecs, dstMSecs);
+ second = qMax(stdMSecs, dstMSecs);
+ if (beforeMSecsSinceEpoch > second)
+ next = second;
+ else if (beforeMSecsSinceEpoch > first)
+ next = first;
+ // If didn't fall in this year, try the previous
+ --year;
+ } while (beforeMSecsSinceEpoch < first && year >= MIN_YEAR);
+
+ if (next == dstMSecs)
+ return ruleToData(rule, next, QTimeZone::DaylightTime);
+ else
+ return ruleToData(rule, next, QTimeZone::StandardTime);
+}
+
+QByteArray QWinTimeZonePrivate::systemTimeZoneId() const
+{
+ const QLocale::Country country = userCountry();
+ const QByteArray windowsId = windowsSystemZoneId();
+ QByteArray olsenId;
+ // If we have a real country, then try get a specific match for that country
+ if (country != QLocale::AnyCountry)
+ olsenId = windowsIdToDefaultOlsenId(windowsId, country);
+ // If we don't have a real country, or there wasn't a specific match, try the global default
+ if (olsenId.isEmpty()) {
+ olsenId = windowsIdToDefaultOlsenId(windowsId);
+ // If no global default then probably an unknown Windows ID so return UTC
+ if (olsenId.isEmpty())
+ return QByteArrayLiteral("UTC");
+ }
+ return olsenId;
+}
+
+QSet<QByteArray> QWinTimeZonePrivate::availableTimeZoneIds() const
+{
+ QSet<QByteArray> set;
+ foreach (const QByteArray &winId, availableWindowsIds()) {
+ foreach (const QByteArray &olsenId, windowsIdToOlsenIds(winId))
+ set << olsenId;
+ }
+ return set;
+}
+
+QWinTimeZonePrivate::QWinTransitionRule QWinTimeZonePrivate::ruleForYear(int year) const
+{
+ for (int i = m_tranRules.size() - 1; i >= 0; --i) {
+ if (m_tranRules.at(i).startYear <= year)
+ return m_tranRules.at(i);
+ }
+ return m_tranRules.at(0);
+}
+
+QTimeZonePrivate::Data QWinTimeZonePrivate::ruleToData(const QWinTransitionRule &rule,
+ qint64 atMSecsSinceEpoch,
+ QTimeZone::TimeType type) const
+{
+ QTimeZonePrivate::Data tran = QTimeZonePrivate::invalidData();
+ tran.atMSecsSinceEpoch = atMSecsSinceEpoch;
+ tran.standardTimeOffset = rule.standardTimeBias * -60;
+ 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
diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri
index 2980d361db..e38bea3181 100644
--- a/src/corelib/tools/tools.pri
+++ b/src/corelib/tools/tools.pri
@@ -124,7 +124,7 @@ else:blackberry {
HEADERS += tools/qlocale_blackberry.h
}
else:unix:SOURCES += tools/qelapsedtimer_unix.cpp tools/qlocale_unix.cpp tools/qtimezoneprivate_tz.cpp
-else:win32:SOURCES += tools/qelapsedtimer_win.cpp tools/qlocale_win.cpp
+else:win32:SOURCES += tools/qelapsedtimer_win.cpp tools/qlocale_win.cpp tools/qtimezoneprivate_win.cpp
else:integrity:SOURCES += tools/qelapsedtimer_unix.cpp tools/qlocale_unix.cpp
else:SOURCES += tools/qelapsedtimer_generic.cpp