mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-24 19:02:48 +00:00
408 lines
13 KiB
C++
408 lines
13 KiB
C++
![]() |
/*
|
||
|
This file is part of the KDE libraries
|
||
|
Copyright (c) 2005-2008 David Jarvie <djarvie@kde.org>
|
||
|
|
||
|
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 "ktzfiletimezone.h"
|
||
|
|
||
|
#include <config.h>
|
||
|
|
||
|
#ifdef HAVE_SYS_TIME_H
|
||
|
#include <sys/time.h>
|
||
|
#endif
|
||
|
#ifdef HAVE_TIME_H
|
||
|
#include <time.h>
|
||
|
#endif
|
||
|
|
||
|
#include <QtCore/QFile>
|
||
|
#include <QtCore/QDataStream>
|
||
|
#include <QtCore/QVector>
|
||
|
|
||
|
#include <kdebug.h>
|
||
|
|
||
|
|
||
|
// Use this replacement for QDateTime::setTime_t(uint) since our time
|
||
|
// values are signed.
|
||
|
static QDateTime fromTime_t(qint32 seconds)
|
||
|
{
|
||
|
static const QDate epochDate(1970,1,1);
|
||
|
static const QTime epochTime(0,0,0);
|
||
|
int days = seconds / 86400;
|
||
|
seconds -= days * 86400;
|
||
|
if (seconds < 0)
|
||
|
{
|
||
|
--days;
|
||
|
seconds += 86400;
|
||
|
}
|
||
|
return QDateTime(epochDate.addDays(days), epochTime.addSecs(seconds), Qt::UTC);
|
||
|
}
|
||
|
|
||
|
/******************************************************************************/
|
||
|
|
||
|
KTzfileTimeZoneBackend::KTzfileTimeZoneBackend(KTzfileTimeZoneSource *source, const QString &name,
|
||
|
const QString &countryCode, float latitude, float longitude, const QString &comment)
|
||
|
: KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)
|
||
|
{}
|
||
|
|
||
|
KTzfileTimeZoneBackend::~KTzfileTimeZoneBackend()
|
||
|
{}
|
||
|
|
||
|
KTimeZoneBackend *KTzfileTimeZoneBackend::clone() const
|
||
|
{
|
||
|
return new KTzfileTimeZoneBackend(*this);
|
||
|
}
|
||
|
|
||
|
QByteArray KTzfileTimeZoneBackend::type() const
|
||
|
{
|
||
|
return "KTzfileTimeZone";
|
||
|
}
|
||
|
|
||
|
bool KTzfileTimeZoneBackend::hasTransitions(const KTimeZone *caller) const
|
||
|
{
|
||
|
Q_UNUSED(caller)
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************/
|
||
|
|
||
|
KTzfileTimeZone::KTzfileTimeZone(KTzfileTimeZoneSource *source, const QString &name,
|
||
|
const QString &countryCode, float latitude, float longitude,
|
||
|
const QString &comment)
|
||
|
: KTimeZone(new KTzfileTimeZoneBackend(source, name, countryCode, latitude, longitude, comment))
|
||
|
{}
|
||
|
|
||
|
KTzfileTimeZone::~KTzfileTimeZone()
|
||
|
{}
|
||
|
|
||
|
|
||
|
/******************************************************************************/
|
||
|
|
||
|
class KTzfileTimeZoneDataPrivate
|
||
|
{
|
||
|
public:
|
||
|
};
|
||
|
|
||
|
|
||
|
KTzfileTimeZoneData::KTzfileTimeZoneData()
|
||
|
// : d(new KTzfileTimeZoneDataPrivate)
|
||
|
{ }
|
||
|
|
||
|
KTzfileTimeZoneData::KTzfileTimeZoneData(const KTzfileTimeZoneData &rhs)
|
||
|
: KTimeZoneData(rhs)
|
||
|
// d(new KTzfileTimeZoneDataPrivate)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
KTzfileTimeZoneData::~KTzfileTimeZoneData()
|
||
|
{
|
||
|
// delete d;
|
||
|
}
|
||
|
|
||
|
KTzfileTimeZoneData &KTzfileTimeZoneData::operator=(const KTzfileTimeZoneData &rhs)
|
||
|
{
|
||
|
KTimeZoneData::operator=(rhs);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
KTimeZoneData *KTzfileTimeZoneData::clone() const
|
||
|
{
|
||
|
return new KTzfileTimeZoneData(*this);
|
||
|
}
|
||
|
|
||
|
bool KTzfileTimeZoneData::hasTransitions() const
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************/
|
||
|
|
||
|
class KTzfileTimeZoneSourcePrivate
|
||
|
{
|
||
|
public:
|
||
|
KTzfileTimeZoneSourcePrivate(const QString &loc)
|
||
|
: location(loc) {}
|
||
|
~KTzfileTimeZoneSourcePrivate() {}
|
||
|
|
||
|
QString location;
|
||
|
};
|
||
|
|
||
|
|
||
|
KTzfileTimeZoneSource::KTzfileTimeZoneSource(const QString &location)
|
||
|
: d(new KTzfileTimeZoneSourcePrivate(location))
|
||
|
{
|
||
|
if (location.length() > 1 && location.endsWith(QLatin1Char('/')))
|
||
|
d->location.chop(1);
|
||
|
}
|
||
|
|
||
|
KTzfileTimeZoneSource::~KTzfileTimeZoneSource()
|
||
|
{
|
||
|
delete d;
|
||
|
}
|
||
|
|
||
|
QString KTzfileTimeZoneSource::location() const
|
||
|
{
|
||
|
return d->location;
|
||
|
}
|
||
|
|
||
|
KTimeZoneData* KTzfileTimeZoneSource::parse(const KTimeZone &zone) const
|
||
|
{
|
||
|
quint32 abbrCharCount; // the number of characters of time zone abbreviation strings
|
||
|
quint32 ttisgmtcnt;
|
||
|
quint8 is;
|
||
|
quint8 T_, Z_, i_, f_; // tzfile identifier prefix
|
||
|
|
||
|
QString path = zone.name();
|
||
|
if (!path.startsWith(QLatin1Char('/')))
|
||
|
{
|
||
|
if (d->location == QLatin1String("/"))
|
||
|
path.prepend(d->location);
|
||
|
else
|
||
|
path = d->location + QLatin1Char('/') + path;
|
||
|
}
|
||
|
QFile f(path);
|
||
|
if (!f.open(QIODevice::ReadOnly))
|
||
|
{
|
||
|
kError() << "Cannot open " << f.fileName() << endl;
|
||
|
return 0;
|
||
|
}
|
||
|
QDataStream str(&f);
|
||
|
|
||
|
// Read the file type identifier
|
||
|
str >> T_ >> Z_ >> i_ >> f_;
|
||
|
if (T_ != 'T' || Z_ != 'Z' || i_ != 'i' || f_ != 'f')
|
||
|
{
|
||
|
kError() << "Not a TZFILE: " << f.fileName() << endl;
|
||
|
return 0;
|
||
|
}
|
||
|
// Discard 16 bytes reserved for future use
|
||
|
unsigned i;
|
||
|
for (i = 0; i < 4; ++i)
|
||
|
str >> ttisgmtcnt;
|
||
|
|
||
|
KTzfileTimeZoneData* data = new KTzfileTimeZoneData;
|
||
|
|
||
|
// Read the sizes of arrays held in the file
|
||
|
quint32 nTransitionTimes;
|
||
|
quint32 nLocalTimeTypes;
|
||
|
quint32 nLeapSecondAdjusts;
|
||
|
quint32 nIsStandard;
|
||
|
quint32 nIsUtc;
|
||
|
str >> nIsUtc
|
||
|
>> nIsStandard
|
||
|
>> nLeapSecondAdjusts
|
||
|
>> nTransitionTimes
|
||
|
>> nLocalTimeTypes
|
||
|
>> abbrCharCount;
|
||
|
// kDebug() << "header: " << nIsUtc << ", " << nIsStandard << ", " << nLeapSecondAdjusts << ", " <<
|
||
|
// nTransitionTimes << ", " << nLocalTimeTypes << ", " << abbrCharCount << endl;
|
||
|
|
||
|
// Read the transition times, at which the rules for computing local time change
|
||
|
struct TransitionTime
|
||
|
{
|
||
|
qint32 time; // time (as returned by time(2)) at which the rules for computing local time change
|
||
|
quint8 localTimeIndex; // index into the LocalTimeType array
|
||
|
};
|
||
|
//kDebug()<<"Reading zone "<<zone.name();
|
||
|
TransitionTime *transitionTimes = new TransitionTime[nTransitionTimes];
|
||
|
for (i = 0; i < nTransitionTimes; ++i)
|
||
|
{
|
||
|
str >> transitionTimes[i].time;
|
||
|
}
|
||
|
for (i = 0; i < nTransitionTimes; ++i)
|
||
|
{
|
||
|
str >> transitionTimes[i].localTimeIndex;
|
||
|
//kDebug() << "Transition time "<<i<<": "<<transitionTimes[i].time<<" lt index="<<(int)transitionTimes[i].localTimeIndex;
|
||
|
}
|
||
|
|
||
|
// Read the local time types
|
||
|
struct LocalTimeType
|
||
|
{
|
||
|
qint32 gmtoff; // number of seconds to be added to UTC
|
||
|
bool isdst; // whether tm_isdst should be set by localtime(3)
|
||
|
quint8 abbrIndex; // index into the list of time zone abbreviations
|
||
|
bool isutc; // transition times are in UTC. If UTC, isstd is ignored.
|
||
|
bool isstd; // if true, transition times are in standard time;
|
||
|
// if false, transition times are in wall clock time,
|
||
|
// i.e. standard time or daylight savings time
|
||
|
// whichever is current before the transition
|
||
|
};
|
||
|
LocalTimeType *localTimeTypes = new LocalTimeType[nLocalTimeTypes];
|
||
|
LocalTimeType *ltt = localTimeTypes;
|
||
|
for (i = 0; i < nLocalTimeTypes; ++ltt, ++i)
|
||
|
{
|
||
|
str >> ltt->gmtoff;
|
||
|
str >> is;
|
||
|
ltt->isdst = (is != 0);
|
||
|
str >> ltt->abbrIndex;
|
||
|
// kDebug() << "local type: " << ltt->gmtoff << ", " << is << ", " << ltt->abbrIndex;
|
||
|
ltt->isstd = false; // default if no data
|
||
|
ltt->isutc = false; // default if no data
|
||
|
}
|
||
|
|
||
|
// Read the timezone abbreviations. They are stored as null terminated strings in
|
||
|
// a character array.
|
||
|
// Make sure we don't fall foul of maliciously coded time zone abbreviations.
|
||
|
if (abbrCharCount > 64)
|
||
|
{
|
||
|
kError() << "excessive length for timezone abbreviations: " << abbrCharCount << endl;
|
||
|
delete data;
|
||
|
delete[] transitionTimes;
|
||
|
delete[] localTimeTypes;
|
||
|
return 0;
|
||
|
}
|
||
|
QByteArray array(abbrCharCount, 0);
|
||
|
str.readRawData(array.data(), array.size());
|
||
|
const char *abbrs = array.constData();
|
||
|
if (abbrs[abbrCharCount - 1] != 0)
|
||
|
{
|
||
|
// These abbreviations are corrupt!
|
||
|
kError() << "timezone abbreviations not null terminated: " << abbrs[abbrCharCount - 1] << endl;
|
||
|
delete data;
|
||
|
delete[] transitionTimes;
|
||
|
delete[] localTimeTypes;
|
||
|
return 0;
|
||
|
}
|
||
|
quint8 n = 0;
|
||
|
QList<QByteArray> abbreviations;
|
||
|
for (i = 0; i < abbrCharCount; ++n, i += strlen(abbrs + i) + 1)
|
||
|
{
|
||
|
abbreviations += QByteArray(abbrs + i);
|
||
|
// Convert the LocalTimeTypes pointer to a sequential index
|
||
|
ltt = localTimeTypes;
|
||
|
for (unsigned j = 0; j < nLocalTimeTypes; ++ltt, ++j)
|
||
|
{
|
||
|
if (ltt->abbrIndex == i)
|
||
|
ltt->abbrIndex = n;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Read the leap second adjustments
|
||
|
qint32 t;
|
||
|
quint32 s;
|
||
|
QList<KTimeZone::LeapSeconds> leapChanges;
|
||
|
for (i = 0; i < nLeapSecondAdjusts; ++i)
|
||
|
{
|
||
|
str >> t >> s;
|
||
|
// kDebug() << "leap entry: " << t << ", " << s;
|
||
|
// Don't use QDateTime::setTime_t() because it takes an unsigned argument
|
||
|
leapChanges += KTimeZone::LeapSeconds(fromTime_t(t), static_cast<int>(s));
|
||
|
}
|
||
|
data->setLeapSecondChanges(leapChanges);
|
||
|
|
||
|
// Read the standard/wall time indicators.
|
||
|
// These are true if the transition times associated with local time types
|
||
|
// are specified as standard time, false if wall clock time.
|
||
|
for (i = 0; i < nIsStandard; ++i)
|
||
|
{
|
||
|
str >> is;
|
||
|
localTimeTypes[i].isstd = (is != 0);
|
||
|
// kDebug() << "standard: " << is;
|
||
|
}
|
||
|
|
||
|
// Read the UTC/local time indicators.
|
||
|
// These are true if the transition times associated with local time types
|
||
|
// are specified as UTC, false if local time.
|
||
|
for (i = 0; i < nIsUtc; ++i)
|
||
|
{
|
||
|
str >> is;
|
||
|
localTimeTypes[i].isutc = (is != 0);
|
||
|
// kDebug() << "UTC: " << is;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Find the starting offset from UTC to use before the first transition time.
|
||
|
// This is first non-daylight savings local time type, or if there is none,
|
||
|
// the first local time type.
|
||
|
LocalTimeType* firstLtt = 0;
|
||
|
ltt = localTimeTypes;
|
||
|
for (i = 0; i < nLocalTimeTypes; ++ltt, ++i)
|
||
|
{
|
||
|
if (!ltt->isdst)
|
||
|
{
|
||
|
firstLtt = ltt;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Compile the time type data into a list of KTimeZone::Phase instances.
|
||
|
// Also check for local time types which are identical (this does happen)
|
||
|
// and use the same Phase instance for each.
|
||
|
QByteArray abbrev;
|
||
|
QList<KTimeZone::Phase> phases;
|
||
|
QList<QByteArray> phaseAbbrevs;
|
||
|
QVector<int> lttLookup(nLocalTimeTypes);
|
||
|
ltt = localTimeTypes;
|
||
|
for (i = 0; i < nLocalTimeTypes; ++ltt, ++i)
|
||
|
{
|
||
|
if (ltt->abbrIndex >= abbreviations.count())
|
||
|
{
|
||
|
kError() << "KTzfileTimeZoneSource::parse(): abbreviation index out of range" << endl;
|
||
|
abbrev = "???";
|
||
|
}
|
||
|
else
|
||
|
abbrev = abbreviations[ltt->abbrIndex];
|
||
|
// Check for an identical Phase
|
||
|
int phindex = 0;
|
||
|
for (int j = 0, jend = phases.count(); j < jend; ++j, ++phindex)
|
||
|
{
|
||
|
if (ltt->gmtoff == phases[j].utcOffset()
|
||
|
&& (bool)ltt->isdst == phases[j].isDst()
|
||
|
&& abbrev == phaseAbbrevs[j])
|
||
|
break;
|
||
|
}
|
||
|
lttLookup[i] = phindex;
|
||
|
if (phindex == phases.count())
|
||
|
{
|
||
|
phases += KTimeZone::Phase(ltt->gmtoff, abbrev, ltt->isdst);
|
||
|
phaseAbbrevs += abbrev;
|
||
|
}
|
||
|
}
|
||
|
KTimeZone::Phase prePhase(firstLtt->gmtoff,
|
||
|
(firstLtt->abbrIndex < abbreviations.count() ? abbreviations[firstLtt->abbrIndex] : ""),
|
||
|
false);
|
||
|
data->setPhases(phases, prePhase);
|
||
|
|
||
|
// Compile the transition list
|
||
|
QList<KTimeZone::Transition> transitions;
|
||
|
TransitionTime *tt = transitionTimes;
|
||
|
for (i = 0; i < nTransitionTimes; ++tt, ++i)
|
||
|
{
|
||
|
if (tt->localTimeIndex >= nLocalTimeTypes)
|
||
|
{
|
||
|
kError() << "KTzfileTimeZoneSource::parse(): transition ignored: local time type out of range: " <<(int)tt->localTimeIndex<<" > "<<nLocalTimeTypes << endl;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Convert local transition times to UTC
|
||
|
ltt = &localTimeTypes[tt->localTimeIndex];
|
||
|
const KTimeZone::Phase phase = phases[lttLookup[tt->localTimeIndex]];
|
||
|
//kDebug(161) << "Transition time "<<i<<": "<<fromTime_t(tt->time)<<", offset="<<phase.utcOffset()/60;
|
||
|
transitions += KTimeZone::Transition(fromTime_t(tt->time), phase);
|
||
|
}
|
||
|
data->setTransitions(transitions);
|
||
|
//for(int xxx=1;xxx<data->transitions().count();xxx++)
|
||
|
//kDebug(161) << "Transition time "<<xxx<<": "<<data->transitions()[xxx].time()<<", offset="<<data->transitions()[xxx].phase().utcOffset()/60;
|
||
|
delete[] localTimeTypes;
|
||
|
delete[] transitionTimes;
|
||
|
|
||
|
return data;
|
||
|
}
|