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