KDECore
ktzfiletimezone.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of the KDE libraries 00003 Copyright (c) 2005-2008 David Jarvie <djarvie@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "ktzfiletimezone.h" 00022 00023 #include <config.h> 00024 00025 #ifdef HAVE_SYS_TIME_H 00026 #include <sys/time.h> 00027 #endif 00028 #ifdef HAVE_TIME_H 00029 #include <time.h> 00030 #endif 00031 00032 #include <QtCore/QFile> 00033 #include <QtCore/QDataStream> 00034 #include <QtCore/QVector> 00035 00036 #include <kdebug.h> 00037 00038 00039 // Use this replacement for QDateTime::setTime_t(uint) since our time 00040 // values are signed. 00041 static QDateTime fromTime_t(qint32 seconds) 00042 { 00043 static const QDate epochDate(1970,1,1); 00044 static const QTime epochTime(0,0,0); 00045 int secs = (seconds >= 0) ? seconds % 86400 : -(-seconds % 86400); 00046 return QDateTime(epochDate.addDays(seconds / 86400), epochTime.addSecs(secs), Qt::UTC); 00047 } 00048 00049 /******************************************************************************/ 00050 00051 KTzfileTimeZoneBackend::KTzfileTimeZoneBackend(KTzfileTimeZoneSource *source, const QString &name, 00052 const QString &countryCode, float latitude, float longitude, const QString &comment) 00053 : KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment) 00054 {} 00055 00056 KTzfileTimeZoneBackend::~KTzfileTimeZoneBackend() 00057 {} 00058 00059 KTimeZoneBackend *KTzfileTimeZoneBackend::clone() const 00060 { 00061 return new KTzfileTimeZoneBackend(*this); 00062 } 00063 00064 QByteArray KTzfileTimeZoneBackend::type() const 00065 { 00066 return "KTzfileTimeZone"; 00067 } 00068 00069 bool KTzfileTimeZoneBackend::hasTransitions(const KTimeZone *caller) const 00070 { 00071 Q_UNUSED(caller) 00072 return true; 00073 } 00074 00075 00076 /******************************************************************************/ 00077 00078 KTzfileTimeZone::KTzfileTimeZone(KTzfileTimeZoneSource *source, const QString &name, 00079 const QString &countryCode, float latitude, float longitude, 00080 const QString &comment) 00081 : KTimeZone(new KTzfileTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)) 00082 {} 00083 00084 KTzfileTimeZone::~KTzfileTimeZone() 00085 {} 00086 00087 00088 /******************************************************************************/ 00089 00090 class KTzfileTimeZoneDataPrivate 00091 { 00092 public: 00093 }; 00094 00095 00096 KTzfileTimeZoneData::KTzfileTimeZoneData() 00097 // : d(new KTzfileTimeZoneDataPrivate) 00098 { } 00099 00100 KTzfileTimeZoneData::KTzfileTimeZoneData(const KTzfileTimeZoneData &rhs) 00101 : KTimeZoneData(rhs) 00102 // d(new KTzfileTimeZoneDataPrivate) 00103 { 00104 } 00105 00106 KTzfileTimeZoneData::~KTzfileTimeZoneData() 00107 { 00108 // delete d; 00109 } 00110 00111 KTzfileTimeZoneData &KTzfileTimeZoneData::operator=(const KTzfileTimeZoneData &rhs) 00112 { 00113 KTimeZoneData::operator=(rhs); 00114 return *this; 00115 } 00116 00117 KTimeZoneData *KTzfileTimeZoneData::clone() const 00118 { 00119 return new KTzfileTimeZoneData(*this); 00120 } 00121 00122 bool KTzfileTimeZoneData::hasTransitions() const 00123 { 00124 return true; 00125 } 00126 00127 00128 /******************************************************************************/ 00129 00130 class KTzfileTimeZoneSourcePrivate 00131 { 00132 public: 00133 KTzfileTimeZoneSourcePrivate(const QString &loc) 00134 : location(loc) {} 00135 ~KTzfileTimeZoneSourcePrivate() {} 00136 00137 QString location; 00138 }; 00139 00140 00141 KTzfileTimeZoneSource::KTzfileTimeZoneSource(const QString &location) 00142 : d(new KTzfileTimeZoneSourcePrivate(location)) 00143 { 00144 if (location.length() > 1 && location.endsWith(QLatin1Char('/'))) 00145 d->location.chop(1); 00146 } 00147 00148 KTzfileTimeZoneSource::~KTzfileTimeZoneSource() 00149 { 00150 delete d; 00151 } 00152 00153 QString KTzfileTimeZoneSource::location() const 00154 { 00155 return d->location; 00156 } 00157 00158 KTimeZoneData* KTzfileTimeZoneSource::parse(const KTimeZone &zone) const 00159 { 00160 quint32 abbrCharCount; // the number of characters of time zone abbreviation strings 00161 quint32 ttisgmtcnt; 00162 quint8 is; 00163 quint8 T_, Z_, i_, f_; // tzfile identifier prefix 00164 00165 QString path = zone.name(); 00166 if (!path.startsWith(QLatin1Char('/'))) 00167 { 00168 if (d->location == QLatin1String("/")) 00169 path.prepend(d->location); 00170 else 00171 path = d->location + QLatin1Char('/') + path; 00172 } 00173 QFile f(path); 00174 if (!f.open(QIODevice::ReadOnly)) 00175 { 00176 kError() << "Cannot open " << f.fileName() << endl; 00177 return 0; 00178 } 00179 QDataStream str(&f); 00180 00181 // Read the file type identifier 00182 str >> T_ >> Z_ >> i_ >> f_; 00183 if (T_ != 'T' || Z_ != 'Z' || i_ != 'i' || f_ != 'f') 00184 { 00185 kError() << "Not a TZFILE: " << f.fileName() << endl; 00186 return 0; 00187 } 00188 // Discard 16 bytes reserved for future use 00189 unsigned i; 00190 for (i = 0; i < 4; ++i) 00191 str >> ttisgmtcnt; 00192 00193 KTzfileTimeZoneData* data = new KTzfileTimeZoneData; 00194 00195 // Read the sizes of arrays held in the file 00196 quint32 nTransitionTimes; 00197 quint32 nLocalTimeTypes; 00198 quint32 nLeapSecondAdjusts; 00199 quint32 nIsStandard; 00200 quint32 nIsUtc; 00201 str >> nIsUtc 00202 >> nIsStandard 00203 >> nLeapSecondAdjusts 00204 >> nTransitionTimes 00205 >> nLocalTimeTypes 00206 >> abbrCharCount; 00207 // kDebug() << "header: " << nIsUtc << ", " << nIsStandard << ", " << nLeapSecondAdjusts << ", " << 00208 // nTransitionTimes << ", " << nLocalTimeTypes << ", " << abbrCharCount << endl; 00209 00210 // Read the transition times, at which the rules for computing local time change 00211 struct TransitionTime 00212 { 00213 qint32 time; // time (as returned by time(2)) at which the rules for computing local time change 00214 quint8 localTimeIndex; // index into the LocalTimeType array 00215 }; 00216 //kDebug()<<"Reading zone "<<zone.name(); 00217 TransitionTime *transitionTimes = new TransitionTime[nTransitionTimes]; 00218 for (i = 0; i < nTransitionTimes; ++i) 00219 { 00220 str >> transitionTimes[i].time; 00221 } 00222 for (i = 0; i < nTransitionTimes; ++i) 00223 { 00224 str >> transitionTimes[i].localTimeIndex; 00225 //kDebug() << "Transition time "<<i<<": "<<transitionTimes[i].time<<" lt index="<<(int)transitionTimes[i].localTimeIndex; 00226 } 00227 00228 // Read the local time types 00229 struct LocalTimeType 00230 { 00231 qint32 gmtoff; // number of seconds to be added to UTC 00232 bool isdst; // whether tm_isdst should be set by localtime(3) 00233 quint8 abbrIndex; // index into the list of time zone abbreviations 00234 bool isutc; // transition times are in UTC. If UTC, isstd is ignored. 00235 bool isstd; // if true, transition times are in standard time; 00236 // if false, transition times are in wall clock time, 00237 // i.e. standard time or daylight savings time 00238 // whichever is current before the transition 00239 }; 00240 LocalTimeType *localTimeTypes = new LocalTimeType[nLocalTimeTypes]; 00241 LocalTimeType *ltt = localTimeTypes; 00242 for (i = 0; i < nLocalTimeTypes; ++ltt, ++i) 00243 { 00244 str >> ltt->gmtoff; 00245 str >> is; 00246 ltt->isdst = (is != 0); 00247 str >> ltt->abbrIndex; 00248 // kDebug() << "local type: " << ltt->gmtoff << ", " << is << ", " << ltt->abbrIndex; 00249 ltt->isstd = false; // default if no data 00250 ltt->isutc = false; // default if no data 00251 } 00252 00253 // Read the timezone abbreviations. They are stored as null terminated strings in 00254 // a character array. 00255 // Make sure we don't fall foul of maliciously coded time zone abbreviations. 00256 if (abbrCharCount > 64) 00257 { 00258 kError() << "excessive length for timezone abbreviations: " << abbrCharCount << endl; 00259 delete data; 00260 delete[] transitionTimes; 00261 delete[] localTimeTypes; 00262 return 0; 00263 } 00264 QByteArray array(abbrCharCount, 0); 00265 str.readRawData(array.data(), array.size()); 00266 char *abbrs = array.data(); 00267 if (abbrs[abbrCharCount - 1] != 0) 00268 { 00269 // These abbreviations are corrupt! 00270 kError() << "timezone abbreviations not null terminated: " << abbrs[abbrCharCount - 1] << endl; 00271 delete data; 00272 delete[] transitionTimes; 00273 delete[] localTimeTypes; 00274 return 0; 00275 } 00276 quint8 n = 0; 00277 QList<QByteArray> abbreviations; 00278 for (i = 0; i < abbrCharCount; ++n, i += strlen(abbrs + i) + 1) 00279 { 00280 abbreviations += QByteArray(abbrs + i); 00281 // Convert the LocalTimeTypes pointer to a sequential index 00282 ltt = localTimeTypes; 00283 for (unsigned j = 0; j < nLocalTimeTypes; ++ltt, ++j) 00284 { 00285 if (ltt->abbrIndex == i) 00286 ltt->abbrIndex = n; 00287 } 00288 } 00289 00290 00291 // Read the leap second adjustments 00292 qint32 t; 00293 quint32 s; 00294 QList<KTimeZone::LeapSeconds> leapChanges; 00295 for (i = 0; i < nLeapSecondAdjusts; ++i) 00296 { 00297 str >> t >> s; 00298 // kDebug() << "leap entry: " << t << ", " << s; 00299 // Don't use QDateTime::setTime_t() because it takes an unsigned argument 00300 leapChanges += KTimeZone::LeapSeconds(fromTime_t(t), static_cast<int>(s)); 00301 } 00302 data->setLeapSecondChanges(leapChanges); 00303 00304 // Read the standard/wall time indicators. 00305 // These are true if the transition times associated with local time types 00306 // are specified as standard time, false if wall clock time. 00307 for (i = 0; i < nIsStandard; ++i) 00308 { 00309 str >> is; 00310 localTimeTypes[i].isstd = (is != 0); 00311 // kDebug() << "standard: " << is; 00312 } 00313 00314 // Read the UTC/local time indicators. 00315 // These are true if the transition times associated with local time types 00316 // are specified as UTC, false if local time. 00317 for (i = 0; i < nIsUtc; ++i) 00318 { 00319 str >> is; 00320 localTimeTypes[i].isutc = (is != 0); 00321 // kDebug() << "UTC: " << is; 00322 } 00323 00324 00325 // Find the starting offset from UTC to use before the first transition time. 00326 // This is first non-daylight savings local time type, or if there is none, 00327 // the first local time type. 00328 int firstoffset = (nLocalTimeTypes > 0) ? localTimeTypes[0].gmtoff : 0; 00329 ltt = localTimeTypes; 00330 for (i = 0; i < nLocalTimeTypes; ++ltt, ++i) 00331 { 00332 if (!ltt->isdst) 00333 { 00334 firstoffset = ltt->gmtoff; 00335 break; 00336 } 00337 } 00338 00339 // Compile the time type data into a list of KTimeZone::Phase instances. 00340 // Also check for local time types which are identical (this does happen) 00341 // and use the same Phase instance for each. 00342 QByteArray abbrev; 00343 QList<KTimeZone::Phase> phases; 00344 QList<QByteArray> phaseAbbrevs; 00345 QVector<int> lttLookup(nLocalTimeTypes); 00346 ltt = localTimeTypes; 00347 for (i = 0; i < nLocalTimeTypes; ++ltt, ++i) 00348 { 00349 if (ltt->abbrIndex >= abbreviations.count()) 00350 { 00351 kError() << "KTzfileTimeZoneSource::parse(): abbreviation index out of range" << endl; 00352 abbrev = "???"; 00353 } 00354 else 00355 abbrev = abbreviations[ltt->abbrIndex]; 00356 // Check for an identical Phase 00357 int phindex = 0; 00358 for (int j = 0, jend = phases.count(); j < jend; ++j, ++phindex) 00359 { 00360 if (ltt->gmtoff == phases[j].utcOffset() 00361 && (bool)ltt->isdst == phases[j].isDst() 00362 && abbrev == phaseAbbrevs[j]) 00363 break; 00364 } 00365 lttLookup[i] = phindex; 00366 if (phindex == phases.count()) 00367 { 00368 phases += KTimeZone::Phase(ltt->gmtoff, abbrev, ltt->isdst); 00369 phaseAbbrevs += abbrev; 00370 } 00371 } 00372 data->setPhases(phases, firstoffset); 00373 00374 // Compile the transition list 00375 QList<KTimeZone::Transition> transitions; 00376 int stdoffset = firstoffset; 00377 int offset = stdoffset; 00378 TransitionTime *tt = transitionTimes; 00379 for (i = 0; i < nTransitionTimes; ++tt, ++i) 00380 { 00381 if (tt->localTimeIndex >= nLocalTimeTypes) 00382 { 00383 kError() << "KTzfileTimeZoneSource::parse(): transition ignored: local time type out of range: " <<(int)tt->localTimeIndex<<" > "<<nLocalTimeTypes << endl; 00384 continue; 00385 } 00386 00387 // Convert local transition times to UTC 00388 ltt = &localTimeTypes[tt->localTimeIndex]; 00389 if (!ltt->isutc) 00390 { 00391 /* The transition time is in local time, so convert it to UTC. 00392 * If the transition is in "local wall clock time", use the UTC offset 00393 * set up by the previous transition; otherwise, the transition is in 00394 * standard time, so use the UTC offset set up by the last non-daylight 00395 * savings time transition. 00396 */ 00397 tt->time -= ltt->isstd ? stdoffset : offset; 00398 offset = ltt->gmtoff; // keep note of latest offset 00399 if (!ltt->isdst) 00400 stdoffset = offset; // keep note of latest standard time offset 00401 } 00402 00403 KTimeZone::Phase phase = phases[lttLookup[tt->localTimeIndex]]; 00404 //kDebug(161) << "Transition time "<<i<<": "<<fromTime_t(tt->time)<<", offset="<<phase.utcOffset()/60; 00405 transitions += KTimeZone::Transition(fromTime_t(tt->time), phase); 00406 } 00407 data->setTransitions(transitions); 00408 //for(int xxx=1;xxx<data->transitions().count();xxx++) 00409 //kDebug(161) << "Transition time "<<xxx<<": "<<data->transitions()[xxx].time()<<", offset="<<data->transitions()[xxx].phase().utcOffset()/60; 00410 delete[] localTimeTypes; 00411 delete[] transitionTimes; 00412 00413 return data; 00414 }
KDE 4.6 API Reference