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
KDE 4.6 API Reference