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

KDECore

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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.5
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