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 kDebug(161) << "-> Using cache"; // enable the debug area to see this in the tests 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 kDebug(161) << "-> No cache"; // enable the debug area to see this in the tests 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 kDebug(161) << "Using cache"; // enable the debug area to see this in the tests 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 kDebug(161) << "No cache"; // enable the debug area to see this in the tests 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 if (validTime) 00719 *validTime = false; 00720 return 0; 00721 } 00722 return d->d->data->transition(dt, secondTransition, validTime); 00723 } 00724 00725 int KTimeZone::transitionIndex(const QDateTime &dt, int *secondIndex, bool *validTime) const 00726 { 00727 if (!data(true)) { 00728 if (validTime) 00729 *validTime = false; 00730 return -1; 00731 } 00732 return d->d->data->transitionIndex(dt, secondIndex, validTime); 00733 } 00734 00735 QList<QDateTime> KTimeZone::transitionTimes(const Phase &phase, const QDateTime &start, const QDateTime &end) const 00736 { 00737 if (!data(true)) 00738 return QList<QDateTime>(); 00739 return d->d->data->transitionTimes(phase, start, end); 00740 } 00741 00742 QList<KTimeZone::LeapSeconds> KTimeZone::leapSecondChanges() const 00743 { 00744 if (!data(true)) 00745 return QList<KTimeZone::LeapSeconds>(); 00746 return d->d->data->leapSecondChanges(); 00747 } 00748 00749 KTimeZoneSource *KTimeZone::source() const 00750 { 00751 return d->d->source; 00752 } 00753 00754 const KTimeZoneData *KTimeZone::data(bool create) const 00755 { 00756 if (!isValid()) 00757 return 0; 00758 if (create && !d->d->data && d->d->source->useZoneParse()) 00759 d->d->data = d->d->source->parse(*this); 00760 return d->d->data; 00761 } 00762 00763 void KTimeZone::setData(KTimeZoneData *data, KTimeZoneSource *source) 00764 { 00765 if (!isValid()) 00766 return; 00767 delete d->d->data; 00768 d->d->data = data; 00769 if (source) 00770 d->d->source = source; 00771 } 00772 00773 bool KTimeZone::updateBase(const KTimeZone &other) 00774 { 00775 if (d->d->name.isEmpty() || d->d->name != other.d->d->name) 00776 return false; 00777 d->d->countryCode = other.d->d->countryCode; 00778 d->d->comment = other.d->d->comment; 00779 d->d->latitude = other.d->d->latitude; 00780 d->d->longitude = other.d->d->longitude; 00781 return true; 00782 } 00783 00784 bool KTimeZone::parse() const 00785 { 00786 if (!isValid()) 00787 return false; 00788 if (d->d->source->useZoneParse()) 00789 { 00790 delete d->d->data; 00791 d->d->data = d->d->source->parse(*this); 00792 } 00793 return d->d->data; 00794 } 00795 00796 QDateTime KTimeZone::toUtc(const QDateTime &zoneDateTime) const 00797 { 00798 if (!zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime) 00799 return QDateTime(); 00800 const int secs = offsetAtZoneTime(zoneDateTime); 00801 if (secs == InvalidOffset) 00802 return QDateTime(); 00803 QDateTime dt = zoneDateTime; 00804 dt.setTimeSpec(Qt::UTC); 00805 return dt.addSecs(-secs); 00806 } 00807 00808 QDateTime KTimeZone::toZoneTime(const QDateTime &utcDateTime, bool *secondOccurrence) const 00809 { 00810 if (secondOccurrence) 00811 *secondOccurrence = false; 00812 if (!utcDateTime.isValid() || utcDateTime.timeSpec() != Qt::UTC) // check for invalid time 00813 return QDateTime(); 00814 00815 // Convert UTC to local time 00816 if (hasTransitions()) 00817 { 00818 if (!data(true)) 00819 { 00820 // No data - default to UTC 00821 QDateTime dt = utcDateTime; 00822 dt.setTimeSpec(Qt::LocalTime); 00823 return dt; 00824 } 00825 00826 const KTimeZoneData *data = d->d->data; 00827 const int index = data->transitionIndex(utcDateTime); 00828 const int secs = (index >= 0) ? data->transitions().at(index).phase().utcOffset() : data->previousUtcOffset(); 00829 QDateTime dt = utcDateTime.addSecs(secs); 00830 if (secondOccurrence) 00831 { 00832 // Check whether the local time occurs twice around a daylight savings time 00833 // shift, and if so, whether it's the first or second occurrence. 00834 *secondOccurrence = data->d->isSecondOccurrence(dt, index); 00835 } 00836 dt.setTimeSpec(Qt::LocalTime); 00837 return dt; 00838 } 00839 else 00840 { 00841 const int secs = offsetAtUtc(utcDateTime); 00842 QDateTime dt = utcDateTime.addSecs(secs); 00843 dt.setTimeSpec(Qt::LocalTime); 00844 if (secondOccurrence) 00845 { 00846 // Check whether the local time occurs twice around a daylight savings time 00847 // shift, and if so, whether it's the first or second occurrence. 00848 *secondOccurrence = (secs != offsetAtZoneTime(dt)); 00849 } 00850 return dt; 00851 } 00852 } 00853 00854 QDateTime KTimeZone::convert(const KTimeZone &newZone, const QDateTime &zoneDateTime) const 00855 { 00856 if (newZone == *this) 00857 { 00858 if (zoneDateTime.timeSpec() != Qt::LocalTime) 00859 return QDateTime(); 00860 return zoneDateTime; 00861 } 00862 return newZone.toZoneTime(toUtc(zoneDateTime)); 00863 } 00864 00865 int KTimeZone::offsetAtZoneTime(const QDateTime &zoneDateTime, int *secondOffset) const 00866 { 00867 return d->offsetAtZoneTime(this, zoneDateTime, secondOffset); 00868 } 00869 00870 int KTimeZone::offsetAtUtc(const QDateTime &utcDateTime) const 00871 { 00872 return d->offsetAtUtc(this, utcDateTime); 00873 } 00874 00875 int KTimeZone::offset(time_t t) const 00876 { 00877 return d->offset(this, t); 00878 } 00879 00880 int KTimeZone::currentOffset(Qt::TimeSpec basis) const 00881 { 00882 // Get current offset of this time zone to UTC 00883 const time_t now = time(0); 00884 const int secs = offset(now); 00885 00886 switch (basis) 00887 { 00888 case Qt::LocalTime: 00889 // Return the current offset of this time zone to the local system time 00890 return secs - gmtoff(now); 00891 case Qt::UTC: 00892 // Return the current offset of this time zone to UTC 00893 return secs; 00894 00895 default: 00896 break; 00897 } 00898 return 0; 00899 } 00900 00901 bool KTimeZone::isDstAtUtc(const QDateTime &utcDateTime) const 00902 { 00903 return d->isDstAtUtc(this, utcDateTime); 00904 } 00905 00906 bool KTimeZone::isDst(time_t t) const 00907 { 00908 return d->isDst(this, t); 00909 } 00910 00911 KTimeZone KTimeZone::utc() 00912 { 00913 static KTimeZone utcZone(QLatin1String("UTC")); 00914 return utcZone; 00915 } 00916 00917 QDateTime KTimeZone::fromTime_t(time_t t) 00918 { 00919 static const int secondsADay = 86400; 00920 static const QDate epochDate(1970,1,1); 00921 static const QTime epochTime(0,0,0); 00922 int days = t / secondsADay; 00923 int secs; 00924 if (t >= 0) 00925 secs = t % secondsADay; 00926 else 00927 { 00928 secs = secondsADay - (-t % secondsADay); 00929 --days; 00930 } 00931 return QDateTime(epochDate.addDays(days), epochTime.addSecs(secs), Qt::UTC); 00932 } 00933 00934 time_t KTimeZone::toTime_t(const QDateTime &utcDateTime) 00935 { 00936 static const QDate epochDate(1970,1,1); 00937 static const QTime epochTime(0,0,0); 00938 if (utcDateTime.timeSpec() != Qt::UTC) 00939 return InvalidTime_t; 00940 const qint64 days = epochDate.daysTo(utcDateTime.date()); 00941 const qint64 secs = epochTime.secsTo(utcDateTime.time()); 00942 const qint64 t64 = days * 86400 + secs; 00943 const time_t t = static_cast<time_t>(t64); 00944 if (static_cast<qint64>(t) != t64) 00945 return InvalidTime_t; 00946 return t; 00947 } 00948 00949 00950 /******************************************************************************/ 00951 00952 class KTimeZoneSourcePrivate 00953 { 00954 public: 00955 bool mUseZoneParse; 00956 }; 00957 00958 00959 KTimeZoneSource::KTimeZoneSource() 00960 : d(new KTimeZoneSourcePrivate) 00961 { 00962 d->mUseZoneParse = true; 00963 } 00964 00965 KTimeZoneSource::KTimeZoneSource(bool useZoneParse) 00966 : d(new KTimeZoneSourcePrivate) 00967 { 00968 d->mUseZoneParse = useZoneParse; 00969 } 00970 00971 KTimeZoneSource::~KTimeZoneSource() 00972 { 00973 delete d; 00974 } 00975 00976 KTimeZoneData *KTimeZoneSource::parse(const KTimeZone &) const 00977 { 00978 Q_ASSERT(d->mUseZoneParse); // method should never be called if it isn't usable 00979 return new KTimeZoneData; 00980 } 00981 00982 bool KTimeZoneSource::useZoneParse() const 00983 { 00984 return d->mUseZoneParse; 00985 } 00986 00987 00988 /******************************************************************************/ 00989 00990 class KTimeZoneLeapSecondsPrivate 00991 { 00992 public: 00993 QDateTime dt; // UTC time when this change occurred 00994 QString comment; // optional comment 00995 int seconds; // number of leap seconds 00996 }; 00997 00998 00999 KTimeZone::LeapSeconds::LeapSeconds() 01000 : d(new KTimeZoneLeapSecondsPrivate) 01001 { 01002 } 01003 01004 KTimeZone::LeapSeconds::LeapSeconds(const QDateTime &utc, int leap, const QString &cmt) 01005 : d(new KTimeZoneLeapSecondsPrivate) 01006 { 01007 if (utc.timeSpec() == Qt::UTC) // invalid if start time is not UTC 01008 { 01009 d->dt = utc; 01010 d->comment = cmt; 01011 d->seconds = leap; 01012 } 01013 } 01014 01015 KTimeZone::LeapSeconds::LeapSeconds(const KTimeZone::LeapSeconds &c) 01016 : d(new KTimeZoneLeapSecondsPrivate) 01017 { 01018 d->dt = c.d->dt; 01019 d->comment = c.d->comment; 01020 d->seconds = c.d->seconds; 01021 } 01022 01023 KTimeZone::LeapSeconds::~LeapSeconds() 01024 { 01025 delete d; 01026 } 01027 01028 KTimeZone::LeapSeconds &KTimeZone::LeapSeconds::operator=(const KTimeZone::LeapSeconds &c) 01029 { 01030 d->dt = c.d->dt; 01031 d->comment = c.d->comment; 01032 d->seconds = c.d->seconds; 01033 return *this; 01034 } 01035 01036 bool KTimeZone::LeapSeconds::operator<(const KTimeZone::LeapSeconds& c) const 01037 { 01038 return d->dt < c.d->dt; 01039 } 01040 01041 QDateTime KTimeZone::LeapSeconds::dateTime() const 01042 { 01043 return d->dt; 01044 } 01045 01046 bool KTimeZone::LeapSeconds::isValid() const 01047 { 01048 return d->dt.isValid(); 01049 } 01050 01051 int KTimeZone::LeapSeconds::leapSeconds() const 01052 { 01053 return d->seconds; 01054 } 01055 01056 QString KTimeZone::LeapSeconds::comment() const 01057 { 01058 return d->comment; 01059 } 01060 01061 01062 /******************************************************************************/ 01063 01064 01065 int KTimeZoneDataPrivate::transitionIndex(const QDateTime &dt) const 01066 { 01067 // Do a binary search to find the last transition before this date/time 01068 int start = -1; 01069 int end = transitions.count(); 01070 if (dt.timeSpec() == Qt::UTC) 01071 { 01072 while (end - start > 1) 01073 { 01074 int i = (start + end) / 2; 01075 if (dt < transitions[i].time()) 01076 end = i; 01077 else 01078 start = i; 01079 } 01080 } 01081 else 01082 { 01083 QDateTime dtutc = dt; 01084 dtutc.setTimeSpec(Qt::UTC); 01085 while (end - start > 1) 01086 { 01087 const int i = (start + end) / 2; 01088 if (dtutc.addSecs(-transitions[i].phase().utcOffset()) < transitions[i].time()) 01089 end = i; 01090 else 01091 start = i; 01092 } 01093 } 01094 return end ? start : -1; 01095 } 01096 01097 // Find the indexes to the transitions at or after start, and before or at end. 01098 // start and end must be UTC. 01099 // Reply = false if none. 01100 bool KTimeZoneDataPrivate::transitionIndexes(const QDateTime &start, const QDateTime &end, int &ixstart, int &ixend) const 01101 { 01102 ixstart = 0; 01103 if (start.isValid() && start.timeSpec() == Qt::UTC) 01104 { 01105 ixstart = transitionIndex(start); 01106 if (ixstart < 0) 01107 ixstart = 0; 01108 else if (transitions[ixstart].time() < start) 01109 { 01110 if (++ixstart >= transitions.count()) 01111 return false; // there are no transitions at/after 'start' 01112 } 01113 } 01114 ixend = -1; 01115 if (end.isValid() && end.timeSpec() == Qt::UTC) 01116 { 01117 ixend = transitionIndex(end); 01118 if (ixend < 0) 01119 return false; // there are no transitions at/before 'end' 01120 } 01121 return true; 01122 } 01123 01124 /* Check if it's a local time which occurs both before and after the specified 01125 * transition (for which it has to span a daylight saving to standard time change). 01126 * @param utcLocalTime local time set to Qt::UTC 01127 */ 01128 bool KTimeZoneDataPrivate::isSecondOccurrence(const QDateTime &utcLocalTime, int transitionIndex) const 01129 { 01130 if (transitionIndex < 0) 01131 return false; 01132 const int offset = transitions[transitionIndex].phase().utcOffset(); 01133 const int prevoffset = (transitionIndex > 0) ? transitions[transitionIndex-1].phase().utcOffset() : prePhase.utcOffset(); 01134 const int phaseDiff = prevoffset - offset; 01135 if (phaseDiff <= 0) 01136 return false; 01137 // Find how long after the start of the latest phase 'dt' is 01138 const int afterStart = transitions[transitionIndex].time().secsTo(utcLocalTime) - offset; 01139 return (afterStart < phaseDiff); 01140 } 01141 01142 01143 01144 KTimeZoneData::KTimeZoneData() 01145 : d(new KTimeZoneDataPrivate) 01146 { } 01147 01148 KTimeZoneData::KTimeZoneData(const KTimeZoneData &c) 01149 : d(new KTimeZoneDataPrivate) 01150 { 01151 d->phases = c.d->phases; 01152 d->transitions = c.d->transitions; 01153 d->leapChanges = c.d->leapChanges; 01154 d->utcOffsets = c.d->utcOffsets; 01155 d->abbreviations = c.d->abbreviations; 01156 d->prePhase = c.d->prePhase; 01157 } 01158 01159 KTimeZoneData::~KTimeZoneData() 01160 { 01161 delete d; 01162 } 01163 01164 KTimeZoneData &KTimeZoneData::operator=(const KTimeZoneData &c) 01165 { 01166 d->phases = c.d->phases; 01167 d->transitions = c.d->transitions; 01168 d->leapChanges = c.d->leapChanges; 01169 d->utcOffsets = c.d->utcOffsets; 01170 d->abbreviations = c.d->abbreviations; 01171 d->prePhase = c.d->prePhase; 01172 return *this; 01173 } 01174 01175 KTimeZoneData *KTimeZoneData::clone() const 01176 { 01177 return new KTimeZoneData(*this); 01178 } 01179 01180 QList<QByteArray> KTimeZoneData::abbreviations() const 01181 { 01182 if (d->abbreviations.isEmpty()) 01183 { 01184 for (int i = 0, end = d->phases.count(); i < end; ++i) 01185 { 01186 const QList<QByteArray> abbrevs = d->phases[i].abbreviations(); 01187 for (int j = 0, jend = abbrevs.count(); j < jend; ++j) 01188 if (!d->abbreviations.contains(abbrevs[j])) 01189 d->abbreviations.append(abbrevs[j]); 01190 } 01191 if (d->abbreviations.isEmpty()) 01192 d->abbreviations += "UTC"; 01193 } 01194 return d->abbreviations; 01195 } 01196 01197 QByteArray KTimeZoneData::abbreviation(const QDateTime &utcDateTime) const 01198 { 01199 if (d->phases.isEmpty()) 01200 return "UTC"; 01201 const KTimeZone::Transition *tr = transition(utcDateTime); 01202 const QList<QByteArray> abbrevs = tr ? tr->phase().abbreviations() 01203 : d->prePhase.abbreviations(); 01204 if (abbrevs.isEmpty()) 01205 return QByteArray(); 01206 return abbrevs[0]; 01207 } 01208 01209 QList<int> KTimeZoneData::utcOffsets() const 01210 { 01211 if (d->utcOffsets.isEmpty()) 01212 { 01213 for (int i = 0, end = d->phases.count(); i < end; ++i) 01214 { 01215 const int offset = d->phases[i].utcOffset(); 01216 if (!d->utcOffsets.contains(offset)) 01217 d->utcOffsets.append(offset); 01218 } 01219 if (d->utcOffsets.isEmpty()) 01220 d->utcOffsets += 0; 01221 else 01222 qSort(d->utcOffsets); 01223 } 01224 return d->utcOffsets; 01225 } 01226 01227 QList<KTimeZone::Phase> KTimeZoneData::phases() const 01228 { 01229 return d->phases; 01230 } 01231 01232 void KTimeZoneData::setPhases(const QList<KTimeZone::Phase> &phases, const KTimeZone::Phase& previousPhase) 01233 { 01234 d->phases = phases; 01235 d->prePhase = previousPhase; 01236 } 01237 01238 void KTimeZoneData::setPhases(const QList<KTimeZone::Phase> &phases, int previousUtcOffset) 01239 { 01240 d->phases = phases; 01241 d->prePhase = KTimeZone::Phase(previousUtcOffset, QByteArray(), false); 01242 } 01243 01244 bool KTimeZoneData::hasTransitions() const 01245 { 01246 return false; 01247 } 01248 01249 QList<KTimeZone::Transition> KTimeZoneData::transitions(const QDateTime &start, const QDateTime &end) const 01250 { 01251 int ixstart, ixend; 01252 if (!d->transitionIndexes(start, end, ixstart, ixend)) 01253 return QList<KTimeZone::Transition>(); // there are no transitions within the time period 01254 if (ixend >= 0) 01255 return d->transitions.mid(ixstart, ixend - ixstart + 1); 01256 if (ixstart > 0) 01257 return d->transitions.mid(ixstart); 01258 return d->transitions; 01259 } 01260 01261 void KTimeZoneData::setTransitions(const QList<KTimeZone::Transition> &transitions) 01262 { 01263 d->transitions = transitions; 01264 } 01265 01266 int KTimeZoneData::previousUtcOffset() const 01267 { 01268 return d->prePhase.utcOffset(); 01269 } 01270 01271 const KTimeZone::Transition *KTimeZoneData::transition(const QDateTime &dt, const KTimeZone::Transition **secondTransition, 01272 bool *validTime) const 01273 { 01274 int secondIndex; 01275 const int index = transitionIndex(dt, (secondTransition ? &secondIndex : 0), validTime); 01276 if (secondTransition) 01277 *secondTransition = (secondIndex >= 0) ? &d->transitions[secondIndex] : 0; 01278 return (index >= 0) ? &d->transitions[index] : 0; 01279 } 01280 01281 int KTimeZoneData::transitionIndex(const QDateTime &dt, int *secondIndex, bool *validTime) const 01282 { 01283 if (validTime) 01284 *validTime = true; 01285 01286 // Find the last transition before this date/time 01287 int index = d->transitionIndex(dt); 01288 if (dt.timeSpec() == Qt::UTC) 01289 { 01290 if (secondIndex) 01291 *secondIndex = index; 01292 return index; 01293 } 01294 else 01295 { 01296 /* Check whether the specified local time actually occurs. 01297 * Find the start of the next phase, and check if it falls in the gap 01298 * between the two phases. 01299 */ 01300 QDateTime dtutc = dt; 01301 dtutc.setTimeSpec(Qt::UTC); 01302 const int count = d->transitions.count(); 01303 const int next = (index >= 0) ? index + 1 : 0; 01304 if (next < count) 01305 { 01306 KTimeZone::Phase nextPhase = d->transitions[next].phase(); 01307 const int offset = (index >= 0) ? d->transitions[index].phase().utcOffset() : d->prePhase.utcOffset(); 01308 const int phaseDiff = nextPhase.utcOffset() - offset; 01309 if (phaseDiff > 0) 01310 { 01311 // Get UTC equivalent as if 'dt' was in the next phase 01312 if (dtutc.secsTo(d->transitions[next].time()) + nextPhase.utcOffset() < phaseDiff) 01313 { 01314 // The time falls in the gap between the two phases, 01315 // so return an invalid value. 01316 if (validTime) 01317 *validTime = false; 01318 if (secondIndex) 01319 *secondIndex = -1; 01320 return -1; 01321 } 01322 } 01323 } 01324 01325 if (index < 0) 01326 { 01327 // The specified time is before the first phase 01328 if (secondIndex) 01329 *secondIndex = -1; 01330 return -1; 01331 } 01332 01333 /* Check if it's a local time which occurs both before and after the 'latest' 01334 * phase start time (for which it has to span a daylight saving to standard 01335 * time change). 01336 */ 01337 bool duplicate = true; 01338 if (d->isSecondOccurrence(dtutc, index)) 01339 { 01340 // 'dt' occurs twice 01341 if (secondIndex) 01342 { 01343 *secondIndex = index; 01344 duplicate = false; 01345 } 01346 // Get the transition containing the first occurrence of 'dt' 01347 if (index <= 0) 01348 return -1; // first occurrence of 'dt' is just before the first transition 01349 --index; 01350 } 01351 01352 if (secondIndex && duplicate) 01353 *secondIndex = index; 01354 return index; 01355 } 01356 } 01357 01358 QList<QDateTime> KTimeZoneData::transitionTimes(const KTimeZone::Phase &phase, const QDateTime &start, const QDateTime &end) const 01359 { 01360 QList<QDateTime> times; 01361 int ixstart, ixend; 01362 if (d->transitionIndexes(start, end, ixstart, ixend)) 01363 { 01364 if (ixend < 0) 01365 ixend = d->transitions.count() - 1; 01366 while (ixstart <= ixend) 01367 { 01368 if (d->transitions[ixstart].phase() == phase) 01369 times += d->transitions[ixstart].time(); 01370 } 01371 } 01372 return times; 01373 } 01374 01375 QList<KTimeZone::LeapSeconds> KTimeZoneData::leapSecondChanges() const 01376 { 01377 return d->leapChanges; 01378 } 01379 01380 void KTimeZoneData::setLeapSecondChanges(const QList<KTimeZone::LeapSeconds> &adjusts) 01381 { 01382 d->leapChanges = adjusts; 01383 } 01384 01385 KTimeZone::LeapSeconds KTimeZoneData::leapSecondChange(const QDateTime &utc) const 01386 { 01387 if (utc.timeSpec() != Qt::UTC) 01388 kError() << "KTimeZoneData::leapSecondChange(): non-UTC time specified" << endl; 01389 else 01390 { 01391 for (int i = d->leapChanges.count(); --i >= 0; ) 01392 { 01393 if (d->leapChanges[i].dateTime() < utc) 01394 return d->leapChanges[i]; 01395 } 01396 } 01397 return KTimeZone::LeapSeconds(); 01398 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:28:13 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:28:13 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.