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