• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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;
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 {
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)    // check for invalid time
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)    // check for invalid time
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)    // check for invalid time
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     // 'impl' should be a newly constructed object, with refCount = 1
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)    // check for invalid time
00741         return QDateTime();
00742 
00743     // Convert UTC to local time
00744     if (hasTransitions())
00745     {
00746         if (!data(true))
00747         {
00748             // No data - default to UTC
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             // Check whether the local time occurs twice around a daylight savings time
00760             // shift, and if so, whether it's the first or second occurrence.
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             // Check whether the local time occurs twice around a daylight savings time
00774             // shift, and if so, whether it's the first or second occurrence.
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     // Get current offset of this time zone to UTC
00810     time_t now = time(0);
00811     int secs = offset(now);
00812 
00813     switch (basis)
00814     {
00815         case Qt::LocalTime:
00816             // Return the current offset of this time zone to the local system time
00817             return secs - gmtoff(now);
00818         case Qt::UTC:
00819             // Return the current offset of this time zone to UTC
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);  // method should never be called if it isn't usable
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;         // UTC time when this change occurred
00920         QString    comment;    // optional comment
00921         int        seconds;    // number of leap 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)   // invalid if start time is not 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     // Do a binary search to find the last transition before this date/time
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 // Find the indexes to the transitions at or after start, and before or at end.
01024 // start and end must be UTC.
01025 // Reply = false if none.
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;   // there are no transitions at/after 'start'
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;   // there are no transitions at/before 'end'
01046     }
01047     return true;
01048 }
01049 
01050 /* Check if it's a local time which occurs both before and after the specified
01051  * transition (for which it has to span a daylight saving to standard time change).
01052  * @param utcLocalTime local time set to Qt::UTC
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     // Find how long after the start of the latest phase 'dt' is
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>();   // there are no transitions within the time period
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     // Find the last transition before this date/time
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         /* Check whether the specified local time actually occurs.
01218          * Find the start of the next phase, and check if it falls in the gap
01219          * between the two phases.
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                 // Get UTC equivalent as if 'dt' was in the next phase
01233                 if (dtutc.secsTo(d->transitions[next].time()) + nextPhase.utcOffset() < phaseDiff)
01234                 {
01235                     // The time falls in the gap between the two phases,
01236                     // so return an invalid value.
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             // The specified time is before the first phase
01249             if (secondIndex)
01250                 *secondIndex = -1;
01251             return -1;
01252         }
01253 
01254         /* Check if it's a local time which occurs both before and after the 'latest'
01255          * phase start time (for which it has to span a daylight saving to standard
01256          * time change).
01257          */
01258         bool duplicate = true;
01259         if (d->isSecondOccurrence(dtutc, index))
01260         {
01261             // 'dt' occurs twice
01262             if (secondIndex)
01263             {
01264                 *secondIndex = index;
01265                 duplicate = false;
01266             }
01267             // Get the transition containing the first occurrence of 'dt'
01268             if (index <= 0)
01269                 return -1;   // first occurrence of 'dt' is just before the first transition
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 }

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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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