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