File indexing completed on 2024-04-28 15:21:24

0001 /*
0002    This file is part of the KDE libraries
0003    Copyright (c) 2005-2008,2011 David Jarvie <djarvie@kde.org>
0004    Copyright (c) 2005 S.R.Haque <srhaque@iee.org>.
0005 
0006    This library is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 
0011    This library is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this library; see the file COPYING.LIB.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019    Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #include "ktimezone.h"
0023 
0024 #include <config-date.h> // SIZEOF_TIME_T
0025 
0026 #if HAVE_SYS_TIME_H
0027 #endif
0028 #if HAVE_TIME_H
0029 #include <time.h>
0030 #endif
0031 #include <climits>
0032 #include <cstdlib>
0033 
0034 #include <QDebug>
0035 #include <QSet>
0036 #include <QSharedData>
0037 #include <QCoreApplication>
0038 
0039 int gmtoff(time_t t);   // defined in ksystemtimezone.cpp
0040 
0041 /******************************************************************************/
0042 
0043 class KTimeZonesPrivate
0044 {
0045 public:
0046     KTimeZonesPrivate() {}
0047 
0048     KTimeZones::ZoneMap zones;
0049 };
0050 
0051 KTimeZones::KTimeZones()
0052     : d(new KTimeZonesPrivate)
0053 {
0054 }
0055 
0056 KTimeZones::~KTimeZones()
0057 {
0058     delete d;
0059 }
0060 
0061 const KTimeZones::ZoneMap KTimeZones::zones() const
0062 {
0063     return d->zones;
0064 }
0065 
0066 bool KTimeZones::add(const KTimeZone &zone)
0067 {
0068     if (!zone.isValid()) {
0069         return false;
0070     }
0071     if (d->zones.find(zone.name()) != d->zones.end()) {
0072         return false;    // name already exists
0073     }
0074     d->zones.insert(zone.name(), zone);
0075     return true;
0076 }
0077 
0078 KTimeZone KTimeZones::remove(const KTimeZone &zone)
0079 {
0080     if (zone.isValid()) {
0081         for (ZoneMap::Iterator it = d->zones.begin(), end = d->zones.end();  it != end;  ++it) {
0082             if (it.value() == zone) {
0083                 d->zones.erase(it);
0084                 return zone;
0085             }
0086         }
0087     }
0088     return KTimeZone();
0089 }
0090 
0091 KTimeZone KTimeZones::remove(const QString &name)
0092 {
0093     if (!name.isEmpty()) {
0094         ZoneMap::Iterator it = d->zones.find(name);
0095         if (it != d->zones.end()) {
0096             KTimeZone zone = it.value();
0097             d->zones.erase(it);
0098             return zone;
0099         }
0100     }
0101     return KTimeZone();
0102 }
0103 
0104 void KTimeZones::clear()
0105 {
0106     d->zones.clear();
0107 }
0108 
0109 KTimeZone KTimeZones::zone(const QString &name) const
0110 {
0111     if (!name.isEmpty()) {
0112         ZoneMap::ConstIterator it = d->zones.constFind(name);
0113         if (it != d->zones.constEnd()) {
0114             return it.value();
0115         }
0116         if (name == KTimeZone::utc().name()) {
0117             return KTimeZone::utc();
0118         }
0119     }
0120     return KTimeZone();    // error
0121 }
0122 
0123 /******************************************************************************/
0124 
0125 class KTimeZonePhasePrivate : public QSharedData
0126 {
0127 public:
0128     QByteArray       abbreviations;  // time zone abbreviations (zero-delimited)
0129     QString          comment;        // optional comment
0130     int              utcOffset;      // seconds to add to UTC
0131     bool             dst;            // true if daylight savings time
0132 
0133     KDELIBS4SUPPORT_DEPRECATED explicit KTimeZonePhasePrivate(int offset = 0, bool ds = false)
0134         : QSharedData(),
0135           utcOffset(offset),
0136           dst(ds)
0137     {}
0138     KTimeZonePhasePrivate(const KTimeZonePhasePrivate &rhs)
0139         : QSharedData(rhs),
0140           abbreviations(rhs.abbreviations),
0141           comment(rhs.comment),
0142           utcOffset(rhs.utcOffset),
0143           dst(rhs.dst)
0144     {}
0145     bool operator==(const KTimeZonePhasePrivate &rhs) const
0146     {
0147         return abbreviations == rhs.abbreviations
0148                &&  comment       == rhs.comment
0149                &&  utcOffset     == rhs.utcOffset
0150                &&  dst           == rhs.dst;
0151     }
0152 };
0153 
0154 KTimeZone::Phase::Phase()
0155     : d(new KTimeZonePhasePrivate)
0156 {
0157 }
0158 
0159 KTimeZone::Phase::Phase(int utcOffset, const QByteArray &abbrevs,
0160                         bool dst, const QString &cmt)
0161     : d(new KTimeZonePhasePrivate(utcOffset, dst))
0162 {
0163     d->abbreviations = abbrevs;
0164     d->comment       = cmt;
0165 }
0166 
0167 KTimeZone::Phase::Phase(int utcOffset, const QList<QByteArray> &abbrevs,
0168                         bool dst, const QString &cmt)
0169     : d(new KTimeZonePhasePrivate(utcOffset, dst))
0170 {
0171     for (int i = 0, end = abbrevs.count();  i < end;  ++i) {
0172         if (i > 0) {
0173             d->abbreviations += '\0';
0174         }
0175         d->abbreviations += abbrevs[i];
0176     }
0177     d->comment = cmt;
0178 }
0179 
0180 KTimeZone::Phase::Phase(const KTimeZone::Phase &rhs)
0181     : d(rhs.d)
0182 {
0183 }
0184 
0185 KTimeZone::Phase::~Phase()
0186 {
0187 }
0188 
0189 KTimeZone::Phase &KTimeZone::Phase::operator=(const KTimeZone::Phase &rhs)
0190 {
0191     d = rhs.d;
0192     return *this;
0193 }
0194 
0195 bool KTimeZone::Phase::operator==(const KTimeZone::Phase &rhs) const
0196 {
0197     return d == rhs.d  ||  *d == *rhs.d;
0198 }
0199 
0200 int KTimeZone::Phase::utcOffset() const
0201 {
0202     return d->utcOffset;
0203 }
0204 
0205 QList<QByteArray> KTimeZone::Phase::abbreviations() const
0206 {
0207     return d->abbreviations.split('\0');
0208 }
0209 
0210 bool KTimeZone::Phase::isDst() const
0211 {
0212     return d->dst;
0213 }
0214 
0215 QString KTimeZone::Phase::comment() const
0216 {
0217     return d->comment;
0218 }
0219 
0220 /******************************************************************************/
0221 
0222 class KTimeZoneTransitionPrivate
0223 {
0224 public:
0225     QDateTime time;
0226     KTimeZone::Phase phase;
0227 };
0228 
0229 KTimeZone::Transition::Transition()
0230     : d(new KTimeZoneTransitionPrivate)
0231 {
0232 }
0233 
0234 KTimeZone::Transition::Transition(const QDateTime &t, const KTimeZone::Phase &p)
0235     : d(new KTimeZoneTransitionPrivate)
0236 {
0237     d->time  = t;
0238     d->phase = p;
0239 }
0240 
0241 KTimeZone::Transition::Transition(const KTimeZone::Transition &t)
0242     : d(new KTimeZoneTransitionPrivate)
0243 {
0244     d->time  = t.d->time;
0245     d->phase = t.d->phase;
0246 }
0247 
0248 KTimeZone::Transition::~Transition()
0249 {
0250     delete d;
0251 }
0252 
0253 KTimeZone::Transition &KTimeZone::Transition::operator=(const KTimeZone::Transition &t)
0254 {
0255     d->time  = t.d->time;
0256     d->phase = t.d->phase;
0257     return *this;
0258 }
0259 
0260 bool KTimeZone::Transition::operator<(const KTimeZone::Transition &rhs) const
0261 {
0262     return d->time < rhs.d->time;
0263 }
0264 
0265 QDateTime        KTimeZone::Transition::time() const
0266 {
0267     return d->time;
0268 }
0269 KTimeZone::Phase KTimeZone::Transition::phase() const
0270 {
0271     return d->phase;
0272 }
0273 
0274 /******************************************************************************/
0275 
0276 class KTimeZoneDataPrivate
0277 {
0278 public:
0279     QList<KTimeZone::Phase>       phases;
0280     QList<KTimeZone::Transition>  transitions;
0281     QList<KTimeZone::LeapSeconds> leapChanges;
0282     QList<int>                    utcOffsets;
0283     QList<QByteArray>             abbreviations;
0284     KTimeZone::Phase              prePhase;    // phase to use before the first transition
0285 
0286     KTimeZoneDataPrivate() {}
0287     // Find the last transition before a specified UTC or local date/time.
0288     int transitionIndex(const QDateTime &dt) const;
0289     bool transitionIndexes(const QDateTime &start, const QDateTime &end, int &ixstart, int &ixend) const;
0290     bool isSecondOccurrence(const QDateTime &utcLocalTime, int transitionIndex) const;
0291 };
0292 
0293 /******************************************************************************/
0294 
0295 class KTimeZonePrivate : public QSharedData
0296 {
0297 public:
0298     KTimeZonePrivate() : source(nullptr), data(nullptr), refCount(1), cachedTransitionIndex(-1) {}
0299     KTimeZonePrivate(KTimeZoneSource *src, const QString &nam,
0300                      const QString &country, float lat, float lon, const QString &cmnt);
0301     KTimeZonePrivate(const KTimeZonePrivate &);
0302     ~KTimeZonePrivate()
0303     {
0304         delete data;
0305     }
0306     KTimeZonePrivate &operator=(const KTimeZonePrivate &);
0307     static KTimeZoneSource *utcSource();
0308     static void cleanup();
0309 
0310     KTimeZoneSource *source;
0311     QString name;
0312     QString countryCode;
0313     QString comment;
0314     float   latitude;
0315     float   longitude;
0316     mutable KTimeZoneData *data;
0317     int     refCount; // holds the number of KTimeZoneBackend instances using the KTimeZonePrivate instance as a d-pointer.
0318     int       cachedTransitionIndex;
0319     QDateTime cachedTransitionStartZoneTime;
0320     QDateTime cachedTransitionEndZoneTime;
0321     bool      cachedTransitionTimesValid;
0322 
0323 private:
0324     static KTimeZoneSource *mUtcSource;
0325 };
0326 
0327 KTimeZoneSource *KTimeZonePrivate::mUtcSource = nullptr;
0328 
0329 KTimeZonePrivate::KTimeZonePrivate(KTimeZoneSource *src, const QString &nam,
0330                                    const QString &country, float lat, float lon, const QString &cmnt)
0331     : source(src),
0332       name(nam),
0333       countryCode(country.toUpper()),
0334       comment(cmnt),
0335       latitude(lat),
0336       longitude(lon),
0337       data(nullptr),
0338       refCount(1),
0339       cachedTransitionIndex(-1)
0340 {
0341     // Detect duff values.
0342     if (latitude > 90 || latitude < -90) {
0343         latitude = KTimeZone::UNKNOWN;
0344     }
0345     if (longitude > 180 || longitude < -180) {
0346         longitude = KTimeZone::UNKNOWN;
0347     }
0348 }
0349 
0350 KTimeZonePrivate::KTimeZonePrivate(const KTimeZonePrivate &rhs)
0351     : QSharedData(rhs),
0352       source(rhs.source),
0353       name(rhs.name),
0354       countryCode(rhs.countryCode),
0355       comment(rhs.comment),
0356       latitude(rhs.latitude),
0357       longitude(rhs.longitude),
0358       refCount(1),
0359       cachedTransitionIndex(rhs.cachedTransitionIndex),
0360       cachedTransitionStartZoneTime(rhs.cachedTransitionStartZoneTime),
0361       cachedTransitionEndZoneTime(rhs.cachedTransitionEndZoneTime),
0362       cachedTransitionTimesValid(rhs.cachedTransitionTimesValid)
0363 {
0364     if (rhs.data) {
0365         data = rhs.data->clone();
0366     } else {
0367         data = nullptr;
0368     }
0369 }
0370 
0371 KTimeZonePrivate &KTimeZonePrivate::operator=(const KTimeZonePrivate &rhs)
0372 {
0373     // Changing the contents of a KTimeZonePrivate instance by means of operator=() doesn't affect how
0374     // many references to it are held.
0375     source      = rhs.source;
0376     name        = rhs.name;
0377     countryCode = rhs.countryCode;
0378     comment     = rhs.comment;
0379     latitude    = rhs.latitude;
0380     longitude   = rhs.longitude;
0381     cachedTransitionIndex         = rhs.cachedTransitionIndex;
0382     cachedTransitionStartZoneTime = rhs.cachedTransitionStartZoneTime;
0383     cachedTransitionEndZoneTime   = rhs.cachedTransitionEndZoneTime;
0384     cachedTransitionTimesValid    = rhs.cachedTransitionTimesValid;
0385     delete data;
0386     if (rhs.data) {
0387         data = rhs.data->clone();
0388     } else {
0389         data = nullptr;
0390     }
0391     // refCount is unchanged
0392     return *this;
0393 }
0394 
0395 KTimeZoneSource *KTimeZonePrivate::utcSource()
0396 {
0397     if (!mUtcSource) {
0398         mUtcSource = new KTimeZoneSource;
0399         qAddPostRoutine(KTimeZonePrivate::cleanup);
0400     }
0401     return mUtcSource;
0402 }
0403 
0404 void KTimeZonePrivate::cleanup()
0405 {
0406     delete mUtcSource;
0407 }
0408 
0409 /******************************************************************************/
0410 
0411 Q_GLOBAL_STATIC(KTimeZonePrivate, s_emptyTimeZonePrivate)
0412 
0413 KTimeZoneBackend::KTimeZoneBackend()
0414     : d(s_emptyTimeZonePrivate())
0415 {
0416     ++d->refCount;
0417 }
0418 
0419 KTimeZoneBackend::KTimeZoneBackend(const QString &name)
0420     : d(new KTimeZonePrivate(KTimeZonePrivate::utcSource(), name, QString(), KTimeZone::UNKNOWN, KTimeZone::UNKNOWN, QString()))
0421 {}
0422 
0423 KTimeZoneBackend::KTimeZoneBackend(KTimeZoneSource *source, const QString &name,
0424                                    const QString &countryCode, float latitude, float longitude, const QString &comment)
0425     : d(new KTimeZonePrivate(source, name, countryCode, latitude, longitude, comment))
0426 {}
0427 
0428 KTimeZoneBackend::KTimeZoneBackend(const KTimeZoneBackend &other)
0429     : d(other.d)
0430 {
0431     ++d->refCount;
0432 }
0433 
0434 KTimeZoneBackend::~KTimeZoneBackend()
0435 {
0436     if (d && --d->refCount == 0) {
0437         delete d;
0438     }
0439     d = nullptr;
0440 }
0441 
0442 KTimeZoneBackend &KTimeZoneBackend::operator=(const KTimeZoneBackend &other)
0443 {
0444     if (d != other.d) {
0445         if (--d->refCount == 0) {
0446             delete d;
0447         }
0448         d = other.d;
0449         ++d->refCount;
0450     }
0451     return *this;
0452 }
0453 
0454 QByteArray KTimeZoneBackend::type() const
0455 {
0456     return "KTimeZone";
0457 }
0458 
0459 KTimeZoneBackend *KTimeZoneBackend::clone() const
0460 {
0461     return new KTimeZoneBackend(*this);
0462 }
0463 
0464 int KTimeZoneBackend::offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const
0465 {
0466     if (!zoneDateTime.isValid()  ||  zoneDateTime.timeSpec() != Qt::LocalTime) {  // check for invalid time
0467         if (secondOffset) {
0468             *secondOffset = KTimeZone::InvalidOffset;
0469         }
0470         return KTimeZone::InvalidOffset;
0471     }
0472     const QList<KTimeZone::Transition> transitions = caller->transitions();
0473     int index = d->cachedTransitionIndex;
0474     if (index >= 0 && index < transitions.count()) {
0475         // There is a cached transition - check whether zoneDateTime uses it.
0476         // Caching is used because this method has been found to consume
0477         // significant CPU in real life applications.
0478         if (!d->cachedTransitionTimesValid) {
0479             const int offset = transitions[index].phase().utcOffset();
0480             const int preoffset = (index > 0) ? transitions[index - 1].phase().utcOffset() : d->data ? d->data->previousUtcOffset() : KTimeZone::InvalidOffset;
0481             d->cachedTransitionStartZoneTime = transitions[index].time().addSecs(qMax(offset, preoffset));
0482             if (index + 1 < transitions.count()) {
0483                 const int postoffset = transitions[index + 1].phase().utcOffset();
0484                 d->cachedTransitionEndZoneTime = transitions[index + 1].time().addSecs(qMin(offset, postoffset));
0485             }
0486             d->cachedTransitionTimesValid = true;
0487         }
0488         QDateTime dtutc = zoneDateTime;
0489         dtutc.setTimeSpec(Qt::UTC);
0490         if (dtutc >= d->cachedTransitionStartZoneTime
0491                 && (index + 1 >= transitions.count() || dtutc < d->cachedTransitionEndZoneTime)) {
0492             // The time falls within the cached transition limits, so return its UTC offset
0493             const int offset = transitions[index].phase().utcOffset();
0494             if (secondOffset) {
0495                 *secondOffset = offset;
0496             }
0497 #ifdef COMPILING_TESTS
0498             // qDebug() << "-> Using cache";   // enable the debug area to see this in the tests
0499 #endif
0500             return offset;
0501         }
0502     }
0503 
0504     // The time doesn't fall within the cached transition, or there isn't a cached transition
0505 #ifdef COMPILING_TESTS
0506     // qDebug() << "-> No cache";   // enable the debug area to see this in the tests
0507 #endif
0508     bool validTime;
0509     int secondIndex = -1;
0510     index = caller->transitionIndex(zoneDateTime, (secondOffset ? &secondIndex : nullptr), &validTime);
0511     const KTimeZone::Transition *tr = (index >= 0) ? &transitions[index] : nullptr;
0512     const int offset = tr ? tr->phase().utcOffset()
0513                        : validTime ? (d->data ? d->data->previousUtcOffset() : KTimeZone::InvalidOffset)
0514                        : KTimeZone::InvalidOffset;
0515     if (secondOffset) {
0516         *secondOffset = (secondIndex >= 0) ? transitions.at(secondIndex).phase().utcOffset() : offset;
0517     }
0518 
0519     // Cache transition data for subsequent date/time values which occur after the same transition.
0520     d->cachedTransitionIndex = index;
0521     d->cachedTransitionTimesValid = false;
0522     return offset;
0523 }
0524 
0525 int KTimeZoneBackend::offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
0526 {
0527     if (!utcDateTime.isValid()  ||  utcDateTime.timeSpec() != Qt::UTC) {  // check for invalid time
0528         return 0;
0529     }
0530     const QList<KTimeZone::Transition> transitions = caller->transitions();
0531     int index = d->cachedTransitionIndex;
0532     if (index >= 0 && index < transitions.count()) {
0533         // There is a cached transition - check whether utcDateTime uses it.
0534         if (utcDateTime >= transitions[index].time()
0535                 && (index + 1 >= transitions.count()
0536                     || utcDateTime < transitions[index + 1].time())) {
0537             // The time falls within the cached transition, so return its UTC offset
0538 #ifdef COMPILING_TESTS
0539             // qDebug() << "Using cache";   // enable the debug area to see this in the tests
0540 #endif
0541             return transitions[index].phase().utcOffset();
0542         }
0543     }
0544 
0545     // The time doesn't fall within the cached transition, or there isn't a cached transition
0546 #ifdef COMPILING_TESTS
0547     // qDebug() << "No cache";   // enable the debug area to see this in the tests
0548 #endif
0549     index = caller->transitionIndex(utcDateTime);
0550     d->cachedTransitionIndex = index;   // cache transition data
0551     d->cachedTransitionTimesValid = false;
0552     const KTimeZone::Transition *tr = (index >= 0) ? &transitions.at(index) : nullptr;
0553     return tr ? tr->phase().utcOffset() : (d->data ? d->data->previousUtcOffset() : KTimeZone::InvalidOffset);
0554 }
0555 
0556 int KTimeZoneBackend::offset(const KTimeZone *caller, time_t t) const
0557 {
0558     return offsetAtUtc(caller, KTimeZone::fromTime_t(t));
0559 }
0560 
0561 bool KTimeZoneBackend::isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
0562 {
0563     if (!utcDateTime.isValid()  ||  utcDateTime.timeSpec() != Qt::UTC) {  // check for invalid time
0564         return false;
0565     }
0566     const KTimeZone::Transition *tr = caller->transition(utcDateTime);
0567     if (!tr) {
0568         return false;
0569     }
0570     return tr->phase().isDst();
0571 }
0572 
0573 bool KTimeZoneBackend::isDst(const KTimeZone *caller, time_t t) const
0574 {
0575     return isDstAtUtc(caller, KTimeZone::fromTime_t(t));
0576 }
0577 
0578 bool KTimeZoneBackend::hasTransitions(const KTimeZone *caller) const
0579 {
0580     Q_UNUSED(caller);
0581     return false;
0582 }
0583 
0584 /******************************************************************************/
0585 
0586 #if SIZEOF_TIME_T == 8
0587 const time_t KTimeZone::InvalidTime_t = 0x800000000000000LL;
0588 #else
0589 const time_t KTimeZone::InvalidTime_t = 0x80000000;
0590 #endif
0591 const int    KTimeZone::InvalidOffset = 0x80000000;
0592 const float  KTimeZone::UNKNOWN = 1000.0;
0593 
0594 KTimeZone::KTimeZone()
0595     : d(new KTimeZoneBackend())
0596 {}
0597 
0598 KTimeZone::KTimeZone(const QString &name)
0599     : d(new KTimeZoneBackend(name))
0600 {}
0601 
0602 KTimeZone::KTimeZone(const KTimeZone &tz)
0603     : d(tz.d->clone())
0604 {}
0605 
0606 KTimeZone::~KTimeZone()
0607 {
0608     delete d;
0609 }
0610 
0611 KTimeZone::KTimeZone(KTimeZoneBackend *impl)
0612     : d(impl)
0613 {
0614     // 'impl' should be a newly constructed object, with refCount = 1
0615     Q_ASSERT(d->d->refCount == 1 || d->d == s_emptyTimeZonePrivate());
0616 }
0617 
0618 KTimeZone &KTimeZone::operator=(const KTimeZone &tz)
0619 {
0620     if (d != tz.d) {
0621         delete d;
0622         d = tz.d->clone();
0623     }
0624     return *this;
0625 }
0626 
0627 bool KTimeZone::operator==(const KTimeZone &rhs) const
0628 {
0629     return d->d == rhs.d->d;
0630 }
0631 
0632 QByteArray KTimeZone::type() const
0633 {
0634     return d->type();
0635 }
0636 
0637 bool KTimeZone::isValid() const
0638 {
0639     return !d->d->name.isEmpty();
0640 }
0641 
0642 QString KTimeZone::countryCode() const
0643 {
0644     return d->d->countryCode;
0645 }
0646 
0647 float KTimeZone::latitude() const
0648 {
0649     return d->d->latitude;
0650 }
0651 
0652 float KTimeZone::longitude() const
0653 {
0654     return d->d->longitude;
0655 }
0656 
0657 QString KTimeZone::comment() const
0658 {
0659     return d->d->comment;
0660 }
0661 
0662 QString KTimeZone::name() const
0663 {
0664     return d->d->name;
0665 }
0666 
0667 QList<QByteArray> KTimeZone::abbreviations() const
0668 {
0669     if (!data(true)) {
0670         return QList<QByteArray>();
0671     }
0672     return d->d->data->abbreviations();
0673 }
0674 
0675 QByteArray KTimeZone::abbreviation(const QDateTime &utcDateTime) const
0676 {
0677     if (utcDateTime.timeSpec() != Qt::UTC  ||  !data(true)) {
0678         return QByteArray();
0679     }
0680     return d->d->data->abbreviation(utcDateTime);
0681 }
0682 
0683 QList<int> KTimeZone::utcOffsets() const
0684 {
0685     if (!data(true)) {
0686         return QList<int>();
0687     }
0688     return d->d->data->utcOffsets();
0689 }
0690 
0691 QList<KTimeZone::Phase> KTimeZone::phases() const
0692 {
0693     if (!data(true)) {
0694         return QList<KTimeZone::Phase>();
0695     }
0696     return d->d->data->phases();
0697 }
0698 
0699 bool KTimeZone::hasTransitions() const
0700 {
0701     return d->hasTransitions(this);
0702 }
0703 
0704 QList<KTimeZone::Transition> KTimeZone::transitions(const QDateTime &start, const QDateTime &end) const
0705 {
0706     if (!data(true)) {
0707         return QList<KTimeZone::Transition>();
0708     }
0709     return d->d->data->transitions(start, end);
0710 }
0711 
0712 const KTimeZone::Transition *KTimeZone::transition(const QDateTime &dt, const Transition **secondTransition,
0713         bool *validTime) const
0714 {
0715     if (!data(true)) {
0716         if (validTime) {
0717             *validTime = false;
0718         }
0719         return nullptr;
0720     }
0721     return d->d->data->transition(dt, secondTransition, validTime);
0722 }
0723 
0724 int KTimeZone::transitionIndex(const QDateTime &dt, int *secondIndex, bool *validTime) const
0725 {
0726     if (!data(true)) {
0727         if (validTime) {
0728             *validTime = false;
0729         }
0730         return -1;
0731     }
0732     return d->d->data->transitionIndex(dt, secondIndex, validTime);
0733 }
0734 
0735 QList<QDateTime> KTimeZone::transitionTimes(const Phase &phase, const QDateTime &start, const QDateTime &end) const
0736 {
0737     if (!data(true)) {
0738         return QList<QDateTime>();
0739     }
0740     return d->d->data->transitionTimes(phase, start, end);
0741 }
0742 
0743 QList<KTimeZone::LeapSeconds> KTimeZone::leapSecondChanges() const
0744 {
0745     if (!data(true)) {
0746         return QList<KTimeZone::LeapSeconds>();
0747     }
0748     return d->d->data->leapSecondChanges();
0749 }
0750 
0751 KTimeZoneSource *KTimeZone::source() const
0752 {
0753     return d->d->source;
0754 }
0755 
0756 const KTimeZoneData *KTimeZone::data(bool create) const
0757 {
0758     if (!isValid()) {
0759         return nullptr;
0760     }
0761     if (create && !d->d->data && d->d->source->useZoneParse()) {
0762         d->d->data = d->d->source->parse(*this);
0763     }
0764     return d->d->data;
0765 }
0766 
0767 void KTimeZone::setData(KTimeZoneData *data, KTimeZoneSource *source)
0768 {
0769     if (!isValid()) {
0770         return;
0771     }
0772     delete d->d->data;
0773     d->d->data = data;
0774     if (source) {
0775         d->d->source = source;
0776     }
0777 }
0778 
0779 bool KTimeZone::updateBase(const KTimeZone &other)
0780 {
0781     if (d->d->name.isEmpty() || d->d->name != other.d->d->name) {
0782         return false;
0783     }
0784     d->d->countryCode = other.d->d->countryCode;
0785     d->d->comment     = other.d->d->comment;
0786     d->d->latitude    = other.d->d->latitude;
0787     d->d->longitude   = other.d->d->longitude;
0788     return true;
0789 }
0790 
0791 bool KTimeZone::parse() const
0792 {
0793     if (!isValid()) {
0794         return false;
0795     }
0796     if (d->d->source->useZoneParse()) {
0797         delete d->d->data;
0798         d->d->data = d->d->source->parse(*this);
0799     }
0800     return d->d->data;
0801 }
0802 
0803 QDateTime KTimeZone::toUtc(const QDateTime &zoneDateTime) const
0804 {
0805     if (!zoneDateTime.isValid()  ||  zoneDateTime.timeSpec() != Qt::LocalTime) {
0806         return QDateTime();
0807     }
0808     const int secs = offsetAtZoneTime(zoneDateTime);
0809     if (secs == InvalidOffset) {
0810         return QDateTime();
0811     }
0812     QDateTime dt = zoneDateTime;
0813     dt.setTimeSpec(Qt::UTC);
0814     return dt.addSecs(-secs);
0815 }
0816 
0817 QDateTime KTimeZone::toZoneTime(const QDateTime &utcDateTime, bool *secondOccurrence) const
0818 {
0819     if (secondOccurrence) {
0820         *secondOccurrence = false;
0821     }
0822     if (!utcDateTime.isValid()  ||  utcDateTime.timeSpec() != Qt::UTC) {  // check for invalid time
0823         return QDateTime();
0824     }
0825 
0826     // Convert UTC to local time
0827     if (hasTransitions()) {
0828         if (!data(true)) {
0829             // No data - default to UTC
0830             QDateTime dt = utcDateTime;
0831             dt.setTimeSpec(Qt::LocalTime);
0832             return dt;
0833         }
0834 
0835         const KTimeZoneData *data = d->d->data;
0836         const int index = data->transitionIndex(utcDateTime);
0837         const int secs = (index >= 0) ? data->transitions().at(index).phase().utcOffset() : data->previousUtcOffset();
0838         QDateTime dt = utcDateTime.addSecs(secs);
0839         if (secondOccurrence) {
0840             // Check whether the local time occurs twice around a daylight savings time
0841             // shift, and if so, whether it's the first or second occurrence.
0842             *secondOccurrence = data->d->isSecondOccurrence(dt, index);
0843         }
0844         dt.setTimeSpec(Qt::LocalTime);
0845         return dt;
0846     } else {
0847         const int secs = offsetAtUtc(utcDateTime);
0848         QDateTime dt = utcDateTime.addSecs(secs);
0849         dt.setTimeSpec(Qt::LocalTime);
0850         if (secondOccurrence) {
0851             // Check whether the local time occurs twice around a daylight savings time
0852             // shift, and if so, whether it's the first or second occurrence.
0853             *secondOccurrence = (secs != offsetAtZoneTime(dt));
0854         }
0855         return dt;
0856     }
0857 }
0858 
0859 QDateTime KTimeZone::convert(const KTimeZone &newZone, const QDateTime &zoneDateTime) const
0860 {
0861     if (newZone == *this) {
0862         if (zoneDateTime.timeSpec() != Qt::LocalTime) {
0863             return QDateTime();
0864         }
0865         return zoneDateTime;
0866     }
0867     return newZone.toZoneTime(toUtc(zoneDateTime));
0868 }
0869 
0870 int KTimeZone::offsetAtZoneTime(const QDateTime &zoneDateTime, int *secondOffset) const
0871 {
0872     return d->offsetAtZoneTime(this, zoneDateTime, secondOffset);
0873 }
0874 
0875 int KTimeZone::offsetAtUtc(const QDateTime &utcDateTime) const
0876 {
0877     return d->offsetAtUtc(this, utcDateTime);
0878 }
0879 
0880 int KTimeZone::offset(time_t t) const
0881 {
0882     return d->offset(this, t);
0883 }
0884 
0885 int KTimeZone::currentOffset(Qt::TimeSpec basis) const
0886 {
0887     // Get current offset of this time zone to UTC
0888     const time_t now = time(nullptr);
0889     const int secs = offset(now);
0890 
0891     switch (basis) {
0892     case Qt::LocalTime:
0893         // Return the current offset of this time zone to the local system time
0894         return secs - gmtoff(now);
0895     case Qt::UTC:
0896         // Return the current offset of this time zone to UTC
0897         return secs;
0898 
0899     default:
0900         break;
0901     }
0902     return 0;
0903 }
0904 
0905 bool KTimeZone::isDstAtUtc(const QDateTime &utcDateTime) const
0906 {
0907     return d->isDstAtUtc(this, utcDateTime);
0908 }
0909 
0910 bool KTimeZone::isDst(time_t t) const
0911 {
0912     return d->isDst(this, t);
0913 }
0914 
0915 KTimeZone KTimeZone::utc()
0916 {
0917     static KTimeZone utcZone(QLatin1String("UTC"));
0918     return utcZone;
0919 }
0920 
0921 QDateTime KTimeZone::fromTime_t(time_t t)
0922 {
0923     static const int secondsADay = 86400;
0924     static const QDate epochDate(1970, 1, 1);
0925     static const QTime epochTime(0, 0, 0);
0926     int days = t / secondsADay;
0927     int secs;
0928     if (t >= 0) {
0929         secs = t % secondsADay;
0930     } else {
0931         secs = secondsADay - (-t % secondsADay);
0932         --days;
0933     }
0934     return QDateTime(epochDate.addDays(days), epochTime.addSecs(secs), Qt::UTC);
0935 }
0936 
0937 time_t KTimeZone::toTime_t(const QDateTime &utcDateTime)
0938 {
0939     static const QDate epochDate(1970, 1, 1);
0940     static const QTime epochTime(0, 0, 0);
0941     if (utcDateTime.timeSpec() != Qt::UTC) {
0942         return InvalidTime_t;
0943     }
0944     const qint64 days = epochDate.daysTo(utcDateTime.date());
0945     const qint64 secs = epochTime.secsTo(utcDateTime.time());
0946     const qint64 t64 = days * 86400 + secs;
0947     const time_t t = static_cast<time_t>(t64);
0948     if (static_cast<qint64>(t) != t64) {
0949         return InvalidTime_t;
0950     }
0951     return t;
0952 }
0953 
0954 /******************************************************************************/
0955 
0956 class KTimeZoneSourcePrivate
0957 {
0958 public:
0959     bool mUseZoneParse;
0960 };
0961 
0962 KTimeZoneSource::KTimeZoneSource()
0963     : d(new KTimeZoneSourcePrivate)
0964 {
0965     d->mUseZoneParse = true;
0966 }
0967 
0968 KTimeZoneSource::KTimeZoneSource(bool useZoneParse)
0969     : d(new KTimeZoneSourcePrivate)
0970 {
0971     d->mUseZoneParse = useZoneParse;
0972 }
0973 
0974 KTimeZoneSource::~KTimeZoneSource()
0975 {
0976     delete d;
0977 }
0978 
0979 KTimeZoneData *KTimeZoneSource::parse(const KTimeZone &) const
0980 {
0981     Q_ASSERT(d->mUseZoneParse);  // method should never be called if it isn't usable
0982     return new KTimeZoneData;
0983 }
0984 
0985 bool KTimeZoneSource::useZoneParse() const
0986 {
0987     return d->mUseZoneParse;
0988 }
0989 
0990 /******************************************************************************/
0991 
0992 class KTimeZoneLeapSecondsPrivate
0993 {
0994 public:
0995     QDateTime  dt;         // UTC time when this change occurred
0996     QString    comment;    // optional comment
0997     int        seconds;    // number of leap seconds
0998 };
0999 
1000 KTimeZone::LeapSeconds::LeapSeconds()
1001     : d(new KTimeZoneLeapSecondsPrivate)
1002 {
1003 }
1004 
1005 KTimeZone::LeapSeconds::LeapSeconds(const QDateTime &utc, int leap, const QString &cmt)
1006     : d(new KTimeZoneLeapSecondsPrivate)
1007 {
1008     if (utc.timeSpec() == Qt::UTC) { // invalid if start time is not UTC
1009         d->dt      = utc;
1010         d->comment = cmt;
1011         d->seconds = leap;
1012     }
1013 }
1014 
1015 KTimeZone::LeapSeconds::LeapSeconds(const KTimeZone::LeapSeconds &c)
1016     : d(new KTimeZoneLeapSecondsPrivate)
1017 {
1018     d->dt      = c.d->dt;
1019     d->comment = c.d->comment;
1020     d->seconds = c.d->seconds;
1021 }
1022 
1023 KTimeZone::LeapSeconds::~LeapSeconds()
1024 {
1025     delete d;
1026 }
1027 
1028 KTimeZone::LeapSeconds &KTimeZone::LeapSeconds::operator=(const KTimeZone::LeapSeconds &c)
1029 {
1030     d->dt      = c.d->dt;
1031     d->comment = c.d->comment;
1032     d->seconds = c.d->seconds;
1033     return *this;
1034 }
1035 
1036 bool KTimeZone::LeapSeconds::operator<(const KTimeZone::LeapSeconds &c) const
1037 {
1038     return d->dt < c.d->dt;
1039 }
1040 
1041 QDateTime KTimeZone::LeapSeconds::dateTime() const
1042 {
1043     return d->dt;
1044 }
1045 
1046 bool KTimeZone::LeapSeconds::isValid() const
1047 {
1048     return d->dt.isValid();
1049 }
1050 
1051 int KTimeZone::LeapSeconds::leapSeconds() const
1052 {
1053     return d->seconds;
1054 }
1055 
1056 QString KTimeZone::LeapSeconds::comment() const
1057 {
1058     return d->comment;
1059 }
1060 
1061 /******************************************************************************/
1062 
1063 int KTimeZoneDataPrivate::transitionIndex(const QDateTime &dt) const
1064 {
1065     // Do a binary search to find the last transition before this date/time
1066     int start = -1;
1067     int end = transitions.count();
1068     if (dt.timeSpec() == Qt::UTC) {
1069         while (end - start > 1) {
1070             int i = (start + end) / 2;
1071             if (dt < transitions[i].time()) {
1072                 end = i;
1073             } else {
1074                 start = i;
1075             }
1076         }
1077     } else {
1078         QDateTime dtutc = dt;
1079         dtutc.setTimeSpec(Qt::UTC);
1080         while (end - start > 1) {
1081             const int i = (start + end) / 2;
1082             if (dtutc.addSecs(-transitions[i].phase().utcOffset()) < transitions[i].time()) {
1083                 end = i;
1084             } else {
1085                 start = i;
1086             }
1087         }
1088     }
1089     return end ? start : -1;
1090 }
1091 
1092 // Find the indexes to the transitions at or after start, and before or at end.
1093 // start and end must be UTC.
1094 // Reply = false if none.
1095 bool KTimeZoneDataPrivate::transitionIndexes(const QDateTime &start, const QDateTime &end, int &ixstart, int &ixend) const
1096 {
1097     ixstart = 0;
1098     if (start.isValid() && start.timeSpec() == Qt::UTC) {
1099         ixstart = transitionIndex(start);
1100         if (ixstart < 0) {
1101             ixstart = 0;
1102         } else if (transitions[ixstart].time() < start) {
1103             if (++ixstart >= transitions.count()) {
1104                 return false;    // there are no transitions at/after 'start'
1105             }
1106         }
1107     }
1108     ixend = -1;
1109     if (end.isValid() && end.timeSpec() == Qt::UTC) {
1110         ixend = transitionIndex(end);
1111         if (ixend < 0) {
1112             return false;    // there are no transitions at/before 'end'
1113         }
1114     }
1115     return true;
1116 }
1117 
1118 /* Check if it's a local time which occurs both before and after the specified
1119  * transition (for which it has to span a daylight saving to standard time change).
1120  * @param utcLocalTime local time set to Qt::UTC
1121  */
1122 bool KTimeZoneDataPrivate::isSecondOccurrence(const QDateTime &utcLocalTime, int transitionIndex) const
1123 {
1124     if (transitionIndex < 0) {
1125         return false;
1126     }
1127     const int offset = transitions[transitionIndex].phase().utcOffset();
1128     const int prevoffset = (transitionIndex > 0) ? transitions[transitionIndex - 1].phase().utcOffset() : prePhase.utcOffset();
1129     const int phaseDiff = prevoffset - offset;
1130     if (phaseDiff <= 0) {
1131         return false;
1132     }
1133     // Find how long after the start of the latest phase 'dt' is
1134     const qint64 afterStart = transitions[transitionIndex].time().msecsTo(utcLocalTime)/1000 - offset;
1135     return (afterStart < phaseDiff);
1136 }
1137 
1138 KTimeZoneData::KTimeZoneData()
1139     : d(new KTimeZoneDataPrivate)
1140 { }
1141 
1142 KTimeZoneData::KTimeZoneData(const KTimeZoneData &c)
1143     : d(new KTimeZoneDataPrivate)
1144 {
1145     d->phases        = c.d->phases;
1146     d->transitions   = c.d->transitions;
1147     d->leapChanges   = c.d->leapChanges;
1148     d->utcOffsets    = c.d->utcOffsets;
1149     d->abbreviations = c.d->abbreviations;
1150     d->prePhase      = c.d->prePhase;
1151 }
1152 
1153 KTimeZoneData::~KTimeZoneData()
1154 {
1155     delete d;
1156 }
1157 
1158 KTimeZoneData &KTimeZoneData::operator=(const KTimeZoneData &c)
1159 {
1160     d->phases        = c.d->phases;
1161     d->transitions   = c.d->transitions;
1162     d->leapChanges   = c.d->leapChanges;
1163     d->utcOffsets    = c.d->utcOffsets;
1164     d->abbreviations = c.d->abbreviations;
1165     d->prePhase      = c.d->prePhase;
1166     return *this;
1167 }
1168 
1169 KTimeZoneData *KTimeZoneData::clone() const
1170 {
1171     return new KTimeZoneData(*this);
1172 }
1173 
1174 QList<QByteArray> KTimeZoneData::abbreviations() const
1175 {
1176     if (d->abbreviations.isEmpty()) {
1177         for (int i = 0, end = d->phases.count();  i < end;  ++i) {
1178             const QList<QByteArray> abbrevs = d->phases[i].abbreviations();
1179             for (int j = 0, jend = abbrevs.count();  j < jend;  ++j)
1180                 if (!d->abbreviations.contains(abbrevs[j])) {
1181                     d->abbreviations.append(abbrevs[j]);
1182                 }
1183         }
1184         if (d->abbreviations.isEmpty()) {
1185             d->abbreviations += "UTC";
1186         }
1187     }
1188     return d->abbreviations;
1189 }
1190 
1191 QByteArray KTimeZoneData::abbreviation(const QDateTime &utcDateTime) const
1192 {
1193     if (d->phases.isEmpty()) {
1194         return "UTC";
1195     }
1196     const KTimeZone::Transition *tr = transition(utcDateTime);
1197     const QList<QByteArray> abbrevs = tr ? tr->phase().abbreviations()
1198                                       : d->prePhase.abbreviations();
1199     if (abbrevs.isEmpty()) {
1200         return QByteArray();
1201     }
1202     return abbrevs[0];
1203 }
1204 
1205 QList<int> KTimeZoneData::utcOffsets() const
1206 {
1207     if (d->utcOffsets.isEmpty()) {
1208         for (int i = 0, end = d->phases.count();  i < end;  ++i) {
1209             const int offset = d->phases[i].utcOffset();
1210             if (!d->utcOffsets.contains(offset)) {
1211                 d->utcOffsets.append(offset);
1212             }
1213         }
1214         if (d->utcOffsets.isEmpty()) {
1215             d->utcOffsets += 0;
1216         } else {
1217             std::sort(d->utcOffsets.begin(), d->utcOffsets.end());
1218         }
1219     }
1220     return d->utcOffsets;
1221 }
1222 
1223 QList<KTimeZone::Phase> KTimeZoneData::phases() const
1224 {
1225     return d->phases;
1226 }
1227 
1228 void KTimeZoneData::setPhases(const QList<KTimeZone::Phase> &phases, const KTimeZone::Phase &previousPhase)
1229 {
1230     d->phases   = phases;
1231     d->prePhase = previousPhase;
1232 }
1233 
1234 void KTimeZoneData::setPhases(const QList<KTimeZone::Phase> &phases, int previousUtcOffset)
1235 {
1236     d->phases   = phases;
1237     d->prePhase = KTimeZone::Phase(previousUtcOffset, QByteArray(), false);
1238 }
1239 
1240 bool KTimeZoneData::hasTransitions() const
1241 {
1242     return false;
1243 }
1244 
1245 QList<KTimeZone::Transition> KTimeZoneData::transitions(const QDateTime &start, const QDateTime &end) const
1246 {
1247     int ixstart, ixend;
1248     if (!d->transitionIndexes(start, end, ixstart, ixend)) {
1249         return QList<KTimeZone::Transition>();    // there are no transitions within the time period
1250     }
1251     if (ixend >= 0) {
1252         return d->transitions.mid(ixstart, ixend - ixstart + 1);
1253     }
1254     if (ixstart > 0) {
1255         return d->transitions.mid(ixstart);
1256     }
1257     return d->transitions;
1258 }
1259 
1260 void KTimeZoneData::setTransitions(const QList<KTimeZone::Transition> &transitions)
1261 {
1262     d->transitions = transitions;
1263 }
1264 
1265 int KTimeZoneData::previousUtcOffset() const
1266 {
1267     return d->prePhase.utcOffset();
1268 }
1269 
1270 const KTimeZone::Transition *KTimeZoneData::transition(const QDateTime &dt, const KTimeZone::Transition **secondTransition,
1271         bool *validTime) const
1272 {
1273     int secondIndex;
1274     const int index = transitionIndex(dt, (secondTransition ? &secondIndex : nullptr), validTime);
1275     if (secondTransition) {
1276         *secondTransition = (secondIndex >= 0) ? &d->transitions[secondIndex] : nullptr;
1277     }
1278     return (index >= 0) ? &d->transitions[index] : nullptr;
1279 }
1280 
1281 int KTimeZoneData::transitionIndex(const QDateTime &dt, int *secondIndex, bool *validTime) const
1282 {
1283     if (validTime) {
1284         *validTime = true;
1285     }
1286 
1287     // Find the last transition before this date/time
1288     int index = d->transitionIndex(dt);
1289     if (dt.timeSpec() == Qt::UTC) {
1290         if (secondIndex) {
1291             *secondIndex = index;
1292         }
1293         return index;
1294     } else {
1295         /* Check whether the specified local time actually occurs.
1296          * Find the start of the next phase, and check if it falls in the gap
1297          * between the two phases.
1298          */
1299         QDateTime dtutc = dt;
1300         dtutc.setTimeSpec(Qt::UTC);
1301         const int count = d->transitions.count();
1302         const int next = (index >= 0) ? index + 1 : 0;
1303         if (next < count) {
1304             KTimeZone::Phase nextPhase = d->transitions.at(next).phase();
1305             const int offset = (index >= 0) ? d->transitions.at(index).phase().utcOffset() : d->prePhase.utcOffset();
1306             const int phaseDiff = nextPhase.utcOffset() - offset;
1307             if (phaseDiff > 0) {
1308                 // Get UTC equivalent as if 'dt' was in the next phase
1309                 if (dtutc.msecsTo(d->transitions.at(next).time())/1000 + nextPhase.utcOffset() <= phaseDiff) {
1310                     // The time falls in the gap between the two phases,
1311                     // so return an invalid value.
1312                     if (validTime) {
1313                         *validTime = false;
1314                     }
1315                     if (secondIndex) {
1316                         *secondIndex = -1;
1317                     }
1318                     return -1;
1319                 }
1320             }
1321         }
1322 
1323         if (index < 0) {
1324             // The specified time is before the first phase
1325             if (secondIndex) {
1326                 *secondIndex = -1;
1327             }
1328             return -1;
1329         }
1330 
1331         /* Check if it's a local time which occurs both before and after the 'latest'
1332          * phase start time (for which it has to span a daylight saving to standard
1333          * time change).
1334          */
1335         bool duplicate = true;
1336         if (d->isSecondOccurrence(dtutc, index)) {
1337             // 'dt' occurs twice
1338             if (secondIndex) {
1339                 *secondIndex = index;
1340                 duplicate = false;
1341             }
1342             // Get the transition containing the first occurrence of 'dt'
1343             if (index <= 0) {
1344                 return -1;    // first occurrence of 'dt' is just before the first transition
1345             }
1346             --index;
1347         }
1348 
1349         if (secondIndex  &&  duplicate) {
1350             *secondIndex = index;
1351         }
1352         return index;
1353     }
1354 }
1355 
1356 QList<QDateTime> KTimeZoneData::transitionTimes(const KTimeZone::Phase &phase, const QDateTime &start, const QDateTime &end) const
1357 {
1358     QList<QDateTime> times;
1359     int ixstart, ixend;
1360     if (d->transitionIndexes(start, end, ixstart, ixend)) {
1361         if (ixend < 0) {
1362             ixend = d->transitions.count() - 1;
1363         }
1364         while (ixstart <= ixend) {
1365             if (d->transitions[ixstart].phase() == phase) {
1366                 times += d->transitions[ixstart].time();
1367             }
1368         }
1369     }
1370     return times;
1371 }
1372 
1373 QList<KTimeZone::LeapSeconds> KTimeZoneData::leapSecondChanges() const
1374 {
1375     return d->leapChanges;
1376 }
1377 
1378 void KTimeZoneData::setLeapSecondChanges(const QList<KTimeZone::LeapSeconds> &adjusts)
1379 {
1380     d->leapChanges = adjusts;
1381 }
1382 
1383 KTimeZone::LeapSeconds KTimeZoneData::leapSecondChange(const QDateTime &utc) const
1384 {
1385     if (utc.timeSpec() != Qt::UTC) {
1386         qCritical() << "KTimeZoneData::leapSecondChange(): non-UTC time specified" << endl;
1387     } else {
1388         for (int i = d->leapChanges.count();  --i >= 0;) {
1389             if (d->leapChanges[i].dateTime() < utc) {
1390                 return d->leapChanges[i];
1391             }
1392         }
1393     }
1394     return KTimeZone::LeapSeconds();
1395 }