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 const 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 const QRegExp lineSeparator(QLatin1String("[ \t]")); 00448 const QRegExp ordinateSeparator(QLatin1String("[+-]")); 00449 if (!m_source) 00450 m_source = new KSystemTimeZoneSource; 00451 while (!str.atEnd()) 00452 { 00453 const QString line = str.readLine(); 00454 if (line.isEmpty() || line[0] == QLatin1Char('#')) 00455 continue; 00456 QStringList tokens = KStringHandler::perlSplit(lineSeparator, line, 4); 00457 const 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 const 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 const float latitude = convertCoordinate(tokens[1].left(i)); 00473 const 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 // Note: KTzfileTimeZone is used in preference to KSystemTimeZone because of 00483 // the large overhead incurred by tzset() - see KSystemTimeZones class 00484 // description for details. 00485 const KTzfileTimeZone tz(tzfileSource(), tokens[2], tokens[0], latitude, longitude, (n > 3 ? tokens[3] : QString())); 00486 if (update) 00487 { 00488 // Update the existing collection with the new zone definition 00489 newZones += tz.name(); 00490 KTimeZone oldTz = zone(tz.name()); 00491 if (oldTz.isValid()) 00492 oldTz.updateBase(tz); // the zone previously existed, so update its definition 00493 else 00494 add(tz); // the zone didn't previously exist, so add it 00495 } 00496 else 00497 add(tz); 00498 } 00499 f.close(); 00500 00501 if (update) 00502 { 00503 // Remove any zones from the collection which no longer exist 00504 const ZoneMap oldZones = zones(); 00505 for (ZoneMap::ConstIterator it = oldZones.constBegin(); it != oldZones.constEnd(); ++it) 00506 { 00507 if (newZones.indexOf(it.key()) < 0) 00508 remove(it.value()); 00509 } 00510 } 00511 } 00512 00516 float KSystemTimeZonesPrivate::convertCoordinate(const QString &coordinate) 00517 { 00518 int value = coordinate.toInt(); 00519 int degrees = 0; 00520 int minutes = 0; 00521 int seconds = 0; 00522 00523 if (coordinate.length() > 6) 00524 { 00525 degrees = value / 10000; 00526 value -= degrees * 10000; 00527 minutes = value / 100; 00528 value -= minutes * 100; 00529 seconds = value; 00530 } 00531 else 00532 { 00533 degrees = value / 100; 00534 value -= degrees * 100; 00535 minutes = value; 00536 } 00537 value = degrees * 3600 + minutes * 60 + seconds; 00538 return value / 3600.0; 00539 } 00540 #endif 00541 00542 00543 /******************************************************************************/ 00544 00545 00546 KSystemTimeZoneBackend::KSystemTimeZoneBackend(KSystemTimeZoneSource *source, const QString &name, 00547 const QString &countryCode, float latitude, float longitude, const QString &comment) 00548 : KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment) 00549 {} 00550 00551 KSystemTimeZoneBackend::~KSystemTimeZoneBackend() 00552 {} 00553 00554 KTimeZoneBackend *KSystemTimeZoneBackend::clone() const 00555 { 00556 return new KSystemTimeZoneBackend(*this); 00557 } 00558 00559 QByteArray KSystemTimeZoneBackend::type() const 00560 { 00561 return "KSystemTimeZone"; 00562 } 00563 00564 int KSystemTimeZoneBackend::offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const 00565 { 00566 if (!caller->isValid() || !zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime) 00567 return 0; 00568 // Make this time zone the current local time zone 00569 const QByteArray originalZone = qgetenv("TZ"); // save the original local time zone 00570 QByteArray tz = caller->name().toUtf8(); 00571 tz.prepend(":"); 00572 const bool change = (tz != originalZone); 00573 if (change) 00574 { 00575 ::setenv("TZ", tz, 1); 00576 ::tzset(); 00577 } 00578 00579 // Convert zone time to UTC, and then get the offset to UTC 00580 tm tmtime; 00581 tmtime.tm_sec = zoneDateTime.time().second(); 00582 tmtime.tm_min = zoneDateTime.time().minute(); 00583 tmtime.tm_hour = zoneDateTime.time().hour(); 00584 tmtime.tm_mday = zoneDateTime.date().day(); 00585 tmtime.tm_mon = zoneDateTime.date().month() - 1; 00586 tmtime.tm_year = zoneDateTime.date().year() - 1900; 00587 tmtime.tm_isdst = -1; 00588 const time_t t = mktime(&tmtime); 00589 int offset1 = (t == (time_t)-1) ? KTimeZone::InvalidOffset : gmtoff(t); 00590 if (secondOffset) 00591 { 00592 int offset2 = offset1; 00593 if (t != (time_t)-1) 00594 { 00595 // Check if there is a backward DST change near to this time, by 00596 // checking if the UTC offset is different 1 hour later or earlier. 00597 // ASSUMPTION: DST SHIFTS ARE NEVER GREATER THAN 1 HOUR. 00598 const int maxShift = 3600; 00599 offset2 = gmtoff(t + maxShift); 00600 if (offset2 < offset1) 00601 { 00602 // There is a backward DST shift during the following hour 00603 if (offset1 - offset2 < maxShift) 00604 offset2 = gmtoff(t + (offset1 - offset2)); 00605 } 00606 else if ((offset2 = gmtoff(t - maxShift)) > offset1) 00607 { 00608 // There is a backward DST shift during the previous hour 00609 if (offset2 - offset1 < maxShift) 00610 offset2 = gmtoff(t - (offset2 - offset1)); 00611 // Put UTC offsets into the correct order 00612 const int o = offset1; 00613 offset1 = offset2; 00614 offset2 = o; 00615 } 00616 else offset2 = offset1; 00617 } 00618 *secondOffset = offset2; 00619 } 00620 00621 if (change) 00622 { 00623 // Restore the original local time zone 00624 if (originalZone.isEmpty()) 00625 ::unsetenv("TZ"); 00626 else 00627 ::setenv("TZ", originalZone, 1); 00628 ::tzset(); 00629 } 00630 return offset1; 00631 } 00632 00633 int KSystemTimeZoneBackend::offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const 00634 { 00635 return offset(caller, KTimeZone::toTime_t(utcDateTime)); 00636 } 00637 00638 int KSystemTimeZoneBackend::offset(const KTimeZone *caller, time_t t) const 00639 { 00640 if (!caller->isValid() || t == KTimeZone::InvalidTime_t) 00641 return 0; 00642 00643 // Make this time zone the current local time zone 00644 const QByteArray originalZone = qgetenv("TZ"); // save the original local time zone 00645 QByteArray tz = caller->name().toUtf8(); 00646 tz.prepend(":"); 00647 const bool change = (tz != originalZone); 00648 if (change) 00649 { 00650 ::setenv("TZ", tz, 1); 00651 ::tzset(); 00652 } 00653 00654 const int secs = gmtoff(t); 00655 00656 if (change) 00657 { 00658 // Restore the original local time zone 00659 if (originalZone.isEmpty()) 00660 ::unsetenv("TZ"); 00661 else 00662 ::setenv("TZ", originalZone, 1); 00663 ::tzset(); 00664 } 00665 return secs; 00666 } 00667 00668 bool KSystemTimeZoneBackend::isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const 00669 { 00670 return isDst(caller, KTimeZone::toTime_t(utcDateTime)); 00671 } 00672 00673 bool KSystemTimeZoneBackend::isDst(const KTimeZone *caller, time_t t) const 00674 { 00675 Q_UNUSED(caller) 00676 if (t != (time_t)-1) 00677 { 00678 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS 00679 tm tmtime; 00680 if (localtime_r(&t, &tmtime)) 00681 return tmtime.tm_isdst > 0; 00682 #else 00683 const tm *tmtime = localtime(&t); 00684 if (tmtime) 00685 return tmtime->tm_isdst > 0; 00686 #endif 00687 } 00688 return false; 00689 } 00690 00691 00692 /******************************************************************************/ 00693 00694 KSystemTimeZone::KSystemTimeZone(KSystemTimeZoneSource *source, const QString &name, 00695 const QString &countryCode, float latitude, float longitude, const QString &comment) 00696 : KTimeZone(new KSystemTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)) 00697 { 00698 } 00699 00700 KSystemTimeZone::~KSystemTimeZone() 00701 { 00702 } 00703 00704 00705 /******************************************************************************/ 00706 00707 class KSystemTimeZoneDataPrivate 00708 { 00709 public: 00710 QByteArray TZ; 00711 QList<QByteArray> abbreviations; 00712 }; 00713 00714 00715 // N.B. KSystemTimeZoneSourcePrivate is also used by KSystemTimeZoneData 00716 class KSystemTimeZoneSourcePrivate 00717 { 00718 public: 00719 static void setTZ(const QByteArray &zoneName); 00720 static void restoreTZ(); 00721 static QByteArray savedTZ; // temporary value of TZ environment variable saved by setTZ() 00722 static QByteArray originalTZ; // saved value of TZ environment variable during multiple parse() calls 00723 static bool TZIsSaved; // TZ has been saved in savedTZ 00724 static bool multiParse; // true if performing multiple parse() calls 00725 }; 00726 00727 QByteArray KSystemTimeZoneSourcePrivate::savedTZ; 00728 QByteArray KSystemTimeZoneSourcePrivate::originalTZ; 00729 bool KSystemTimeZoneSourcePrivate::TZIsSaved = false; 00730 bool KSystemTimeZoneSourcePrivate::multiParse = false; 00731 00732 00733 KSystemTimeZoneSource::KSystemTimeZoneSource() 00734 : d(0) 00735 // : d(new KSystemTimeZoneSourcePrivate) 00736 { 00737 } 00738 00739 KSystemTimeZoneSource::~KSystemTimeZoneSource() 00740 { 00741 // delete d; 00742 } 00743 00744 KTimeZoneData* KSystemTimeZoneSource::parse(const KTimeZone &zone) const 00745 { 00746 const QByteArray tz = zone.name().toUtf8(); 00747 KSystemTimeZoneSourcePrivate::setTZ(tz); // make this time zone the current local time zone 00748 00749 tzset(); // initialize the tzname array 00750 KSystemTimeZoneData* data = new KSystemTimeZoneData; 00751 data->d->TZ = tz; 00752 data->d->abbreviations.append(tzname[0]); 00753 data->d->abbreviations.append(tzname[1]); 00754 00755 // There is no easy means to access the sequence of daylight savings time 00756 // changes, or leap seconds adjustments, so leave that data empty. 00757 00758 KSystemTimeZoneSourcePrivate::restoreTZ(); // restore the original local time zone if necessary 00759 return data; 00760 } 00761 00762 void KSystemTimeZoneSource::startParseBlock() 00763 { 00764 KSystemTimeZoneSourcePrivate::originalTZ = qgetenv("TZ"); // save the original local time zone 00765 KSystemTimeZoneSourcePrivate::multiParse = true; 00766 } 00767 00768 void KSystemTimeZoneSource::endParseBlock() 00769 { 00770 if (KSystemTimeZoneSourcePrivate::multiParse) 00771 { 00772 // Restore the original local time zone 00773 if (KSystemTimeZoneSourcePrivate::originalTZ.isEmpty()) 00774 ::unsetenv("TZ"); 00775 else 00776 ::setenv("TZ", KSystemTimeZoneSourcePrivate::originalTZ, 1); 00777 ::tzset(); 00778 KSystemTimeZoneSourcePrivate::multiParse = false; 00779 } 00780 } 00781 00782 // Set the TZ environment variable to the specified time zone, 00783 // saving its current setting first if necessary. 00784 void KSystemTimeZoneSourcePrivate::setTZ(const QByteArray &zoneName) 00785 { 00786 QByteArray tz = zoneName; 00787 tz.prepend(":"); 00788 bool setTZ = multiParse; 00789 if (!setTZ) 00790 { 00791 savedTZ = qgetenv("TZ"); // save the original local time zone 00792 TZIsSaved = true; 00793 setTZ = (tz != savedTZ); 00794 } 00795 if (setTZ) 00796 { 00797 ::setenv("TZ", tz, 1); 00798 ::tzset(); 00799 } 00800 } 00801 00802 // Restore the TZ environment variable if it was saved by setTz() 00803 void KSystemTimeZoneSourcePrivate::restoreTZ() 00804 { 00805 if (TZIsSaved) 00806 { 00807 if (savedTZ.isEmpty()) 00808 ::unsetenv("TZ"); 00809 else 00810 ::setenv("TZ", savedTZ, 1); 00811 ::tzset(); 00812 TZIsSaved = false; 00813 } 00814 } 00815 00816 00817 /******************************************************************************/ 00818 00819 KSystemTimeZoneData::KSystemTimeZoneData() 00820 : d(new KSystemTimeZoneDataPrivate) 00821 { } 00822 00823 KSystemTimeZoneData::KSystemTimeZoneData(const KSystemTimeZoneData &rhs) 00824 : KTimeZoneData(), 00825 d(new KSystemTimeZoneDataPrivate) 00826 { 00827 operator=(rhs); 00828 } 00829 00830 KSystemTimeZoneData::~KSystemTimeZoneData() 00831 { 00832 delete d; 00833 } 00834 00835 KSystemTimeZoneData &KSystemTimeZoneData::operator=(const KSystemTimeZoneData &rhs) 00836 { 00837 d->TZ = rhs.d->TZ; 00838 d->abbreviations = rhs.d->abbreviations; 00839 return *this; 00840 } 00841 00842 KTimeZoneData *KSystemTimeZoneData::clone() const 00843 { 00844 return new KSystemTimeZoneData(*this); 00845 } 00846 00847 QList<QByteArray> KSystemTimeZoneData::abbreviations() const 00848 { 00849 return d->abbreviations; 00850 } 00851 00852 QByteArray KSystemTimeZoneData::abbreviation(const QDateTime &utcDateTime) const 00853 { 00854 QByteArray abbr; 00855 if (utcDateTime.timeSpec() != Qt::UTC) 00856 return abbr; 00857 time_t t = utcDateTime.toTime_t(); 00858 if (t != KTimeZone::InvalidTime_t) 00859 { 00860 KSystemTimeZoneSourcePrivate::setTZ(d->TZ); // make this time zone the current local time zone 00861 00862 /* Use tm.tm_zone if available because it returns the abbreviation 00863 * in use at the time specified. Otherwise, use tzname[] which 00864 * returns the appropriate current abbreviation instead. 00865 */ 00866 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS 00867 tm tmtime; 00868 if (localtime_r(&t, &tmtime)) 00869 #ifdef HAVE_STRUCT_TM_TM_ZONE 00870 abbr = tmtime.tm_zone; 00871 #else 00872 abbr = tzname[(tmtime.tm_isdst > 0) ? 1 : 0]; 00873 #endif 00874 #else 00875 const tm *tmtime = localtime(&t); 00876 if (tmtime) 00877 #ifdef HAVE_STRUCT_TM_TM_ZONE 00878 abbr = tmtime->tm_zone; 00879 #else 00880 abbr = tzname[(tmtime->tm_isdst > 0) ? 1 : 0]; 00881 #endif 00882 #endif 00883 KSystemTimeZoneSourcePrivate::restoreTZ(); // restore the original local time zone if necessary 00884 } 00885 return abbr; 00886 } 00887 00888 QList<int> KSystemTimeZoneData::utcOffsets() const 00889 { 00890 return QList<int>(); 00891 } 00892
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:49:32 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:49:32 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.