00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "ktimezone_win.h"
00023 #include <config.h>
00024
00025 #include <kdebug.h>
00026
00027 #include <QStringList>
00028 #include <windows.h>
00029
00030 #include <memory>
00031 #include <string>
00032 #include <cassert>
00033
00034
00035 namespace {
00036 class HKeyCloser {
00037 const HKEY hkey;
00038 Q_DISABLE_COPY( HKeyCloser )
00039 public:
00040 explicit HKeyCloser( HKEY hk ) : hkey( hk ) {}
00041 ~HKeyCloser() { RegCloseKey( hkey ); }
00042 };
00043
00044 struct TZI {
00045 LONG Bias;
00046 LONG StandardBias;
00047 LONG DaylightBias;
00048 SYSTEMTIME StandardDate;
00049 SYSTEMTIME DaylightDate;
00050 };
00051 }
00052
00053
00054 static const TCHAR timeZonesKey[] = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones");
00055 static inline QDateTime systemtime_to_qdatetime( const SYSTEMTIME & st ) {
00056 return QDateTime( QDate( st.wYear, st.wMonth, st.wDay ),
00057 QTime( st.wHour, st.wMinute, st.wSecond, st.wMilliseconds ) );
00058 }
00059
00060 static SYSTEMTIME qdatetime_to_systemtime( const QDateTime & dt ) {
00061 const QDate d = dt.date();
00062 const QTime t = dt.time();
00063 const SYSTEMTIME st = {
00064 d.year(),
00065 d.month(),
00066 d.dayOfWeek() % 7,
00067 d.day(),
00068 t.hour(),
00069 t.minute(),
00070 t.second(),
00071 t.msec(),
00072 };
00073 return st;
00074 }
00075
00076 static bool TzSpecificLocalTimeToSystemTime_Portable( TIME_ZONE_INFORMATION* tz,
00077 SYSTEMTIME *i_stLocal,
00078 SYSTEMTIME *o_stUniversal )
00079 {
00080
00081
00082
00083
00084 #if 0
00085 if ( QSysInfo::windowsVersion() > QSysInfo::WV_2000 )
00086 {
00087 return TzSpecificLocalTimeToSystemTime( &tz, i_stLocal , o_stUniversal ) != 0;
00088 }
00089 #endif
00090
00091
00092
00093
00094
00095
00096 FILETIME ft, ft_utc;
00097 int result = 1;
00098 TIME_ZONE_INFORMATION currentTimeZone;
00099 result = GetTimeZoneInformation(¤tTimeZone);
00100 if ( result == TIME_ZONE_ID_INVALID ) {
00101 kWarning(161) << "Getting time zone information failed";
00102 return false;
00103 }
00104 result = SetTimeZoneInformation(tz);
00105 if ( result == 0 ) {
00106 kWarning(161) << "Setting temporary time zone failed";
00107 return false;
00108 }
00109 result = SystemTimeToFileTime(i_stLocal, &ft);
00110 if ( result == 0 ) {
00111 kWarning(161) << "SysteTimeToFileTime failed";
00112 return false;
00113 }
00114 result = LocalFileTimeToFileTime(&ft, &ft_utc);
00115 if ( result == 0 ) {
00116 kWarning(161) << "LocalFileTimeToFileTime failed";
00117 return false;
00118 }
00119 result = FileTimeToSystemTime(&ft_utc,o_stUniversal);
00120 if ( result == 0 ) {
00121 kWarning(161) << "FileTimeToSystemTime failed";
00122 return false;
00123 }
00124 result = SetTimeZoneInformation(¤tTimeZone);
00125 if ( result == 0 ) {
00126 kWarning(161) << "Re-setting time zone information failed";
00127 return false;
00128 }
00129 return true;
00130 }
00131
00132
00133
00134
00135 static bool get_binary_value( HKEY key, const TCHAR * value, void * data, DWORD numData, DWORD * outNumData=0 ) {
00136 DWORD size = numData;
00137 DWORD type = REG_BINARY;
00138 if ( RegQueryValueEx( key, value, 0, &type, (LPBYTE)data, &size ) != ERROR_SUCCESS )
00139 return false;
00140 assert( type == REG_BINARY );
00141 if ( type != REG_BINARY )
00142 return false;
00143 if ( outNumData )
00144 *outNumData = size;
00145 return true;
00146 }
00147
00148 static bool get_string_value( HKEY key, const WCHAR * value, WCHAR * dest, DWORD destSizeInBytes ) {
00149 DWORD size = destSizeInBytes;
00150 DWORD type = REG_SZ;
00151 dest[0] = '\0';
00152 if ( RegQueryValueExW( key, value, 0, &type, (LPBYTE)dest, &size ) != ERROR_SUCCESS )
00153 return false;
00154
00155 assert( type == REG_SZ );
00156 if ( type != REG_SZ )
00157 return false;
00158 return true;
00159 }
00160
00161
00162
00163
00164
00165
00166
00167 static bool check_prereq( const KTimeZone * caller, const QDateTime & dt, Qt::TimeSpec spec ) {
00168 return caller && caller->isValid() && dt.isValid() && dt.timeSpec() == spec ;
00169 }
00170
00171 static inline bool check_local( const KTimeZone * caller, const QDateTime & dt ) {
00172 return check_prereq( caller, dt, Qt::LocalTime );
00173 }
00174
00175 static inline bool check_utc( const KTimeZone * caller, const QDateTime & dt ) {
00176 return check_prereq( caller, dt, Qt::UTC );
00177 }
00178
00179 static bool has_transition( const TIME_ZONE_INFORMATION & tz ) {
00180 return tz.StandardDate.wMonth != 0 && tz.DaylightDate.wMonth != 0 ;
00181 }
00182
00183 static int win_dayofweek_to_qt_dayofweek( int wdow ) {
00184
00185 return wdow ? wdow : 7 ;
00186 }
00187
00188 static int qt_dayofweek_to_win_dayofweek( int qdow ) {
00189
00190 return qdow % 7;
00191 }
00192
00193 static QDate find_nth_weekday_in_month_of_year( int nth, int dayOfWeek, int month, int year ) {
00194 assert( nth >= 1 );
00195 assert( nth <= 5 );
00196
00197 const QDate first( year, month, 1 );
00198 const int actualDayOfWeek = first.dayOfWeek();
00199 QDate candidate = first.addDays( ( nth - 1 ) * 7 + dayOfWeek - actualDayOfWeek );
00200 assert( candidate.dayOfWeek() == dayOfWeek );
00201 if ( nth == 5 )
00202 if ( candidate.month() != month )
00203 candidate = candidate.addDays( -7 );
00204 assert( candidate.month() == month );
00205 return candidate;
00206 }
00207
00208 static QDateTime transition( const SYSTEMTIME & st, int year ) {
00209 assert( st.wYear == 0 );
00210 assert( st.wMonth != 0 );
00211 return QDateTime( find_nth_weekday_in_month_of_year( st.wDay, win_dayofweek_to_qt_dayofweek( st.wDayOfWeek ), st.wMonth, year ),
00212 QTime( st.wHour, st.wMinute, st.wSecond, st.wMilliseconds ) );
00213 }
00214
00215 struct Transitions {
00216 QDateTime stdStart, dstStart;
00217 };
00218
00219 Transitions transitions( const TIME_ZONE_INFORMATION & tz, int year ) {
00220 const Transitions t = {
00221 transition( tz.StandardDate, year ), transition( tz.DaylightDate, year )
00222 };
00223 return t;
00224 }
00225
00226
00227
00228
00229 class KSystemTimeZoneSourceWindowsPrivate
00230 {
00231 public:
00232 KSystemTimeZoneSourceWindowsPrivate() {}
00233 ~KSystemTimeZoneSourceWindowsPrivate() {}
00234 };
00235
00236
00237 class KSystemTimeZoneBackendWindows : public KTimeZoneBackend
00238 {
00239 public:
00240 KSystemTimeZoneBackendWindows(KTimeZoneSource *source, const QString &name)
00241 : KTimeZoneBackend(source, name) {}
00242
00243 ~KSystemTimeZoneBackendWindows() {}
00244
00245 KSystemTimeZoneBackendWindows *clone() const;
00246
00247 QByteArray type() const;
00248
00249 int offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const;
00250 int offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const;
00251 int offset(const KTimeZone *caller, time_t t) const;
00252 bool isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const;
00253 bool isDst(const KTimeZone *caller, time_t t) const;
00254 };
00255
00256 class KSystemTimeZoneDataWindows : public KTimeZoneData
00257 {
00258 public:
00259 KSystemTimeZoneDataWindows()
00260 :KTimeZoneData()
00261 {
00262
00263 }
00264 TIME_ZONE_INFORMATION _tzi;
00265 QString displayName;
00266
00267 const TIME_ZONE_INFORMATION & tzi( int year = 0 ) const { Q_UNUSED( year ); return _tzi; }
00268 };
00269
00270 KSystemTimeZoneSourceWindows::KSystemTimeZoneSourceWindows()
00271 :d( new KSystemTimeZoneSourceWindowsPrivate )
00272 {
00273 }
00274
00275 KTimeZoneData* KSystemTimeZoneSourceWindows::parse(const KTimeZone &zone) const
00276 {
00277 KSystemTimeZoneDataWindows* data = new KSystemTimeZoneDataWindows();
00278
00279 std::basic_string<TCHAR> path( timeZonesKey );
00280 path += TEXT( "\\" );
00281 path += reinterpret_cast<TCHAR*>( zone.name().toLocal8Bit().data() );
00282
00283 HKEY key;
00284 if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, path.c_str(), 0, KEY_READ, &key ) != ERROR_SUCCESS ) {
00285 delete data;
00286 return 0;
00287 }
00288
00289 const HKeyCloser closer( key );
00290
00291 TZI tzi = { 0 };
00292
00293 if ( !get_binary_value( key, TEXT( "TZI" ), &tzi, sizeof( TZI ) ) ) {
00294 delete data;
00295 return 0;
00296 }
00297
00298 get_string_value( key, L"Std", data->_tzi.StandardName, sizeof( data->_tzi.StandardName ) );
00299 get_string_value( key, L"Dlt", data->_tzi.DaylightName, sizeof( data->_tzi.DaylightName ) );
00300
00301 WCHAR display[512];
00302 get_string_value( key, L"Display", display, sizeof( display ) );
00303 data->displayName = QString::fromUtf16( reinterpret_cast<ushort*>( display ) );
00304
00305 #define COPY( name ) data->_tzi.name = tzi.name
00306 COPY( Bias );
00307 COPY( StandardBias );
00308 COPY( StandardDate );
00309 COPY( DaylightBias );
00310 COPY( DaylightDate );
00311 #undef COPY
00312
00313 return data;
00314 }
00315
00316 Transitions transitions( const KTimeZone * caller, int year ) {
00317 return transitions( static_cast<const KSystemTimeZoneDataWindows*>( caller->data(true) )->tzi( year ), year );
00318 }
00319
00320 static bool is_dst( const TIME_ZONE_INFORMATION & tzi, const QDateTime & utc, int year ) {
00321 if ( !has_transition( tzi ) )
00322 return false;
00323 const Transitions trans = transitions( tzi, year );
00324 if ( trans.stdStart < trans.dstStart )
00325 return trans.dstStart <= utc || utc < trans.stdStart ;
00326 else
00327 return trans.dstStart <= utc && utc < trans.stdStart ;
00328 }
00329
00330 static bool is_dst( const KTimeZone * caller, const QDateTime & utc ) {
00331 assert( caller );
00332 assert( caller->isValid() );
00333 const int year = utc.date().year();
00334 const TIME_ZONE_INFORMATION & tzi = static_cast<const KSystemTimeZoneDataWindows*>( caller->data(true) )->tzi( year );
00335 return is_dst( tzi, utc, year );
00336 }
00337
00338 static int effective_offset( const TIME_ZONE_INFORMATION& tz, bool isDst ) {
00339 int bias = tz.Bias;
00340 if ( has_transition( tz ) )
00341 if ( isDst )
00342 bias += tz.DaylightBias;
00343 else
00344 bias += tz.StandardBias;
00345 return bias * -60;
00346 }
00347
00348 static int offset_at_utc( const KTimeZone * caller, const QDateTime & utc ) {
00349 assert( caller );
00350 assert( caller->isValid() );
00351 const int year = utc.date().year();
00352 const TIME_ZONE_INFORMATION & tz = static_cast<const KSystemTimeZoneDataWindows*>( caller->data(true) )->tzi( year );
00353 return effective_offset( tz, is_dst( tz, utc, year ) );
00354 }
00355
00356 static const int OneHour = 3600;
00357
00358 static int difference( const SYSTEMTIME & st1, const SYSTEMTIME & st2 ) {
00359 return systemtime_to_qdatetime( st1 ).secsTo( systemtime_to_qdatetime( st2 ) );
00360 }
00361
00362 static int offset_at_zone_time( const KTimeZone * caller, const SYSTEMTIME & zone, int * secondOffset ) {
00363 assert( caller );
00364 assert( caller->isValid() );
00365 assert(caller->data(true));
00366 const KSystemTimeZoneDataWindows * const data = static_cast<const KSystemTimeZoneDataWindows*>( caller->data(true) );
00367 const TIME_ZONE_INFORMATION & tz = data->tzi( zone.wYear );
00368 SYSTEMTIME utc;
00369 if ( !TzSpecificLocalTimeToSystemTime_Portable( const_cast<LPTIME_ZONE_INFORMATION>( &tz ), const_cast<LPSYSTEMTIME>( &zone ), &utc ) )
00370 return 0;
00371 const bool isDst = is_dst( tz, systemtime_to_qdatetime( utc ), utc.wYear );
00372 int result = effective_offset( tz, isDst );
00373 if ( secondOffset ) {
00374 const SYSTEMTIME utcplus1 = qdatetime_to_systemtime( systemtime_to_qdatetime( utc ).addSecs( OneHour ) );
00375 const SYSTEMTIME utcminus1 = qdatetime_to_systemtime( systemtime_to_qdatetime( utc ).addSecs( -OneHour ) );
00376 SYSTEMTIME zoneplus1, zoneminus1;
00377 if ( !SystemTimeToTzSpecificLocalTime( const_cast<LPTIME_ZONE_INFORMATION>( &tz ), const_cast<LPSYSTEMTIME>( &utcplus1 ), &zoneplus1 ) ||
00378 !SystemTimeToTzSpecificLocalTime( const_cast<LPTIME_ZONE_INFORMATION>( &tz ), const_cast<LPSYSTEMTIME>( &utcminus1 ), &zoneminus1 ) )
00379 return result;
00380 if ( difference( zoneminus1, zone ) != OneHour ||
00381 difference( zone, zoneplus1 ) != OneHour )
00382 {
00383 *secondOffset = effective_offset( tz, !isDst );
00384 if ( result < *secondOffset )
00385 qSwap( result, *secondOffset );
00386 }
00387 }
00388 return result;
00389 }
00390
00391
00392 static const int MAX_KEY_LENGTH = 255;
00393
00394
00395 static inline QString tchar_to_qstring( TCHAR * ustr ) {
00396 const char * str = reinterpret_cast<const char*>( ustr );
00397 return QString::fromLocal8Bit( str );
00398 }
00399 static inline QString tchar_to_qstring( const wchar_t * str ) {
00400 return QString::fromUtf16( reinterpret_cast<const ushort*>( str ) );
00401 }
00402
00403 static QStringList list_key( HKEY key ) {
00404
00405 DWORD numSubKeys = 0;
00406 QStringList result;
00407
00408 if ( RegQueryInfoKey( key, 0, 0, 0, &numSubKeys, 0, 0, 0, 0, 0, 0, 0 ) == ERROR_SUCCESS )
00409 for ( DWORD i = 0 ; i < numSubKeys ; ++i ) {
00410 TCHAR name[MAX_KEY_LENGTH+1];
00411 DWORD nameLen = MAX_KEY_LENGTH;
00412 if ( RegEnumKeyEx( key, i, name, &nameLen, 0, 0, 0, 0 ) == ERROR_SUCCESS )
00413 result.push_back( tchar_to_qstring( name ) );
00414 }
00415
00416 return result;
00417 }
00418
00419
00420 KSystemTimeZoneBackendWindows * KSystemTimeZoneBackendWindows::clone() const
00421 {
00422 return new KSystemTimeZoneBackendWindows(*this);
00423 }
00424
00425 QByteArray KSystemTimeZoneBackendWindows::type() const
00426 {
00427 return "KSystemTimeZoneWindows";
00428 }
00429
00430 int KSystemTimeZoneBackendWindows::offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const
00431 {
00432 if (!caller->isValid() || !zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime)
00433 return 0;
00434 if ( !check_local( caller, zoneDateTime ) )
00435 return 0;
00436
00437 return offset_at_zone_time( caller, qdatetime_to_systemtime( zoneDateTime ), secondOffset );
00438 }
00439
00440 int KSystemTimeZoneBackendWindows::offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
00441 {
00442 if (!caller->isValid() || !utcDateTime.isValid())
00443 return 0;
00444 if ( !check_utc( caller, utcDateTime ) )
00445 return 0;
00446 return offset_at_utc( caller, utcDateTime );
00447 }
00448
00449 int KSystemTimeZoneBackendWindows::offset(const KTimeZone *caller, time_t t) const
00450 {
00451 if (!caller->isValid() || t == KTimeZone::InvalidTime_t)
00452 return 0;
00453 return offsetAtUtc( caller, KTimeZone::fromTime_t( t ) );
00454 }
00455
00456 bool KSystemTimeZoneBackendWindows::isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
00457 {
00458 return check_utc( caller, utcDateTime ) && is_dst( caller, utcDateTime );
00459 }
00460
00461
00462 bool KSystemTimeZoneBackendWindows::isDst(const KTimeZone *caller, time_t t) const
00463 {
00464 return isDstAtUtc( caller, KTimeZone::fromTime_t( t ) );
00465 }
00466
00467 KSystemTimeZoneWindows::KSystemTimeZoneWindows(KTimeZoneSource *source, const QString &name)
00468 : KTimeZone(new KSystemTimeZoneBackendWindows(source, name))
00469 {}
00470
00471 QStringList KSystemTimeZoneWindows::listTimeZones()
00472 {
00473 HKEY timeZones;
00474 if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, timeZonesKey, 0, KEY_READ, &timeZones ) != ERROR_SUCCESS )
00475 return QStringList();
00476 const HKeyCloser closer( timeZones );
00477 return list_key( timeZones );
00478 }
00479