KDECore
ktimezone.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of the KDE libraries 00003 Copyright (c) 2005-2008,2011 David Jarvie <djarvie@kde.org> 00004 Copyright (c) 2005 S.R.Haque <srhaque@iee.org>. 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include "ktimezone.h" 00023 00024 #include <config.h> 00025 #include <config-date.h> // SIZEOF_TIME_T 00026 00027 #ifdef HAVE_SYS_TIME_H 00028 #include <sys/time.h> 00029 #endif 00030 #ifdef HAVE_TIME_H 00031 #include <time.h> 00032 #endif 00033 #include <climits> 00034 #include <cstdlib> 00035 00036 #include <QtCore/QSet> 00037 #include <QtCore/QSharedData> 00038 #include <QtCore/QCoreApplication> 00039 00040 #include <kdebug.h> 00041 #include <kglobal.h> 00042 00043 int gmtoff(time_t t); // defined in ksystemtimezone.cpp 00044 00045 00046 /******************************************************************************/ 00047 00048 class KTimeZonesPrivate 00049 { 00050 public: 00051 KTimeZonesPrivate() {} 00052 00053 KTimeZones::ZoneMap zones; 00054 }; 00055 00056 00057 KTimeZones::KTimeZones() 00058 : d(new KTimeZonesPrivate) 00059 { 00060 } 00061 00062 KTimeZones::~KTimeZones() 00063 { 00064 delete d; 00065 } 00066 00067 const KTimeZones::ZoneMap KTimeZones::zones() const 00068 { 00069 return d->zones; 00070 } 00071 00072 bool KTimeZones::add(const KTimeZone &zone) 00073 { 00074 if (!zone.isValid()) 00075 return false; 00076 if (d->zones.find(zone.name()) != d->zones.end()) 00077 return false; // name already exists 00078 d->zones.insert(zone.name(), zone); 00079 return true; 00080 } 00081 00082 KTimeZone KTimeZones::remove(const KTimeZone &zone) 00083 { 00084 if (zone.isValid()) 00085 { 00086 for (ZoneMap::Iterator it = d->zones.begin(), end = d->zones.end(); it != end; ++it) 00087 { 00088 if (it.value() == zone) 00089 { 00090 d->zones.erase(it); 00091 return zone; 00092 } 00093 } 00094 } 00095 return KTimeZone(); 00096 } 00097 00098 KTimeZone KTimeZones::remove(const QString &name) 00099 { 00100 if (!name.isEmpty()) 00101 { 00102 ZoneMap::Iterator it = d->zones.find(name); 00103 if (it != d->zones.end()) 00104 { 00105 KTimeZone zone = it.value(); 00106 d->zones.erase(it); 00107 return zone; 00108 } 00109 } 00110 return KTimeZone(); 00111 } 00112 00113 void KTimeZones::clear() 00114 { 00115 d->zones.clear(); 00116 } 00117 00118 KTimeZone KTimeZones::zone(const QString &name) const 00119 { 00120 if (!name.isEmpty()) 00121 { 00122 ZoneMap::ConstIterator it = d->zones.constFind(name); 00123 if (it != d->zones.constEnd()) 00124 return it.value(); 00125 if (name == KTimeZone::utc().name()) 00126 return KTimeZone::utc(); 00127 } 00128 return KTimeZone(); // error 00129 } 00130 00131 00132 /******************************************************************************/ 00133 00134 class KTimeZonePhasePrivate : public QSharedData 00135 { 00136 public: 00137 QByteArray abbreviations; // time zone abbreviations (zero-delimited) 00138 QString comment; // optional comment 00139 int utcOffset; // seconds to add to UTC 00140 bool dst; // true if daylight savings time 00141 00142 explicit KTimeZonePhasePrivate(int offset = 0, bool ds = false) 00143 : QSharedData(), 00144 utcOffset(offset), 00145 dst(ds) 00146 {} 00147 KTimeZonePhasePrivate(const KTimeZonePhasePrivate& rhs) 00148 : QSharedData(rhs), 00149 abbreviations(rhs.abbreviations), 00150 comment(rhs.comment), 00151 utcOffset(rhs.utcOffset), 00152 dst(rhs.dst) 00153 {} 00154 bool operator==(const KTimeZonePhasePrivate &rhs) const 00155 { 00156 return abbreviations == rhs.abbreviations 00157 && comment == rhs.comment 00158 && utcOffset == rhs.utcOffset 00159 && dst == rhs.dst; 00160 } 00161 }; 00162 00163 00164 KTimeZone::Phase::Phase() 00165 : d(new KTimeZonePhasePrivate) 00166 { 00167 } 00168 00169 KTimeZone::Phase::Phase(int utcOffset, const QByteArray &abbrevs, 00170 bool dst, const QString &cmt) 00171 : d(new KTimeZonePhasePrivate(utcOffset, dst)) 00172 { 00173 d->abbreviations = abbrevs; 00174 d->comment = cmt; 00175 } 00176 00177 KTimeZone::Phase::Phase(int utcOffset, const QList<QByteArray> &abbrevs, 00178 bool dst, const QString &cmt) 00179 : d(new KTimeZonePhasePrivate(utcOffset, dst)) 00180 { 00181 for (int i = 0, end = abbrevs.count(); i < end; ++i) 00182 { 00183 if (i > 0) 00184 d->abbreviations += '\0'; 00185 d->abbreviations += abbrevs[i]; 00186 } 00187 d->comment = cmt; 00188 } 00189 00190 KTimeZone::Phase::Phase(const KTimeZone::Phase &rhs) 00191 : d(rhs.d) 00192 { 00193 } 00194 00195 KTimeZone::Phase::~Phase() 00196 { 00197 } 00198 00199 KTimeZone::Phase &KTimeZone::Phase::operator=(const KTimeZone::Phase &rhs) 00200 { 00201 d = rhs.d; 00202 return *this; 00203 } 00204 00205 bool KTimeZone::Phase::operator==(const KTimeZone::Phase &rhs) const 00206 { 00207 return d == rhs.d || *d == *rhs.d; 00208 } 00209 00210 int KTimeZone::Phase::utcOffset() const 00211 { 00212 return d->utcOffset; 00213 } 00214 00215 QList<QByteArray> KTimeZone::Phase::abbreviations() const 00216 { 00217 return d->abbreviations.split('\0'); 00218 } 00219 00220 bool KTimeZone::Phase::isDst() const 00221 { 00222 return d->dst; 00223 } 00224 00225 QString KTimeZone::Phase::comment() const 00226 { 00227 return d->comment; 00228 } 00229 00230 00231 /******************************************************************************/ 00232 00233 class KTimeZoneTransitionPrivate 00234 { 00235 public: 00236 QDateTime time; 00237 KTimeZone::Phase phase; 00238 }; 00239 00240 00241 KTimeZone::Transition::Transition() 00242 : d(new KTimeZoneTransitionPrivate) 00243 { 00244 } 00245 00246 KTimeZone::Transition::Transition(const QDateTime &t, const KTimeZone::Phase &p) 00247 : d(new KTimeZoneTransitionPrivate) 00248 { 00249 d->time = t; 00250 d->phase = p; 00251 } 00252 00253 KTimeZone::Transition::Transition(const KTimeZone::Transition &t) 00254 : d(new KTimeZoneTransitionPrivate) 00255 { 00256 d->time = t.d->time; 00257 d->phase = t.d->phase; 00258 } 00259 00260 KTimeZone::Transition::~Transition() 00261 { 00262 delete d; 00263 } 00264 00265 KTimeZone::Transition &KTimeZone::Transition::operator=(const KTimeZone::Transition &t) 00266 { 00267 d->time = t.d->time; 00268 d->phase = t.d->phase; 00269 return *this; 00270 } 00271 00272 bool KTimeZone::Transition::operator<(const KTimeZone::Transition &rhs) const 00273 { 00274 return d->time < rhs.d->time; 00275 } 00276 00277 QDateTime KTimeZone::Transition::time() const { return d->time; } 00278 KTimeZone::Phase KTimeZone::Transition::phase() const { return d->phase; } 00279 00280 00281 /******************************************************************************/ 00282 00283 class KTimeZoneDataPrivate 00284 { 00285 public: 00286 QList<KTimeZone::Phase> phases; 00287 QList<KTimeZone::Transition> transitions; 00288 QList<KTimeZone::LeapSeconds> leapChanges; 00289 QList<int> utcOffsets; 00290 QList<QByteArray> abbreviations; 00291 int preUtcOffset; // UTC offset to use before the first phase 00292 00293 KTimeZoneDataPrivate() : preUtcOffset(0) {} 00294 // Find the last transition before a specified UTC or local date/time. 00295 int transitionIndex(const QDateTime &dt) const; 00296 bool transitionIndexes(const QDateTime &start, const QDateTime &end, int &ixstart, int &ixend) const; 00297 bool isSecondOccurrence(const QDateTime &utcLocalTime, int transitionIndex) const; 00298 }; 00299 00300 00301 /******************************************************************************/ 00302 00303 class KTimeZonePrivate : public QSharedData 00304 { 00305 public: 00306 KTimeZonePrivate() : source(0), data(0), refCount(1), cachedTransitionIndex(-1) {} 00307 KTimeZonePrivate(KTimeZoneSource *src, const QString& nam, 00308 const QString &country, float lat, float lon, const QString &cmnt); 00309 KTimeZonePrivate(const KTimeZonePrivate &); 00310 ~KTimeZonePrivate() { delete data; } 00311 KTimeZonePrivate &operator=(const KTimeZonePrivate &); 00312 static KTimeZoneSource *utcSource(); 00313 static void cleanup(); 00314 00315 KTimeZoneSource *source; 00316 QString name; 00317 QString countryCode; 00318 QString comment; 00319 float latitude; 00320 float longitude; 00321 mutable KTimeZoneData *data; 00322 int refCount; // holds the number of KTimeZoneBackend instances using the KTimeZonePrivate instance as a d-pointer. 00323 int cachedTransitionIndex; 00324 QDateTime cachedTransitionStartZoneTime; 00325 QDateTime cachedTransitionEndZoneTime; 00326 bool cachedTransitionTimesValid; 00327 00328 private: 00329 static KTimeZoneSource *mUtcSource; 00330 }; 00331 00332 KTimeZoneSource *KTimeZonePrivate::mUtcSource = 0; 00333 00334 00335 KTimeZonePrivate::KTimeZonePrivate(KTimeZoneSource *src, const QString& nam, 00336 const QString &country, float lat, float lon, const QString &cmnt) 00337 : source(src), 00338 name(nam), 00339 countryCode(country.toUpper()), 00340 comment(cmnt), 00341 latitude(lat), 00342 longitude(lon), 00343 data(0), 00344 refCount(1), 00345 cachedTransitionIndex(-1) 00346 { 00347 // Detect duff values. 00348 if (latitude > 90 || latitude < -90) 00349 latitude = KTimeZone::UNKNOWN; 00350 if (longitude > 180 || longitude < -180) 00351 longitude = KTimeZone::UNKNOWN; 00352 } 00353 00354 KTimeZonePrivate::KTimeZonePrivate(const KTimeZonePrivate &rhs) 00355 : QSharedData(rhs), 00356 source(rhs.source), 00357 name(rhs.name), 00358 countryCode(rhs.countryCode), 00359 comment(rhs.comment), 00360 latitude(rhs.latitude), 00361 longitude(rhs.longitude), 00362 refCount(1), 00363 cachedTransitionIndex(rhs.cachedTransitionIndex), 00364 cachedTransitionStartZoneTime(rhs.cachedTransitionStartZoneTime), 00365 cachedTransitionEndZoneTime(rhs.cachedTransitionEndZoneTime), 00366 cachedTransitionTimesValid(rhs.cachedTransitionTimesValid) 00367 { 00368 if (rhs.data) 00369 data = rhs.data->clone(); 00370 else 00371 data = 0; 00372 } 00373 00374 KTimeZonePrivate &KTimeZonePrivate::operator=(const KTimeZonePrivate &rhs) 00375 { 00376 // Changing the contents of a KTimeZonePrivate instance by means of operator=() doesn't affect how 00377 // many references to it are held. 00378 source = rhs.source; 00379 name = rhs.name; 00380 countryCode = rhs.countryCode; 00381 comment = rhs.comment; 00382 latitude = rhs.latitude; 00383 longitude = rhs.longitude; 00384 cachedTransitionIndex = rhs.cachedTransitionIndex; 00385 cachedTransitionStartZoneTime = rhs.cachedTransitionStartZoneTime; 00386 cachedTransitionEndZoneTime = rhs.cachedTransitionEndZoneTime; 00387 cachedTransitionTimesValid = rhs.cachedTransitionTimesValid; 00388 delete data; 00389 if (rhs.data) 00390 data = rhs.data->clone(); 00391 else 00392 data = 0; 00393 // refCount is unchanged 00394 return *this; 00395 } 00396 00397 KTimeZoneSource *KTimeZonePrivate::utcSource() 00398 { 00399 if (!mUtcSource) 00400 { 00401 mUtcSource = new KTimeZoneSource; 00402 qAddPostRoutine(KTimeZonePrivate::cleanup); 00403 } 00404 return mUtcSource; 00405 } 00406 00407 void KTimeZonePrivate::cleanup() 00408 { 00409 delete mUtcSource; 00410 } 00411 00412 00413 /******************************************************************************/ 00414 00415 K_GLOBAL_STATIC(KTimeZonePrivate, s_emptyTimeZonePrivate) 00416 00417 KTimeZoneBackend::KTimeZoneBackend() 00418 : d(&*s_emptyTimeZonePrivate) 00419 { 00420 ++d->refCount; 00421 } 00422 00423 KTimeZoneBackend::KTimeZoneBackend(const QString &name) 00424 : d(new KTimeZonePrivate(KTimeZonePrivate::utcSource(), name, QString(), KTimeZone::UNKNOWN, KTimeZone::UNKNOWN, QString())) 00425 {} 00426 00427 KTimeZoneBackend::KTimeZoneBackend(KTimeZoneSource *source, const QString &name, 00428 const QString &countryCode, float latitude, float longitude, const QString &comment) 00429 : d(new KTimeZonePrivate(source, name, countryCode, latitude, longitude, comment)) 00430 {} 00431 00432 KTimeZoneBackend::KTimeZoneBackend(const KTimeZoneBackend &other) 00433 : d(other.d) 00434 { 00435 ++d->refCount; 00436 } 00437 00438 KTimeZoneBackend::~KTimeZoneBackend() 00439 { 00440 if (d && --d->refCount == 0) 00441 delete d; 00442 d = 0; 00443 } 00444 00445 KTimeZoneBackend &KTimeZoneBackend::operator=(const KTimeZoneBackend &other) 00446 { 00447 if (d != other.d) 00448 { 00449 if (--d->refCount == 0) 00450 delete d; 00451 d = other.d; 00452 ++d->refCount; 00453 } 00454 return *this; 00455 } 00456 00457 QByteArray KTimeZoneBackend::type() const 00458 { 00459 return "KTimeZone"; 00460 } 00461 00462 KTimeZoneBackend *KTimeZoneBackend::clone() const 00463 { 00464 return new KTimeZoneBackend(*this); 00465 } 00466 00467 int KTimeZoneBackend::offsetAtZoneTime(const KTimeZone* caller, const QDateTime &zoneDateTime, int *secondOffset) const 00468 { 00469 if (!zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime) // check for invalid time 00470 { 00471 if (secondOffset) 00472 *secondOffset = 0; 00473 return 0; 00474 } 00475 QList<KTimeZone::Transition> transitions = caller->transitions(); 00476 int index = d->cachedTransitionIndex; 00477 if (index >= 0 && index < transitions.count()) 00478 { 00479 // There is a cached transition - check whether zoneDateTime uses it. 00480 // Caching is used because this method has been found to consume 00481 // significant CPU in real life applications. 00482 if (!d->cachedTransitionTimesValid) 00483 { 00484 int offset = transitions[index].phase().utcOffset(); 00485 int preoffset = (index > 0) ? transitions[index - 1].phase().utcOffset() : d->data ? d->data->previousUtcOffset() : 0; 00486 d->cachedTransitionStartZoneTime = transitions[index].time().addSecs(qMax(offset, preoffset)); 00487 if (index + 1 < transitions.count()) 00488 { 00489 int postoffset = transitions[index + 1].phase().utcOffset(); 00490 d->cachedTransitionEndZoneTime = transitions[index + 1].time().addSecs(qMin(offset, postoffset)); 00491 } 00492 d->cachedTransitionTimesValid = true; 00493 } 00494 QDateTime dtutc = zoneDateTime; 00495 dtutc.setTimeSpec(Qt::UTC); 00496 if (dtutc >= d->cachedTransitionStartZoneTime 00497 && (index + 1 >= transitions.count() || dtutc < d->cachedTransitionEndZoneTime)) 00498 { 00499 // The time falls within the cached transition limits, so return its UTC offset 00500 int offset = transitions[index].phase().utcOffset(); 00501 if (secondOffset) 00502 *secondOffset = offset; 00503 #ifdef COMPILING_TESTS 00504 qDebug("-> Using cache"); // test output requires qDebug instead of kDebug 00505 #endif 00506 return offset; 00507 } 00508 } 00509 00510 // The time doesn't fall within the cached transition, or there isn't a cached transition 00511 #ifdef COMPILING_TESTS 00512 qDebug("-> No cache"); // test output requires qDebug instead of kDebug 00513 #endif 00514 bool validTime; 00515 int secondIndex = -1; 00516 index = caller->transitionIndex(zoneDateTime, (secondOffset ? &secondIndex : 0), &validTime); 00517 const KTimeZone::Transition* tr = (index >= 0) ? &transitions[index] : 0; 00518 int offset = tr ? tr->phase().utcOffset() 00519 : validTime ? (d->data ? d->data->previousUtcOffset() : 0) 00520 : KTimeZone::InvalidOffset; 00521 if (secondOffset) 00522 *secondOffset = (secondIndex >= 0) ? transitions[secondIndex].phase().utcOffset() : offset; 00523 00524 // Cache transition data for subsequent date/time values which occur after the same transition. 00525 d->cachedTransitionIndex = index; 00526 d->cachedTransitionTimesValid = false; 00527 return offset; 00528 } 00529 00530 int KTimeZoneBackend::offsetAtUtc(const KTimeZone* caller, const QDateTime &utcDateTime) const 00531 { 00532 if (!utcDateTime.isValid() || utcDateTime.timeSpec() != Qt::UTC) // check for invalid time 00533 return 0; 00534 QList<KTimeZone::Transition> transitions = caller->transitions(); 00535 int index = d->cachedTransitionIndex; 00536 if (index >= 0 && index < transitions.count()) 00537 { 00538 // There is a cached transition - check whether utcDateTime uses it. 00539 int offset = transitions[index].phase().utcOffset(); 00540 if (utcDateTime >= transitions[index].time() 00541 && (index + 1 >= transitions.count() 00542 || utcDateTime < transitions[index + 1].time())) 00543 { 00544 // The time falls within the cached transition, so return its UTC offset 00545 #ifdef COMPILING_TESTS 00546 qDebug("Using cache"); // test output requires qDebug instead of kDebug 00547 #endif 00548 return offset; 00549 } 00550 } 00551 00552 // The time doesn't fall within the cached transition, or there isn't a cached transition 00553 #ifdef COMPILING_TESTS 00554 qDebug("No cache"); // test output requires qDebug instead of kDebug 00555 #endif 00556 index = caller->transitionIndex(utcDateTime); 00557 d->cachedTransitionIndex = index; // cache transition data 00558 d->cachedTransitionTimesValid = false; 00559 const KTimeZone::Transition* tr = (index >= 0) ? &transitions[index] : 0; 00560 return tr ? tr->phase().utcOffset() : (d->data ? d->data->previousUtcOffset() : 0); 00561 } 00562 00563 int KTimeZoneBackend::offset(const KTimeZone* caller, time_t t) const 00564 { 00565 return offsetAtUtc(caller, KTimeZone::fromTime_t(t)); 00566 } 00567 00568 bool KTimeZoneBackend::isDstAtUtc(const KTimeZone* caller, const QDateTime &utcDateTime) const 00569 { 00570 if (!utcDateTime.isValid() || utcDateTime.timeSpec() != Qt::UTC) // check for invalid time 00571 return false; 00572 const KTimeZone::Transition *tr = caller->transition(utcDateTime); 00573 if (!tr) 00574 return false; 00575 return tr->phase().isDst(); 00576 } 00577 00578 bool KTimeZoneBackend::isDst(const KTimeZone* caller, time_t t) const 00579 { 00580 return isDstAtUtc(caller, KTimeZone::fromTime_t(t)); 00581 } 00582 00583 bool KTimeZoneBackend::hasTransitions(const KTimeZone* caller) const 00584 { 00585 Q_UNUSED(caller); 00586 return false; 00587 } 00588 00589 00590 /******************************************************************************/ 00591 00592 #if SIZEOF_TIME_T == 8 00593 const time_t KTimeZone::InvalidTime_t = 0x800000000000000LL; 00594 #else 00595 const time_t KTimeZone::InvalidTime_t = 0x80000000; 00596 #endif 00597 const int KTimeZone::InvalidOffset = 0x80000000; 00598 const float KTimeZone::UNKNOWN = 1000.0; 00599 00600 00601 KTimeZone::KTimeZone() 00602 : d(new KTimeZoneBackend()) 00603 {} 00604 00605 KTimeZone::KTimeZone(const QString &name) 00606 : d(new KTimeZoneBackend(name)) 00607 {} 00608 00609 KTimeZone::KTimeZone(const KTimeZone &tz) 00610 : d(tz.d->clone()) 00611 {} 00612 00613 KTimeZone::~KTimeZone() 00614 { 00615 delete d; 00616 } 00617 00618 KTimeZone::KTimeZone(KTimeZoneBackend *impl) 00619 : d(impl) 00620 { 00621 // 'impl' should be a newly constructed object, with refCount = 1 00622 Q_ASSERT(d->d->refCount == 1 || d->d == &*s_emptyTimeZonePrivate); 00623 } 00624 00625 KTimeZone &KTimeZone::operator=(const KTimeZone &tz) 00626 { 00627 if (d != tz.d) 00628 { 00629 delete d; 00630 d = tz.d->clone(); 00631 } 00632 return *this; 00633 } 00634 00635 bool KTimeZone::operator==(const KTimeZone &rhs) const 00636 { 00637 return d->d == rhs.d->d; 00638 } 00639 00640 QByteArray KTimeZone::type() const 00641 { 00642 return d->type(); 00643 } 00644 00645 bool KTimeZone::isValid() const 00646 { 00647 return !d->d->name.isEmpty(); 00648 } 00649 00650 QString KTimeZone::countryCode() const 00651 { 00652 return d->d->countryCode; 00653 } 00654 00655 float KTimeZone::latitude() const 00656 { 00657 return d->d->latitude; 00658 } 00659 00660 float KTimeZone::longitude() const 00661 { 00662 return d->d->longitude; 00663 } 00664 00665 QString KTimeZone::comment() const 00666 { 00667 return d->d->comment; 00668 } 00669 00670 QString KTimeZone::name() const 00671 { 00672 return d->d->name; 00673 } 00674 00675 QList<QByteArray> KTimeZone::abbreviations() const 00676 { 00677 if (!data(true)) 00678 return QList<QByteArray>(); 00679 return d->d->data->abbreviations(); 00680 } 00681 00682 QByteArray KTimeZone::abbreviation(const QDateTime &utcDateTime) const 00683 { 00684 if (utcDateTime.timeSpec() != Qt::UTC || !data(true)) 00685 return QByteArray(); 00686 return d->d->data->abbreviation(utcDateTime); 00687 } 00688 00689 QList<int> KTimeZone::utcOffsets() const 00690 { 00691 if (!data(true)) 00692 return QList<int>(); 00693 return d->d->data->utcOffsets(); 00694 } 00695 00696 QList<KTimeZone::Phase> KTimeZone::phases() const 00697 { 00698 if (!data(true)) 00699 return QList<KTimeZone::Phase>(); 00700 return d->d->data->phases(); 00701 } 00702 00703 bool KTimeZone::hasTransitions() const 00704 { 00705 return d->hasTransitions(this); 00706 } 00707 00708 QList<KTimeZone::Transition> KTimeZone::transitions(const QDateTime &start, const QDateTime &end) const 00709 { 00710 if (!data(true)) 00711 return QList<KTimeZone::Transition>(); 00712 return d->d->data->transitions(start, end); 00713 } 00714 00715 const KTimeZone::Transition *KTimeZone::transition(const QDateTime &dt, const Transition **secondTransition, 00716 bool *validTime) const 00717 { 00718 if (!data(true)) 00719 return 0; 00720 return d->d->data->transition(dt, secondTransition, validTime); 00721 } 00722 00723 int KTimeZone::transitionIndex(const QDateTime &dt, int *secondIndex, bool *validTime) const 00724 { 00725 if (!data(true)) 00726 return -1; 00727 return d->d->data->transitionIndex(dt, secondIndex, validTime); 00728 } 00729 00730 QList<QDateTime> KTimeZone::transitionTimes(const Phase &phase, const QDateTime &start, const QDateTime &end) const 00731 { 00732 if (!data(true)) 00733 return QList<QDateTime>(); 00734 return d->d->data->transitionTimes(phase, start, end); 00735 } 00736 00737 QList<KTimeZone::LeapSeconds> KTimeZone::leapSecondChanges() const 00738 { 00739 if (!data(true)) 00740 return QList<KTimeZone::LeapSeconds>(); 00741 return d->d->data->leapSecondChanges(); 00742 } 00743 00744 KTimeZoneSource *KTimeZone::source() const 00745 { 00746 return d->d->source; 00747 } 00748 00749 const KTimeZoneData *KTimeZone::data(bool create) const 00750 { 00751 if (!isValid()) 00752 return 0; 00753 if (create && !d->d->data && d->d->source->useZoneParse()) 00754 d->d->data = d->d->source->parse(*this); 00755 return d->d->data; 00756 } 00757 00758 void KTimeZone::setData(KTimeZoneData *data, KTimeZoneSource *source) 00759 { 00760 if (!isValid()) 00761 return; 00762 delete d->d->data; 00763 d->d->data = data; 00764 if (source) 00765 d->d->source = source; 00766 } 00767 00768 bool KTimeZone::updateBase(const KTimeZone &other) 00769 { 00770 if (d->d->name.isEmpty() || d->d->name != other.d->d->name) 00771 return false; 00772 d->d->countryCode = other.d->d->countryCode; 00773 d->d->comment = other.d->d->comment; 00774 d->d->latitude = other.d->d->latitude; 00775 d->d->longitude = other.d->d->longitude; 00776 return true; 00777 } 00778 00779 bool KTimeZone::parse() const 00780 { 00781 if (!isValid()) 00782 return false; 00783 if (d->d->source->useZoneParse()) 00784 { 00785 delete d->d->data; 00786 d->d->data = d->d->source->parse(*this); 00787 } 00788 return d->d->data; 00789 } 00790 00791 QDateTime KTimeZone::toUtc(const QDateTime &zoneDateTime) const 00792 { 00793 if (!zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime) 00794 return QDateTime(); 00795 int secs = offsetAtZoneTime(zoneDateTime); 00796 if (secs == InvalidOffset) 00797 return QDateTime(); 00798 QDateTime dt = zoneDateTime; 00799 dt.setTimeSpec(Qt::UTC); 00800 return dt.addSecs(-secs); 00801 } 00802 00803 QDateTime KTimeZone::toZoneTime(const QDateTime &utcDateTime, bool *secondOccurrence) const 00804 { 00805 if (secondOccurrence) 00806 *secondOccurrence = false; 00807 if (!utcDateTime.isValid() || utcDateTime.timeSpec() != Qt::UTC) // check for invalid time 00808 return QDateTime(); 00809 00810 // Convert UTC to local time 00811 if (hasTransitions()) 00812 { 00813 if (!data(true)) 00814 { 00815 // No data - default to UTC 00816 QDateTime dt = utcDateTime; 00817 dt.setTimeSpec(Qt::LocalTime); 00818 return dt; 00819 } 00820 00821 int index = d->d->data->transitionIndex(utcDateTime); 00822 int secs = (index >= 0) ? d->d->data->transitions()[index].phase().utcOffset() : d->d->data->previousUtcOffset(); 00823 QDateTime dt = utcDateTime.addSecs(secs); 00824 if (secondOccurrence) 00825 { 00826 // Check whether the local time occurs twice around a daylight savings time 00827 // shift, and if so, whether it's the first or second occurrence. 00828 *secondOccurrence = d->d->data->d->isSecondOccurrence(dt, index); 00829 } 00830 dt.setTimeSpec(Qt::LocalTime); 00831 return dt; 00832 } 00833 else 00834 { 00835 int secs = offsetAtUtc(utcDateTime); 00836 QDateTime dt = utcDateTime.addSecs(secs); 00837 dt.setTimeSpec(Qt::LocalTime); 00838 if (secondOccurrence) 00839 { 00840 // Check whether the local time occurs twice around a daylight savings time 00841 // shift, and if so, whether it's the first or second occurrence. 00842 *secondOccurrence = (secs != offsetAtZoneTime(dt)); 00843 } 00844 return dt; 00845 } 00846 } 00847 00848 QDateTime KTimeZone::convert(const KTimeZone &newZone, const QDateTime &zoneDateTime) const 00849 { 00850 if (newZone == *this) 00851 { 00852 if (zoneDateTime.timeSpec() != Qt::LocalTime) 00853 return QDateTime(); 00854 return zoneDateTime; 00855 } 00856 return newZone.toZoneTime(toUtc(zoneDateTime)); 00857 } 00858 00859 int KTimeZone::offsetAtZoneTime(const QDateTime &zoneDateTime, int *secondOffset) const 00860 { 00861 return d->offsetAtZoneTime(this, zoneDateTime, secondOffset); 00862 } 00863 00864 int KTimeZone::offsetAtUtc(const QDateTime &utcDateTime) const 00865 { 00866 return d->offsetAtUtc(this, utcDateTime); 00867 } 00868 00869 int KTimeZone::offset(time_t t) const 00870 { 00871 return d->offset(this, t); 00872 } 00873 00874 int KTimeZone::currentOffset(Qt::TimeSpec basis) const 00875 { 00876 // Get current offset of this time zone to UTC 00877 time_t now = time(0); 00878 int secs = offset(now); 00879 00880 switch (basis) 00881 { 00882 case Qt::LocalTime: 00883 // Return the current offset of this time zone to the local system time 00884 return secs - gmtoff(now); 00885 case Qt::UTC: 00886 // Return the current offset of this time zone to UTC 00887 return secs; 00888 00889 default: 00890 break; 00891 } 00892 return 0; 00893 } 00894 00895 bool KTimeZone::isDstAtUtc(const QDateTime &utcDateTime) const 00896 { 00897 return d->isDstAtUtc(this, utcDateTime); 00898 } 00899 00900 bool KTimeZone::isDst(time_t t) const 00901 { 00902 return d->isDst(this, t); 00903 } 00904 00905 KTimeZone KTimeZone::utc() 00906 { 00907 static KTimeZone utcZone(QLatin1String("UTC")); 00908 return utcZone; 00909 } 00910 00911 QDateTime KTimeZone::fromTime_t(time_t t) 00912 { 00913 static const int secondsADay = 86400; 00914 static const QDate epochDate(1970,1,1); 00915 static const QTime epochTime(0,0,0); 00916 int days = t / secondsADay; 00917 int secs; 00918 if (t >= 0) 00919 secs = t % secondsADay; 00920 else 00921 { 00922 secs = secondsADay - (-t % secondsADay); 00923 --days; 00924 } 00925 return QDateTime(epochDate.addDays(days), epochTime.addSecs(secs), Qt::UTC); 00926 } 00927 00928 time_t KTimeZone::toTime_t(const QDateTime &utcDateTime) 00929 { 00930 static const QDate epochDate(1970,1,1); 00931 static const QTime epochTime(0,0,0); 00932 if (utcDateTime.timeSpec() != Qt::UTC) 00933 return InvalidTime_t; 00934 qint64 days = epochDate.daysTo(utcDateTime.date()); 00935 qint64 secs = epochTime.secsTo(utcDateTime.time()); 00936 qint64 t64 = days * 86400 + secs; 00937 time_t t = static_cast<time_t>(t64); 00938 if (static_cast<qint64>(t) != t64) 00939 return InvalidTime_t; 00940 return t; 00941 } 00942 00943 00944 /******************************************************************************/ 00945 00946 class KTimeZoneSourcePrivate 00947 { 00948 public: 00949 bool mUseZoneParse; 00950 }; 00951 00952 00953 KTimeZoneSource::KTimeZoneSource() 00954 : d(new KTimeZoneSourcePrivate) 00955 { 00956 d->mUseZoneParse = true; 00957 } 00958 00959 KTimeZoneSource::KTimeZoneSource(bool useZoneParse) 00960 : d(new KTimeZoneSourcePrivate) 00961 { 00962 d->mUseZoneParse = useZoneParse; 00963 } 00964 00965 KTimeZoneSource::~KTimeZoneSource() 00966 { 00967 delete d; 00968 } 00969 00970 KTimeZoneData *KTimeZoneSource::parse(const KTimeZone &) const 00971 { 00972 Q_ASSERT(d->mUseZoneParse); // method should never be called if it isn't usable 00973 return new KTimeZoneData; 00974 } 00975 00976 bool KTimeZoneSource::useZoneParse() const 00977 { 00978 return d->mUseZoneParse; 00979 } 00980 00981 00982 /******************************************************************************/ 00983 00984 class KTimeZoneLeapSecondsPrivate 00985 { 00986 public: 00987 QDateTime dt; // UTC time when this change occurred 00988 QString comment; // optional comment 00989 int seconds; // number of leap seconds 00990 }; 00991 00992 00993 KTimeZone::LeapSeconds::LeapSeconds() 00994 : d(new KTimeZoneLeapSecondsPrivate) 00995 { 00996 } 00997 00998 KTimeZone::LeapSeconds::LeapSeconds(const QDateTime &utc, int leap, const QString &cmt) 00999 : d(new KTimeZoneLeapSecondsPrivate) 01000 { 01001 if (utc.timeSpec() == Qt::UTC) // invalid if start time is not UTC 01002 { 01003 d->dt = utc; 01004 d->comment = cmt; 01005 d->seconds = leap; 01006 } 01007 } 01008 01009 KTimeZone::LeapSeconds::LeapSeconds(const KTimeZone::LeapSeconds &c) 01010 : d(new KTimeZoneLeapSecondsPrivate) 01011 { 01012 d->dt = c.d->dt; 01013 d->comment = c.d->comment; 01014 d->seconds = c.d->seconds; 01015 } 01016 01017 KTimeZone::LeapSeconds::~LeapSeconds() 01018 { 01019 delete d; 01020 } 01021 01022 KTimeZone::LeapSeconds &KTimeZone::LeapSeconds::operator=(const KTimeZone::LeapSeconds &c) 01023 { 01024 d->dt = c.d->dt; 01025 d->comment = c.d->comment; 01026 d->seconds = c.d->seconds; 01027 return *this; 01028 } 01029 01030 bool KTimeZone::LeapSeconds::operator<(const KTimeZone::LeapSeconds& c) const 01031 { 01032 return d->dt < c.d->dt; 01033 } 01034 01035 QDateTime KTimeZone::LeapSeconds::dateTime() const 01036 { 01037 return d->dt; 01038 } 01039 01040 bool KTimeZone::LeapSeconds::isValid() const 01041 { 01042 return d->dt.isValid(); 01043 } 01044 01045 int KTimeZone::LeapSeconds::leapSeconds() const 01046 { 01047 return d->seconds; 01048 } 01049 01050 QString KTimeZone::LeapSeconds::comment() const 01051 { 01052 return d->comment; 01053 } 01054 01055 01056 /******************************************************************************/ 01057 01058 01059 int KTimeZoneDataPrivate::transitionIndex(const QDateTime &dt) const 01060 { 01061 // Do a binary search to find the last transition before this date/time 01062 int start = -1; 01063 int end = transitions.count(); 01064 if (dt.timeSpec() == Qt::UTC) 01065 { 01066 while (end - start > 1) 01067 { 01068 int i = (start + end) / 2; 01069 if (dt < transitions[i].time()) 01070 end = i; 01071 else 01072 start = i; 01073 } 01074 } 01075 else 01076 { 01077 QDateTime dtutc = dt; 01078 dtutc.setTimeSpec(Qt::UTC); 01079 while (end - start > 1) 01080 { 01081 int i = (start + end) / 2; 01082 if (dtutc.addSecs(-transitions[i].phase().utcOffset()) < transitions[i].time()) 01083 end = i; 01084 else 01085 start = i; 01086 } 01087 } 01088 return end ? start : -1; 01089 } 01090 01091 // Find the indexes to the transitions at or after start, and before or at end. 01092 // start and end must be UTC. 01093 // Reply = false if none. 01094 bool KTimeZoneDataPrivate::transitionIndexes(const QDateTime &start, const QDateTime &end, int &ixstart, int &ixend) const 01095 { 01096 ixstart = 0; 01097 if (start.isValid() && start.timeSpec() == Qt::UTC) 01098 { 01099 ixstart = transitionIndex(start); 01100 if (ixstart < 0) 01101 ixstart = 0; 01102 else if (transitions[ixstart].time() < start) 01103 { 01104 if (++ixstart >= transitions.count()) 01105 return false; // there are no transitions at/after 'start' 01106 } 01107 } 01108 ixend = -1; 01109 if (end.isValid() && end.timeSpec() == Qt::UTC) 01110 { 01111 ixend = transitionIndex(end); 01112 if (ixend < 0) 01113 return false; // there are no transitions at/before 'end' 01114 } 01115 return true; 01116 } 01117 01118 /* Check if it's a local time which occurs both before and after the specified 01119 * transition (for which it has to span a daylight saving to standard time change). 01120 * @param utcLocalTime local time set to Qt::UTC 01121 */ 01122 bool KTimeZoneDataPrivate::isSecondOccurrence(const QDateTime &utcLocalTime, int transitionIndex) const 01123 { 01124 if (transitionIndex < 0) 01125 return false; 01126 int offset = transitions[transitionIndex].phase().utcOffset(); 01127 int prevoffset = (transitionIndex > 0) ? transitions[transitionIndex-1].phase().utcOffset() : preUtcOffset; 01128 int phaseDiff = prevoffset - offset; 01129 if (phaseDiff <= 0) 01130 return false; 01131 // Find how long after the start of the latest phase 'dt' is 01132 int afterStart = transitions[transitionIndex].time().secsTo(utcLocalTime) - offset; 01133 return (afterStart < phaseDiff); 01134 } 01135 01136 01137 01138 KTimeZoneData::KTimeZoneData() 01139 : d(new KTimeZoneDataPrivate) 01140 { } 01141 01142 KTimeZoneData::KTimeZoneData(const KTimeZoneData &c) 01143 : d(new KTimeZoneDataPrivate) 01144 { 01145 d->phases = c.d->phases; 01146 d->transitions = c.d->transitions; 01147 d->leapChanges = c.d->leapChanges; 01148 d->utcOffsets = c.d->utcOffsets; 01149 d->abbreviations = c.d->abbreviations; 01150 d->preUtcOffset = c.d->preUtcOffset; 01151 } 01152 01153 KTimeZoneData::~KTimeZoneData() 01154 { 01155 delete d; 01156 } 01157 01158 KTimeZoneData &KTimeZoneData::operator=(const KTimeZoneData &c) 01159 { 01160 d->phases = c.d->phases; 01161 d->transitions = c.d->transitions; 01162 d->leapChanges = c.d->leapChanges; 01163 d->utcOffsets = c.d->utcOffsets; 01164 d->abbreviations = c.d->abbreviations; 01165 d->preUtcOffset = c.d->preUtcOffset; 01166 return *this; 01167 } 01168 01169 KTimeZoneData *KTimeZoneData::clone() const 01170 { 01171 return new KTimeZoneData(*this); 01172 } 01173 01174 QList<QByteArray> KTimeZoneData::abbreviations() const 01175 { 01176 if (d->abbreviations.isEmpty()) 01177 { 01178 for (int i = 0, end = d->phases.count(); i < end; ++i) 01179 { 01180 const QList<QByteArray> abbrevs = d->phases[i].abbreviations(); 01181 for (int j = 0, jend = abbrevs.count(); j < jend; ++j) 01182 if (!d->abbreviations.contains(abbrevs[j])) 01183 d->abbreviations.append(abbrevs[j]); 01184 } 01185 if (d->abbreviations.isEmpty()) 01186 d->abbreviations += "UTC"; 01187 } 01188 return d->abbreviations; 01189 } 01190 01191 QByteArray KTimeZoneData::abbreviation(const QDateTime &utcDateTime) const 01192 { 01193 if (d->phases.isEmpty()) 01194 return "UTC"; 01195 const KTimeZone::Transition *tr = transition(utcDateTime); 01196 if (!tr) 01197 return QByteArray(); 01198 const QList<QByteArray> abbrevs = tr->phase().abbreviations(); 01199 if (abbrevs.isEmpty()) 01200 return QByteArray(); 01201 return abbrevs[0]; 01202 } 01203 01204 QList<int> KTimeZoneData::utcOffsets() const 01205 { 01206 if (d->utcOffsets.isEmpty()) 01207 { 01208 for (int i = 0, end = d->phases.count(); i < end; ++i) 01209 { 01210 int offset = d->phases[i].utcOffset(); 01211 if (!d->utcOffsets.contains(offset)) 01212 d->utcOffsets.append(offset); 01213 } 01214 if (d->utcOffsets.isEmpty()) 01215 d->utcOffsets += 0; 01216 else 01217 qSort(d->utcOffsets); 01218 } 01219 return d->utcOffsets; 01220 } 01221 01222 QList<KTimeZone::Phase> KTimeZoneData::phases() const 01223 { 01224 return d->phases; 01225 } 01226 01227 void KTimeZoneData::setPhases(const QList<KTimeZone::Phase> &phases, int previousUtcOffset) 01228 { 01229 d->phases = phases; 01230 d->preUtcOffset = previousUtcOffset; 01231 } 01232 01233 bool KTimeZoneData::hasTransitions() const 01234 { 01235 return false; 01236 } 01237 01238 QList<KTimeZone::Transition> KTimeZoneData::transitions(const QDateTime &start, const QDateTime &end) const 01239 { 01240 int ixstart, ixend; 01241 if (!d->transitionIndexes(start, end, ixstart, ixend)) 01242 return QList<KTimeZone::Transition>(); // there are no transitions within the time period 01243 if (ixend >= 0) 01244 return d->transitions.mid(ixstart, ixend - ixstart + 1); 01245 if (ixstart > 0) 01246 return d->transitions.mid(ixstart); 01247 return d->transitions; 01248 } 01249 01250 void KTimeZoneData::setTransitions(const QList<KTimeZone::Transition> &transitions) 01251 { 01252 d->transitions = transitions; 01253 } 01254 01255 int KTimeZoneData::previousUtcOffset() const 01256 { 01257 return d->preUtcOffset; 01258 } 01259 01260 const KTimeZone::Transition *KTimeZoneData::transition(const QDateTime &dt, const KTimeZone::Transition **secondTransition, 01261 bool *validTime) const 01262 { 01263 int secondIndex; 01264 int index = transitionIndex(dt, (secondTransition ? &secondIndex : 0), validTime); 01265 if (secondTransition) 01266 *secondTransition = (secondIndex >= 0) ? &d->transitions[secondIndex] : 0; 01267 return (index >= 0) ? &d->transitions[index] : 0; 01268 } 01269 01270 int KTimeZoneData::transitionIndex(const QDateTime &dt, int *secondIndex, bool *validTime) const 01271 { 01272 if (validTime) 01273 *validTime = true; 01274 01275 // Find the last transition before this date/time 01276 int index = d->transitionIndex(dt); 01277 if (dt.timeSpec() == Qt::UTC) 01278 { 01279 if (secondIndex) 01280 *secondIndex = index; 01281 return index; 01282 } 01283 else 01284 { 01285 /* Check whether the specified local time actually occurs. 01286 * Find the start of the next phase, and check if it falls in the gap 01287 * between the two phases. 01288 */ 01289 QDateTime dtutc = dt; 01290 dtutc.setTimeSpec(Qt::UTC); 01291 int count = d->transitions.count(); 01292 int next = (index >= 0) ? index + 1 : 0; 01293 if (next < count) 01294 { 01295 KTimeZone::Phase nextPhase = d->transitions[next].phase(); 01296 int offset = (index >= 0) ? d->transitions[index].phase().utcOffset() : d->preUtcOffset; 01297 int phaseDiff = nextPhase.utcOffset() - offset; 01298 if (phaseDiff > 0) 01299 { 01300 // Get UTC equivalent as if 'dt' was in the next phase 01301 if (dtutc.secsTo(d->transitions[next].time()) + nextPhase.utcOffset() < phaseDiff) 01302 { 01303 // The time falls in the gap between the two phases, 01304 // so return an invalid value. 01305 if (validTime) 01306 *validTime = false; 01307 if (secondIndex) 01308 *secondIndex = -1; 01309 return -1; 01310 } 01311 } 01312 } 01313 01314 if (index < 0) 01315 { 01316 // The specified time is before the first phase 01317 if (secondIndex) 01318 *secondIndex = -1; 01319 return -1; 01320 } 01321 01322 /* Check if it's a local time which occurs both before and after the 'latest' 01323 * phase start time (for which it has to span a daylight saving to standard 01324 * time change). 01325 */ 01326 bool duplicate = true; 01327 if (d->isSecondOccurrence(dtutc, index)) 01328 { 01329 // 'dt' occurs twice 01330 if (secondIndex) 01331 { 01332 *secondIndex = index; 01333 duplicate = false; 01334 } 01335 // Get the transition containing the first occurrence of 'dt' 01336 if (index <= 0) 01337 return -1; // first occurrence of 'dt' is just before the first transition 01338 --index; 01339 } 01340 01341 if (secondIndex && duplicate) 01342 *secondIndex = index; 01343 return index; 01344 } 01345 } 01346 01347 QList<QDateTime> KTimeZoneData::transitionTimes(const KTimeZone::Phase &phase, const QDateTime &start, const QDateTime &end) const 01348 { 01349 QList<QDateTime> times; 01350 int ixstart, ixend; 01351 if (d->transitionIndexes(start, end, ixstart, ixend)) 01352 { 01353 if (ixend < 0) 01354 ixend = d->transitions.count() - 1; 01355 while (ixstart <= ixend) 01356 { 01357 if (d->transitions[ixstart].phase() == phase) 01358 times += d->transitions[ixstart].time(); 01359 } 01360 } 01361 return times; 01362 } 01363 01364 QList<KTimeZone::LeapSeconds> KTimeZoneData::leapSecondChanges() const 01365 { 01366 return d->leapChanges; 01367 } 01368 01369 void KTimeZoneData::setLeapSecondChanges(const QList<KTimeZone::LeapSeconds> &adjusts) 01370 { 01371 d->leapChanges = adjusts; 01372 } 01373 01374 KTimeZone::LeapSeconds KTimeZoneData::leapSecondChange(const QDateTime &utc) const 01375 { 01376 if (utc.timeSpec() != Qt::UTC) 01377 kError() << "KTimeZoneData::leapSecondChange(): non-UTC time specified" << endl; 01378 else 01379 { 01380 for (int i = d->leapChanges.count(); --i >= 0; ) 01381 { 01382 if (d->leapChanges[i].dateTime() < utc) 01383 return d->leapChanges[i]; 01384 } 01385 } 01386 return KTimeZone::LeapSeconds(); 01387 }
KDE 4.7 API Reference