File indexing completed on 2024-04-14 14:20:09

0001 /*
0002    This file is part of the KDE libraries
0003    Copyright (c) 2008 Marc Mutz <mutz@kde.org>, Till Adam <adam@kde.org>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018    Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "ktimezone_win.h"
0022 
0023 #include <QStringList>
0024 #include <QLibrary>
0025 
0026 #include "kdebug.h"
0027 
0028 
0029 #include <memory>
0030 #include <string>
0031 #include <cassert>
0032 
0033 struct ZoneKey {
0034     QString zoneOlson;
0035     QString zoneWin;
0036 };
0037 
0038 static const ZoneKey ZoneTbl[] = {
0039     {QLatin1String("Australia/Darwin"), QLatin1String("AUS Central Standard Time")},
0040     {QLatin1String("Australia/Sydney"), QLatin1String("AUS Eastern Standard Time")},
0041     {QLatin1String("Asia/Kabul"), QLatin1String("Afghanistan Standard Time")},
0042     {QLatin1String("America/Anchorage"), QLatin1String("Alaskan Standard Time")},
0043     {QLatin1String("Asia/Riyadh"), QLatin1String("Arab Standard Time")},
0044     {QLatin1String("Asia/Dubai"), QLatin1String("Arabian Standard Time")},
0045     {QLatin1String("Asia/Baghdad"), QLatin1String("Arabic Standard Time")},
0046     {QLatin1String("America/Buenos_Aires"), QLatin1String("Argentina Standard Time")},
0047     {QLatin1String("Asia/Yerevan"), QLatin1String("Armenian Standard Time")},
0048     {QLatin1String("America/Halifax"), QLatin1String("Atlantic Standard Time")},
0049     {QLatin1String("Asia/Baku"), QLatin1String("Azerbaijan Standard Time")},
0050     {QLatin1String("Atlantic/Azores"), QLatin1String("Azores Standard Time")},
0051     {QLatin1String("America/Regina"), QLatin1String("Canada Central Standard Time")},
0052     {QLatin1String("Atlantic/Cape_Verde"), QLatin1String("Cape Verde Standard Time")},
0053     {QLatin1String("Asia/Yerevan"), QLatin1String("Caucasus Standard Time")},
0054     {QLatin1String("Australia/Adelaide"), QLatin1String("Cen. Australia Standard Time")},
0055     {QLatin1String("America/Guatemala"), QLatin1String("Central America Standard Time")},
0056     {QLatin1String("Asia/Dhaka"), QLatin1String("Central Asia Standard Time")},
0057     {QLatin1String("America/Manaus"), QLatin1String("Central Brazilian Standard Time")},
0058     {QLatin1String("Europe/Budapest"), QLatin1String("Central Europe Standard Time")},
0059     {QLatin1String("Europe/Warsaw"), QLatin1String("Central European Standard Time")},
0060     {QLatin1String("Pacific/Guadalcanal"), QLatin1String("Central Pacific Standard Time")},
0061     {QLatin1String("America/Chicago"), QLatin1String("Central Standard Time")},
0062     {QLatin1String("America/Mexico_City"), QLatin1String("Central Standard Time (Mexico)")},
0063     {QLatin1String("Asia/Shanghai"), QLatin1String("China Standard Time")},
0064     {QLatin1String("Etc/GMT+12"), QLatin1String("Dateline Standard Time")},
0065     {QLatin1String("Africa/Nairobi"), QLatin1String("E. Africa Standard Time")},
0066     {QLatin1String("Australia/Brisbane"), QLatin1String("E. Australia Standard Time")},
0067     {QLatin1String("Europe/Minsk"), QLatin1String("E. Europe Standard Time")},
0068     {QLatin1String("America/Sao_Paulo"), QLatin1String("E. South America Standard Time")},
0069     {QLatin1String("America/New_York"), QLatin1String("Eastern Standard Time")},
0070     {QLatin1String("Africa/Cairo"), QLatin1String("Egypt Standard Time")},
0071     {QLatin1String("Asia/Yekaterinburg"), QLatin1String("Ekaterinburg Standard Time")},
0072     {QLatin1String("Europe/Kiev"), QLatin1String("FLE Standard Time")},
0073     {QLatin1String("Pacific/Fiji"), QLatin1String("Fiji Standard Time")},
0074     {QLatin1String("Europe/London"), QLatin1String("GMT Standard Time")},
0075     {QLatin1String("Europe/Istanbul"), QLatin1String("GTB Standard Time")},
0076     {QLatin1String("Etc/GMT-3"), QLatin1String("Georgian Standard Time")},
0077     {QLatin1String("America/Godthab"), QLatin1String("Greenland Standard Time")},
0078     {QLatin1String("Atlantic/Reykjavik"), QLatin1String("Greenwich Standard Time")},
0079     {QLatin1String("Pacific/Honolulu"), QLatin1String("Hawaiian Standard Time")},
0080     {QLatin1String("Asia/Calcutta"), QLatin1String("India Standard Time")},
0081     {QLatin1String("Asia/Tehran"), QLatin1String("Iran Standard Time")},
0082     {QLatin1String("Asia/Jerusalem"), QLatin1String("Israel Standard Time")},
0083     {QLatin1String("Asia/Amman"), QLatin1String("Jordan Standard Time")},
0084     {QLatin1String("Asia/Seoul"), QLatin1String("Korea Standard Time")},
0085     {QLatin1String("Indian/Mauritius"), QLatin1String("Mauritius Standard Time")},
0086     {QLatin1String("America/Mexico_City"), QLatin1String("Mexico Standard Time")},
0087     {QLatin1String("America/Chihuahua"), QLatin1String("Mexico Standard Time 2")},
0088     {QLatin1String("Atlantic/South_Georgia"), QLatin1String("Mid-Atlantic Standard Time")},
0089     {QLatin1String("Asia/Beirut"), QLatin1String("Middle East Standard Time")},
0090     {QLatin1String("America/Montevideo"), QLatin1String("Montevideo Standard Time")},
0091     {QLatin1String("Africa/Casablanca"), QLatin1String("Morocco Standard Time")},
0092     {QLatin1String("America/Denver"), QLatin1String("Mountain Standard Time")},
0093     {QLatin1String("America/Chihuahua"), QLatin1String("Mountain Standard Time (Mexico)")},
0094     {QLatin1String("Asia/Rangoon"), QLatin1String("Myanmar Standard Time")},
0095     {QLatin1String("Asia/Novosibirsk"), QLatin1String("N. Central Asia Standard Time")},
0096     {QLatin1String("Africa/Windhoek"), QLatin1String("Namibia Standard Time")},
0097     {QLatin1String("Asia/Katmandu"), QLatin1String("Nepal Standard Time")},
0098     {QLatin1String("Pacific/Auckland"), QLatin1String("New Zealand Standard Time")},
0099     {QLatin1String("America/St_Johns"), QLatin1String("Newfoundland Standard Time")},
0100     {QLatin1String("Asia/Irkutsk"), QLatin1String("North Asia East Standard Time")},
0101     {QLatin1String("Asia/Krasnoyarsk"), QLatin1String("North Asia Standard Time")},
0102     {QLatin1String("America/Santiago"), QLatin1String("Pacific SA Standard Time")},
0103     {QLatin1String("America/Los_Angeles"), QLatin1String("Pacific Standard Time")},
0104     {QLatin1String("America/Tijuana"), QLatin1String("Pacific Standard Time (Mexico)")},
0105     {QLatin1String("Asia/Karachi"), QLatin1String("Pakistan Standard Time")},
0106     {QLatin1String("Europe/Paris"), QLatin1String("Romance Standard Time")},
0107     {QLatin1String("Europe/Moscow"), QLatin1String("Russian Standard Time")},
0108     {QLatin1String("Etc/GMT+3"), QLatin1String("SA Eastern Standard Time")},
0109     {QLatin1String("America/Bogota"), QLatin1String("SA Pacific Standard Time")},
0110     {QLatin1String("America/La_Paz"), QLatin1String("SA Western Standard Time")},
0111     {QLatin1String("Asia/Bangkok"), QLatin1String("SE Asia Standard Time")},
0112     {QLatin1String("Pacific/Apia"), QLatin1String("Samoa Standard Time")},
0113     {QLatin1String("Asia/Singapore"), QLatin1String("Singapore Standard Time")},
0114     {QLatin1String("Africa/Johannesburg"), QLatin1String("South Africa Standard Time")},
0115     {QLatin1String("Asia/Colombo"), QLatin1String("Sri Lanka Standard Time")},
0116     {QLatin1String("Asia/Taipei"), QLatin1String("Taipei Standard Time")},
0117     {QLatin1String("Australia/Hobart"), QLatin1String("Tasmania Standard Time")},
0118     {QLatin1String("Asia/Tokyo"), QLatin1String("Tokyo Standard Time")},
0119     {QLatin1String("Pacific/Tongatapu"), QLatin1String("Tonga Standard Time")},
0120     {QLatin1String("Etc/GMT+5"), QLatin1String("US Eastern Standard Time")},
0121     {QLatin1String("America/Phoenix"), QLatin1String("US Mountain Standard Time")},
0122     {QLatin1String("America/Caracas"), QLatin1String("Venezuela Standard Time")},
0123     {QLatin1String("Asia/Vladivostok"), QLatin1String("Vladivostok Standard Time")},
0124     {QLatin1String("Australia/Perth"), QLatin1String("W. Australia Standard Time")},
0125     {QLatin1String("Africa/Lagos"), QLatin1String("W. Central Africa Standard Time")},
0126     {QLatin1String("Europe/Berlin"), QLatin1String("W. Europe Standard Time")},
0127     {QLatin1String("Asia/Tashkent"), QLatin1String("West Asia Standard Time")},
0128     {QLatin1String("Pacific/Port_Moresby"), QLatin1String("West Pacific Standard Time")},
0129     {QLatin1String("Asia/Yakutsk"), QLatin1String("Yakutsk Standard Time")}
0130 };
0131 
0132 static QString getWinZoneName(const QString &name)
0133 {
0134     for (int i = 0; i < sizeof(ZoneTbl) / sizeof(ZoneTbl[0]); ++i) {
0135         if (ZoneTbl[i].zoneOlson == name) {
0136             return ZoneTbl[i].zoneWin;
0137         }
0138     }
0139 
0140     return name;
0141 }
0142 
0143 typedef BOOL (WINAPI *PtrTzSpecificLocalTimeToSystemTime)(LPTIME_ZONE_INFORMATION lpTimeZoneInformation,
0144         LPSYSTEMTIME lpLocalTime,
0145         LPSYSTEMTIME lpUniversalTime
0146                                                          );
0147 static PtrTzSpecificLocalTimeToSystemTime pTzSpecificLocalTimeToSystemTime = 0;
0148 
0149 namespace
0150 {
0151 class HKeyCloser
0152 {
0153     const HKEY hkey;
0154     Q_DISABLE_COPY(HKeyCloser)
0155 public:
0156     KDELIBS4SUPPORT_DEPRECATED explicit HKeyCloser(HKEY hk) : hkey(hk) {}
0157     ~HKeyCloser()
0158     {
0159         RegCloseKey(hkey);
0160     }
0161 };
0162 
0163 struct TZI {
0164     LONG Bias;
0165     LONG StandardBias;
0166     LONG DaylightBias;
0167     SYSTEMTIME StandardDate;
0168     SYSTEMTIME DaylightDate;
0169 };
0170 }
0171 
0172 // TCHAR can be either uchar, or wchar_t:
0173 #ifdef UNICODE
0174 
0175 static inline QString tchar_to_qstring(const TCHAR *str)
0176 {
0177     return QString::fromUtf16(reinterpret_cast<const ushort *>(str));
0178 }
0179 
0180 static inline const TCHAR *qstring_to_tchar(const QString &str)
0181 {
0182     return reinterpret_cast<const TCHAR *>(str.utf16());
0183 }
0184 
0185 static inline std::basic_string<TCHAR> qstring_to_tcharstring(const QString &str)
0186 {
0187     return std::basic_string<TCHAR>(qstring_to_tchar(str));
0188 }
0189 
0190 #else
0191 
0192 static inline QString tchar_to_qstring(const TCHAR *str)
0193 {
0194     return QString::fromLocal8Bit(str);
0195 }
0196 
0197 static inline const TCHAR *qstring_to_tchar(const QString &str)
0198 {
0199     return str.toLocal8Bit().constData();
0200 }
0201 
0202 static inline std::basic_string<TCHAR> qstring_to_tcharstring(const QString &str)
0203 {
0204     return std::basic_string<TCHAR>(qstring_to_tchar(str));
0205 }
0206 
0207 #endif
0208 
0209 static const TCHAR timeZonesKey[] = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones");
0210 static inline QDateTime systemtime_to_qdatetime(const SYSTEMTIME &st)
0211 {
0212     return QDateTime(QDate(st.wYear, st.wMonth, st.wDay),
0213                      QTime(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds));
0214 }
0215 
0216 static SYSTEMTIME qdatetime_to_systemtime(const QDateTime &dt)
0217 {
0218     const QDate d = dt.date();
0219     const QTime t = dt.time();
0220     const SYSTEMTIME st = {
0221         d.year(),
0222         d.month(),
0223         d.dayOfWeek() % 7, // 1..7 (Mon..Sun)->0..6(Sun..Sat)
0224         d.day(),
0225         t.hour(),
0226         t.minute(),
0227         t.second(),
0228         t.msec(),
0229     };
0230     return st;
0231 }
0232 
0233 static bool TzSpecificLocalTimeToSystemTime_Portable(TIME_ZONE_INFORMATION *tz,
0234         SYSTEMTIME *i_stLocal,
0235         SYSTEMTIME *o_stUniversal)
0236 {
0237 
0238     // the method below was introduced in XP. If it's there, use it, otherwise
0239     // fall back to doing things manually
0240     if (!pTzSpecificLocalTimeToSystemTime) {
0241         QLibrary kernelLib(QLatin1String("kernel32"));
0242         pTzSpecificLocalTimeToSystemTime  = (PtrTzSpecificLocalTimeToSystemTime)kernelLib.resolve("TzSpecificLocalTimeToSystemTime");
0243     }
0244 
0245     if (pTzSpecificLocalTimeToSystemTime) {
0246         return pTzSpecificLocalTimeToSystemTime(tz, i_stLocal, o_stUniversal) != 0;
0247     }
0248 
0249     // the algorithm is:
0250     // - switch to the desired timezone temporarily
0251     // - convert system time to (local) file time in that timezone
0252     // - convert local file time to utc file time
0253     // - convert utc file time to system time
0254     // - reset timezone
0255     FILETIME ft, ft_utc;
0256     int result = 1;
0257     TIME_ZONE_INFORMATION currentTimeZone;
0258     result = GetTimeZoneInformation(&currentTimeZone);
0259     if (result == TIME_ZONE_ID_INVALID) {
0260         kWarning(161) << "Getting time zone information failed";
0261         return false;
0262     }
0263     result = SetTimeZoneInformation(tz);
0264     if (result == 0) {
0265         kWarning(161) << "Setting temporary time zone failed";
0266         return false;
0267     }
0268     result = SystemTimeToFileTime(i_stLocal, &ft);
0269     if (result == 0) {
0270         kWarning(161) << "SysteTimeToFileTime failed";
0271         return false;
0272     }
0273     result = LocalFileTimeToFileTime(&ft, &ft_utc);
0274     if (result == 0) {
0275         kWarning(161) << "LocalFileTimeToFileTime failed";
0276         return false;
0277     }
0278     result = FileTimeToSystemTime(&ft_utc, o_stUniversal);
0279     if (result == 0) {
0280         kWarning(161) << "FileTimeToSystemTime failed";
0281         return false;
0282     }
0283     result = SetTimeZoneInformation(&currentTimeZone);
0284     if (result == 0) {
0285         kWarning(161) << "Re-setting time zone information failed";
0286         return false;
0287     }
0288     return true;
0289 }
0290 
0291 static bool get_binary_value(HKEY key, const TCHAR *value, void *data, DWORD numData, DWORD *outNumData = 0)
0292 {
0293     DWORD size = numData;
0294     DWORD type = REG_BINARY;
0295     if (RegQueryValueEx(key, value, 0, &type, (LPBYTE)data, &size) != ERROR_SUCCESS) {
0296         return false;
0297     }
0298     assert(type == REG_BINARY);
0299     if (type != REG_BINARY) {
0300         return false;
0301     }
0302     if (outNumData) {
0303         *outNumData = size;
0304     }
0305     return true;
0306 }
0307 
0308 static bool get_string_value(HKEY key, const TCHAR *value, TCHAR *dest, DWORD destSizeInBytes)
0309 {
0310     DWORD size = destSizeInBytes;
0311     DWORD type = REG_SZ;
0312     dest[0] = '\0';
0313     if (RegQueryValueEx(key, value, 0, &type, (LPBYTE)dest, &size) != ERROR_SUCCESS) {
0314         return false;
0315     }
0316     //dest[ qMin( size, destSizeInBytes - sizeof( WCHAR ) ) / sizeof( WCHAR ) ] = 0;
0317     assert(type == REG_SZ);
0318     if (type != REG_SZ) {
0319         return false;
0320     }
0321     return true;
0322 }
0323 
0324 //
0325 //
0326 // Backend interface impl:
0327 //
0328 //
0329 
0330 static bool check_prereq(const KTimeZone *caller, const QDateTime &dt, Qt::TimeSpec spec)
0331 {
0332     return caller && caller->isValid() && dt.isValid() && dt.timeSpec() == spec;
0333 }
0334 
0335 static inline bool check_local(const KTimeZone *caller, const QDateTime &dt)
0336 {
0337     return check_prereq(caller, dt, Qt::LocalTime);
0338 }
0339 
0340 static inline bool check_utc(const KTimeZone *caller, const QDateTime &dt)
0341 {
0342     return check_prereq(caller, dt, Qt::UTC);
0343 }
0344 
0345 static bool has_transition(const TIME_ZONE_INFORMATION &tz)
0346 {
0347     return tz.StandardDate.wMonth != 0 && tz.DaylightDate.wMonth != 0;
0348 }
0349 
0350 static int win_dayofweek_to_qt_dayofweek(int wdow)
0351 {
0352     // Sun(0)..Sat(6) -> Mon(1)...Sun(7)
0353     return wdow ? wdow : 7;
0354 }
0355 
0356 static int qt_dayofweek_to_win_dayofweek(int qdow)
0357 {
0358     // Mon(1)...Sun(7) -> Sub(0)...Sat(6)
0359     return qdow % 7;
0360 }
0361 
0362 static QDate find_nth_weekday_in_month_of_year(int nth, int dayOfWeek, int month, int year)
0363 {
0364     assert(nth >= 1);
0365     assert(nth <= 5);
0366 
0367     const QDate first(year, month, 1);
0368     const int actualDayOfWeek = first.dayOfWeek();
0369     QDate candidate = first.addDays((nth - 1) * 7 + dayOfWeek - actualDayOfWeek);
0370     assert(candidate.dayOfWeek() == dayOfWeek);
0371     if (nth == 5)
0372         if (candidate.month() != month) {
0373             candidate = candidate.addDays(-7);
0374         }
0375     assert(candidate.month() == month);
0376     return candidate;
0377 }
0378 
0379 static QDateTime transition(const SYSTEMTIME &st, int year)
0380 {
0381     assert(st.wYear == 0);
0382     assert(st.wMonth != 0);
0383     return QDateTime(find_nth_weekday_in_month_of_year(st.wDay, win_dayofweek_to_qt_dayofweek(st.wDayOfWeek), st.wMonth, year),
0384                      QTime(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds));
0385 }
0386 
0387 struct Transitions {
0388     QDateTime stdStart, dstStart;
0389 };
0390 
0391 Transitions transitions(const TIME_ZONE_INFORMATION &tz, int year)
0392 {
0393     const Transitions t = {
0394         transition(tz.StandardDate, year), transition(tz.DaylightDate, year)
0395     };
0396     return t;
0397 }
0398 
0399 static const int MAX_KEY_LENGTH = 255;
0400 
0401 static QStringList list_key(HKEY key)
0402 {
0403 
0404     DWORD numSubKeys = 0;
0405     QStringList result;
0406 
0407     if (RegQueryInfoKey(key, 0, 0, 0, &numSubKeys, 0, 0, 0, 0, 0, 0, 0) == ERROR_SUCCESS)
0408         for (DWORD i = 0; i < numSubKeys; ++i) {
0409             TCHAR name[MAX_KEY_LENGTH + 1];
0410             DWORD nameLen = MAX_KEY_LENGTH;
0411             if (RegEnumKeyEx(key, i, name, &nameLen, 0, 0, 0, 0) == ERROR_SUCCESS) {
0412                 result.push_back(tchar_to_qstring(name));
0413             }
0414         }
0415 
0416     return result;
0417 }
0418 
0419 static QStringList list_standard_names()
0420 {
0421     QStringList standardNames;
0422 
0423     HKEY timeZones;
0424     QStringList keys;
0425     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, timeZonesKey, 0, KEY_READ, &timeZones) == ERROR_SUCCESS) {
0426         keys = list_key(timeZones);
0427     }
0428 
0429     std::basic_string<TCHAR> path(timeZonesKey);
0430     path += TEXT("\\");
0431 
0432     const HKeyCloser closer(timeZones);
0433     Q_FOREACH (const QString &keyname, keys) {
0434 
0435         std::basic_string<TCHAR> keypath(path);
0436         keypath += qstring_to_tcharstring(keyname);
0437         HKEY key;
0438         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keypath.c_str(), 0, KEY_READ, &key) != ERROR_SUCCESS) {
0439             return standardNames; // FIXME what's the right error handling here?
0440         }
0441 
0442         const HKeyCloser closer(key);
0443 
0444         TIME_ZONE_INFORMATION tz;
0445         get_string_value(key, L"Std", tz.StandardName, sizeof(tz.StandardName));
0446 
0447         standardNames << tchar_to_qstring(tz.StandardName);
0448     }
0449 
0450     for (int i = 0; i < sizeof(ZoneTbl) / sizeof(ZoneTbl[0]); ++i) {
0451         standardNames << ZoneTbl[i].zoneOlson;
0452     }
0453 
0454     return standardNames;
0455 }
0456 
0457 static std::basic_string<TCHAR> pathFromZoneName(const KTimeZone &zone)
0458 {
0459     std::basic_string<TCHAR> path(timeZonesKey);
0460     path += TEXT("\\");
0461 
0462     QString name = zone.name();
0463 
0464     name = getWinZoneName(name);
0465 
0466     HKEY timeZones;
0467     QStringList keys;
0468     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, timeZonesKey, 0, KEY_READ, &timeZones) == ERROR_SUCCESS) {
0469         keys = list_key(timeZones);
0470     }
0471 
0472     const HKeyCloser closer(timeZones);
0473     Q_FOREACH (const QString &keyname, keys) {
0474 
0475         std::basic_string<TCHAR> keypath(path);
0476         keypath += qstring_to_tcharstring(keyname);
0477         HKEY key;
0478         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keypath.c_str(), 0, KEY_READ, &key) != ERROR_SUCCESS) {
0479             return 0; // FIXME what's the right error handling here?
0480         }
0481 
0482         const HKeyCloser closer(key);
0483 
0484         TIME_ZONE_INFORMATION tz;
0485         get_string_value(key, L"Std", tz.StandardName, sizeof(tz.StandardName));
0486 
0487         if (tchar_to_qstring(tz.StandardName) == name) {
0488             return keypath;
0489         }
0490     }
0491     Q_ASSERT(false);
0492 
0493     return path;
0494 }
0495 
0496 /******************************************************************************/
0497 
0498 class KSystemTimeZoneSourceWindowsPrivate
0499 {
0500 public:
0501     KSystemTimeZoneSourceWindowsPrivate() {}
0502     ~KSystemTimeZoneSourceWindowsPrivate() {}
0503 };
0504 
0505 class KSystemTimeZoneBackendWindows : public KTimeZoneBackend
0506 {
0507 public:
0508     KSystemTimeZoneBackendWindows(KTimeZoneSource *source, const QString &name)
0509         : KTimeZoneBackend(source, name) {}
0510 
0511     ~KSystemTimeZoneBackendWindows() {}
0512 
0513     KSystemTimeZoneBackendWindows *clone() const;
0514 
0515     QByteArray type() const;
0516 
0517     int offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const;
0518     int offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const;
0519     int offset(const KTimeZone *caller, time_t t) const;
0520     bool isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const;
0521     bool isDst(const KTimeZone *caller, time_t t) const;
0522 };
0523 
0524 class KSystemTimeZoneDataWindows : public KTimeZoneData
0525 {
0526 public:
0527     KSystemTimeZoneDataWindows()
0528         : KTimeZoneData()
0529     {
0530 
0531     }
0532     TIME_ZONE_INFORMATION _tzi;
0533     QString displayName;
0534 
0535     const TIME_ZONE_INFORMATION &tzi(int year = 0) const
0536     {
0537         Q_UNUSED(year);
0538         return _tzi;
0539     }
0540 };
0541 
0542 KSystemTimeZoneSourceWindows::KSystemTimeZoneSourceWindows()
0543     : d(new KSystemTimeZoneSourceWindowsPrivate)
0544 {
0545 }
0546 
0547 KTimeZoneData *KSystemTimeZoneSourceWindows::parse(const KTimeZone &zone) const
0548 {
0549     KSystemTimeZoneDataWindows *data = new KSystemTimeZoneDataWindows();
0550 
0551     std::basic_string<TCHAR> path = pathFromZoneName(zone);
0552 
0553     HKEY key;
0554     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path.c_str(), 0, KEY_READ, &key) != ERROR_SUCCESS) {
0555         delete data;
0556         return 0; // FIXME what's the right error handling here?
0557     }
0558 
0559     const HKeyCloser closer(key);
0560 
0561     TZI tzi = { 0 };
0562 
0563     if (!get_binary_value(key, TEXT("TZI"), &tzi, sizeof(TZI))) {
0564         delete data;
0565         return 0; // ?
0566     }
0567 
0568     get_string_value(key, L"Std", data->_tzi.StandardName, sizeof(data->_tzi.StandardName));
0569     get_string_value(key, L"Dlt", data->_tzi.DaylightName, sizeof(data->_tzi.DaylightName));
0570 
0571     TCHAR display[512];
0572     get_string_value(key, L"Display", display, sizeof(display));
0573     data->displayName = tchar_to_qstring(display);
0574 
0575 #define COPY( name ) data->_tzi.name = tzi.name
0576     COPY(Bias);
0577     COPY(StandardBias);
0578     COPY(StandardDate);
0579     COPY(DaylightBias);
0580     COPY(DaylightDate);
0581 #undef COPY
0582 
0583     return data;
0584 }
0585 
0586 Transitions transitions(const KTimeZone *caller, int year)
0587 {
0588     return transitions(static_cast<const KSystemTimeZoneDataWindows *>(caller->data(true))->tzi(year), year);
0589 }
0590 
0591 static bool is_dst(const TIME_ZONE_INFORMATION &tzi, const QDateTime &utc, int year)
0592 {
0593     if (!has_transition(tzi)) {
0594         return false;
0595     }
0596     const Transitions trans = transitions(tzi, year);
0597     if (trans.stdStart < trans.dstStart) {
0598         return trans.dstStart <= utc || utc < trans.stdStart;
0599     } else {
0600         return trans.dstStart <= utc && utc < trans.stdStart;
0601     }
0602 }
0603 
0604 static bool is_dst(const KTimeZone *caller, const QDateTime &utc)
0605 {
0606     assert(caller);
0607     assert(caller->isValid());
0608     const int year = utc.date().year();
0609     const TIME_ZONE_INFORMATION &tzi = static_cast<const KSystemTimeZoneDataWindows *>(caller->data(true))->tzi(year);
0610     return is_dst(tzi, utc, year);
0611 }
0612 
0613 static int effective_offset(const TIME_ZONE_INFORMATION &tz, bool isDst)
0614 {
0615     int bias = tz.Bias;
0616     if (has_transition(tz))
0617         if (isDst) {
0618             bias += tz.DaylightBias;
0619         } else {
0620             bias += tz.StandardBias;
0621         }
0622     return bias * -60; // min -> secs
0623 }
0624 
0625 static int offset_at_utc(const KTimeZone *caller, const QDateTime &utc)
0626 {
0627     assert(caller);
0628     assert(caller->isValid());
0629     const int year = utc.date().year();
0630     const TIME_ZONE_INFORMATION &tz = static_cast<const KSystemTimeZoneDataWindows *>(caller->data(true))->tzi(year);
0631     return effective_offset(tz, is_dst(tz, utc, year));
0632 }
0633 
0634 static const int OneHour = 3600; //sec
0635 
0636 static int difference(const SYSTEMTIME &st1, const SYSTEMTIME &st2)
0637 {
0638     return systemtime_to_qdatetime(st1).secsTo(systemtime_to_qdatetime(st2));
0639 }
0640 
0641 static int offset_at_zone_time(const KTimeZone *caller, const SYSTEMTIME &zone, int *secondOffset)
0642 {
0643     assert(caller);
0644     assert(caller->isValid());
0645     assert(caller->data(true));
0646     const KSystemTimeZoneDataWindows *const data = static_cast<const KSystemTimeZoneDataWindows *>(caller->data(true));
0647     const TIME_ZONE_INFORMATION &tz = data->tzi(zone.wYear);
0648     SYSTEMTIME utc;
0649     if (!TzSpecificLocalTimeToSystemTime_Portable(const_cast<LPTIME_ZONE_INFORMATION>(&tz), const_cast<LPSYSTEMTIME>(&zone), &utc)) {
0650         return 0;
0651     }
0652     const bool isDst = is_dst(tz, systemtime_to_qdatetime(utc), utc.wYear);
0653     int result = effective_offset(tz, isDst);
0654 //FIXME: SystemTimeToTzSpecificLocalTime does not exsit on wince
0655 #ifndef _WIN32_WCE
0656     if (secondOffset) {
0657         const SYSTEMTIME utcplus1 = qdatetime_to_systemtime(systemtime_to_qdatetime(utc).addSecs(OneHour));
0658         const SYSTEMTIME utcminus1 = qdatetime_to_systemtime(systemtime_to_qdatetime(utc).addSecs(-OneHour));
0659         SYSTEMTIME zoneplus1, zoneminus1;
0660         if (!SystemTimeToTzSpecificLocalTime(const_cast<LPTIME_ZONE_INFORMATION>(&tz), const_cast<LPSYSTEMTIME>(&utcplus1), &zoneplus1) ||
0661                 !SystemTimeToTzSpecificLocalTime(const_cast<LPTIME_ZONE_INFORMATION>(&tz), const_cast<LPSYSTEMTIME>(&utcminus1), &zoneminus1)) {
0662             return result;
0663         }
0664         if (difference(zoneminus1, zone) != OneHour ||
0665                 difference(zone, zoneplus1) != OneHour) {
0666             *secondOffset = effective_offset(tz, !isDst);
0667             if (result < *secondOffset) {
0668                 qSwap(result, *secondOffset);
0669             }
0670         }
0671     }
0672 #endif
0673     return result;
0674 }
0675 
0676 KSystemTimeZoneBackendWindows *KSystemTimeZoneBackendWindows::clone() const
0677 {
0678     return new KSystemTimeZoneBackendWindows(*this);
0679 }
0680 
0681 QByteArray KSystemTimeZoneBackendWindows::type() const
0682 {
0683     return "KSystemTimeZoneWindows";
0684 }
0685 
0686 int KSystemTimeZoneBackendWindows::offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const
0687 {
0688     if (!caller->isValid()  ||  !zoneDateTime.isValid()  ||  zoneDateTime.timeSpec() != Qt::LocalTime) {
0689         return 0;
0690     }
0691     if (!check_local(caller, zoneDateTime)) {
0692         return 0;
0693     }
0694 
0695     return offset_at_zone_time(caller, qdatetime_to_systemtime(zoneDateTime), secondOffset);
0696 }
0697 
0698 int KSystemTimeZoneBackendWindows::offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
0699 {
0700     if (!caller->isValid()  ||  !utcDateTime.isValid()) {
0701         return 0;
0702     }
0703     if (!check_utc(caller, utcDateTime)) {
0704         return 0;
0705     }
0706     return offset_at_utc(caller, utcDateTime);
0707 }
0708 
0709 int KSystemTimeZoneBackendWindows::offset(const KTimeZone *caller, time_t t) const
0710 {
0711     if (!caller->isValid()  ||  t == KTimeZone::InvalidTime_t) {
0712         return 0;
0713     }
0714     return offsetAtUtc(caller, KTimeZone::fromTime_t(t));
0715 }
0716 
0717 bool KSystemTimeZoneBackendWindows::isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
0718 {
0719     return check_utc(caller, utcDateTime) && is_dst(caller, utcDateTime);
0720 }
0721 
0722 bool KSystemTimeZoneBackendWindows::isDst(const KTimeZone *caller, time_t t) const
0723 {
0724     return isDstAtUtc(caller, KTimeZone::fromTime_t(t));
0725 }
0726 
0727 KSystemTimeZoneWindows::KSystemTimeZoneWindows(KTimeZoneSource *source, const QString &name)
0728     : KTimeZone(new KSystemTimeZoneBackendWindows(source, name))
0729 {}
0730 
0731 QStringList KSystemTimeZoneWindows::listTimeZones()
0732 {
0733     return list_standard_names();
0734 }
0735