Warning, file /frameworks/kdelibs4support/src/kdecore/ksystemtimezone.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 This file is part of the KDE libraries 0003 Copyright (c) 2005-2010 David Jarvie <djarvie@kde.org> 0004 Copyright (c) 2005 S.R.Haque <srhaque@iee.org>. 0005 0006 This library is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Library General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 Boston, MA 02110-1301, USA. 0020 */ 0021 0022 // This file requires HAVE_STRUCT_TM_TM_ZONE to be defined if struct tm member tm_zone is available. 0023 // This file requires HAVE_TM_GMTOFF to be defined if struct tm member tm_gmtoff is available. 0024 0025 #include "moc_ksystemtimezone.cpp" 0026 0027 #include <config-date.h> 0028 0029 #if HAVE_SYS_TIME_H 0030 #include <sys/time.h> 0031 #endif 0032 #if HAVE_TIME_H 0033 #include <time.h> 0034 #endif 0035 #include <climits> 0036 #include <cstdlib> 0037 0038 #include <QDebug> 0039 #include <QCoreApplication> 0040 #include <QFile> 0041 #include <QFileInfo> 0042 #include <QDir> 0043 #include <QRegExp> 0044 #include <QStringList> 0045 #include <QTextStream> 0046 #include <QDBusConnection> 0047 #include <QDBusInterface> 0048 #include <QDBusConnectionInterface> 0049 #include <QDBusReply> 0050 0051 #include <kconfig.h> 0052 #include <klocalizedstring.h> 0053 #include <kstringhandler.h> 0054 #include <kconfiggroup.h> 0055 #include "ktzfiletimezone.h" 0056 #ifdef Q_OS_WIN 0057 #include "ktimezone_win.h" 0058 #endif 0059 0060 #define KTIMEZONED_DBUS_IFACE "org.kde.KTimeZoned" 0061 0062 /* Return the offset to UTC in the current time zone at the specified UTC time. 0063 * The thread-safe function localtime_r() is used in preference if available. 0064 */ 0065 int gmtoff(time_t t) 0066 { 0067 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS 0068 tm tmtime; 0069 if (!localtime_r(&t, &tmtime)) { 0070 return 0; 0071 } 0072 #if HAVE_TM_GMTOFF 0073 return tmtime.tm_gmtoff; 0074 #else 0075 int lwday = tmtime.tm_wday; 0076 int lt = 3600 * tmtime.tm_hour + 60 * tmtime.tm_min + tmtime.tm_sec; 0077 if (!gmtime_r(&t, &tmtime)) { 0078 return 0; 0079 } 0080 int uwday = tmtime.tm_wday; 0081 int ut = 3600 * tmtime.tm_hour + 60 * tmtime.tm_min + tmtime.tm_sec; 0082 #endif 0083 #else 0084 tm *tmtime = localtime(&t); 0085 if (!tmtime) { 0086 return 0; 0087 } 0088 #if HAVE_TM_GMTOFF 0089 return tmtime->tm_gmtoff; 0090 #else 0091 int lwday = tmtime->tm_wday; 0092 int lt = 3600 * tmtime->tm_hour + 60 * tmtime->tm_min + tmtime->tm_sec; 0093 tmtime = gmtime(&t); 0094 int uwday = tmtime->tm_wday; 0095 int ut = 3600 * tmtime->tm_hour + 60 * tmtime->tm_min + tmtime->tm_sec; 0096 #endif 0097 #endif 0098 #if ! HAVE_TM_GMTOFF 0099 if (lwday != uwday) { 0100 // Adjust for different day 0101 if (lwday == uwday + 1 || (lwday == 0 && uwday == 6)) { 0102 lt += 24 * 3600; 0103 } else { 0104 lt -= 24 * 3600; 0105 } 0106 } 0107 return lt - ut; 0108 #endif 0109 } 0110 0111 /******************************************************************************/ 0112 0113 class KSystemTimeZonesPrivate : public KTimeZones 0114 { 0115 public: 0116 static KSystemTimeZonesPrivate *instance(); 0117 static KTzfileTimeZoneSource *tzfileSource(); 0118 static void setLocalZone(); 0119 static void cleanup(); 0120 static void readConfig(bool init); 0121 #ifdef Q_OS_WIN 0122 static void updateTimezoneInformation() 0123 { 0124 instance()->updateTimezoneInformation(true); 0125 } 0126 #else 0127 static void updateZonetab() 0128 { 0129 instance()->readZoneTab(true); 0130 } 0131 #endif 0132 0133 static KTimeZone m_localZone; 0134 static QString m_localZoneName; 0135 static QString m_zoneinfoDir; 0136 static QString m_zonetab; 0137 static KSystemTimeZoneSource *m_source; 0138 static bool m_ktimezonedError; 0139 0140 private: 0141 KSystemTimeZonesPrivate() {} 0142 #ifdef Q_OS_WIN 0143 void updateTimezoneInformation(bool update); 0144 #else 0145 void readZoneTab(bool update); 0146 static float convertCoordinate(const QString &coordinate); 0147 #endif 0148 0149 static KSystemTimeZones *m_parent; 0150 static KSystemTimeZonesPrivate *m_instance; 0151 static KTzfileTimeZoneSource *m_tzfileSource; 0152 }; 0153 0154 KTimeZone KSystemTimeZonesPrivate::m_localZone; 0155 QString KSystemTimeZonesPrivate::m_localZoneName; 0156 QString KSystemTimeZonesPrivate::m_zoneinfoDir; 0157 QString KSystemTimeZonesPrivate::m_zonetab; 0158 KSystemTimeZoneSource *KSystemTimeZonesPrivate::m_source = nullptr; 0159 bool KSystemTimeZonesPrivate::m_ktimezonedError = true; 0160 KTzfileTimeZoneSource *KSystemTimeZonesPrivate::m_tzfileSource = nullptr; 0161 KSystemTimeZones *KSystemTimeZonesPrivate::m_parent = nullptr; 0162 KSystemTimeZonesPrivate *KSystemTimeZonesPrivate::m_instance = nullptr; 0163 0164 KTzfileTimeZoneSource *KSystemTimeZonesPrivate::tzfileSource() 0165 { 0166 if (!m_tzfileSource) { 0167 instance(); 0168 m_tzfileSource = new KTzfileTimeZoneSource(m_zoneinfoDir); 0169 } 0170 return m_tzfileSource; 0171 } 0172 0173 // for the benefit of KTimeZoneTest 0174 KDELIBS4SUPPORT_EXPORT void k_system_time_zone_private_reset_config() 0175 { 0176 // Remove any old zones from the collection 0177 const KTimeZones::ZoneMap oldZones = KSystemTimeZonesPrivate::instance()->zones(); 0178 for (KTimeZones::ZoneMap::ConstIterator it = oldZones.constBegin(); it != oldZones.constEnd(); ++it) { 0179 KSystemTimeZonesPrivate::instance()->remove(it.value()); 0180 } 0181 0182 // Read new config file 0183 KSystemTimeZonesPrivate::readConfig(false); 0184 } 0185 0186 #ifndef NDEBUG 0187 Q_GLOBAL_STATIC(KTimeZone, simulatedLocalZone) 0188 #endif 0189 0190 KSystemTimeZones::KSystemTimeZones() 0191 : d(nullptr) 0192 { 0193 QDBusConnection dbus = QDBusConnection::sessionBus(); 0194 const QString dbusIface = QString::fromLatin1(KTIMEZONED_DBUS_IFACE); 0195 dbus.connect(QString(), QString(), dbusIface, QLatin1String("timeZoneChanged"), this, SLOT(configChanged())); 0196 dbus.connect(QString(), QString(), dbusIface, QLatin1String("timeZoneDatabaseUpdated"), this, SLOT(zonetabChanged(QString))); 0197 // No need to connect to definitionChanged() - see comments in zoneDefinitionChanged() 0198 //dbus.connect(QString(), QString(), dbusIface, QLatin1String("definitionChanged"), this, SLOT(zoneDefinitionChanged(QString))); 0199 } 0200 0201 KSystemTimeZones::~KSystemTimeZones() 0202 { 0203 } 0204 0205 KTimeZone KSystemTimeZones::local() 0206 { 0207 #ifndef NDEBUG 0208 if (simulatedLocalZone()->isValid()) { 0209 return *simulatedLocalZone(); 0210 } 0211 #endif 0212 KSystemTimeZonesPrivate::instance(); 0213 return KSystemTimeZonesPrivate::m_localZone; 0214 } 0215 0216 KTimeZone KSystemTimeZones::realLocalZone() 0217 { 0218 KSystemTimeZonesPrivate::instance(); 0219 return KSystemTimeZonesPrivate::m_localZone; 0220 } 0221 0222 void KSystemTimeZones::setLocalZone(const KTimeZone &tz) 0223 { 0224 Q_UNUSED(tz); 0225 #ifndef NDEBUG 0226 *simulatedLocalZone() = tz; 0227 #endif 0228 } 0229 0230 bool KSystemTimeZones::isSimulated() 0231 { 0232 #ifndef NDEBUG 0233 return simulatedLocalZone()->isValid(); 0234 #else 0235 return false; 0236 #endif 0237 } 0238 0239 QString KSystemTimeZones::zoneinfoDir() 0240 { 0241 KSystemTimeZonesPrivate::instance(); 0242 return KSystemTimeZonesPrivate::m_zoneinfoDir; 0243 } 0244 0245 bool KSystemTimeZones::isTimeZoneDaemonAvailable() 0246 { 0247 KSystemTimeZonesPrivate::instance(); 0248 return !KSystemTimeZonesPrivate::m_ktimezonedError; 0249 } 0250 0251 KTimeZones *KSystemTimeZones::timeZones() 0252 { 0253 return KSystemTimeZonesPrivate::instance(); 0254 } 0255 0256 KTimeZone KSystemTimeZones::readZone(const QString &name) 0257 { 0258 return KTzfileTimeZone(KSystemTimeZonesPrivate::tzfileSource(), name); 0259 } 0260 0261 const KTimeZones::ZoneMap KSystemTimeZones::zones() 0262 { 0263 return KSystemTimeZonesPrivate::instance()->zones(); 0264 } 0265 0266 KTimeZone KSystemTimeZones::zone(const QString &name) 0267 { 0268 return KSystemTimeZonesPrivate::instance()->zone(name); 0269 } 0270 0271 void KSystemTimeZones::configChanged() 0272 { 0273 //qDebug() << "KSystemTimeZones::configChanged()"; 0274 KSystemTimeZonesPrivate::m_ktimezonedError = false; 0275 KSystemTimeZonesPrivate::readConfig(false); 0276 } 0277 0278 void KSystemTimeZones::zonetabChanged(const QString &zonetab) 0279 { 0280 Q_UNUSED(zonetab) 0281 #ifndef Q_OS_WIN 0282 //qDebug() << "KSystemTimeZones::zonetabChanged()"; 0283 KSystemTimeZonesPrivate::m_ktimezonedError = false; 0284 // Re-read zone.tab and update our collection, removing any deleted 0285 // zones and adding any new zones. 0286 KSystemTimeZonesPrivate::updateZonetab(); 0287 #endif 0288 } 0289 0290 void KSystemTimeZones::zoneDefinitionChanged(const QString &zone) 0291 { 0292 // No need to do anything when the definition (as opposed to the 0293 // identity) of the local zone changes, since the updated details 0294 // will always be accessed by the system library calls to fetch 0295 // local zone information. 0296 Q_UNUSED(zone) 0297 KSystemTimeZonesPrivate::m_ktimezonedError = false; 0298 } 0299 0300 // Perform initialization, create the unique KSystemTimeZones instance, 0301 // whose only function is to receive D-Bus signals from KTimeZoned, 0302 // and create the unique KSystemTimeZonesPrivate instance. 0303 KSystemTimeZonesPrivate *KSystemTimeZonesPrivate::instance() 0304 { 0305 if (!m_instance) { 0306 m_instance = new KSystemTimeZonesPrivate; 0307 0308 // A KSystemTimeZones instance is required only to catch D-Bus signals. 0309 m_parent = new KSystemTimeZones; 0310 // Ensure that the KDED time zones module has initialized. The call loads the module on demand. 0311 QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); 0312 if (!bus->isServiceRegistered(QLatin1String("org.kde.kded5"))) { 0313 // kded isn't even running: start it 0314 QDBusReply<void> reply = bus->startService(QLatin1String("org.kde.kded5")); 0315 if (!reply.isValid()) { 0316 qWarning() << "Couldn't start kded5 from org.kde.kded5.service:" << reply.error(); 0317 } 0318 } 0319 const QString dbusIface = QString::fromLatin1(KTIMEZONED_DBUS_IFACE); 0320 QDBusInterface *ktimezoned = new QDBusInterface(QLatin1String("org.kde.kded5"), QLatin1String("/modules/ktimezoned"), dbusIface); 0321 QDBusReply<void> reply = ktimezoned->call(QLatin1String("initialize"), false); 0322 m_ktimezonedError = !reply.isValid(); 0323 if (m_ktimezonedError) { 0324 qCritical() << "KSystemTimeZones: ktimezoned initialize() D-Bus call failed: " << reply.error().message() << endl; 0325 } 0326 //qDebug()<<"instance(): ... initialised"; 0327 delete ktimezoned; 0328 0329 // Read the time zone config written by ktimezoned 0330 readConfig(true); 0331 0332 // Go read the database. 0333 #ifdef Q_OS_WIN 0334 // On Windows, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones 0335 // is the place to look. The TZI binary value is the TIME_ZONE_INFORMATION structure. 0336 m_instance->updateTimezoneInformation(false); 0337 #else 0338 // For Unix, read zone.tab. 0339 if (!m_zonetab.isEmpty()) { 0340 m_instance->readZoneTab(false); 0341 } 0342 #endif 0343 setLocalZone(); 0344 0345 qAddPostRoutine(KSystemTimeZonesPrivate::cleanup); 0346 } 0347 return m_instance; 0348 } 0349 0350 void KSystemTimeZonesPrivate::readConfig(bool init) 0351 { 0352 KConfig config(QLatin1String("ktimezonedrc")); 0353 if (!init) { 0354 config.reparseConfiguration(); 0355 } 0356 KConfigGroup group(&config, "TimeZones"); 0357 if (!group.exists()) { 0358 qCritical() << "No time zone information obtained from ktimezoned"; 0359 m_ktimezonedError = true; 0360 } 0361 m_zoneinfoDir = group.readEntry("ZoneinfoDir"); 0362 m_zonetab = group.readEntry("Zonetab"); 0363 m_localZoneName = group.readEntry("LocalZone"); 0364 if (m_zoneinfoDir.length() > 1 && m_zoneinfoDir.endsWith(QLatin1Char('/'))) { 0365 m_zoneinfoDir.truncate(m_zoneinfoDir.length() - 1); // strip trailing '/' 0366 } 0367 if (!init) { 0368 #ifdef Q_OS_WIN 0369 updateTimezoneInformation(); 0370 #else 0371 updateZonetab(); 0372 #endif 0373 setLocalZone(); 0374 } 0375 //qDebug() << "readConfig(): local zone=" << m_localZoneName; 0376 } 0377 0378 void KSystemTimeZonesPrivate::setLocalZone() 0379 { 0380 // Check whether the local zone name is set at all 0381 if (m_localZoneName.isEmpty()) { 0382 m_localZone = KTimeZone::utc(); 0383 return; 0384 } 0385 0386 // Check if the zone name is a known zone 0387 if (m_instance) { 0388 m_localZone = m_instance->zone(m_localZoneName); 0389 if (m_localZone.isValid()) { 0390 return; 0391 } 0392 } 0393 0394 QString filename; 0395 if (m_localZoneName.startsWith(QLatin1Char('/'))) { 0396 // The time zone is specified by a file outside the zoneinfo directory 0397 filename = m_localZoneName; 0398 } else { 0399 // The zone name is specified by a relative file name in zoneinfo directory. 0400 filename = m_zoneinfoDir + QLatin1Char('/') + m_localZoneName; 0401 } 0402 0403 // Verify that the time zone file actually exists 0404 if (!QFile::exists(filename)) { 0405 m_localZone = KTimeZone::utc(); 0406 return; 0407 } 0408 0409 // Parse the specified time zone data file 0410 QString zonename = filename; 0411 if (zonename.startsWith(m_zoneinfoDir + QLatin1Char('/'))) { 0412 zonename = zonename.mid(m_zoneinfoDir.length() + 1); 0413 } 0414 m_localZone = KTzfileTimeZone(KSystemTimeZonesPrivate::tzfileSource(), zonename); 0415 0416 // Add the new time zone to the list of known time zones 0417 if (m_instance) { 0418 const KTimeZone oldzone = m_instance->zone(zonename); 0419 if (!oldzone.isValid() || oldzone.type() != "KTzfileTimeZone") { 0420 m_instance->remove(oldzone); 0421 m_instance->add(m_localZone); 0422 } 0423 } 0424 } 0425 0426 void KSystemTimeZonesPrivate::cleanup() 0427 { 0428 delete m_parent; 0429 delete m_instance; 0430 delete m_source; 0431 delete m_tzfileSource; 0432 } 0433 0434 #ifdef Q_OS_WIN 0435 0436 void KSystemTimeZonesPrivate::updateTimezoneInformation(bool update) 0437 { 0438 if (!m_source) { 0439 m_source = new KSystemTimeZoneSourceWindows; 0440 } 0441 QStringList newZones; 0442 Q_FOREACH (const QString &tz, KSystemTimeZoneWindows::listTimeZones()) { 0443 // const std::wstring wstr = tz.toStdWString(); 0444 // const KTimeZone info = make_time_zone( wstr.c_str() ); 0445 KSystemTimeZoneWindows stz(m_source, tz); 0446 if (update) { 0447 // Update the existing collection with the new zone definition 0448 newZones += stz.name(); 0449 KTimeZone oldTz = zone(stz.name()); 0450 if (oldTz.isValid()) { 0451 oldTz.updateBase(stz); // the zone previously existed, so update its definition 0452 } else { 0453 add(stz); // the zone didn't previously exist, so add it 0454 } 0455 } else { 0456 add(stz); 0457 } 0458 } 0459 if (update) { 0460 // Remove any zones from the collection which no longer exist 0461 const ZoneMap oldZones = zones(); 0462 for (ZoneMap::const_iterator it = oldZones.begin(); it != oldZones.end(); ++it) { 0463 if (newZones.indexOf(it.key()) < 0) { 0464 remove(it.value()); 0465 } 0466 } 0467 } 0468 } 0469 0470 #else 0471 /* 0472 * Find the location of the zoneinfo files and store in mZoneinfoDir. 0473 * Parse zone.tab and for each time zone, create a KSystemTimeZone instance. 0474 */ 0475 void KSystemTimeZonesPrivate::readZoneTab(bool update) 0476 { 0477 //qDebug() << "readZoneTab(" << m_zonetab<< ")"; 0478 QStringList newZones; 0479 QFile f; 0480 f.setFileName(m_zonetab); 0481 if (!f.open(QIODevice::ReadOnly)) { 0482 return; 0483 } 0484 QTextStream str(&f); 0485 const QRegExp lineSeparator(QLatin1String("[ \t]")); 0486 const QRegExp ordinateSeparator(QLatin1String("[+-]")); 0487 if (!m_source) { 0488 m_source = new KSystemTimeZoneSource; 0489 } 0490 while (!str.atEnd()) { 0491 const QString line = str.readLine(); 0492 if (line.isEmpty() || line[0] == QLatin1Char('#')) { 0493 continue; 0494 } 0495 QStringList tokens = KStringHandler::perlSplit(lineSeparator, line, 4); 0496 const int n = tokens.count(); 0497 if (n < 3) { 0498 qCritical() << "readZoneTab(): invalid record: " << line << endl; 0499 continue; 0500 } 0501 0502 // Got three tokens. Now check for two ordinates plus first one is "". 0503 const int i = tokens[1].indexOf(ordinateSeparator, 1); 0504 if (i < 0) { 0505 qCritical() << "readZoneTab() " << tokens[2] << ": invalid coordinates: " << tokens[1] << endl; 0506 continue; 0507 } 0508 0509 const float latitude = convertCoordinate(tokens[1].left(i)); 0510 const float longitude = convertCoordinate(tokens[1].mid(i)); 0511 0512 // Add entry to list. 0513 if (tokens[0] == QLatin1String("??")) { 0514 tokens[0] = QString::fromLatin1(""); 0515 } 0516 // Solaris sets the empty Comments field to '-', making it not empty. 0517 // Clean it up. 0518 if (n > 3 && tokens[3] == QLatin1String("-")) { 0519 tokens[3] = QString::fromLatin1(""); 0520 } 0521 // Note: KTzfileTimeZone is used in preference to KSystemTimeZone because of 0522 // the large overhead incurred by tzset() - see KSystemTimeZones class 0523 // description for details. 0524 const KTzfileTimeZone tz(tzfileSource(), tokens[2], tokens[0], latitude, longitude, (n > 3 ? tokens[3] : QString())); 0525 if (update) { 0526 // Update the existing collection with the new zone definition 0527 newZones += tz.name(); 0528 KTimeZone oldTz = zone(tz.name()); 0529 if (oldTz.isValid()) { 0530 oldTz.updateBase(tz); // the zone previously existed, so update its definition 0531 } else { 0532 add(tz); // the zone didn't previously exist, so add it 0533 } 0534 } else { 0535 add(tz); 0536 } 0537 } 0538 f.close(); 0539 0540 if (update) { 0541 // Remove any zones from the collection which no longer exist 0542 const ZoneMap oldZones = zones(); 0543 for (ZoneMap::ConstIterator it = oldZones.constBegin(); it != oldZones.constEnd(); ++it) { 0544 if (newZones.indexOf(it.key()) < 0) { 0545 remove(it.value()); 0546 } 0547 } 0548 } 0549 } 0550 0551 /** 0552 * Convert sHHMM or sHHMMSS to a floating point number of degrees. 0553 */ 0554 float KSystemTimeZonesPrivate::convertCoordinate(const QString &coordinate) 0555 { 0556 int value = coordinate.toInt(); 0557 int degrees = 0; 0558 int minutes = 0; 0559 int seconds = 0; 0560 0561 if (coordinate.length() > 6) { 0562 degrees = value / 10000; 0563 value -= degrees * 10000; 0564 minutes = value / 100; 0565 value -= minutes * 100; 0566 seconds = value; 0567 } else { 0568 degrees = value / 100; 0569 value -= degrees * 100; 0570 minutes = value; 0571 } 0572 value = degrees * 3600 + minutes * 60 + seconds; 0573 return value / 3600.0; 0574 } 0575 #endif 0576 0577 /******************************************************************************/ 0578 0579 KSystemTimeZoneBackend::KSystemTimeZoneBackend(KSystemTimeZoneSource *source, const QString &name, 0580 const QString &countryCode, float latitude, float longitude, const QString &comment) 0581 : KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment) 0582 {} 0583 0584 KSystemTimeZoneBackend::~KSystemTimeZoneBackend() 0585 {} 0586 0587 KTimeZoneBackend *KSystemTimeZoneBackend::clone() const 0588 { 0589 return new KSystemTimeZoneBackend(*this); 0590 } 0591 0592 QByteArray KSystemTimeZoneBackend::type() const 0593 { 0594 return "KSystemTimeZone"; 0595 } 0596 0597 int KSystemTimeZoneBackend::offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const 0598 { 0599 if (!caller->isValid() || !zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime) { 0600 return 0; 0601 } 0602 // Make this time zone the current local time zone 0603 const QByteArray originalZone = qgetenv("TZ"); // save the original local time zone 0604 QByteArray tz = caller->name().toUtf8(); 0605 tz.prepend(":"); 0606 const bool change = (tz != originalZone); 0607 if (change) { 0608 qputenv("TZ", tz); 0609 ::tzset(); 0610 } 0611 0612 // Convert zone time to UTC, and then get the offset to UTC 0613 tm tmtime; 0614 tmtime.tm_sec = zoneDateTime.time().second(); 0615 tmtime.tm_min = zoneDateTime.time().minute(); 0616 tmtime.tm_hour = zoneDateTime.time().hour(); 0617 tmtime.tm_mday = zoneDateTime.date().day(); 0618 tmtime.tm_mon = zoneDateTime.date().month() - 1; 0619 tmtime.tm_year = zoneDateTime.date().year() - 1900; 0620 tmtime.tm_isdst = -1; 0621 const time_t t = mktime(&tmtime); 0622 int offset1 = (t == (time_t) - 1) ? KTimeZone::InvalidOffset : gmtoff(t); 0623 if (secondOffset) { 0624 int offset2 = offset1; 0625 if (t != (time_t) - 1) { 0626 // Check if there is a backward DST change near to this time, by 0627 // checking if the UTC offset is different 1 hour later or earlier. 0628 // ASSUMPTION: DST SHIFTS ARE NEVER GREATER THAN 1 HOUR. 0629 const int maxShift = 3600; 0630 offset2 = gmtoff(t + maxShift); 0631 if (offset2 < offset1) { 0632 // There is a backward DST shift during the following hour 0633 if (offset1 - offset2 < maxShift) { 0634 offset2 = gmtoff(t + (offset1 - offset2)); 0635 } 0636 } else if ((offset2 = gmtoff(t - maxShift)) > offset1) { 0637 // There is a backward DST shift during the previous hour 0638 if (offset2 - offset1 < maxShift) { 0639 offset2 = gmtoff(t - (offset2 - offset1)); 0640 } 0641 // Put UTC offsets into the correct order 0642 const int o = offset1; 0643 offset1 = offset2; 0644 offset2 = o; 0645 } else { 0646 offset2 = offset1; 0647 } 0648 } 0649 *secondOffset = offset2; 0650 } 0651 0652 if (change) { 0653 // Restore the original local time zone 0654 if (originalZone.isEmpty()) { 0655 ::unsetenv("TZ"); 0656 } else { 0657 qputenv("TZ", originalZone); 0658 } 0659 ::tzset(); 0660 } 0661 return offset1; 0662 } 0663 0664 int KSystemTimeZoneBackend::offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const 0665 { 0666 return offset(caller, KTimeZone::toTime_t(utcDateTime)); 0667 } 0668 0669 int KSystemTimeZoneBackend::offset(const KTimeZone *caller, time_t t) const 0670 { 0671 if (!caller->isValid() || t == KTimeZone::InvalidTime_t) { 0672 return 0; 0673 } 0674 0675 // Make this time zone the current local time zone 0676 const QByteArray originalZone = qgetenv("TZ"); // save the original local time zone 0677 QByteArray tz = caller->name().toUtf8(); 0678 tz.prepend(":"); 0679 const bool change = (tz != originalZone); 0680 if (change) { 0681 qputenv("TZ", tz); 0682 ::tzset(); 0683 } 0684 0685 const int secs = gmtoff(t); 0686 0687 if (change) { 0688 // Restore the original local time zone 0689 if (originalZone.isEmpty()) { 0690 ::unsetenv("TZ"); 0691 } else { 0692 qputenv("TZ", originalZone); 0693 } 0694 ::tzset(); 0695 } 0696 return secs; 0697 } 0698 0699 bool KSystemTimeZoneBackend::isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const 0700 { 0701 return isDst(caller, KTimeZone::toTime_t(utcDateTime)); 0702 } 0703 0704 bool KSystemTimeZoneBackend::isDst(const KTimeZone *caller, time_t t) const 0705 { 0706 Q_UNUSED(caller) 0707 if (t != (time_t) - 1) { 0708 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS 0709 tm tmtime; 0710 if (localtime_r(&t, &tmtime)) { 0711 return tmtime.tm_isdst > 0; 0712 } 0713 #else 0714 const tm *tmtime = localtime(&t); 0715 if (tmtime) { 0716 return tmtime->tm_isdst > 0; 0717 } 0718 #endif 0719 } 0720 return false; 0721 } 0722 0723 /******************************************************************************/ 0724 0725 KSystemTimeZone::KSystemTimeZone(KSystemTimeZoneSource *source, const QString &name, 0726 const QString &countryCode, float latitude, float longitude, const QString &comment) 0727 : KTimeZone(new KSystemTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)) 0728 { 0729 } 0730 0731 KSystemTimeZone::~KSystemTimeZone() 0732 { 0733 } 0734 0735 /******************************************************************************/ 0736 0737 class KSystemTimeZoneDataPrivate 0738 { 0739 public: 0740 QByteArray TZ; 0741 QList<QByteArray> abbreviations; 0742 }; 0743 0744 // N.B. KSystemTimeZoneSourcePrivate is also used by KSystemTimeZoneData 0745 class KSystemTimeZoneSourcePrivate 0746 { 0747 public: 0748 static void setTZ(const QByteArray &zoneName); 0749 static void restoreTZ(); 0750 static QByteArray savedTZ; // temporary value of TZ environment variable saved by setTZ() 0751 static QByteArray originalTZ; // saved value of TZ environment variable during multiple parse() calls 0752 static bool TZIsSaved; // TZ has been saved in savedTZ 0753 static bool multiParse; // true if performing multiple parse() calls 0754 }; 0755 0756 QByteArray KSystemTimeZoneSourcePrivate::savedTZ; 0757 QByteArray KSystemTimeZoneSourcePrivate::originalTZ; 0758 bool KSystemTimeZoneSourcePrivate::TZIsSaved = false; 0759 bool KSystemTimeZoneSourcePrivate::multiParse = false; 0760 0761 KSystemTimeZoneSource::KSystemTimeZoneSource() 0762 : d(nullptr) 0763 // : d(new KSystemTimeZoneSourcePrivate) 0764 { 0765 } 0766 0767 KSystemTimeZoneSource::~KSystemTimeZoneSource() 0768 { 0769 // delete d; 0770 } 0771 0772 KTimeZoneData *KSystemTimeZoneSource::parse(const KTimeZone &zone) const 0773 { 0774 const QByteArray tz = zone.name().toUtf8(); 0775 KSystemTimeZoneSourcePrivate::setTZ(tz); // make this time zone the current local time zone 0776 0777 tzset(); // initialize the tzname array 0778 KSystemTimeZoneData *data = new KSystemTimeZoneData; 0779 data->d->TZ = tz; 0780 data->d->abbreviations.append(tzname[0]); 0781 data->d->abbreviations.append(tzname[1]); 0782 0783 // There is no easy means to access the sequence of daylight savings time 0784 // changes, or leap seconds adjustments, so leave that data empty. 0785 0786 KSystemTimeZoneSourcePrivate::restoreTZ(); // restore the original local time zone if necessary 0787 return data; 0788 } 0789 0790 void KSystemTimeZoneSource::startParseBlock() 0791 { 0792 KSystemTimeZoneSourcePrivate::originalTZ = qgetenv("TZ"); // save the original local time zone 0793 KSystemTimeZoneSourcePrivate::multiParse = true; 0794 } 0795 0796 void KSystemTimeZoneSource::endParseBlock() 0797 { 0798 if (KSystemTimeZoneSourcePrivate::multiParse) { 0799 // Restore the original local time zone 0800 if (KSystemTimeZoneSourcePrivate::originalTZ.isEmpty()) { 0801 ::unsetenv("TZ"); 0802 } else { 0803 qputenv("TZ", KSystemTimeZoneSourcePrivate::originalTZ); 0804 } 0805 ::tzset(); 0806 KSystemTimeZoneSourcePrivate::multiParse = false; 0807 } 0808 } 0809 0810 // Set the TZ environment variable to the specified time zone, 0811 // saving its current setting first if necessary. 0812 void KSystemTimeZoneSourcePrivate::setTZ(const QByteArray &zoneName) 0813 { 0814 QByteArray tz = zoneName; 0815 tz.prepend(":"); 0816 bool setTZ = multiParse; 0817 if (!setTZ) { 0818 savedTZ = qgetenv("TZ"); // save the original local time zone 0819 TZIsSaved = true; 0820 setTZ = (tz != savedTZ); 0821 } 0822 if (setTZ) { 0823 qputenv("TZ", tz); 0824 ::tzset(); 0825 } 0826 } 0827 0828 // Restore the TZ environment variable if it was saved by setTz() 0829 void KSystemTimeZoneSourcePrivate::restoreTZ() 0830 { 0831 if (TZIsSaved) { 0832 if (savedTZ.isEmpty()) { 0833 ::unsetenv("TZ"); 0834 } else { 0835 qputenv("TZ", savedTZ); 0836 } 0837 ::tzset(); 0838 TZIsSaved = false; 0839 } 0840 } 0841 0842 /******************************************************************************/ 0843 0844 KSystemTimeZoneData::KSystemTimeZoneData() 0845 : d(new KSystemTimeZoneDataPrivate) 0846 { } 0847 0848 KSystemTimeZoneData::KSystemTimeZoneData(const KSystemTimeZoneData &rhs) 0849 : KTimeZoneData(), 0850 d(new KSystemTimeZoneDataPrivate) 0851 { 0852 operator=(rhs); 0853 } 0854 0855 KSystemTimeZoneData::~KSystemTimeZoneData() 0856 { 0857 delete d; 0858 } 0859 0860 KSystemTimeZoneData &KSystemTimeZoneData::operator=(const KSystemTimeZoneData &rhs) 0861 { 0862 d->TZ = rhs.d->TZ; 0863 d->abbreviations = rhs.d->abbreviations; 0864 return *this; 0865 } 0866 0867 KTimeZoneData *KSystemTimeZoneData::clone() const 0868 { 0869 return new KSystemTimeZoneData(*this); 0870 } 0871 0872 QList<QByteArray> KSystemTimeZoneData::abbreviations() const 0873 { 0874 return d->abbreviations; 0875 } 0876 0877 QByteArray KSystemTimeZoneData::abbreviation(const QDateTime &utcDateTime) const 0878 { 0879 QByteArray abbr; 0880 if (utcDateTime.timeSpec() != Qt::UTC) { 0881 return abbr; 0882 } 0883 time_t t = utcDateTime.toTime_t(); 0884 if (t != KTimeZone::InvalidTime_t) { 0885 KSystemTimeZoneSourcePrivate::setTZ(d->TZ); // make this time zone the current local time zone 0886 0887 /* Use tm.tm_zone if available because it returns the abbreviation 0888 * in use at the time specified. Otherwise, use tzname[] which 0889 * returns the appropriate current abbreviation instead. 0890 */ 0891 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS 0892 tm tmtime; 0893 if (localtime_r(&t, &tmtime)) 0894 #if HAVE_STRUCT_TM_TM_ZONE 0895 abbr = tmtime.tm_zone; 0896 #else 0897 abbr = tzname[(tmtime.tm_isdst > 0) ? 1 : 0]; 0898 #endif 0899 #else 0900 const tm *tmtime = localtime(&t); 0901 if (tmtime) 0902 #if HAVE_STRUCT_TM_TM_ZONE 0903 abbr = tmtime->tm_zone; 0904 #else 0905 abbr = tzname[(tmtime->tm_isdst > 0) ? 1 : 0]; 0906 #endif 0907 #endif 0908 KSystemTimeZoneSourcePrivate::restoreTZ(); // restore the original local time zone if necessary 0909 } 0910 return abbr; 0911 } 0912 0913 QList<int> KSystemTimeZoneData::utcOffsets() const 0914 { 0915 return QList<int>(); 0916 } 0917