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 KTimeZone::Phase prePhase; // phase to use before the first transition 00292 00293 KTimeZoneDataPrivate() {} 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 const 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 const int offset = transitions[index].phase().utcOffset(); 00485 const 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 const 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 const 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 const 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.at(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 const 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 if (utcDateTime >= transitions[index].time() 00540 && (index + 1 >= transitions.count() 00541 || utcDateTime < transitions[index + 1].time())) 00542 { 00543 // The time falls within the cached transition, so return its UTC offset 00544 #ifdef COMPILING_TESTS 00545 qDebug("Using cache"); // test output requires qDebug instead of kDebug 00546 #endif 00547 return transitions[index].phase().utcOffset(); 00548 } 00549 } 00550 00551 // The time doesn't fall within the cached transition, or there isn't a cached transition 00552 #ifdef COMPILING_TESTS 00553 qDebug("No cache"); // test output requires qDebug instead of kDebug 00554 #endif 00555 index = caller->transitionIndex(utcDateTime); 00556 d->cachedTransitionIndex = index; // cache transition data 00557 d->cachedTransitionTimesValid = false; 00558 const KTimeZone::Transition* tr = (index >= 0) ? &transitions.at(index) : 0; 00559 return tr ? tr->phase().utcOffset() : (d->data ? d->data->previousUtcOffset() : 0); 00560 } 00561 00562 int KTimeZoneBackend::offset(const KTimeZone* caller, time_t t) const 00563 { 00564 return offsetAtUtc(caller, KTimeZone::fromTime_t(t)); 00565 } 00566 00567 bool KTimeZoneBackend::isDstAtUtc(const KTimeZone* caller, const QDateTime &utcDateTime) const 00568 { 00569 if (!utcDateTime.isValid() || utcDateTime.timeSpec() != Qt::UTC) // check for invalid time 00570 return false; 00571 const KTimeZone::Transition *tr = caller->transition(utcDateTime); 00572 if (!tr) 00573 return false; 00574 return tr->phase().isDst(); 00575 } 00576 00577 bool KTimeZoneBackend::isDst(const KTimeZone* caller, time_t t) const 00578 { 00579 return isDstAtUtc(caller, KTimeZone::fromTime_t(t)); 00580 } 00581 00582 bool KTimeZoneBackend::hasTransitions(const KTimeZone* caller) const 00583 { 00584 Q_UNUSED(caller); 00585 return false; 00586 } 00587 00588 00589 /******************************************************************************/ 00590 00591 #if SIZEOF_TIME_T == 8 00592 const time_t KTimeZone::InvalidTime_t = 0x800000000000000LL; 00593 #else 00594 const time_t KTimeZone::InvalidTime_t = 0x80000000; 00595 #endif 00596 const int KTimeZone::InvalidOffset = 0x80000000; 00597 const float KTimeZone::UNKNOWN = 1000.0; 00598 00599 00600 KTimeZone::KTimeZone() 00601 : d(new KTimeZoneBackend()) 00602 {} 00603 00604 KTimeZone::KTimeZone(const QString &name) 00605 : d(new KTimeZoneBackend(name)) 00606 {} 00607 00608 KTimeZone::KTimeZone(const KTimeZone &tz) 00609 : d(tz.d->clone()) 00610 {} 00611 00612 KTimeZone::~KTimeZone() 00613 { 00614 delete d; 00615 } 00616 00617 KTimeZone::KTimeZone(KTimeZoneBackend *impl) 00618 : d(impl) 00619 { 00620 // 'impl' should be a newly constructed object, with refCount = 1 00621 Q_ASSERT(d->d->refCount == 1 || d->d == &*s_emptyTimeZonePrivate); 00622 } 00623 00624 KTimeZone &KTimeZone::operator=(const KTimeZone &tz) 00625 { 00626 if (d != tz.d) 00627 { 00628 delete d; 00629 d = tz.d->clone(); 00630 } 00631 return *this; 00632 } 00633 00634 bool KTimeZone::operator==(const KTimeZone &rhs) const 00635 { 00636 return d->d == rhs.d->d; 00637 } 00638 00639 QByteArray KTimeZone::type() const 00640 { 00641 return d->type(); 00642 } 00643 00644 bool KTimeZone::isValid() const 00645 { 00646 return !d->d->name.isEmpty(); 00647 } 00648 00649 QString KTimeZone::countryCode() const 00650 { 00651 return d->d->countryCode; 00652 } 00653 00654 float KTimeZone::latitude() const 00655 { 00656 return d->d->latitude; 00657 } 00658 00659 float KTimeZone::longitude() const 00660 { 00661 return d->d->longitude; 00662 } 00663 00664 QString KTimeZone::comment() const 00665 { 00666 return d->d->comment; 00667 } 00668 00669 QString KTimeZone::name() const 00670 { 00671 return d->d->name; 00672 } 00673 00674 QList<QByteArray> KTimeZone::abbreviations() const 00675 { 00676 if (!data(true)) 00677 return QList<QByteArray>(); 00678 return d->d->data->abbreviations(); 00679 } 00680 00681 QByteArray KTimeZone::abbreviation(const QDateTime &utcDateTime) const 00682 { 00683 if (utcDateTime.timeSpec() != Qt::UTC || !data(true)) 00684 return QByteArray(); 00685 return d->d->data->abbreviation(utcDateTime); 00686 } 00687 00688 QList<int> KTimeZone::utcOffsets() const 00689 { 00690 if (!data(true)) 00691 return QList<int>(); 00692 return d->d->data->utcOffsets(); 00693 } 00694 00695 QList<KTimeZone::Phase> KTimeZone::phases() const 00696 { 00697 if (!data(true)) 00698 return QList<KTimeZone::Phase>(); 00699 return d->d->data->phases(); 00700 } 00701 00702 bool KTimeZone::hasTransitions() const 00703 { 00704 return d->hasTransitions(this); 00705 } 00706 00707 QList<KTimeZone::Transition> KTimeZone::transitions(const QDateTime &start, const QDateTime &end) const 00708 { 00709 if (!data(true)) 00710 return QList<KTimeZone::Transition>(); 00711 return d->d->data->transitions(start, end); 00712 } 00713 00714 const KTimeZone::Transition *KTimeZone::transition(const QDateTime &dt, const Transition **secondTransition, 00715 bool *validTime) const 00716 { 00717 if (!data(true)) 00718 return 0; 00719 return d->d->data->transition(dt, secondTransition, validTime); 00720 } 00721 00722 int KTimeZone::transitionIndex(const QDateTime &dt, int *secondIndex, bool *validTime) const 00723 { 00724 if (!data(true)) 00725 return -1; 00726 return d->d->data->transitionIndex(dt, secondIndex, validTime); 00727 } 00728 00729 QList<QDateTime> KTimeZone::transitionTimes(const Phase &phase, const QDateTime &start, const QDateTime &end) const 00730 { 00731 if (!data(true)) 00732 return QList<QDateTime>(); 00733 return d->d->data->transitionTimes(phase, start, end); 00734 } 00735 00736 QList<KTimeZone::LeapSeconds> KTimeZone::leapSecondChanges() const 00737 { 00738 if (!data(true)) 00739 return QList<KTimeZone::LeapSeconds>(); 00740 return d->d->data->leapSecondChanges(); 00741 } 00742 00743 KTimeZoneSource *KTimeZone::source() const 00744 { 00745 return d->d->source; 00746 } 00747 00748 const KTimeZoneData *KTimeZone::data(bool create) const 00749 { 00750 if (!isValid()) 00751 return 0; 00752 if (create && !d->d->data && d->d->source->useZoneParse()) 00753 d->d->data = d->d->source->parse(*this); 00754 return d->d->data; 00755 } 00756 00757 void KTimeZone::setData(KTimeZoneData *data, KTimeZoneSource *source) 00758 { 00759 if (!isValid()) 00760 return; 00761 delete d->d->data; 00762 d->d->data = data; 00763 if (source) 00764 d->d->source = source; 00765 } 00766 00767 bool KTimeZone::updateBase(const KTimeZone &other) 00768 { 00769 if (d->d->name.isEmpty() || d->d->name != other.d->d->name) 00770 return false; 00771 d->d->countryCode = other.d->d->countryCode; 00772 d->d->comment = other.d->d->comment; 00773 d->d->latitude = other.d->d->latitude; 00774 d->d->longitude = other.d->d->longitude; 00775 return true; 00776 } 00777 00778 bool KTimeZone::parse() const 00779 { 00780 if (!isValid()) 00781 return false; 00782 if (d->d->source->useZoneParse()) 00783 { 00784 delete d->d->data; 00785 d->d->data = d->d->source->parse(*this); 00786 } 00787 return d->d->data; 00788 } 00789 00790 QDateTime KTimeZone::toUtc(const QDateTime &zoneDateTime) const 00791 { 00792 if (!zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime) 00793 return QDateTime(); 00794 const int secs = offsetAtZoneTime(zoneDateTime); 00795 if (secs == InvalidOffset) 00796 return QDateTime(); 00797 QDateTime dt = zoneDateTime; 00798 dt.setTimeSpec(Qt::UTC); 00799 return dt.addSecs(-secs); 00800 } 00801 00802 QDateTime KTimeZone::toZoneTime(const QDateTime &utcDateTime, bool *secondOccurrence) const 00803 { 00804 if (secondOccurrence) 00805 *secondOccurrence = false; 00806 if (!utcDateTime.isValid() || utcDateTime.timeSpec() != Qt::UTC) // check for invalid time 00807 return QDateTime(); 00808 00809 // Convert UTC to local time 00810 if (hasTransitions()) 00811 { 00812 if (!data(true)) 00813 { 00814 // No data - default to UTC 00815 QDateTime dt = utcDateTime; 00816 dt.setTimeSpec(Qt::LocalTime); 00817 return dt; 00818 } 00819 00820 const KTimeZoneData *data = d->d->data; 00821 const int index = data->transitionIndex(utcDateTime); 00822 const int secs = (index >= 0) ? data->transitions()[index].phase().utcOffset() : 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 = data->d->isSecondOccurrence(dt, index); 00829 } 00830 dt.setTimeSpec(Qt::LocalTime); 00831 return dt; 00832 } 00833 else 00834 { 00835 const 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 const time_t now = time(0); 00878 const 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 const qint64 days = epochDate.daysTo(utcDateTime.date()); 00935 const qint64 secs = epochTime.secsTo(utcDateTime.time()); 00936 const qint64 t64 = days * 86400 + secs; 00937 const 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 const 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 const int offset = transitions[transitionIndex].phase().utcOffset(); 01127 const int prevoffset = (transitionIndex > 0) ? transitions[transitionIndex-1].phase().utcOffset() : prePhase.utcOffset(); 01128 const 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 const 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->prePhase = c.d->prePhase; 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->prePhase = c.d->prePhase; 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 const QList<QByteArray> abbrevs = tr ? tr->phase().abbreviations() 01197 : d->prePhase.abbreviations(); 01198 if (abbrevs.isEmpty()) 01199 return QByteArray(); 01200 return abbrevs[0]; 01201 } 01202 01203 QList<int> KTimeZoneData::utcOffsets() const 01204 { 01205 if (d->utcOffsets.isEmpty()) 01206 { 01207 for (int i = 0, end = d->phases.count(); i < end; ++i) 01208 { 01209 const int offset = d->phases[i].utcOffset(); 01210 if (!d->utcOffsets.contains(offset)) 01211 d->utcOffsets.append(offset); 01212 } 01213 if (d->utcOffsets.isEmpty()) 01214 d->utcOffsets += 0; 01215 else 01216 qSort(d->utcOffsets); 01217 } 01218 return d->utcOffsets; 01219 } 01220 01221 QList<KTimeZone::Phase> KTimeZoneData::phases() const 01222 { 01223 return d->phases; 01224 } 01225 01226 void KTimeZoneData::setPhases(const QList<KTimeZone::Phase> &phases, const KTimeZone::Phase& previousPhase) 01227 { 01228 d->phases = phases; 01229 d->prePhase = previousPhase; 01230 } 01231 01232 void KTimeZoneData::setPhases(const QList<KTimeZone::Phase> &phases, int previousUtcOffset) 01233 { 01234 d->phases = phases; 01235 d->prePhase = KTimeZone::Phase(previousUtcOffset, QByteArray(), false); 01236 } 01237 01238 bool KTimeZoneData::hasTransitions() const 01239 { 01240 return false; 01241 } 01242 01243 QList<KTimeZone::Transition> KTimeZoneData::transitions(const QDateTime &start, const QDateTime &end) const 01244 { 01245 int ixstart, ixend; 01246 if (!d->transitionIndexes(start, end, ixstart, ixend)) 01247 return QList<KTimeZone::Transition>(); // there are no transitions within the time period 01248 if (ixend >= 0) 01249 return d->transitions.mid(ixstart, ixend - ixstart + 1); 01250 if (ixstart > 0) 01251 return d->transitions.mid(ixstart); 01252 return d->transitions; 01253 } 01254 01255 void KTimeZoneData::setTransitions(const QList<KTimeZone::Transition> &transitions) 01256 { 01257 d->transitions = transitions; 01258 } 01259 01260 int KTimeZoneData::previousUtcOffset() const 01261 { 01262 return d->prePhase.utcOffset(); 01263 } 01264 01265 const KTimeZone::Transition *KTimeZoneData::transition(const QDateTime &dt, const KTimeZone::Transition **secondTransition, 01266 bool *validTime) const 01267 { 01268 int secondIndex; 01269 const int index = transitionIndex(dt, (secondTransition ? &secondIndex : 0), validTime); 01270 if (secondTransition) 01271 *secondTransition = (secondIndex >= 0) ? &d->transitions[secondIndex] : 0; 01272 return (index >= 0) ? &d->transitions[index] : 0; 01273 } 01274 01275 int KTimeZoneData::transitionIndex(const QDateTime &dt, int *secondIndex, bool *validTime) const 01276 { 01277 if (validTime) 01278 *validTime = true; 01279 01280 // Find the last transition before this date/time 01281 int index = d->transitionIndex(dt); 01282 if (dt.timeSpec() == Qt::UTC) 01283 { 01284 if (secondIndex) 01285 *secondIndex = index; 01286 return index; 01287 } 01288 else 01289 { 01290 /* Check whether the specified local time actually occurs. 01291 * Find the start of the next phase, and check if it falls in the gap 01292 * between the two phases. 01293 */ 01294 QDateTime dtutc = dt; 01295 dtutc.setTimeSpec(Qt::UTC); 01296 const int count = d->transitions.count(); 01297 const int next = (index >= 0) ? index + 1 : 0; 01298 if (next < count) 01299 { 01300 KTimeZone::Phase nextPhase = d->transitions[next].phase(); 01301 const int offset = (index >= 0) ? d->transitions[index].phase().utcOffset() : d->prePhase.utcOffset(); 01302 const int phaseDiff = nextPhase.utcOffset() - offset; 01303 if (phaseDiff > 0) 01304 { 01305 // Get UTC equivalent as if 'dt' was in the next phase 01306 if (dtutc.secsTo(d->transitions[next].time()) + nextPhase.utcOffset() < phaseDiff) 01307 { 01308 // The time falls in the gap between the two phases, 01309 // so return an invalid value. 01310 if (validTime) 01311 *validTime = false; 01312 if (secondIndex) 01313 *secondIndex = -1; 01314 return -1; 01315 } 01316 } 01317 } 01318 01319 if (index < 0) 01320 { 01321 // The specified time is before the first phase 01322 if (secondIndex) 01323 *secondIndex = -1; 01324 return -1; 01325 } 01326 01327 /* Check if it's a local time which occurs both before and after the 'latest' 01328 * phase start time (for which it has to span a daylight saving to standard 01329 * time change). 01330 */ 01331 bool duplicate = true; 01332 if (d->isSecondOccurrence(dtutc, index)) 01333 { 01334 // 'dt' occurs twice 01335 if (secondIndex) 01336 { 01337 *secondIndex = index; 01338 duplicate = false; 01339 } 01340 // Get the transition containing the first occurrence of 'dt' 01341 if (index <= 0) 01342 return -1; // first occurrence of 'dt' is just before the first transition 01343 --index; 01344 } 01345 01346 if (secondIndex && duplicate) 01347 *secondIndex = index; 01348 return index; 01349 } 01350 } 01351 01352 QList<QDateTime> KTimeZoneData::transitionTimes(const KTimeZone::Phase &phase, const QDateTime &start, const QDateTime &end) const 01353 { 01354 QList<QDateTime> times; 01355 int ixstart, ixend; 01356 if (d->transitionIndexes(start, end, ixstart, ixend)) 01357 { 01358 if (ixend < 0) 01359 ixend = d->transitions.count() - 1; 01360 while (ixstart <= ixend) 01361 { 01362 if (d->transitions[ixstart].phase() == phase) 01363 times += d->transitions[ixstart].time(); 01364 } 01365 } 01366 return times; 01367 } 01368 01369 QList<KTimeZone::LeapSeconds> KTimeZoneData::leapSecondChanges() const 01370 { 01371 return d->leapChanges; 01372 } 01373 01374 void KTimeZoneData::setLeapSecondChanges(const QList<KTimeZone::LeapSeconds> &adjusts) 01375 { 01376 d->leapChanges = adjusts; 01377 } 01378 01379 KTimeZone::LeapSeconds KTimeZoneData::leapSecondChange(const QDateTime &utc) const 01380 { 01381 if (utc.timeSpec() != Qt::UTC) 01382 kError() << "KTimeZoneData::leapSecondChange(): non-UTC time specified" << endl; 01383 else 01384 { 01385 for (int i = d->leapChanges.count(); --i >= 0; ) 01386 { 01387 if (d->leapChanges[i].dateTime() < utc) 01388 return d->leapChanges[i]; 01389 } 01390 } 01391 return KTimeZone::LeapSeconds(); 01392 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 17:05:23 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:23 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.