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 days = seconds / 86400; 00046 seconds -= days * 86400; 00047 if (seconds < 0) 00048 { 00049 --days; 00050 seconds += 86400; 00051 } 00052 return QDateTime(epochDate.addDays(days), epochTime.addSecs(seconds), Qt::UTC); 00053 } 00054 00055 /******************************************************************************/ 00056 00057 KTzfileTimeZoneBackend::KTzfileTimeZoneBackend(KTzfileTimeZoneSource *source, const QString &name, 00058 const QString &countryCode, float latitude, float longitude, const QString &comment) 00059 : KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment) 00060 {} 00061 00062 KTzfileTimeZoneBackend::~KTzfileTimeZoneBackend() 00063 {} 00064 00065 KTimeZoneBackend *KTzfileTimeZoneBackend::clone() const 00066 { 00067 return new KTzfileTimeZoneBackend(*this); 00068 } 00069 00070 QByteArray KTzfileTimeZoneBackend::type() const 00071 { 00072 return "KTzfileTimeZone"; 00073 } 00074 00075 bool KTzfileTimeZoneBackend::hasTransitions(const KTimeZone *caller) const 00076 { 00077 Q_UNUSED(caller) 00078 return true; 00079 } 00080 00081 00082 /******************************************************************************/ 00083 00084 KTzfileTimeZone::KTzfileTimeZone(KTzfileTimeZoneSource *source, const QString &name, 00085 const QString &countryCode, float latitude, float longitude, 00086 const QString &comment) 00087 : KTimeZone(new KTzfileTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)) 00088 {} 00089 00090 KTzfileTimeZone::~KTzfileTimeZone() 00091 {} 00092 00093 00094 /******************************************************************************/ 00095 00096 class KTzfileTimeZoneDataPrivate 00097 { 00098 public: 00099 }; 00100 00101 00102 KTzfileTimeZoneData::KTzfileTimeZoneData() 00103 // : d(new KTzfileTimeZoneDataPrivate) 00104 { } 00105 00106 KTzfileTimeZoneData::KTzfileTimeZoneData(const KTzfileTimeZoneData &rhs) 00107 : KTimeZoneData(rhs) 00108 // d(new KTzfileTimeZoneDataPrivate) 00109 { 00110 } 00111 00112 KTzfileTimeZoneData::~KTzfileTimeZoneData() 00113 { 00114 // delete d; 00115 } 00116 00117 KTzfileTimeZoneData &KTzfileTimeZoneData::operator=(const KTzfileTimeZoneData &rhs) 00118 { 00119 KTimeZoneData::operator=(rhs); 00120 return *this; 00121 } 00122 00123 KTimeZoneData *KTzfileTimeZoneData::clone() const 00124 { 00125 return new KTzfileTimeZoneData(*this); 00126 } 00127 00128 bool KTzfileTimeZoneData::hasTransitions() const 00129 { 00130 return true; 00131 } 00132 00133 00134 /******************************************************************************/ 00135 00136 class KTzfileTimeZoneSourcePrivate 00137 { 00138 public: 00139 KTzfileTimeZoneSourcePrivate(const QString &loc) 00140 : location(loc) {} 00141 ~KTzfileTimeZoneSourcePrivate() {} 00142 00143 QString location; 00144 }; 00145 00146 00147 KTzfileTimeZoneSource::KTzfileTimeZoneSource(const QString &location) 00148 : d(new KTzfileTimeZoneSourcePrivate(location)) 00149 { 00150 if (location.length() > 1 && location.endsWith(QLatin1Char('/'))) 00151 d->location.chop(1); 00152 } 00153 00154 KTzfileTimeZoneSource::~KTzfileTimeZoneSource() 00155 { 00156 delete d; 00157 } 00158 00159 QString KTzfileTimeZoneSource::location() const 00160 { 00161 return d->location; 00162 } 00163 00164 KTimeZoneData* KTzfileTimeZoneSource::parse(const KTimeZone &zone) const 00165 { 00166 quint32 abbrCharCount; // the number of characters of time zone abbreviation strings 00167 quint32 ttisgmtcnt; 00168 quint8 is; 00169 quint8 T_, Z_, i_, f_; // tzfile identifier prefix 00170 00171 QString path = zone.name(); 00172 if (!path.startsWith(QLatin1Char('/'))) 00173 { 00174 if (d->location == QLatin1String("/")) 00175 path.prepend(d->location); 00176 else 00177 path = d->location + QLatin1Char('/') + path; 00178 } 00179 QFile f(path); 00180 if (!f.open(QIODevice::ReadOnly)) 00181 { 00182 kError() << "Cannot open " << f.fileName() << endl; 00183 return 0; 00184 } 00185 QDataStream str(&f); 00186 00187 // Read the file type identifier 00188 str >> T_ >> Z_ >> i_ >> f_; 00189 if (T_ != 'T' || Z_ != 'Z' || i_ != 'i' || f_ != 'f') 00190 { 00191 kError() << "Not a TZFILE: " << f.fileName() << endl; 00192 return 0; 00193 } 00194 // Discard 16 bytes reserved for future use 00195 unsigned i; 00196 for (i = 0; i < 4; ++i) 00197 str >> ttisgmtcnt; 00198 00199 KTzfileTimeZoneData* data = new KTzfileTimeZoneData; 00200 00201 // Read the sizes of arrays held in the file 00202 quint32 nTransitionTimes; 00203 quint32 nLocalTimeTypes; 00204 quint32 nLeapSecondAdjusts; 00205 quint32 nIsStandard; 00206 quint32 nIsUtc; 00207 str >> nIsUtc 00208 >> nIsStandard 00209 >> nLeapSecondAdjusts 00210 >> nTransitionTimes 00211 >> nLocalTimeTypes 00212 >> abbrCharCount; 00213 // kDebug() << "header: " << nIsUtc << ", " << nIsStandard << ", " << nLeapSecondAdjusts << ", " << 00214 // nTransitionTimes << ", " << nLocalTimeTypes << ", " << abbrCharCount << endl; 00215 00216 // Read the transition times, at which the rules for computing local time change 00217 struct TransitionTime 00218 { 00219 qint32 time; // time (as returned by time(2)) at which the rules for computing local time change 00220 quint8 localTimeIndex; // index into the LocalTimeType array 00221 }; 00222 //kDebug()<<"Reading zone "<<zone.name(); 00223 TransitionTime *transitionTimes = new TransitionTime[nTransitionTimes]; 00224 for (i = 0; i < nTransitionTimes; ++i) 00225 { 00226 str >> transitionTimes[i].time; 00227 } 00228 for (i = 0; i < nTransitionTimes; ++i) 00229 { 00230 str >> transitionTimes[i].localTimeIndex; 00231 //kDebug() << "Transition time "<<i<<": "<<transitionTimes[i].time<<" lt index="<<(int)transitionTimes[i].localTimeIndex; 00232 } 00233 00234 // Read the local time types 00235 struct LocalTimeType 00236 { 00237 qint32 gmtoff; // number of seconds to be added to UTC 00238 bool isdst; // whether tm_isdst should be set by localtime(3) 00239 quint8 abbrIndex; // index into the list of time zone abbreviations 00240 bool isutc; // transition times are in UTC. If UTC, isstd is ignored. 00241 bool isstd; // if true, transition times are in standard time; 00242 // if false, transition times are in wall clock time, 00243 // i.e. standard time or daylight savings time 00244 // whichever is current before the transition 00245 }; 00246 LocalTimeType *localTimeTypes = new LocalTimeType[nLocalTimeTypes]; 00247 LocalTimeType *ltt = localTimeTypes; 00248 for (i = 0; i < nLocalTimeTypes; ++ltt, ++i) 00249 { 00250 str >> ltt->gmtoff; 00251 str >> is; 00252 ltt->isdst = (is != 0); 00253 str >> ltt->abbrIndex; 00254 // kDebug() << "local type: " << ltt->gmtoff << ", " << is << ", " << ltt->abbrIndex; 00255 ltt->isstd = false; // default if no data 00256 ltt->isutc = false; // default if no data 00257 } 00258 00259 // Read the timezone abbreviations. They are stored as null terminated strings in 00260 // a character array. 00261 // Make sure we don't fall foul of maliciously coded time zone abbreviations. 00262 if (abbrCharCount > 64) 00263 { 00264 kError() << "excessive length for timezone abbreviations: " << abbrCharCount << endl; 00265 delete data; 00266 delete[] transitionTimes; 00267 delete[] localTimeTypes; 00268 return 0; 00269 } 00270 QByteArray array(abbrCharCount, 0); 00271 str.readRawData(array.data(), array.size()); 00272 const char *abbrs = array.constData(); 00273 if (abbrs[abbrCharCount - 1] != 0) 00274 { 00275 // These abbreviations are corrupt! 00276 kError() << "timezone abbreviations not null terminated: " << abbrs[abbrCharCount - 1] << endl; 00277 delete data; 00278 delete[] transitionTimes; 00279 delete[] localTimeTypes; 00280 return 0; 00281 } 00282 quint8 n = 0; 00283 QList<QByteArray> abbreviations; 00284 for (i = 0; i < abbrCharCount; ++n, i += strlen(abbrs + i) + 1) 00285 { 00286 abbreviations += QByteArray(abbrs + i); 00287 // Convert the LocalTimeTypes pointer to a sequential index 00288 ltt = localTimeTypes; 00289 for (unsigned j = 0; j < nLocalTimeTypes; ++ltt, ++j) 00290 { 00291 if (ltt->abbrIndex == i) 00292 ltt->abbrIndex = n; 00293 } 00294 } 00295 00296 00297 // Read the leap second adjustments 00298 qint32 t; 00299 quint32 s; 00300 QList<KTimeZone::LeapSeconds> leapChanges; 00301 for (i = 0; i < nLeapSecondAdjusts; ++i) 00302 { 00303 str >> t >> s; 00304 // kDebug() << "leap entry: " << t << ", " << s; 00305 // Don't use QDateTime::setTime_t() because it takes an unsigned argument 00306 leapChanges += KTimeZone::LeapSeconds(fromTime_t(t), static_cast<int>(s)); 00307 } 00308 data->setLeapSecondChanges(leapChanges); 00309 00310 // Read the standard/wall time indicators. 00311 // These are true if the transition times associated with local time types 00312 // are specified as standard time, false if wall clock time. 00313 for (i = 0; i < nIsStandard; ++i) 00314 { 00315 str >> is; 00316 localTimeTypes[i].isstd = (is != 0); 00317 // kDebug() << "standard: " << is; 00318 } 00319 00320 // Read the UTC/local time indicators. 00321 // These are true if the transition times associated with local time types 00322 // are specified as UTC, false if local time. 00323 for (i = 0; i < nIsUtc; ++i) 00324 { 00325 str >> is; 00326 localTimeTypes[i].isutc = (is != 0); 00327 // kDebug() << "UTC: " << is; 00328 } 00329 00330 00331 // Find the starting offset from UTC to use before the first transition time. 00332 // This is first non-daylight savings local time type, or if there is none, 00333 // the first local time type. 00334 LocalTimeType* firstLtt = 0; 00335 ltt = localTimeTypes; 00336 for (i = 0; i < nLocalTimeTypes; ++ltt, ++i) 00337 { 00338 if (!ltt->isdst) 00339 { 00340 firstLtt = ltt; 00341 break; 00342 } 00343 } 00344 00345 // Compile the time type data into a list of KTimeZone::Phase instances. 00346 // Also check for local time types which are identical (this does happen) 00347 // and use the same Phase instance for each. 00348 QByteArray abbrev; 00349 QList<KTimeZone::Phase> phases; 00350 QList<QByteArray> phaseAbbrevs; 00351 QVector<int> lttLookup(nLocalTimeTypes); 00352 ltt = localTimeTypes; 00353 for (i = 0; i < nLocalTimeTypes; ++ltt, ++i) 00354 { 00355 if (ltt->abbrIndex >= abbreviations.count()) 00356 { 00357 kError() << "KTzfileTimeZoneSource::parse(): abbreviation index out of range" << endl; 00358 abbrev = "???"; 00359 } 00360 else 00361 abbrev = abbreviations[ltt->abbrIndex]; 00362 // Check for an identical Phase 00363 int phindex = 0; 00364 for (int j = 0, jend = phases.count(); j < jend; ++j, ++phindex) 00365 { 00366 if (ltt->gmtoff == phases[j].utcOffset() 00367 && (bool)ltt->isdst == phases[j].isDst() 00368 && abbrev == phaseAbbrevs[j]) 00369 break; 00370 } 00371 lttLookup[i] = phindex; 00372 if (phindex == phases.count()) 00373 { 00374 phases += KTimeZone::Phase(ltt->gmtoff, abbrev, ltt->isdst); 00375 phaseAbbrevs += abbrev; 00376 } 00377 } 00378 KTimeZone::Phase prePhase(firstLtt->gmtoff, 00379 (firstLtt->abbrIndex < abbreviations.count() ? abbreviations[firstLtt->abbrIndex] : ""), 00380 false); 00381 data->setPhases(phases, prePhase); 00382 00383 // Compile the transition list 00384 QList<KTimeZone::Transition> transitions; 00385 TransitionTime *tt = transitionTimes; 00386 for (i = 0; i < nTransitionTimes; ++tt, ++i) 00387 { 00388 if (tt->localTimeIndex >= nLocalTimeTypes) 00389 { 00390 kError() << "KTzfileTimeZoneSource::parse(): transition ignored: local time type out of range: " <<(int)tt->localTimeIndex<<" > "<<nLocalTimeTypes << endl; 00391 continue; 00392 } 00393 00394 // Convert local transition times to UTC 00395 ltt = &localTimeTypes[tt->localTimeIndex]; 00396 const KTimeZone::Phase phase = phases[lttLookup[tt->localTimeIndex]]; 00397 //kDebug(161) << "Transition time "<<i<<": "<<fromTime_t(tt->time)<<", offset="<<phase.utcOffset()/60; 00398 transitions += KTimeZone::Transition(fromTime_t(tt->time), phase); 00399 } 00400 data->setTransitions(transitions); 00401 //for(int xxx=1;xxx<data->transitions().count();xxx++) 00402 //kDebug(161) << "Transition time "<<xxx<<": "<<data->transitions()[xxx].time()<<", offset="<<data->transitions()[xxx].phase().utcOffset()/60; 00403 delete[] localTimeTypes; 00404 delete[] transitionTimes; 00405 00406 return data; 00407 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 17:05:27 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 17:05:27 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.