KDECore
ktimezone_win.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of the KDE libraries 00003 Copyright (c) 2008 Marc Mutz <mutz@kde.org>, Till Adam <adam@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 00022 #include "ktimezone_win.h" 00023 #include <config.h> 00024 00025 #include <kdebug.h> 00026 00027 #include <QStringList> 00028 #include <QLibrary> 00029 00030 #include <windows.h> 00031 00032 #include <memory> 00033 #include <string> 00034 #include <cassert> 00035 00036 struct ZoneKey { 00037 QString zoneOlson; 00038 QString zoneWin; 00039 }; 00040 00041 static const ZoneKey ZoneTbl[] = 00042 { 00043 {QLatin1String("Australia/Darwin"), QLatin1String("AUS Central Standard Time")}, 00044 {QLatin1String("Australia/Sydney"), QLatin1String("AUS Eastern Standard Time")}, 00045 {QLatin1String("Asia/Kabul"), QLatin1String("Afghanistan Standard Time")}, 00046 {QLatin1String("America/Anchorage"), QLatin1String("Alaskan Standard Time")}, 00047 {QLatin1String("Asia/Riyadh"), QLatin1String("Arab Standard Time")}, 00048 {QLatin1String("Asia/Dubai"), QLatin1String("Arabian Standard Time")}, 00049 {QLatin1String("Asia/Baghdad"), QLatin1String("Arabic Standard Time")}, 00050 {QLatin1String("America/Buenos_Aires"), QLatin1String("Argentina Standard Time")}, 00051 {QLatin1String("Asia/Yerevan"), QLatin1String("Armenian Standard Time")}, 00052 {QLatin1String("America/Halifax"), QLatin1String("Atlantic Standard Time")}, 00053 {QLatin1String("Asia/Baku"), QLatin1String("Azerbaijan Standard Time")}, 00054 {QLatin1String("Atlantic/Azores"), QLatin1String("Azores Standard Time")}, 00055 {QLatin1String("America/Regina"), QLatin1String("Canada Central Standard Time")}, 00056 {QLatin1String("Atlantic/Cape_Verde"), QLatin1String("Cape Verde Standard Time")}, 00057 {QLatin1String("Asia/Yerevan"), QLatin1String("Caucasus Standard Time")}, 00058 {QLatin1String("Australia/Adelaide"), QLatin1String("Cen. Australia Standard Time")}, 00059 {QLatin1String("America/Guatemala"), QLatin1String("Central America Standard Time")}, 00060 {QLatin1String("Asia/Dhaka"), QLatin1String("Central Asia Standard Time")}, 00061 {QLatin1String("America/Manaus"), QLatin1String("Central Brazilian Standard Time")}, 00062 {QLatin1String("Europe/Budapest"), QLatin1String("Central Europe Standard Time")}, 00063 {QLatin1String("Europe/Warsaw"), QLatin1String("Central European Standard Time")}, 00064 {QLatin1String("Pacific/Guadalcanal"), QLatin1String("Central Pacific Standard Time")}, 00065 {QLatin1String("America/Chicago"), QLatin1String("Central Standard Time")}, 00066 {QLatin1String("America/Mexico_City"), QLatin1String("Central Standard Time (Mexico)")}, 00067 {QLatin1String("Asia/Shanghai"), QLatin1String("China Standard Time")}, 00068 {QLatin1String("Etc/GMT+12"), QLatin1String("Dateline Standard Time")}, 00069 {QLatin1String("Africa/Nairobi"), QLatin1String("E. Africa Standard Time")}, 00070 {QLatin1String("Australia/Brisbane"), QLatin1String("E. Australia Standard Time")}, 00071 {QLatin1String("Europe/Minsk"), QLatin1String("E. Europe Standard Time")}, 00072 {QLatin1String("America/Sao_Paulo"), QLatin1String("E. South America Standard Time")}, 00073 {QLatin1String("America/New_York"), QLatin1String("Eastern Standard Time")}, 00074 {QLatin1String("Africa/Cairo"), QLatin1String("Egypt Standard Time")}, 00075 {QLatin1String("Asia/Yekaterinburg"), QLatin1String("Ekaterinburg Standard Time")}, 00076 {QLatin1String("Europe/Kiev"), QLatin1String("FLE Standard Time")}, 00077 {QLatin1String("Pacific/Fiji"), QLatin1String("Fiji Standard Time")}, 00078 {QLatin1String("Europe/London"), QLatin1String("GMT Standard Time")}, 00079 {QLatin1String("Europe/Istanbul"), QLatin1String("GTB Standard Time")}, 00080 {QLatin1String("Etc/GMT-3"), QLatin1String("Georgian Standard Time")}, 00081 {QLatin1String("America/Godthab"), QLatin1String("Greenland Standard Time")}, 00082 {QLatin1String("Atlantic/Reykjavik"), QLatin1String("Greenwich Standard Time")}, 00083 {QLatin1String("Pacific/Honolulu"), QLatin1String("Hawaiian Standard Time")}, 00084 {QLatin1String("Asia/Calcutta"), QLatin1String("India Standard Time")}, 00085 {QLatin1String("Asia/Tehran"), QLatin1String("Iran Standard Time")}, 00086 {QLatin1String("Asia/Jerusalem"), QLatin1String("Israel Standard Time")}, 00087 {QLatin1String("Asia/Amman"), QLatin1String("Jordan Standard Time")}, 00088 {QLatin1String("Asia/Seoul"), QLatin1String("Korea Standard Time")}, 00089 {QLatin1String("Indian/Mauritius"), QLatin1String("Mauritius Standard Time")}, 00090 {QLatin1String("America/Mexico_City"), QLatin1String("Mexico Standard Time")}, 00091 {QLatin1String("America/Chihuahua"), QLatin1String("Mexico Standard Time 2")}, 00092 {QLatin1String("Atlantic/South_Georgia"), QLatin1String("Mid-Atlantic Standard Time")}, 00093 {QLatin1String("Asia/Beirut"), QLatin1String("Middle East Standard Time")}, 00094 {QLatin1String("America/Montevideo"), QLatin1String("Montevideo Standard Time")}, 00095 {QLatin1String("Africa/Casablanca"), QLatin1String("Morocco Standard Time")}, 00096 {QLatin1String("America/Denver"), QLatin1String("Mountain Standard Time")}, 00097 {QLatin1String("America/Chihuahua"), QLatin1String("Mountain Standard Time (Mexico)")}, 00098 {QLatin1String("Asia/Rangoon"), QLatin1String("Myanmar Standard Time")}, 00099 {QLatin1String("Asia/Novosibirsk"), QLatin1String("N. Central Asia Standard Time")}, 00100 {QLatin1String("Africa/Windhoek"), QLatin1String("Namibia Standard Time")}, 00101 {QLatin1String("Asia/Katmandu"), QLatin1String("Nepal Standard Time")}, 00102 {QLatin1String("Pacific/Auckland"), QLatin1String("New Zealand Standard Time")}, 00103 {QLatin1String("America/St_Johns"), QLatin1String("Newfoundland Standard Time")}, 00104 {QLatin1String("Asia/Irkutsk"), QLatin1String("North Asia East Standard Time")}, 00105 {QLatin1String("Asia/Krasnoyarsk"), QLatin1String("North Asia Standard Time")}, 00106 {QLatin1String("America/Santiago"), QLatin1String("Pacific SA Standard Time")}, 00107 {QLatin1String("America/Los_Angeles"), QLatin1String("Pacific Standard Time")}, 00108 {QLatin1String("America/Tijuana"), QLatin1String("Pacific Standard Time (Mexico)")}, 00109 {QLatin1String("Asia/Karachi"), QLatin1String("Pakistan Standard Time")}, 00110 {QLatin1String("Europe/Paris"), QLatin1String("Romance Standard Time")}, 00111 {QLatin1String("Europe/Moscow"), QLatin1String("Russian Standard Time")}, 00112 {QLatin1String("Etc/GMT+3"), QLatin1String("SA Eastern Standard Time")}, 00113 {QLatin1String("America/Bogota"), QLatin1String("SA Pacific Standard Time")}, 00114 {QLatin1String("America/La_Paz"), QLatin1String("SA Western Standard Time")}, 00115 {QLatin1String("Asia/Bangkok"), QLatin1String("SE Asia Standard Time")}, 00116 {QLatin1String("Pacific/Apia"), QLatin1String("Samoa Standard Time")}, 00117 {QLatin1String("Asia/Singapore"), QLatin1String("Singapore Standard Time")}, 00118 {QLatin1String("Africa/Johannesburg"), QLatin1String("South Africa Standard Time")}, 00119 {QLatin1String("Asia/Colombo"), QLatin1String("Sri Lanka Standard Time")}, 00120 {QLatin1String("Asia/Taipei"), QLatin1String("Taipei Standard Time")}, 00121 {QLatin1String("Australia/Hobart"), QLatin1String("Tasmania Standard Time")}, 00122 {QLatin1String("Asia/Tokyo"), QLatin1String("Tokyo Standard Time")}, 00123 {QLatin1String("Pacific/Tongatapu"), QLatin1String("Tonga Standard Time")}, 00124 {QLatin1String("Etc/GMT+5"), QLatin1String("US Eastern Standard Time")}, 00125 {QLatin1String("America/Phoenix"), QLatin1String("US Mountain Standard Time")}, 00126 {QLatin1String("America/Caracas"), QLatin1String("Venezuela Standard Time")}, 00127 {QLatin1String("Asia/Vladivostok"), QLatin1String("Vladivostok Standard Time")}, 00128 {QLatin1String("Australia/Perth"), QLatin1String("W. Australia Standard Time")}, 00129 {QLatin1String("Africa/Lagos"), QLatin1String("W. Central Africa Standard Time")}, 00130 {QLatin1String("Europe/Berlin"), QLatin1String("W. Europe Standard Time")}, 00131 {QLatin1String("Asia/Tashkent"), QLatin1String("West Asia Standard Time")}, 00132 {QLatin1String("Pacific/Port_Moresby"), QLatin1String("West Pacific Standard Time")}, 00133 {QLatin1String("Asia/Yakutsk"), QLatin1String("Yakutsk Standard Time")} 00134 }; 00135 00136 static QString getWinZoneName(const QString &name) 00137 { 00138 for ( int i = 0; i < sizeof(ZoneTbl) / sizeof(ZoneTbl[0]); ++i ) { 00139 if (ZoneTbl[i].zoneOlson == name) { 00140 return ZoneTbl[i].zoneWin; 00141 } 00142 } 00143 00144 return name; 00145 } 00146 00147 typedef BOOL (WINAPI *PtrTzSpecificLocalTimeToSystemTime )(LPTIME_ZONE_INFORMATION lpTimeZoneInformation, 00148 LPSYSTEMTIME lpLocalTime, 00149 LPSYSTEMTIME lpUniversalTime 00150 ); 00151 static PtrTzSpecificLocalTimeToSystemTime pTzSpecificLocalTimeToSystemTime = 0; 00152 00153 namespace { 00154 class HKeyCloser { 00155 const HKEY hkey; 00156 Q_DISABLE_COPY( HKeyCloser ) 00157 public: 00158 explicit HKeyCloser( HKEY hk ) : hkey( hk ) {} 00159 ~HKeyCloser() { RegCloseKey( hkey ); } 00160 }; 00161 00162 struct TZI { 00163 LONG Bias; 00164 LONG StandardBias; 00165 LONG DaylightBias; 00166 SYSTEMTIME StandardDate; 00167 SYSTEMTIME DaylightDate; 00168 }; 00169 } 00170 00171 // TCHAR can be either uchar, or wchar_t: 00172 #ifdef UNICODE 00173 00174 static inline QString tchar_to_qstring( const TCHAR * str ) { 00175 return QString::fromUtf16( reinterpret_cast<const ushort*>( str ) ); 00176 } 00177 00178 static inline const TCHAR * qstring_to_tchar( const QString& str ) { 00179 return reinterpret_cast<const TCHAR*>( str.utf16() ); 00180 } 00181 00182 static inline std::basic_string<TCHAR> qstring_to_tcharstring( const QString& str ) { 00183 return std::basic_string<TCHAR>( qstring_to_tchar(str) ); 00184 } 00185 00186 #else 00187 00188 static inline QString tchar_to_qstring( const TCHAR * str ) { 00189 return QString::fromLocal8Bit( str ); 00190 } 00191 00192 static inline const TCHAR * qstring_to_tchar( const QString& str ) { 00193 return str.toLocal8Bit().constData(); 00194 } 00195 00196 static inline std::basic_string<TCHAR> qstring_to_tcharstring( const QString& str ) { 00197 return std::basic_string<TCHAR>( qstring_to_tchar(str) ); 00198 } 00199 00200 #endif 00201 00202 static const TCHAR timeZonesKey[] = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"); 00203 static inline QDateTime systemtime_to_qdatetime( const SYSTEMTIME & st ) { 00204 return QDateTime( QDate( st.wYear, st.wMonth, st.wDay ), 00205 QTime( st.wHour, st.wMinute, st.wSecond, st.wMilliseconds ) ); 00206 } 00207 00208 static SYSTEMTIME qdatetime_to_systemtime( const QDateTime & dt ) { 00209 const QDate d = dt.date(); 00210 const QTime t = dt.time(); 00211 const SYSTEMTIME st = { 00212 d.year(), 00213 d.month(), 00214 d.dayOfWeek() % 7, // 1..7 (Mon..Sun)->0..6(Sun..Sat) 00215 d.day(), 00216 t.hour(), 00217 t.minute(), 00218 t.second(), 00219 t.msec(), 00220 }; 00221 return st; 00222 } 00223 00224 static bool TzSpecificLocalTimeToSystemTime_Portable( TIME_ZONE_INFORMATION* tz, 00225 SYSTEMTIME *i_stLocal, 00226 SYSTEMTIME *o_stUniversal ) 00227 { 00228 00229 // the method below was introduced in XP. If it's there, use it, otherwise 00230 // fall back to doing things manually 00231 if (!pTzSpecificLocalTimeToSystemTime) { 00232 QLibrary kernelLib(QLatin1String("kernel32")); 00233 pTzSpecificLocalTimeToSystemTime = (PtrTzSpecificLocalTimeToSystemTime)kernelLib.resolve("TzSpecificLocalTimeToSystemTime"); 00234 } 00235 00236 if ( pTzSpecificLocalTimeToSystemTime ) 00237 return pTzSpecificLocalTimeToSystemTime( tz, i_stLocal , o_stUniversal ) != 0; 00238 00239 // the algorithm is: 00240 // - switch to the desired timezone temporarily 00241 // - convert system time to (local) file time in that timezone 00242 // - convert local file time to utc file time 00243 // - convert utc file time to system time 00244 // - reset timezone 00245 FILETIME ft, ft_utc; 00246 int result = 1; 00247 TIME_ZONE_INFORMATION currentTimeZone; 00248 result = GetTimeZoneInformation(¤tTimeZone); 00249 if ( result == TIME_ZONE_ID_INVALID ) { 00250 kWarning(161) << "Getting time zone information failed"; 00251 return false; 00252 } 00253 result = SetTimeZoneInformation(tz); 00254 if ( result == 0 ) { 00255 kWarning(161) << "Setting temporary time zone failed"; 00256 return false; 00257 } 00258 result = SystemTimeToFileTime(i_stLocal, &ft); 00259 if ( result == 0 ) { 00260 kWarning(161) << "SysteTimeToFileTime failed"; 00261 return false; 00262 } 00263 result = LocalFileTimeToFileTime(&ft, &ft_utc); 00264 if ( result == 0 ) { 00265 kWarning(161) << "LocalFileTimeToFileTime failed"; 00266 return false; 00267 } 00268 result = FileTimeToSystemTime(&ft_utc,o_stUniversal); 00269 if ( result == 0 ) { 00270 kWarning(161) << "FileTimeToSystemTime failed"; 00271 return false; 00272 } 00273 result = SetTimeZoneInformation(¤tTimeZone); 00274 if ( result == 0 ) { 00275 kWarning(161) << "Re-setting time zone information failed"; 00276 return false; 00277 } 00278 return true; 00279 } 00280 00281 00282 00283 00284 static bool get_binary_value( HKEY key, const TCHAR * value, void * data, DWORD numData, DWORD * outNumData=0 ) { 00285 DWORD size = numData; 00286 DWORD type = REG_BINARY; 00287 if ( RegQueryValueEx( key, value, 0, &type, (LPBYTE)data, &size ) != ERROR_SUCCESS ) 00288 return false; 00289 assert( type == REG_BINARY ); 00290 if ( type != REG_BINARY ) 00291 return false; 00292 if ( outNumData ) 00293 *outNumData = size; 00294 return true; 00295 } 00296 00297 static bool get_string_value( HKEY key, const TCHAR * value, TCHAR * dest, DWORD destSizeInBytes ) { 00298 DWORD size = destSizeInBytes; 00299 DWORD type = REG_SZ; 00300 dest[0] = '\0'; 00301 if ( RegQueryValueEx( key, value, 0, &type, (LPBYTE)dest, &size ) != ERROR_SUCCESS ) 00302 return false; 00303 //dest[ qMin( size, destSizeInBytes - sizeof( WCHAR ) ) / sizeof( WCHAR ) ] = 0; 00304 assert( type == REG_SZ ); 00305 if ( type != REG_SZ ) 00306 return false; 00307 return true; 00308 } 00309 00310 // 00311 // 00312 // Backend interface impl: 00313 // 00314 // 00315 00316 static bool check_prereq( const KTimeZone * caller, const QDateTime & dt, Qt::TimeSpec spec ) { 00317 return caller && caller->isValid() && dt.isValid() && dt.timeSpec() == spec ; 00318 } 00319 00320 static inline bool check_local( const KTimeZone * caller, const QDateTime & dt ) { 00321 return check_prereq( caller, dt, Qt::LocalTime ); 00322 } 00323 00324 static inline bool check_utc( const KTimeZone * caller, const QDateTime & dt ) { 00325 return check_prereq( caller, dt, Qt::UTC ); 00326 } 00327 00328 static bool has_transition( const TIME_ZONE_INFORMATION & tz ) { 00329 return tz.StandardDate.wMonth != 0 && tz.DaylightDate.wMonth != 0 ; 00330 } 00331 00332 static int win_dayofweek_to_qt_dayofweek( int wdow ) { 00333 // Sun(0)..Sat(6) -> Mon(1)...Sun(7) 00334 return wdow ? wdow : 7 ; 00335 } 00336 00337 static int qt_dayofweek_to_win_dayofweek( int qdow ) { 00338 // Mon(1)...Sun(7) -> Sub(0)...Sat(6) 00339 return qdow % 7; 00340 } 00341 00342 static QDate find_nth_weekday_in_month_of_year( int nth, int dayOfWeek, int month, int year ) { 00343 assert( nth >= 1 ); 00344 assert( nth <= 5 ); 00345 00346 const QDate first( year, month, 1 ); 00347 const int actualDayOfWeek = first.dayOfWeek(); 00348 QDate candidate = first.addDays( ( nth - 1 ) * 7 + dayOfWeek - actualDayOfWeek ); 00349 assert( candidate.dayOfWeek() == dayOfWeek ); 00350 if ( nth == 5 ) 00351 if ( candidate.month() != month ) 00352 candidate = candidate.addDays( -7 ); 00353 assert( candidate.month() == month ); 00354 return candidate; 00355 } 00356 00357 static QDateTime transition( const SYSTEMTIME & st, int year ) { 00358 assert( st.wYear == 0 ); 00359 assert( st.wMonth != 0 ); 00360 return QDateTime( find_nth_weekday_in_month_of_year( st.wDay, win_dayofweek_to_qt_dayofweek( st.wDayOfWeek ), st.wMonth, year ), 00361 QTime( st.wHour, st.wMinute, st.wSecond, st.wMilliseconds ) ); 00362 } 00363 00364 struct Transitions { 00365 QDateTime stdStart, dstStart; 00366 }; 00367 00368 Transitions transitions( const TIME_ZONE_INFORMATION & tz, int year ) { 00369 const Transitions t = { 00370 transition( tz.StandardDate, year ), transition( tz.DaylightDate, year ) 00371 }; 00372 return t; 00373 } 00374 00375 00376 static const int MAX_KEY_LENGTH = 255; 00377 00378 static QStringList list_key( HKEY key ) { 00379 00380 DWORD numSubKeys = 0; 00381 QStringList result; 00382 00383 if ( RegQueryInfoKey( key, 0, 0, 0, &numSubKeys, 0, 0, 0, 0, 0, 0, 0 ) == ERROR_SUCCESS ) 00384 for ( DWORD i = 0 ; i < numSubKeys ; ++i ) { 00385 TCHAR name[MAX_KEY_LENGTH+1]; 00386 DWORD nameLen = MAX_KEY_LENGTH; 00387 if ( RegEnumKeyEx( key, i, name, &nameLen, 0, 0, 0, 0 ) == ERROR_SUCCESS ) 00388 result.push_back( tchar_to_qstring( name ) ); 00389 } 00390 00391 return result; 00392 } 00393 00394 static QStringList list_standard_names() 00395 { 00396 QStringList standardNames; 00397 00398 HKEY timeZones; 00399 QStringList keys; 00400 if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, timeZonesKey, 0, KEY_READ, &timeZones ) == ERROR_SUCCESS ) 00401 keys = list_key(timeZones); 00402 00403 std::basic_string<TCHAR> path( timeZonesKey ); 00404 path += TEXT( "\\" ); 00405 00406 const HKeyCloser closer( timeZones ); 00407 Q_FOREACH( const QString & keyname, keys ) { 00408 00409 std::basic_string<TCHAR> keypath(path); 00410 keypath += qstring_to_tcharstring(keyname); 00411 HKEY key; 00412 if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, keypath.c_str(), 0, KEY_READ, &key ) != ERROR_SUCCESS ) { 00413 return standardNames; // FIXME what's the right error handling here? 00414 } 00415 00416 const HKeyCloser closer( key ); 00417 00418 TIME_ZONE_INFORMATION tz; 00419 get_string_value( key, L"Std", tz.StandardName, sizeof( tz.StandardName ) ); 00420 00421 standardNames << tchar_to_qstring(tz.StandardName); 00422 } 00423 00424 for ( int i = 0; i < sizeof(ZoneTbl) / sizeof(ZoneTbl[0]); ++i ) { 00425 standardNames << ZoneTbl[i].zoneOlson; 00426 } 00427 00428 return standardNames; 00429 } 00430 00431 static std::basic_string<TCHAR> pathFromZoneName(const KTimeZone& zone) 00432 { 00433 std::basic_string<TCHAR> path( timeZonesKey ); 00434 path += TEXT( "\\" ); 00435 00436 QString name = zone.name(); 00437 00438 name = getWinZoneName(name); 00439 00440 HKEY timeZones; 00441 QStringList keys; 00442 if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, timeZonesKey, 0, KEY_READ, &timeZones ) == ERROR_SUCCESS ) 00443 keys = list_key(timeZones); 00444 00445 const HKeyCloser closer( timeZones ); 00446 Q_FOREACH( const QString & keyname, keys ) { 00447 00448 std::basic_string<TCHAR> keypath(path); 00449 keypath += qstring_to_tcharstring(keyname); 00450 HKEY key; 00451 if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, keypath.c_str(), 0, KEY_READ, &key ) != ERROR_SUCCESS ) { 00452 return 0; // FIXME what's the right error handling here? 00453 } 00454 00455 const HKeyCloser closer( key ); 00456 00457 TIME_ZONE_INFORMATION tz; 00458 get_string_value( key, L"Std", tz.StandardName, sizeof( tz.StandardName ) ); 00459 00460 if ( tchar_to_qstring(tz.StandardName) == name ) { 00461 return keypath; 00462 } 00463 } 00464 Q_ASSERT(false); 00465 00466 return path; 00467 } 00468 00469 /******************************************************************************/ 00470 00471 class KSystemTimeZoneSourceWindowsPrivate 00472 { 00473 public: 00474 KSystemTimeZoneSourceWindowsPrivate() {} 00475 ~KSystemTimeZoneSourceWindowsPrivate() {} 00476 }; 00477 00478 00479 class KSystemTimeZoneBackendWindows : public KTimeZoneBackend 00480 { 00481 public: 00482 KSystemTimeZoneBackendWindows(KTimeZoneSource *source, const QString &name) 00483 : KTimeZoneBackend(source, name) {} 00484 00485 ~KSystemTimeZoneBackendWindows() {} 00486 00487 KSystemTimeZoneBackendWindows *clone() const; 00488 00489 QByteArray type() const; 00490 00491 int offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const; 00492 int offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const; 00493 int offset(const KTimeZone *caller, time_t t) const; 00494 bool isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const; 00495 bool isDst(const KTimeZone *caller, time_t t) const; 00496 }; 00497 00498 class KSystemTimeZoneDataWindows : public KTimeZoneData 00499 { 00500 public: 00501 KSystemTimeZoneDataWindows() 00502 :KTimeZoneData() 00503 { 00504 00505 } 00506 TIME_ZONE_INFORMATION _tzi; 00507 QString displayName; 00508 00509 const TIME_ZONE_INFORMATION & tzi( int year = 0 ) const { Q_UNUSED( year ); return _tzi; } 00510 }; 00511 00512 KSystemTimeZoneSourceWindows::KSystemTimeZoneSourceWindows() 00513 :d( new KSystemTimeZoneSourceWindowsPrivate ) 00514 { 00515 } 00516 00517 KTimeZoneData* KSystemTimeZoneSourceWindows::parse(const KTimeZone &zone) const 00518 { 00519 KSystemTimeZoneDataWindows* data = new KSystemTimeZoneDataWindows(); 00520 00521 std::basic_string<TCHAR> path = pathFromZoneName(zone); 00522 00523 HKEY key; 00524 if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, path.c_str(), 0, KEY_READ, &key ) != ERROR_SUCCESS ) { 00525 delete data; 00526 return 0; // FIXME what's the right error handling here? 00527 } 00528 00529 const HKeyCloser closer( key ); 00530 00531 TZI tzi = { 0 }; 00532 00533 if ( !get_binary_value( key, TEXT( "TZI" ), &tzi, sizeof( TZI ) ) ) { 00534 delete data; 00535 return 0; // ? 00536 } 00537 00538 get_string_value( key, L"Std", data->_tzi.StandardName, sizeof( data->_tzi.StandardName ) ); 00539 get_string_value( key, L"Dlt", data->_tzi.DaylightName, sizeof( data->_tzi.DaylightName ) ); 00540 00541 TCHAR display[512]; 00542 get_string_value( key, L"Display", display, sizeof( display ) ); 00543 data->displayName = tchar_to_qstring( display ); 00544 00545 #define COPY( name ) data->_tzi.name = tzi.name 00546 COPY( Bias ); 00547 COPY( StandardBias ); 00548 COPY( StandardDate ); 00549 COPY( DaylightBias ); 00550 COPY( DaylightDate ); 00551 #undef COPY 00552 00553 return data; 00554 } 00555 00556 Transitions transitions( const KTimeZone * caller, int year ) { 00557 return transitions( static_cast<const KSystemTimeZoneDataWindows*>( caller->data(true) )->tzi( year ), year ); 00558 } 00559 00560 static bool is_dst( const TIME_ZONE_INFORMATION & tzi, const QDateTime & utc, int year ) { 00561 if ( !has_transition( tzi ) ) 00562 return false; 00563 const Transitions trans = transitions( tzi, year ); 00564 if ( trans.stdStart < trans.dstStart ) 00565 return trans.dstStart <= utc || utc < trans.stdStart ; 00566 else 00567 return trans.dstStart <= utc && utc < trans.stdStart ; 00568 } 00569 00570 static bool is_dst( const KTimeZone * caller, const QDateTime & utc ) { 00571 assert( caller ); 00572 assert( caller->isValid() ); 00573 const int year = utc.date().year(); 00574 const TIME_ZONE_INFORMATION & tzi = static_cast<const KSystemTimeZoneDataWindows*>( caller->data(true) )->tzi( year ); 00575 return is_dst( tzi, utc, year ); 00576 } 00577 00578 static int effective_offset( const TIME_ZONE_INFORMATION& tz, bool isDst ) { 00579 int bias = tz.Bias; 00580 if ( has_transition( tz ) ) 00581 if ( isDst ) 00582 bias += tz.DaylightBias; 00583 else 00584 bias += tz.StandardBias; 00585 return bias * -60; // min -> secs 00586 } 00587 00588 static int offset_at_utc( const KTimeZone * caller, const QDateTime & utc ) { 00589 assert( caller ); 00590 assert( caller->isValid() ); 00591 const int year = utc.date().year(); 00592 const TIME_ZONE_INFORMATION & tz = static_cast<const KSystemTimeZoneDataWindows*>( caller->data(true) )->tzi( year ); 00593 return effective_offset( tz, is_dst( tz, utc, year ) ); 00594 } 00595 00596 static const int OneHour = 3600; //sec 00597 00598 static int difference( const SYSTEMTIME & st1, const SYSTEMTIME & st2 ) { 00599 return systemtime_to_qdatetime( st1 ).secsTo( systemtime_to_qdatetime( st2 ) ); 00600 } 00601 00602 static int offset_at_zone_time( const KTimeZone * caller, const SYSTEMTIME & zone, int * secondOffset ) { 00603 assert( caller ); 00604 assert( caller->isValid() ); 00605 assert(caller->data(true)); 00606 const KSystemTimeZoneDataWindows * const data = static_cast<const KSystemTimeZoneDataWindows*>( caller->data(true) ); 00607 const TIME_ZONE_INFORMATION & tz = data->tzi( zone.wYear ); 00608 SYSTEMTIME utc; 00609 if ( !TzSpecificLocalTimeToSystemTime_Portable( const_cast<LPTIME_ZONE_INFORMATION>( &tz ), const_cast<LPSYSTEMTIME>( &zone ), &utc ) ) 00610 return 0; 00611 const bool isDst = is_dst( tz, systemtime_to_qdatetime( utc ), utc.wYear ); 00612 int result = effective_offset( tz, isDst ); 00613 //FIXME: SystemTimeToTzSpecificLocalTime does not exsit on wince 00614 #ifndef _WIN32_WCE 00615 if ( secondOffset ) { 00616 const SYSTEMTIME utcplus1 = qdatetime_to_systemtime( systemtime_to_qdatetime( utc ).addSecs( OneHour ) ); 00617 const SYSTEMTIME utcminus1 = qdatetime_to_systemtime( systemtime_to_qdatetime( utc ).addSecs( -OneHour ) ); 00618 SYSTEMTIME zoneplus1, zoneminus1; 00619 if ( !SystemTimeToTzSpecificLocalTime( const_cast<LPTIME_ZONE_INFORMATION>( &tz ), const_cast<LPSYSTEMTIME>( &utcplus1 ), &zoneplus1 ) || 00620 !SystemTimeToTzSpecificLocalTime( const_cast<LPTIME_ZONE_INFORMATION>( &tz ), const_cast<LPSYSTEMTIME>( &utcminus1 ), &zoneminus1 ) ) 00621 return result; 00622 if ( difference( zoneminus1, zone ) != OneHour || 00623 difference( zone, zoneplus1 ) != OneHour ) 00624 { 00625 *secondOffset = effective_offset( tz, !isDst ); 00626 if ( result < *secondOffset ) 00627 qSwap( result, *secondOffset ); 00628 } 00629 } 00630 #endif 00631 return result; 00632 } 00633 00634 00635 00636 KSystemTimeZoneBackendWindows * KSystemTimeZoneBackendWindows::clone() const 00637 { 00638 return new KSystemTimeZoneBackendWindows(*this); 00639 } 00640 00641 QByteArray KSystemTimeZoneBackendWindows::type() const 00642 { 00643 return "KSystemTimeZoneWindows"; 00644 } 00645 00646 int KSystemTimeZoneBackendWindows::offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const 00647 { 00648 if (!caller->isValid() || !zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime) 00649 return 0; 00650 if ( !check_local( caller, zoneDateTime ) ) 00651 return 0; 00652 00653 return offset_at_zone_time( caller, qdatetime_to_systemtime( zoneDateTime ), secondOffset ); 00654 } 00655 00656 int KSystemTimeZoneBackendWindows::offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const 00657 { 00658 if (!caller->isValid() || !utcDateTime.isValid()) 00659 return 0; 00660 if ( !check_utc( caller, utcDateTime ) ) 00661 return 0; 00662 return offset_at_utc( caller, utcDateTime ); 00663 } 00664 00665 int KSystemTimeZoneBackendWindows::offset(const KTimeZone *caller, time_t t) const 00666 { 00667 if (!caller->isValid() || t == KTimeZone::InvalidTime_t) 00668 return 0; 00669 return offsetAtUtc( caller, KTimeZone::fromTime_t( t ) ); 00670 } 00671 00672 bool KSystemTimeZoneBackendWindows::isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const 00673 { 00674 return check_utc( caller, utcDateTime ) && is_dst( caller, utcDateTime ); 00675 } 00676 00677 00678 bool KSystemTimeZoneBackendWindows::isDst(const KTimeZone *caller, time_t t) const 00679 { 00680 return isDstAtUtc( caller, KTimeZone::fromTime_t( t ) ); 00681 } 00682 00683 KSystemTimeZoneWindows::KSystemTimeZoneWindows(KTimeZoneSource *source, const QString &name) 00684 : KTimeZone(new KSystemTimeZoneBackendWindows(source, name)) 00685 {} 00686 00687 QStringList KSystemTimeZoneWindows::listTimeZones() 00688 { 00689 return list_standard_names(); 00690 } 00691
KDE 4.6 API Reference