/* This file is part of the KDE libraries Copyright (c) 2005-2010 David Jarvie Copyright (c) 2005 S.R.Haque . This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config-date.h" #include "config-prefix.h" #include #include #include #include #include "kglobal.h" #include "kdebug.h" #include "ksystemtimezone.h" #include "kde_file.h" #include #include static const QString s_localtime = QString::fromLatin1("/etc/localtime"); QString zoneinfoDir() { static const QStringList zoneinfodirs = QStringList() << QString::fromLatin1("/share/zoneinfo") << QString::fromLatin1("/lib/zoneinfo") << QString::fromLatin1("/usr/share/zoneinfo") << QString::fromLatin1("/usr/lib/zoneinfo") << QString::fromLatin1("/usr/local/share/zoneinfo") << QString::fromLatin1("/usr/local/lib/zoneinfo") << (QString::fromLatin1(KDEDIR) + QLatin1String("/share/zoneinfo")) << (QString::fromLatin1(KDEDIR) + QLatin1String("/lib/zoneinfo")); foreach (const QString &zoneinfodir, zoneinfodirs) { KDE_struct_stat statbuf; if (KDE::stat(zoneinfodir, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { return zoneinfodir; } } return zoneinfodirs.last(); } // Convert sHHMM or sHHMMSS to a floating point number of degrees. static float convertCoordinate(const QByteArray &coordinate) { int value = coordinate.toInt(); int degrees = 0; int minutes = 0; int seconds = 0; if (coordinate.length() > 6) { degrees = value / 10000; value -= degrees * 10000; minutes = value / 100; value -= minutes * 100; seconds = value; } else { degrees = value / 100; value -= degrees * 100; minutes = value; } value = degrees * 3600 + minutes * 60 + seconds; return value / 3600.0; } static QList splitZoneTabCoordinates(const QByteArray &zonetabcoordinates) { QList result; int startindex = 0; if (zonetabcoordinates.startsWith('+') || zonetabcoordinates.startsWith('-')) { startindex = 1; } int signindex = zonetabcoordinates.indexOf('+', startindex); if (signindex < startindex) { signindex = zonetabcoordinates.indexOf('-', startindex); } if (signindex < startindex) { return result; } const float latitude = convertCoordinate(zonetabcoordinates.mid(0, signindex)); const float longitude = convertCoordinate(zonetabcoordinates.mid(signindex, zonetabcoordinates.size() - signindex)); result.append(latitude); result.append(longitude); return result; } class KSystemTimeZonesPrivate : public QObject, public KTimeZones { Q_OBJECT public: KSystemTimeZonesPrivate(); KTimeZone m_localtz; QString m_zoneinfoDir; KTimeZoneSource* m_tzfileSource; private Q_SLOTS: void update(const QString &path); private: QFileSystemWatcher *m_watcher; }; KSystemTimeZonesPrivate::KSystemTimeZonesPrivate() : m_tzfileSource(nullptr), m_watcher(nullptr) { update(QString()); } void KSystemTimeZonesPrivate::update(const QString &path) { Q_UNUSED(path); m_localtz = KTimeZone::utc(); static const QByteArray kdezoneinfodir = qgetenv("KDE_ZONEINFO_DIR"); m_zoneinfoDir = QFile::decodeName(kdezoneinfodir); if (m_zoneinfoDir.isEmpty()) { m_zoneinfoDir = zoneinfoDir(); } delete m_tzfileSource; m_tzfileSource = new KTimeZoneSource(m_zoneinfoDir); KTimeZones::clear(); const QString zonetab = m_zoneinfoDir + QLatin1String("/zone.tab"); QFile zonetabfile(zonetab); if (!zonetabfile.open(QFile::ReadOnly)) { kWarning() << "Could not open zone.tab" << zonetab; return; } QStringList watchlist = QStringList() << zonetab << s_localtime; QString reallocaltime(s_localtime); QFileInfo localtimeinfo(s_localtime); if (localtimeinfo.isSymLink()) { reallocaltime = localtimeinfo.readLink(); watchlist.append(reallocaltime); } if (m_watcher) { m_watcher->deleteLater(); } m_watcher = new QFileSystemWatcher(this); m_watcher->addPaths(watchlist); connect(m_watcher, SIGNAL(fileChanged(QString)), this, SLOT(update(QString))); kDebug() << "Parsing" << zonetab; char zonecode[4]; char zonecoordinates[1024]; char zonename[1024]; char zonecomment[1024]; while (!zonetabfile.atEnd()) { const QByteArray zonetabline = zonetabfile.readLine().trimmed(); if (zonetabline.isEmpty() || zonetabline.startsWith('#')) { continue; } ::memset(zonecode, '\0', sizeof(zonecode)); ::memset(zonecoordinates, '\0', sizeof(zonecode)); ::memset(zonename, '\0', sizeof(zonename)); ::memset(zonecomment, '\0', sizeof(zonecomment)); const int sscanfresult = sscanf( zonetabline.constData(), "%4s %1024s %1024s %1024[^\n]]", zonecode, zonecoordinates, zonename, zonecomment ); if (Q_UNLIKELY(sscanfresult < 3 || sscanfresult > 4)) { kWarning() << "Invalid zone.tab entry" << zonetabline; continue; } const QList zonetabcoordinates = splitZoneTabCoordinates( QByteArray::fromRawData(zonecoordinates, qstrlen(zonecoordinates)) ); if (Q_UNLIKELY(zonetabcoordinates.size() != 2)) { kWarning() << "Invalid zone.tab coordinates" << zonetabline; continue; } const KTimeZone ktimezone( m_tzfileSource, QString::fromLatin1(zonename), QString::fromLatin1(zonecode), zonetabcoordinates.at(0), zonetabcoordinates.at(1), QString::fromLatin1(zonecomment) ); KTimeZones::add(ktimezone); } if (localtimeinfo.isSymLink()) { const int zonediroffset = (m_zoneinfoDir.size() + 1); const QString localtz = reallocaltime.mid(zonediroffset, reallocaltime.size() - zonediroffset); m_localtz = KTimeZones::zone(localtz); return; } time_t ltime; ::time(<ime); ::tzset(); struct tm res; struct tm *t = ::localtime_r(<ime, &res); #if defined(HAVE_STRUCT_TM_TM_ZONE) const QByteArray localtz(t->tm_zone); #else const QByteArray localtz(tzname[t->tm_isdst]); #endif const KTimeZones::ZoneMap allzones = KTimeZones::zones(); KTimeZones::ZoneMap::const_iterator it = allzones.constBegin(); while (it != allzones.constEnd()) { if (it.value().abbreviations().contains(localtz)) { m_localtz = KTimeZones::zone(it.key()); break; } it++; } } K_GLOBAL_STATIC(KSystemTimeZonesPrivate, s_systemzones); /******************************************************************************/ KTimeZone KSystemTimeZones::local() { const QByteArray envtz = qgetenv("TZ"); if (!envtz.isEmpty()) { if (envtz.at(0) == ':') { return KSystemTimeZones::zone(QString::fromLocal8Bit(envtz.constData() + 1, envtz.size() - 1)); } return KSystemTimeZones::zone(QString::fromLocal8Bit(envtz.constData(), envtz.size())); } return s_systemzones->m_localtz; } QString KSystemTimeZones::zoneinfoDir() { return s_systemzones->m_zoneinfoDir; } KTimeZones *KSystemTimeZones::timeZones() { return s_systemzones; } KTimeZone KSystemTimeZones::readZone(const QString &name) { return KTimeZone(s_systemzones->m_tzfileSource, name); } const KTimeZones::ZoneMap KSystemTimeZones::zones() { return s_systemzones->zones(); } KTimeZone KSystemTimeZones::zone(const QString &name) { return s_systemzones->zone(name); } #include "ksystemtimezone.moc"