File indexing completed on 2023-10-03 03:16:31
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(¤tTimeZone); 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(¤tTimeZone); 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