File indexing completed on 2023-09-24 04:04:56
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 }