• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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 <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, // 1..7 (Mon..Sun)->0..6(Sun..Sat)
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     // the method below was introduced in XP. If it's there, use it, otherwise
00082     // fall back to doing things manually
00083 //#if Q_OS_VERSION > 5.0
00084 #if 0
00085     if ( QSysInfo::windowsVersion() > QSysInfo::WV_2000 )
00086     {
00087         return TzSpecificLocalTimeToSystemTime( &tz, i_stLocal , o_stUniversal ) != 0;
00088     }
00089 #endif
00090     // the algorithm is:
00091     // - switch to the desired timezone temporarily
00092     // - convert system time to (local) file time in that timezone
00093     // - convert local file time to utc file time
00094     // - convert utc file time to system time
00095     // - reset timezone
00096     FILETIME ft, ft_utc;
00097     int result = 1;
00098     TIME_ZONE_INFORMATION currentTimeZone;
00099     result = GetTimeZoneInformation(&currentTimeZone);
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(&currentTimeZone);
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     //dest[ qMin( size, destSizeInBytes - sizeof( WCHAR ) ) / sizeof( WCHAR ) ] = 0;
00155     assert( type == REG_SZ );
00156     if ( type != REG_SZ )
00157         return false;
00158     return true;
00159 }
00160 
00161 //
00162 //
00163 // Backend interface impl:
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     // Sun(0)..Sat(6) -> Mon(1)...Sun(7)
00185     return wdow ? wdow : 7 ;
00186 }
00187 
00188 static int qt_dayofweek_to_win_dayofweek( int qdow ) {
00189     // Mon(1)...Sun(7) -> Sub(0)...Sat(6)
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; // FIXME what's the right error handling here?
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; // min -> secs
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; //sec
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 // TCHAR can be either uchar, or wchar_t:
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 

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal