• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal