|
|
|
/*
|
|
|
|
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 <tdetempfile.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(
|
|
|
|
TDESharedPtr<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::currentDateTime(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::currentDateTime(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(TDEProcess::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("[+-]");
|
|
|
|
TDESharedPtr<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;
|
|
|
|
}
|