summaryrefslogtreecommitdiffstats
path: root/kdecore/ktimezones.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdecore/ktimezones.cpp')
-rw-r--r--kdecore/ktimezones.cpp790
1 files changed, 0 insertions, 790 deletions
diff --git a/kdecore/ktimezones.cpp b/kdecore/ktimezones.cpp
deleted file mode 100644
index dbd589d85..000000000
--- a/kdecore/ktimezones.cpp
+++ /dev/null
@@ -1,790 +0,0 @@
-/*
- This file is part of the KDE libraries
- Copyright (c) 2005 S.R.Haque <srhaque@iee.org>.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License version 2 as published by the Free Software Foundation.
-
- 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.h>
-
-#include <ktimezones.h>
-#include <kdebug.h>
-#include <kmdcodec.h>
-#include <kprocess.h>
-#include <kstringhandler.h>
-#include <ktempfile.h>
-
-#include <tqdatetime.h>
-#include <tqfile.h>
-#include <tqregexp.h>
-#include <tqstringlist.h>
-#include <tqtextstream.h>
-
-#include <cerrno>
-#include <climits>
-#include <cstdlib>
-#include <cstring>
-#include <ctime>
-
-#define UTC_ZONE "UTC"
-
-/**
- * Find out if the given standard (e.g. "GMT") and daylight savings time
- * (e.g. "BST", but which may be empty) abbreviated timezone names match
- * this timezone.
- *
- * Thus, this class can be used as a heuristic when trying to lookup the
- * real timezone from the abbreviated zone names.
- */
-class AbbreviationsMatch :
- public KTimezoneDetails
-{
-public:
- AbbreviationsMatch(const TQString &stdZone, const TQString &dstZone = "")
- {
- m_stdZone = stdZone;
- m_dstZone = dstZone;
- }
-
- void parseStarted()
- {
- m_foundStd = false;
- m_foundDst = m_dstZone.isEmpty();
- }
-
- bool test()
- {
- return (m_foundStd && m_foundDst);
- }
-
-private:
- bool m_foundStd;
- bool m_foundDst;
- TQString m_stdZone;
- TQString m_dstZone;
-
- virtual void gotAbbreviation(int /*index*/, const TQString &value)
- {
- if (m_stdZone == value)
- {
- m_foundStd = true;
- }
- if (m_dstZone == value)
- {
- m_foundDst = true;
- }
- }
-};
-
-/**
- * Internal dummy source for UTC timezone.
- */
-class DummySource :
- public KTimezoneSource
-{
-public:
- DummySource() :
- KTimezoneSource("")
- {
- }
-
- virtual bool parse(const TQString &/*zone*/, KTimezoneDetails &/*dataReceiver*/) const
- {
- return true;
- }
-};
-
-/**
- * Find offset at a particular point in time.
- */
-class OffsetFind :
- public KTimezoneDetails
-{
-public:
- OffsetFind(unsigned dateTime)
- {
- m_dateTime = dateTime;
- }
-
- void parseStarted()
- {
- m_transitionTimeIndex = 0;
- m_localTimeIndex = -1;
- m_abbrIndex = -1;
- m_offset = 0;
- m_isDst = false;
- m_abbr = UTC_ZONE;
- }
-
- int offset()
- {
- return m_offset;
- }
-
- bool isDst()
- {
- return m_isDst;
- }
-
- TQString abbreviation()
- {
- return m_abbr;
- }
-
-private:
- unsigned m_dateTime;
- int m_transitionTimeIndex;
- int m_localTimeIndex;
- int m_abbrIndex;
- int m_offset;
- bool m_isDst;
- TQString m_abbr;
-
- virtual void gotTransitionTime(int index, unsigned transitionTime)
- {
- if (transitionTime <= m_dateTime)
- {
- // Remember the index of the transition time that relates to dateTime.
- m_transitionTimeIndex = index;
- }
- }
-
- virtual void gotLocalTimeIndex(int index, unsigned localTimeIndex)
- {
- if (index == m_transitionTimeIndex)
- {
- // Remember the index of the local time that relates to dateTime.
- m_localTimeIndex = localTimeIndex;
- }
- }
-
- virtual void gotLocalTime(int index, int gmtOff, bool isDst, unsigned abbrInd)
- {
- if (index == m_localTimeIndex)
- {
- // Remember the results that relate to gmtOffset.
- m_offset = gmtOff;
- m_isDst = isDst;
- m_abbrIndex = abbrInd;
- }
- }
-
- virtual void gotAbbreviation(int index, const TQString &value)
- {
- if (index == m_abbrIndex)
- {
- m_abbr = value;
- }
- }
-};
-
-const float KTimezone::UNKNOWN = 1000.0;
-
-bool KTimezone::isValidLatitude(float latitude)
-{
- return (latitude >= -90.0) && (latitude <= 90.0);
-}
-
-bool KTimezone::isValidLongitude(float longitude)
-{
- return (longitude >= -180.0) && (longitude <= 180.0);
-}
-
-KTimezone::KTimezone(
- KSharedPtr<KTimezoneSource> db, const TQString& name,
- const TQString &countryCode, float latitude, float longitude,
- const TQString &comment) :
- m_db(db),
- m_name(name),
- m_countryCode(countryCode),
- m_latitude(latitude),
- m_longitude(longitude),
- m_comment(comment),
- d(0)
-{
- // Detect duff values.
- if (m_latitude * m_latitude > 90 * 90)
- m_latitude = UNKNOWN;
- if (m_longitude * m_longitude > 180 * 180)
- m_longitude = UNKNOWN;
-}
-
-KTimezone::~KTimezone()
-{
- // FIXME when needed:
- // delete d;
-}
-
-TQString KTimezone::comment() const
-{
- return m_comment;
-}
-
-TQDateTime KTimezone::convert(const KTimezone *newZone, const TQDateTime &dateTime) const
-{
- char *originalZone = ::getenv("TZ");
-
- // Convert the given localtime to UTC.
- ::setenv("TZ", m_name.utf8(), 1);
- tzset();
- unsigned utc = dateTime.toTime_t();
-
- // Set the timezone and convert UTC to localtime.
- ::setenv("TZ", newZone->name().utf8(), 1);
- tzset();
- TQDateTime remoteTime;
- remoteTime.setTime_t(utc, Qt::LocalTime);
-
- // Now restore things
- if (!originalZone)
- {
- ::unsetenv("TZ");
- }
- else
- {
- ::setenv("TZ", originalZone, 1);
- }
- tzset();
- return remoteTime;
-}
-
-TQString KTimezone::countryCode() const
-{
- return m_countryCode;
-}
-
-float KTimezone::latitude() const
-{
- return m_latitude;
-}
-
-float KTimezone::longitude() const
-{
- return m_longitude;
-}
-
-TQString KTimezone::name() const
-{
- return m_name;
-}
-
-int KTimezone::offset(Qt::TimeSpec basisSpec) const
-{
- char *originalZone = ::getenv("TZ");
-
- // Get the time in the current timezone.
- TQDateTime basisTime = TQDateTime::tqcurrentDateTime(basisSpec);
-
- // Set the timezone and find out what time it is there compared to the basis.
- ::setenv("TZ", m_name.utf8(), 1);
- tzset();
- TQDateTime remoteTime = TQDateTime::tqcurrentDateTime(Qt::LocalTime);
- int offset = remoteTime.secsTo(basisTime);
-
- // Now restore things
- if (!originalZone)
- {
- ::unsetenv("TZ");
- }
- else
- {
- ::setenv("TZ", originalZone, 1);
- }
- tzset();
- return offset;
-}
-
-int KTimezone::offset(const TQDateTime &dateTime) const
-{
- OffsetFind finder(dateTime.toTime_t());
- int result = 0;
- if (parse(finder))
- {
- result = finder.offset();
- }
- return result;
-}
-
-bool KTimezone::parse(KTimezoneDetails &dataReceiver) const
-{
- dataReceiver.parseStarted();
- bool result = m_db->parse(m_name, dataReceiver);
- dataReceiver.parseEnded();
- return result;
-}
-
-KTimezones::KTimezones() :
- m_zoneinfoDir(),
- m_zones(0),
- d(0)
-{
- // Create the database (and resolve m_zoneinfoDir!).
- allZones();
- m_UTC = new KTimezone(new DummySource(), UTC_ZONE);
- add(m_UTC);
-}
-
-KTimezones::~KTimezones()
-{
- // FIXME when needed:
- // delete d;
-
- // Autodelete behavior.
- if (m_zones)
- {
- for (ZoneMap::ConstIterator it = m_zones->begin(); it != m_zones->end(); ++it)
- {
- delete it.data();
- }
- }
- delete m_zones;
-}
-
-void KTimezones::add(KTimezone *zone)
-{
- m_zones->insert(zone->name(), zone);
-}
-
-const KTimezones::ZoneMap KTimezones::allZones()
-{
- // Have we already done all the hard work? If not, create the cache.
- if (m_zones)
- return *m_zones;
- m_zones = new ZoneMap();
-
- // Go read the database.
- //
- // On Windows, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
- // is the place to look. The TZI binary value is the TIME_ZONE_INFORMATION structure.
- //
- // For Unix its all easy except knowing where to look. Try the LSB location first.
- TQFile f;
- m_zoneinfoDir = "/usr/share/zoneinfo";
- f.setName(m_zoneinfoDir + "/zone.tab");
- if (!f.open(IO_ReadOnly))
- {
- kdDebug() << "Can't open " << f.name() << endl;
- m_zoneinfoDir = "/usr/lib/zoneinfo";
- f.setName(m_zoneinfoDir + "/zone.tab");
- if (!f.open(IO_ReadOnly))
- {
- kdDebug() << "Can't open " << f.name() << endl;
- m_zoneinfoDir = ::getenv("TZDIR");
- f.setName(m_zoneinfoDir + "/zone.tab");
- if (m_zoneinfoDir.isEmpty() || !f.open(IO_ReadOnly))
- {
- kdDebug() << "Can't open " << f.name() << endl;
-
- // Solaris support. Synthesise something that looks like a zone.tab.
- //
- // /bin/grep -h ^Zone /usr/share/lib/zoneinfo/src/* | /bin/awk '{print "??\t+9999+99999\t" $2}'
- //
- // where the country code is set to "??" and the lattitude/longitude
- // values are dummies.
- m_zoneinfoDir = "/usr/share/lib/zoneinfo";
- KTempFile temp;
- KShellProcess reader;
- reader << "/bin/grep" << "-h" << "^Zone" << m_zoneinfoDir << "/src/*" << temp.name() << "|" <<
- "/bin/awk" << "'{print \"??\\t+9999+99999\\t\" $2}'";
- // Note the use of blocking here...it is a trivial amount of data!
- temp.close();
- reader.start(KProcess::Block);
- f.setName(temp.name());
- if (!temp.status() || !f.open(IO_ReadOnly))
- {
- kdDebug() << "Can't open " << f.name() << endl;
- return *m_zones;
- }
- }
- }
- }
-
- // Parse the zone.tab.
- TQTextStream str(&f);
- TQRegExp lineSeparator("[ \t]");
- TQRegExp ordinateSeparator("[+-]");
- KSharedPtr<KTimezoneSource> db(new KTimezoneSource(m_zoneinfoDir));
- while (!str.atEnd())
- {
- TQString line = str.readLine();
- if (line.isEmpty() || '#' == line[0])
- continue;
- TQStringList tokens = KStringHandler::perlSplit(lineSeparator, line, 4);
- if (tokens.count() < 3)
- {
- kdError() << "invalid record: " << line << endl;
- continue;
- }
-
- // Got three tokens. Now check for two ordinates plus first one is "".
- TQStringList ordinates = KStringHandler::perlSplit(ordinateSeparator, tokens[1], 2);
- if (ordinates.count() < 2)
- {
- kdError() << "invalid coordinates: " << tokens[1] << endl;
- continue;
- }
-
- float latitude = convertCoordinate(ordinates[1]);
- float longitude = convertCoordinate(ordinates[2]);
-
- // Add entry to list.
- if (tokens[0] == "??")
- tokens[0] = "";
- KTimezone *timezone = new KTimezone(db, tokens[2], tokens[0], latitude, longitude, tokens[3]);
- add(timezone);
- }
- f.close();
- return *m_zones;
-}
-
-/**
- * Convert sHHMM or sHHMMSS to a floating point number of degrees.
- */
-float KTimezones::convertCoordinate(const TQString &coordinate)
-{
- int value = coordinate.toInt();
- int degrees = 0;
- int minutes = 0;
- int seconds = 0;
-
- if (coordinate.length() > 11)
- {
- 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;
-}
-
-const KTimezone *KTimezones::local()
-{
- const KTimezone *local = 0;
-
- // First try the simplest solution of checking for well-formed TZ setting.
- char *envZone = ::getenv("TZ");
- if (envZone)
- {
- if (envZone[0] == '\0')
- {
- return m_UTC;
- }
- else
- if (envZone[0] == ':')
- {
- envZone++;
- }
- local = zone(envZone);
- }
- if (local)
- return local;
-
- // Try to match /etc/localtime against the list of zoneinfo files.
- TQFile f;
- f.setName("/etc/localtime");
- if (f.open(IO_ReadOnly))
- {
- // Compute the MD5 sum of /etc/localtime.
- KMD5 context("");
- context.reset();
- context.update(TQT_TQIODEVICE_OBJECT(f));
- TQIODevice::Offset referenceSize = f.size();
- TQString referenceMd5Sum = context.hexDigest();
- f.close();
- if (!m_zoneinfoDir.isEmpty())
- {
- // Compare it with each zoneinfo file.
- for (ZoneMap::Iterator it = m_zones->begin(); it != m_zones->end(); ++it)
- {
- KTimezone *zone = it.data();
- f.setName(m_zoneinfoDir + '/' + zone->name());
- if (f.open(IO_ReadOnly))
- {
- TQIODevice::Offset candidateSize = f.size();
- TQString candidateMd5Sum;
- if (candidateSize == referenceSize)
- {
- // Only do the heavy lifting for file sizes which match.
- context.reset();
- context.update(TQT_TQIODEVICE_OBJECT(f));
- candidateMd5Sum = context.hexDigest();
- }
- f.close();
- if (candidateMd5Sum == referenceMd5Sum)
- {
- // kdError() << "local=" << zone->name() << endl;
- local = zone;
- break;
- }
- }
- }
- }
- }
- if (local)
- return local;
-
- // BSD support.
- TQString fileZone;
- f.setName("/etc/timezone");
- if (!f.open(IO_ReadOnly))
- {
- kdDebug() << "Can't open " << f.name() << endl;
-
- // Solaris support using /etc/default/init.
- f.setName("/etc/default/init");
- if (!f.open(IO_ReadOnly))
- {
- kdDebug() << "Can't open " << f.name() << endl;
- }
- else
- {
- TQTextStream ts(&f);
- ts.setEncoding(TQTextStream::Latin1);
-
- // Read the last line starting "TZ=".
- while (!ts.atEnd())
- {
- fileZone = ts.readLine();
- if (fileZone.startsWith("TZ="))
- {
- fileZone = fileZone.mid(3);
-
- // kdError() << "local=" << fileZone << endl;
- local = zone(fileZone);
- }
- }
- f.close();
- }
- }
- else
- {
- TQTextStream ts(&f);
- ts.setEncoding(TQTextStream::Latin1);
-
- // Read the first line.
- if (!ts.atEnd())
- {
- fileZone = ts.readLine();
-
- // kdError() << "local=" << fileZone << endl;
- local = zone(fileZone);
- }
- f.close();
- }
- if (local)
- return local;
-
- // None of the deterministic stuff above has worked: try a heuristic. We
- // try to find a pair of matching timezone abbreviations...that way, we'll
- // likely return a value in the user's own country.
- if (!m_zoneinfoDir.isEmpty())
- {
- tzset();
- AbbreviationsMatch matcher(tzname[0], tzname[1]);
- int bestOffset = INT_MAX;
- for (ZoneMap::Iterator it = m_zones->begin(); it != m_zones->end(); ++it)
- {
- KTimezone *zone = it.data();
- int candidateOffset = QABS(zone->offset(Qt::LocalTime));
- if (zone->parse(matcher) && matcher.test() && (candidateOffset < bestOffset))
- {
- // kdError() << "local=" << zone->name() << endl;
- bestOffset = candidateOffset;
- local = zone;
- }
- }
- }
- if (local)
- return local;
- return m_UTC;
-}
-
-const KTimezone *KTimezones::zone(const TQString &name)
-{
- if (name.isEmpty())
- return m_UTC;
- ZoneMap::ConstIterator it = m_zones->find(name);
- if (it != m_zones->end())
- return it.data();
-
- // Error.
- return 0;
-}
-
-KTimezoneDetails::KTimezoneDetails()
-{
-}
-
-KTimezoneDetails::~KTimezoneDetails()
-{
-}
-
-void KTimezoneDetails::gotAbbreviation(int /*index*/, const TQString &)
-{}
-
-void KTimezoneDetails::gotHeader(
- unsigned, unsigned, unsigned,
- unsigned, unsigned, unsigned)
-{}
-
-void KTimezoneDetails::gotLeapAdjustment(int /*index*/, unsigned, unsigned)
-{}
-
-void KTimezoneDetails::gotLocalTime(int /*index*/, int, bool, unsigned)
-{}
-
-void KTimezoneDetails::gotLocalTimeIndex(int /*index*/, unsigned)
-{}
-
-void KTimezoneDetails::gotIsStandard(int /*index*/, bool)
-{}
-
-void KTimezoneDetails::gotTransitionTime(int /*index*/, unsigned)
-{}
-
-void KTimezoneDetails::gotIsUTC(int /*index*/, bool)
-{}
-
-void KTimezoneDetails::parseEnded()
-{}
-
-void KTimezoneDetails::parseStarted()
-{}
-
-KTimezoneSource::KTimezoneSource(const TQString &db) :
- m_db(db)
-{
-}
-
-KTimezoneSource::~KTimezoneSource()
-{
-}
-
-TQString KTimezoneSource::db()
-{
- return m_db;
-}
-
-bool KTimezoneSource::parse(const TQString &zone, KTimezoneDetails &dataReceiver) const
-{
- TQFile f(m_db + '/' + zone);
- if (!f.open(IO_ReadOnly))
- {
- kdError() << "Cannot open " << f.name() << endl;
- return false;
- }
-
- // Structures that represent the zoneinfo file.
- TQ_UINT8 T, z, i_, f_;
- struct
- {
- TQ_UINT32 ttisgmtcnt;
- TQ_UINT32 ttisstdcnt;
- TQ_UINT32 leapcnt;
- TQ_UINT32 timecnt;
- TQ_UINT32 typecnt;
- TQ_UINT32 charcnt;
- } tzh;
- TQ_UINT32 transitionTime;
- TQ_UINT8 localTimeIndex;
- struct
- {
- TQ_INT32 gmtoff;
- TQ_INT8 isdst;
- TQ_UINT8 abbrind;
- } tt;
- TQ_UINT32 leapTime;
- TQ_UINT32 leapSeconds;
- TQ_UINT8 isStandard;
- TQ_UINT8 isUTC;
-
- TQDataStream str(&f);
- str >> T >> z >> i_ >> f_;
- // kdError() << "signature: " << TQChar(T) << TQChar(z) << TQChar(i_) << TQChar(f_) << endl;
- unsigned i;
- for (i = 0; i < 4; i++)
- str >> tzh.ttisgmtcnt;
- str >> tzh.ttisgmtcnt >> tzh.ttisstdcnt >> tzh.leapcnt >> tzh.timecnt >> tzh.typecnt >> tzh.charcnt;
- // kdError() << "header: " << tzh.ttisgmtcnt << ", " << tzh.ttisstdcnt << ", " << tzh.leapcnt << ", " <<
- // tzh.timecnt << ", " << tzh.typecnt << ", " << tzh.charcnt << endl;
- dataReceiver.gotHeader(tzh.ttisgmtcnt, tzh.ttisstdcnt, tzh.leapcnt, tzh.timecnt, tzh.typecnt, tzh.charcnt);
- for (i = 0; i < tzh.timecnt; i++)
- {
- str >> transitionTime;
- dataReceiver.gotTransitionTime(i, transitionTime);
- }
- for (i = 0; i < tzh.timecnt; i++)
- {
- // NB: these appear to be 1-based, not zero-based!
- str >> localTimeIndex;
- dataReceiver.gotLocalTimeIndex(i, localTimeIndex);
- }
- for (i = 0; i < tzh.typecnt; i++)
- {
- str >> tt.gmtoff >> tt.isdst >> tt.abbrind;
- // kdError() << "local type: " << tt.gmtoff << ", " << tt.isdst << ", " << tt.abbrind << endl;
- dataReceiver.gotLocalTime(i, tt.gmtoff, (tt.isdst != 0), tt.abbrind);
- }
-
- // Make sure we don't run foul of maliciously coded timezone abbreviations.
- if (tzh.charcnt > 64)
- {
- kdError() << "excessive length for timezone abbreviations: " << tzh.charcnt << endl;
- return false;
- }
- TQByteArray array(tzh.charcnt);
- str.readRawBytes(array.data(), array.size());
- char *abbrs = array.data();
- if (abbrs[tzh.charcnt - 1] != 0)
- {
- // These abbrevations are corrupt!
- kdError() << "timezone abbreviations not terminated: " << abbrs[tzh.charcnt - 1] << endl;
- return false;
- }
- char *abbr = abbrs;
- while (abbr < abbrs + tzh.charcnt)
- {
- // kdError() << "abbr: " << abbr << endl;
- dataReceiver.gotAbbreviation((abbr - abbrs), abbr);
- abbr += strlen(abbr) + 1;
- }
- for (i = 0; i < tzh.leapcnt; i++)
- {
- str >> leapTime >> leapSeconds;
- // kdError() << "leap entry: " << leapTime << ", " << leapSeconds << endl;
- dataReceiver.gotLeapAdjustment(i, leapTime, leapSeconds);
- }
- for (i = 0; i < tzh.ttisstdcnt; i++)
- {
- str >> isStandard;
- // kdError() << "standard: " << isStandard << endl;
- dataReceiver.gotIsStandard(i, (isStandard != 0));
- }
- for (i = 0; i < tzh.ttisgmtcnt; i++)
- {
- str >> isUTC;
- // kdError() << "UTC: " << isUTC << endl;
- dataReceiver.gotIsUTC(i, (isUTC != 0));
- }
- return true;
-}