• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDECore

ksystemtimezone.cpp

Go to the documentation of this file.
00001 /*
00002    This file is part of the KDE libraries
00003    Copyright (c) 2005-2010 David Jarvie <djarvie@kde.org>
00004    Copyright (c) 2005 S.R.Haque <srhaque@iee.org>.
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019    Boston, MA 02110-1301, USA.
00020 */
00021 
00022 // This file requires HAVE_STRUCT_TM_TM_ZONE to be defined if struct tm member tm_zone is available.
00023 // This file requires HAVE_TM_GMTOFF to be defined if struct tm member tm_gmtoff is available.
00024 
00025 #include "ksystemtimezone.moc"
00026 
00027 #include <config.h>
00028 
00029 #ifdef HAVE_SYS_TIME_H
00030 #include <sys/time.h>
00031 #endif
00032 #ifdef HAVE_TIME_H
00033 #include <time.h>
00034 #endif
00035 #include <climits>
00036 #include <cstdlib>
00037 
00038 #include <QtCore/QCoreApplication>
00039 #include <QtCore/QFile>
00040 #include <QtCore/QFileInfo>
00041 #include <QtCore/QDir>
00042 #include <QtCore/QRegExp>
00043 #include <QtCore/QStringList>
00044 #include <QtCore/QTextStream>
00045 #include <QtDBus/QDBusConnection>
00046 #include <QtDBus/QDBusInterface>
00047 #include <QtDBus/QDBusConnectionInterface>
00048 #include <QtDBus/QDBusReply>
00049 
00050 #include <kglobal.h>
00051 #include <klocale.h>
00052 #include <kcodecs.h>
00053 #include <kstringhandler.h>
00054 #include <ktemporaryfile.h>
00055 #include <ktoolinvocation.h>
00056 #include <kdebug.h>
00057 #include <kconfiggroup.h>
00058 #include "ktzfiletimezone.h"
00059 #ifdef Q_OS_WIN
00060 #include "ktimezone_win.h"
00061 #endif
00062 
00063 #define KTIMEZONED_DBUS_IFACE "org.kde.KTimeZoned"
00064 
00065 
00066 /* Return the offset to UTC in the current time zone at the specified UTC time.
00067  * The thread-safe function localtime_r() is used in preference if available.
00068  */
00069 int gmtoff(time_t t)
00070 {
00071 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS
00072     tm tmtime;
00073     if (!localtime_r(&t, &tmtime))
00074         return 0;
00075 #ifdef HAVE_TM_GMTOFF
00076     return tmtime.tm_gmtoff;
00077 #else
00078     int lwday = tmtime.tm_wday;
00079     int lt = 3600*tmtime.tm_hour + 60*tmtime.tm_min + tmtime.tm_sec;
00080     if (!gmtime_r(&t, &tmtime))
00081         return 0;
00082     int uwday = tmtime.tm_wday;
00083     int ut = 3600*tmtime.tm_hour + 60*tmtime.tm_min + tmtime.tm_sec;
00084 #endif
00085 #else
00086     tm *tmtime = localtime(&t);
00087     if (!tmtime)
00088         return 0;
00089 #ifdef HAVE_TM_GMTOFF
00090     return tmtime->tm_gmtoff;
00091 #else
00092     int lwday = tmtime->tm_wday;
00093     int lt = 3600*tmtime->tm_hour + 60*tmtime->tm_min + tmtime->tm_sec;
00094     tmtime = gmtime(&t);
00095     int uwday = tmtime->tm_wday;
00096     int ut = 3600*tmtime->tm_hour + 60*tmtime->tm_min + tmtime->tm_sec;
00097 #endif
00098 #endif
00099 #ifndef HAVE_TM_GMTOFF
00100     if (lwday != uwday)
00101     {
00102       // Adjust for different day
00103       if (lwday == uwday + 1  ||  (lwday == 0 && uwday == 6))
00104         lt += 24*3600;
00105       else
00106         lt -= 24*3600;
00107     }
00108     return lt - ut;
00109 #endif
00110 }
00111 
00112 
00113 /******************************************************************************/
00114 
00115 class KSystemTimeZonesPrivate : public KTimeZones
00116 {
00117 public:
00118     static KSystemTimeZonesPrivate *instance();
00119     static KTzfileTimeZoneSource *tzfileSource();
00120     static void setLocalZone();
00121     static void cleanup();
00122     static void readConfig(bool init);
00123 #ifdef Q_OS_WIN
00124     static void updateTimezoneInformation()
00125     {
00126       instance()->updateTimezoneInformation(true);
00127     }
00128 #else
00129     static void updateZonetab()  { instance()->readZoneTab(true); }
00130 #endif
00131 
00132     static KTimeZone m_localZone;
00133     static QString m_localZoneName;
00134     static QString m_zoneinfoDir;
00135     static QString m_zonetab;
00136     static KSystemTimeZoneSource *m_source;
00137     static bool m_ktimezonedError;
00138 
00139 private:
00140     KSystemTimeZonesPrivate() {}
00141 #ifdef Q_OS_WIN
00142     void updateTimezoneInformation(bool update);
00143 #else
00144     void readZoneTab(bool update);
00145     static float convertCoordinate(const QString &coordinate);
00146 #endif
00147 
00148     static KSystemTimeZones *m_parent;
00149     static KSystemTimeZonesPrivate *m_instance;
00150     static KTzfileTimeZoneSource *m_tzfileSource;
00151 };
00152 
00153 KTimeZone                KSystemTimeZonesPrivate::m_localZone;
00154 QString                  KSystemTimeZonesPrivate::m_localZoneName;
00155 QString                  KSystemTimeZonesPrivate::m_zoneinfoDir;
00156 QString                  KSystemTimeZonesPrivate::m_zonetab;
00157 KSystemTimeZoneSource   *KSystemTimeZonesPrivate::m_source = 0;
00158 bool                     KSystemTimeZonesPrivate::m_ktimezonedError = true;
00159 KTzfileTimeZoneSource   *KSystemTimeZonesPrivate::m_tzfileSource = 0;
00160 KSystemTimeZones        *KSystemTimeZonesPrivate::m_parent = 0;
00161 KSystemTimeZonesPrivate *KSystemTimeZonesPrivate::m_instance = 0;
00162 
00163 KTzfileTimeZoneSource *KSystemTimeZonesPrivate::tzfileSource()
00164 {
00165     if (!m_tzfileSource)
00166     {
00167         instance();
00168         m_tzfileSource = new KTzfileTimeZoneSource(m_zoneinfoDir);
00169     }
00170     return m_tzfileSource;
00171 }
00172 
00173 
00174 #ifndef NDEBUG
00175 K_GLOBAL_STATIC(KTimeZone, simulatedLocalZone)
00176 #endif
00177 
00178 
00179 KSystemTimeZones::KSystemTimeZones()
00180   : d(0)
00181 {
00182     QDBusConnection dbus = QDBusConnection::sessionBus();
00183     const QString dbusIface = QString::fromLatin1(KTIMEZONED_DBUS_IFACE);
00184     dbus.connect(QString(), QString(), dbusIface, QLatin1String("configChanged"), this, SLOT(configChanged()));
00185     dbus.connect(QString(), QString(), dbusIface, QLatin1String("zonetabChanged"), this, SLOT(zonetabChanged(QString)));
00186     // No need to connect to definitionChanged() - see comments in zoneDefinitionChanged()
00187     //dbus.connect(QString(), QString(), dbusIface, QLatin1String("definitionChanged"), this, SLOT(zoneDefinitionChanged(QString)));
00188 }
00189 
00190 KSystemTimeZones::~KSystemTimeZones()
00191 {
00192 }
00193 
00194 KTimeZone KSystemTimeZones::local()
00195 {
00196 #ifndef NDEBUG
00197     if (simulatedLocalZone->isValid())
00198         return *simulatedLocalZone;
00199 #endif
00200     KSystemTimeZonesPrivate::instance();
00201     return KSystemTimeZonesPrivate::m_localZone;
00202 }
00203 
00204 KTimeZone KSystemTimeZones::realLocalZone()
00205 {
00206     KSystemTimeZonesPrivate::instance();
00207     return KSystemTimeZonesPrivate::m_localZone;
00208 }
00209 
00210 void KSystemTimeZones::setLocalZone(const KTimeZone& tz)
00211 {
00212     Q_UNUSED(tz);
00213 #ifndef NDEBUG
00214     *simulatedLocalZone = tz;
00215 #endif
00216 }
00217 
00218 bool KSystemTimeZones::isSimulated()
00219 {
00220 #ifndef NDEBUG
00221     return simulatedLocalZone->isValid();
00222 #else
00223     return false;
00224 #endif
00225 }
00226 
00227 QString KSystemTimeZones::zoneinfoDir()
00228 {
00229     KSystemTimeZonesPrivate::instance();
00230     return KSystemTimeZonesPrivate::m_zoneinfoDir;
00231 }
00232 
00233 bool KSystemTimeZones::isTimeZoneDaemonAvailable()
00234 {
00235     KSystemTimeZonesPrivate::instance();
00236     return !KSystemTimeZonesPrivate::m_ktimezonedError;
00237 }
00238 
00239 KTimeZones *KSystemTimeZones::timeZones()
00240 {
00241     return KSystemTimeZonesPrivate::instance();
00242 }
00243 
00244 KTimeZone KSystemTimeZones::readZone(const QString &name)
00245 {
00246     return KTzfileTimeZone(KSystemTimeZonesPrivate::tzfileSource(), name);
00247 }
00248 
00249 const KTimeZones::ZoneMap KSystemTimeZones::zones()
00250 {
00251     return KSystemTimeZonesPrivate::instance()->zones();
00252 }
00253 
00254 KTimeZone KSystemTimeZones::zone(const QString& name)
00255 {
00256     return KSystemTimeZonesPrivate::instance()->zone(name);
00257 }
00258 
00259 void KSystemTimeZones::configChanged()
00260 {
00261     kDebug(161) << "KSystemTimeZones::configChanged()";
00262     KSystemTimeZonesPrivate::m_ktimezonedError = false;
00263     KSystemTimeZonesPrivate::readConfig(false);
00264 }
00265 
00266 void KSystemTimeZones::zonetabChanged(const QString &zonetab)
00267 {
00268     Q_UNUSED(zonetab)
00269 #ifndef Q_OS_WIN
00270     kDebug(161) << "KSystemTimeZones::zonetabChanged()";
00271     KSystemTimeZonesPrivate::m_ktimezonedError = false;
00272     // Re-read zone.tab and update our collection, removing any deleted
00273     // zones and adding any new zones.
00274     KSystemTimeZonesPrivate::updateZonetab();
00275 #endif
00276 }
00277 
00278 void KSystemTimeZones::zoneDefinitionChanged(const QString &zone)
00279 {
00280     // No need to do anything when the definition (as opposed to the
00281     // identity) of the local zone changes, since the updated details
00282     // will always be accessed by the system library calls to fetch
00283     // local zone information.
00284     Q_UNUSED(zone)
00285     KSystemTimeZonesPrivate::m_ktimezonedError = false;
00286 }
00287 
00288 // Perform initialization, create the unique KSystemTimeZones instance,
00289 // whose only function is to receive D-Bus signals from KTimeZoned,
00290 // and create the unique KSystemTimeZonesPrivate instance.
00291 KSystemTimeZonesPrivate *KSystemTimeZonesPrivate::instance()
00292 {
00293     if (!m_instance)
00294     {
00295         m_instance = new KSystemTimeZonesPrivate;
00296 
00297         // A KSystemTimeZones instance is required only to catch D-Bus signals.
00298         m_parent = new KSystemTimeZones;
00299         // Ensure that the KDED time zones module has initialized. The call loads the module on demand.
00300         if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(QLatin1String("org.kde.kded")))
00301             KToolInvocation::klauncher();   // this calls startKdeinit, and blocks until it returns
00302         const QString dbusIface = QString::fromLatin1(KTIMEZONED_DBUS_IFACE);
00303         QDBusInterface *ktimezoned = new QDBusInterface(QLatin1String("org.kde.kded"), QLatin1String("/modules/ktimezoned"), dbusIface);
00304         QDBusReply<void> reply = ktimezoned->call(QLatin1String("initialize"), false);
00305         m_ktimezonedError = !reply.isValid();
00306         if (m_ktimezonedError)
00307             kError(161) << "KSystemTimeZones: ktimezoned initialize() D-Bus call failed: " << reply.error().message() << endl;
00308 kDebug(161)<<"instance(): ... initialised";
00309         delete ktimezoned;
00310 
00311         // Read the time zone config written by ktimezoned
00312         readConfig(true);
00313 
00314         // Go read the database.
00315 #ifdef Q_OS_WIN
00316         // On Windows, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
00317         // is the place to look. The TZI binary value is the TIME_ZONE_INFORMATION structure.
00318         m_instance->updateTimezoneInformation(false);
00319 #else
00320         // For Unix, read zone.tab.
00321         if (!m_zonetab.isEmpty())
00322             m_instance->readZoneTab(false);
00323 #endif
00324         setLocalZone();
00325         if (!m_localZone.isValid())
00326             m_localZone = KTimeZone::utc();   // ensure a time zone is always returned
00327 
00328         qAddPostRoutine(KSystemTimeZonesPrivate::cleanup);
00329     }
00330     return m_instance;
00331 }
00332 
00333 void KSystemTimeZonesPrivate::readConfig(bool init)
00334 {
00335     KConfig config(QLatin1String("ktimezonedrc"));
00336     if (!init)
00337         config.reparseConfiguration();
00338     KConfigGroup group(&config, "TimeZones");
00339     m_zoneinfoDir   = group.readEntry("ZoneinfoDir");
00340     m_zonetab       = group.readEntry("Zonetab");
00341     m_localZoneName = group.readEntry("LocalZone");
00342     if (m_zoneinfoDir.length() > 1 && m_zoneinfoDir.endsWith(QLatin1Char('/')))
00343         m_zoneinfoDir.truncate(m_zoneinfoDir.length() - 1);  // strip trailing '/'
00344     if (!init)
00345         setLocalZone();
00346     kDebug(161) << "readConfig(): local zone=" << m_localZoneName;
00347 }
00348 
00349 void KSystemTimeZonesPrivate::setLocalZone()
00350 {
00351     QString filename;
00352     if (m_localZoneName.startsWith(QLatin1Char('/'))) {
00353         // The time zone is specified by a file outside the zoneinfo directory
00354         filename = m_localZoneName;
00355     } else {
00356         // The zone name is either a known zone, or it's a relative file name
00357         // in zoneinfo directory which isn't in zone.tab.
00358         m_localZone = m_instance->zone(m_localZoneName);
00359         if (m_localZone.isValid())
00360             return;
00361         // It's a relative file name
00362         filename = m_zoneinfoDir + QLatin1Char('/') + m_localZoneName;
00363     }
00364 
00365     // Parse the specified time zone data file
00366     QString zonename = filename;
00367     if (zonename.startsWith(m_zoneinfoDir + QLatin1Char('/')))
00368         zonename = zonename.mid(m_zoneinfoDir.length() + 1);
00369     m_localZone = KTzfileTimeZone(KSystemTimeZonesPrivate::tzfileSource(), zonename);
00370     if (m_localZone.isValid() && m_instance)
00371     {
00372         // Add the new time zone to the list
00373         KTimeZone oldzone = m_instance->zone(zonename);
00374         if (!oldzone.isValid() || oldzone.type() != "KTzfileTimeZone")
00375         {
00376             m_instance->remove(oldzone);
00377             m_instance->add(m_localZone);
00378         }
00379     }
00380 }
00381 
00382 void KSystemTimeZonesPrivate::cleanup()
00383 {
00384     delete m_parent;
00385     delete m_instance;
00386     delete m_source;
00387     delete m_tzfileSource;
00388 }
00389 
00390 #ifdef Q_OS_WIN
00391 
00392 void KSystemTimeZonesPrivate::updateTimezoneInformation(bool update)
00393 {
00394     if (!m_source)
00395         m_source = new KSystemTimeZoneSourceWindows;
00396     QStringList newZones;
00397     Q_FOREACH(const QString & tz, KSystemTimeZoneWindows::listTimeZones())
00398     {
00399         // const std::wstring wstr = tz.toStdWString();
00400         // const KTimeZone info = make_time_zone( wstr.c_str() );
00401         KSystemTimeZoneWindows stz(m_source, tz);
00402         if (update)
00403         {
00404             // Update the existing collection with the new zone definition
00405             newZones += stz.name();
00406             KTimeZone oldTz = zone(stz.name());
00407             if (oldTz.isValid())
00408                 oldTz.updateBase(stz);   // the zone previously existed, so update its definition
00409             else
00410                 add(stz);   // the zone didn't previously exist, so add it
00411         }
00412         else
00413             add(stz);
00414     }
00415     if (update)
00416     {
00417         // Remove any zones from the collection which no longer exist
00418         const ZoneMap oldZones = zones();
00419         for (ZoneMap::const_iterator it = oldZones.begin();  it != oldZones.end();  ++it)
00420         {
00421             if (newZones.indexOf(it.key()) < 0)
00422                 remove(it.value());
00423         }
00424     }
00425 }
00426 
00427 #else
00428 /*
00429  * Find the location of the zoneinfo files and store in mZoneinfoDir.
00430  * Parse zone.tab and for each time zone, create a KSystemTimeZone instance.
00431  */
00432 void KSystemTimeZonesPrivate::readZoneTab(bool update)
00433 {
00434     kDebug(161) << "readZoneTab(" << m_zonetab<< ")";
00435     QStringList newZones;
00436     QFile f;
00437     f.setFileName(m_zonetab);
00438     if (!f.open(QIODevice::ReadOnly))
00439         return;
00440     QTextStream str(&f);
00441     QRegExp lineSeparator(QLatin1String("[ \t]"));
00442     QRegExp ordinateSeparator(QLatin1String("[+-]"));
00443     if (!m_source)
00444         m_source = new KSystemTimeZoneSource;
00445     while (!str.atEnd())
00446     {
00447         QString line = str.readLine();
00448         if (line.isEmpty() || line[0] == QLatin1Char('#'))
00449             continue;
00450         QStringList tokens = KStringHandler::perlSplit(lineSeparator, line, 4);
00451         int n = tokens.count();
00452         if (n < 3)
00453         {
00454             kError(161) << "readZoneTab(): invalid record: " << line << endl;
00455             continue;
00456         }
00457 
00458         // Got three tokens. Now check for two ordinates plus first one is "".
00459         int i = tokens[1].indexOf(ordinateSeparator, 1);
00460         if (i < 0)
00461         {
00462             kError(161) << "readZoneTab() " << tokens[2] << ": invalid coordinates: " << tokens[1] << endl;
00463             continue;
00464         }
00465 
00466         float latitude = convertCoordinate(tokens[1].left(i));
00467         float longitude = convertCoordinate(tokens[1].mid(i));
00468 
00469         // Add entry to list.
00470         if (tokens[0] == QLatin1String("??"))
00471             tokens[0] = QString::fromLatin1("");
00472         // Solaris sets the empty Comments field to '-', making it not empty.
00473         // Clean it up.
00474         if (n > 3  && tokens[3] == QLatin1String("-"))
00475             tokens[3] = QString::fromLatin1("");
00476         KSystemTimeZone tz(m_source, tokens[2], tokens[0], latitude, longitude, (n > 3 ? tokens[3] : QString()));
00477         if (update)
00478         {
00479             // Update the existing collection with the new zone definition
00480             newZones += tz.name();
00481             KTimeZone oldTz = zone(tz.name());
00482             if (oldTz.isValid())
00483                 oldTz.updateBase(tz);   // the zone previously existed, so update its definition
00484             else
00485                 add(tz);   // the zone didn't previously exist, so add it
00486         }
00487         else
00488             add(tz);
00489     }
00490     f.close();
00491 
00492     if (update)
00493     {
00494         // Remove any zones from the collection which no longer exist
00495         const ZoneMap oldZones = zones();
00496         for (ZoneMap::ConstIterator it = oldZones.constBegin();  it != oldZones.constEnd();  ++it)
00497         {
00498             if (newZones.indexOf(it.key()) < 0)
00499                 remove(it.value());
00500         }
00501     }
00502 }
00503 
00507 float KSystemTimeZonesPrivate::convertCoordinate(const QString &coordinate)
00508 {
00509     int value = coordinate.toInt();
00510     int degrees = 0;
00511     int minutes = 0;
00512     int seconds = 0;
00513 
00514     if (coordinate.length() > 6)
00515     {
00516         degrees = value / 10000;
00517         value -= degrees * 10000;
00518         minutes = value / 100;
00519         value -= minutes * 100;
00520         seconds = value;
00521     }
00522     else
00523     {
00524         degrees = value / 100;
00525         value -= degrees * 100;
00526         minutes = value;
00527     }
00528     value = degrees * 3600 + minutes * 60 + seconds;
00529     return value / 3600.0;
00530 }
00531 #endif
00532 
00533 
00534 /******************************************************************************/
00535 
00536 
00537 KSystemTimeZoneBackend::KSystemTimeZoneBackend(KSystemTimeZoneSource *source, const QString &name,
00538         const QString &countryCode, float latitude, float longitude, const QString &comment)
00539   : KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)
00540 {}
00541 
00542 KSystemTimeZoneBackend::~KSystemTimeZoneBackend()
00543 {}
00544 
00545 KTimeZoneBackend *KSystemTimeZoneBackend::clone() const
00546 {
00547     return new KSystemTimeZoneBackend(*this);
00548 }
00549 
00550 QByteArray KSystemTimeZoneBackend::type() const
00551 {
00552     return "KSystemTimeZone";
00553 }
00554 
00555 int KSystemTimeZoneBackend::offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const
00556 {
00557     if (!caller->isValid()  ||  !zoneDateTime.isValid()  ||  zoneDateTime.timeSpec() != Qt::LocalTime)
00558         return 0;
00559     // Make this time zone the current local time zone
00560     const QByteArray originalZone = qgetenv("TZ");   // save the original local time zone
00561     QByteArray tz = caller->name().toUtf8();
00562     tz.prepend(":");
00563     bool change = (tz != originalZone);
00564     if (change)
00565     {
00566         ::setenv("TZ", tz, 1);
00567         ::tzset();
00568     }
00569 
00570     // Convert zone time to UTC, and then get the offset to UTC
00571     tm tmtime;
00572     tmtime.tm_sec    = zoneDateTime.time().second();
00573     tmtime.tm_min    = zoneDateTime.time().minute();
00574     tmtime.tm_hour   = zoneDateTime.time().hour();
00575     tmtime.tm_mday   = zoneDateTime.date().day();
00576     tmtime.tm_mon    = zoneDateTime.date().month() - 1;
00577     tmtime.tm_year   = zoneDateTime.date().year() - 1900;
00578     tmtime.tm_isdst  = -1;
00579     time_t t = mktime(&tmtime);
00580     int offset1 = (t == (time_t)-1) ? 0 : gmtoff(t);
00581     if (secondOffset)
00582     {
00583         int offset2 = offset1;
00584         if (t != (time_t)-1)
00585         {
00586             // Check if there is a backward DST change near to this time, by
00587             // checking if the UTC offset is different 1 hour later or earlier.
00588             // ASSUMPTION: DST SHIFTS ARE NEVER GREATER THAN 1 HOUR.
00589             int maxShift = 3600;
00590             offset2 = gmtoff(t + maxShift);
00591             if (offset2 < offset1)
00592             {
00593                 // There is a backward DST shift during the following hour
00594                 if (offset1 - offset2 < maxShift)
00595                     offset2 = gmtoff(t + (offset1 - offset2));
00596             }
00597             else if ((offset2 = gmtoff(t - maxShift)) > offset1)
00598             {
00599                 // There is a backward DST shift during the previous hour
00600                 if (offset2 - offset1 < maxShift)
00601                     offset2 = gmtoff(t - (offset2 - offset1));
00602                 // Put UTC offsets into the correct order
00603                 int o = offset1;
00604                 offset1 = offset2;
00605                 offset2 = o;
00606             }
00607             else offset2 = offset1;
00608         }
00609         *secondOffset = offset2;
00610     }
00611 
00612     if (change)
00613     {
00614         // Restore the original local time zone
00615         if (originalZone.isEmpty())
00616             ::unsetenv("TZ");
00617         else
00618             ::setenv("TZ", originalZone, 1);
00619         ::tzset();
00620     }
00621     return offset1;
00622 }
00623 
00624 int KSystemTimeZoneBackend::offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
00625 {
00626     return offset(caller, KTimeZone::toTime_t(utcDateTime));
00627 }
00628 
00629 int KSystemTimeZoneBackend::offset(const KTimeZone *caller, time_t t) const
00630 {
00631     if (!caller->isValid()  ||  t == KTimeZone::InvalidTime_t)
00632         return 0;
00633 
00634     // Make this time zone the current local time zone
00635     const QByteArray originalZone = qgetenv("TZ");   // save the original local time zone
00636     QByteArray tz = caller->name().toUtf8();
00637     tz.prepend(":");
00638     bool change = (tz != originalZone);
00639     if (change)
00640     {
00641         ::setenv("TZ", tz, 1);
00642         ::tzset();
00643     }
00644 
00645     int secs = gmtoff(t);
00646 
00647     if (change)
00648     {
00649         // Restore the original local time zone
00650         if (originalZone.isEmpty())
00651             ::unsetenv("TZ");
00652         else
00653             ::setenv("TZ", originalZone, 1);
00654         ::tzset();
00655     }
00656     return secs;
00657 }
00658 
00659 bool KSystemTimeZoneBackend::isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
00660 {
00661     return isDst(caller, KTimeZone::toTime_t(utcDateTime));
00662 }
00663 
00664 bool KSystemTimeZoneBackend::isDst(const KTimeZone *caller, time_t t) const
00665 {
00666     Q_UNUSED(caller)
00667     if (t != (time_t)-1)
00668     {
00669 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS
00670         tm tmtime;
00671         if (localtime_r(&t, &tmtime))
00672             return tmtime.tm_isdst > 0;
00673 #else
00674         tm *tmtime = localtime(&t);
00675         if (tmtime)
00676             return tmtime->tm_isdst > 0;
00677 #endif
00678     }
00679     return false;
00680 }
00681 
00682 
00683 /******************************************************************************/
00684 
00685 KSystemTimeZone::KSystemTimeZone(KSystemTimeZoneSource *source, const QString &name,
00686         const QString &countryCode, float latitude, float longitude, const QString &comment)
00687   : KTimeZone(new KSystemTimeZoneBackend(source, name, countryCode, latitude, longitude, comment))
00688 {
00689 }
00690 
00691 KSystemTimeZone::~KSystemTimeZone()
00692 {
00693 }
00694 
00695 
00696 /******************************************************************************/
00697 
00698 class KSystemTimeZoneDataPrivate
00699 {
00700 public:
00701     QByteArray TZ;
00702     QList<QByteArray> abbreviations;
00703 };
00704 
00705 
00706 // N.B. KSystemTimeZoneSourcePrivate is also used by KSystemTimeZoneData
00707 class KSystemTimeZoneSourcePrivate
00708 {
00709 public:
00710     static void setTZ(const QByteArray &zoneName);
00711     static void restoreTZ();
00712     static QByteArray savedTZ;       // temporary value of TZ environment variable saved by setTZ()
00713     static QByteArray originalTZ;    // saved value of TZ environment variable during multiple parse() calls
00714     static bool       TZIsSaved;     // TZ has been saved in savedTZ
00715     static bool       multiParse;    // true if performing multiple parse() calls
00716 };
00717 
00718 QByteArray KSystemTimeZoneSourcePrivate::savedTZ;
00719 QByteArray KSystemTimeZoneSourcePrivate::originalTZ;
00720 bool       KSystemTimeZoneSourcePrivate::TZIsSaved = false;
00721 bool       KSystemTimeZoneSourcePrivate::multiParse = false;
00722 
00723 
00724 KSystemTimeZoneSource::KSystemTimeZoneSource()
00725     : d(0)
00726 //  : d(new KSystemTimeZoneSourcePrivate)
00727 {
00728 }
00729 
00730 KSystemTimeZoneSource::~KSystemTimeZoneSource()
00731 {
00732 //    delete d;
00733 }
00734 
00735 KTimeZoneData* KSystemTimeZoneSource::parse(const KTimeZone &zone) const
00736 {
00737     QByteArray tz = zone.name().toUtf8();
00738     KSystemTimeZoneSourcePrivate::setTZ(tz);   // make this time zone the current local time zone
00739 
00740     tzset();    // initialize the tzname array
00741     KSystemTimeZoneData* data = new KSystemTimeZoneData;
00742     data->d->TZ = tz;
00743     data->d->abbreviations.append(tzname[0]);
00744     data->d->abbreviations.append(tzname[1]);
00745 
00746     // There is no easy means to access the sequence of daylight savings time
00747     // changes, or leap seconds adjustments, so leave that data empty.
00748 
00749     KSystemTimeZoneSourcePrivate::restoreTZ();   // restore the original local time zone if necessary
00750     return data;
00751 }
00752 
00753 void KSystemTimeZoneSource::startParseBlock()
00754 {
00755     KSystemTimeZoneSourcePrivate::originalTZ = qgetenv("TZ");   // save the original local time zone
00756     KSystemTimeZoneSourcePrivate::multiParse = true;
00757 }
00758 
00759 void KSystemTimeZoneSource::endParseBlock()
00760 {
00761     if (KSystemTimeZoneSourcePrivate::multiParse)
00762     {
00763         // Restore the original local time zone
00764         if (KSystemTimeZoneSourcePrivate::originalTZ.isEmpty())
00765             ::unsetenv("TZ");
00766         else
00767             ::setenv("TZ", KSystemTimeZoneSourcePrivate::originalTZ, 1);
00768         ::tzset();
00769         KSystemTimeZoneSourcePrivate::multiParse = false;
00770     }
00771 }
00772 
00773 // Set the TZ environment variable to the specified time zone,
00774 // saving its current setting first if necessary.
00775 void KSystemTimeZoneSourcePrivate::setTZ(const QByteArray &zoneName)
00776 {
00777     QByteArray tz = zoneName;
00778     tz.prepend(":");
00779     bool setTZ = multiParse;
00780     if (!setTZ)
00781     {
00782         savedTZ = qgetenv("TZ");   // save the original local time zone
00783         TZIsSaved = true;
00784         setTZ = (tz != savedTZ);
00785     }
00786     if (setTZ)
00787     {
00788         ::setenv("TZ", tz, 1);
00789         ::tzset();
00790     }
00791 }
00792 
00793 // Restore the TZ environment variable if it was saved by setTz()
00794 void KSystemTimeZoneSourcePrivate::restoreTZ()
00795 {
00796     if (TZIsSaved)
00797     {
00798         if (savedTZ.isEmpty())
00799             ::unsetenv("TZ");
00800         else
00801             ::setenv("TZ", savedTZ, 1);
00802         ::tzset();
00803         TZIsSaved = false;
00804     }
00805 }
00806 
00807 
00808 /******************************************************************************/
00809 
00810 KSystemTimeZoneData::KSystemTimeZoneData()
00811   : d(new KSystemTimeZoneDataPrivate)
00812 { }
00813 
00814 KSystemTimeZoneData::KSystemTimeZoneData(const KSystemTimeZoneData &rhs)
00815   : KTimeZoneData(),
00816     d(new KSystemTimeZoneDataPrivate)
00817 {
00818     operator=(rhs);
00819 }
00820 
00821 KSystemTimeZoneData::~KSystemTimeZoneData()
00822 {
00823     delete d;
00824 }
00825 
00826 KSystemTimeZoneData &KSystemTimeZoneData::operator=(const KSystemTimeZoneData &rhs)
00827 {
00828     d->TZ = rhs.d->TZ;
00829     d->abbreviations = rhs.d->abbreviations;
00830     return *this;
00831 }
00832 
00833 KTimeZoneData *KSystemTimeZoneData::clone() const
00834 {
00835     return new KSystemTimeZoneData(*this);
00836 }
00837 
00838 QList<QByteArray> KSystemTimeZoneData::abbreviations() const
00839 {
00840     return d->abbreviations;
00841 }
00842 
00843 QByteArray KSystemTimeZoneData::abbreviation(const QDateTime &utcDateTime) const
00844 {
00845     QByteArray abbr;
00846     if (utcDateTime.timeSpec() != Qt::UTC)
00847         return abbr;
00848     time_t t = utcDateTime.toTime_t();
00849     if (t != KTimeZone::InvalidTime_t)
00850     {
00851         KSystemTimeZoneSourcePrivate::setTZ(d->TZ);   // make this time zone the current local time zone
00852 
00853         /* Use tm.tm_zone if available because it returns the abbreviation
00854          * in use at the time specified. Otherwise, use tzname[] which
00855          * returns the appropriate current abbreviation instead.
00856          */
00857 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS
00858         tm tmtime;
00859         if (localtime_r(&t, &tmtime))
00860 #ifdef HAVE_STRUCT_TM_TM_ZONE
00861             abbr = tmtime.tm_zone;
00862 #else
00863             abbr = tzname[(tmtime.tm_isdst > 0) ? 1 : 0];
00864 #endif
00865 #else
00866         tm *tmtime = localtime(&t);
00867         if (tmtime)
00868 #ifdef HAVE_STRUCT_TM_TM_ZONE
00869             abbr = tmtime->tm_zone;
00870 #else
00871             abbr = tzname[(tmtime->tm_isdst > 0) ? 1 : 0];
00872 #endif
00873 #endif
00874         KSystemTimeZoneSourcePrivate::restoreTZ();   // restore the original local time zone if necessary
00875     }
00876     return abbr;
00877 }
00878 
00879 QList<int> KSystemTimeZoneData::utcOffsets() const
00880 {
00881     return QList<int>();
00882 }
00883 

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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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