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