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

KDECore

  • kdecore
  • date
ktimezone.cpp
Go to the documentation of this file.
1 /*
2  This file is part of the KDE libraries
3  Copyright (c) 2005-2008,2011 David Jarvie <djarvie@kde.org>
4  Copyright (c) 2005 S.R.Haque <srhaque@iee.org>.
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License as published by the Free Software Foundation; either
9  version 2 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Library General Public License for more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  Boston, MA 02110-1301, USA.
20 */
21 
22 #include "ktimezone.h"
23 
24 #include <config.h>
25 #include <config-date.h> // SIZEOF_TIME_T
26 
27 #ifdef HAVE_SYS_TIME_H
28 #include <sys/time.h>
29 #endif
30 #ifdef HAVE_TIME_H
31 #include <time.h>
32 #endif
33 #include <climits>
34 #include <cstdlib>
35 
36 #include <QtCore/QSet>
37 #include <QtCore/QSharedData>
38 #include <QtCore/QCoreApplication>
39 
40 #include <kdebug.h>
41 #include <kglobal.h>
42 
43 int gmtoff(time_t t); // defined in ksystemtimezone.cpp
44 
45 
46 /******************************************************************************/
47 
48 class KTimeZonesPrivate
49 {
50 public:
51  KTimeZonesPrivate() {}
52 
53  KTimeZones::ZoneMap zones;
54 };
55 
56 
57 KTimeZones::KTimeZones()
58  : d(new KTimeZonesPrivate)
59 {
60 }
61 
62 KTimeZones::~KTimeZones()
63 {
64  delete d;
65 }
66 
67 const KTimeZones::ZoneMap KTimeZones::zones() const
68 {
69  return d->zones;
70 }
71 
72 bool KTimeZones::add(const KTimeZone &zone)
73 {
74  if (!zone.isValid())
75  return false;
76  if (d->zones.find(zone.name()) != d->zones.end())
77  return false; // name already exists
78  d->zones.insert(zone.name(), zone);
79  return true;
80 }
81 
82 KTimeZone KTimeZones::remove(const KTimeZone &zone)
83 {
84  if (zone.isValid())
85  {
86  for (ZoneMap::Iterator it = d->zones.begin(), end = d->zones.end(); it != end; ++it)
87  {
88  if (it.value() == zone)
89  {
90  d->zones.erase(it);
91  return zone;
92  }
93  }
94  }
95  return KTimeZone();
96 }
97 
98 KTimeZone KTimeZones::remove(const QString &name)
99 {
100  if (!name.isEmpty())
101  {
102  ZoneMap::Iterator it = d->zones.find(name);
103  if (it != d->zones.end())
104  {
105  KTimeZone zone = it.value();
106  d->zones.erase(it);
107  return zone;
108  }
109  }
110  return KTimeZone();
111 }
112 
113 void KTimeZones::clear()
114 {
115  d->zones.clear();
116 }
117 
118 KTimeZone KTimeZones::zone(const QString &name) const
119 {
120  if (!name.isEmpty())
121  {
122  ZoneMap::ConstIterator it = d->zones.constFind(name);
123  if (it != d->zones.constEnd())
124  return it.value();
125  if (name == KTimeZone::utc().name())
126  return KTimeZone::utc();
127  }
128  return KTimeZone(); // error
129 }
130 
131 
132 /******************************************************************************/
133 
134 class KTimeZonePhasePrivate : public QSharedData
135 {
136  public:
137  QByteArray abbreviations; // time zone abbreviations (zero-delimited)
138  QString comment; // optional comment
139  int utcOffset; // seconds to add to UTC
140  bool dst; // true if daylight savings time
141 
142  explicit KTimeZonePhasePrivate(int offset = 0, bool ds = false)
143  : QSharedData(),
144  utcOffset(offset),
145  dst(ds)
146  {}
147  KTimeZonePhasePrivate(const KTimeZonePhasePrivate& rhs)
148  : QSharedData(rhs),
149  abbreviations(rhs.abbreviations),
150  comment(rhs.comment),
151  utcOffset(rhs.utcOffset),
152  dst(rhs.dst)
153  {}
154  bool operator==(const KTimeZonePhasePrivate &rhs) const
155  {
156  return abbreviations == rhs.abbreviations
157  && comment == rhs.comment
158  && utcOffset == rhs.utcOffset
159  && dst == rhs.dst;
160  }
161 };
162 
163 
164 KTimeZone::Phase::Phase()
165  : d(new KTimeZonePhasePrivate)
166 {
167 }
168 
169 KTimeZone::Phase::Phase(int utcOffset, const QByteArray &abbrevs,
170  bool dst, const QString &cmt)
171  : d(new KTimeZonePhasePrivate(utcOffset, dst))
172 {
173  d->abbreviations = abbrevs;
174  d->comment = cmt;
175 }
176 
177 KTimeZone::Phase::Phase(int utcOffset, const QList<QByteArray> &abbrevs,
178  bool dst, const QString &cmt)
179  : d(new KTimeZonePhasePrivate(utcOffset, dst))
180 {
181  for (int i = 0, end = abbrevs.count(); i < end; ++i)
182  {
183  if (i > 0)
184  d->abbreviations += '\0';
185  d->abbreviations += abbrevs[i];
186  }
187  d->comment = cmt;
188 }
189 
190 KTimeZone::Phase::Phase(const KTimeZone::Phase &rhs)
191  : d(rhs.d)
192 {
193 }
194 
195 KTimeZone::Phase::~Phase()
196 {
197 }
198 
199 KTimeZone::Phase &KTimeZone::Phase::operator=(const KTimeZone::Phase &rhs)
200 {
201  d = rhs.d;
202  return *this;
203 }
204 
205 bool KTimeZone::Phase::operator==(const KTimeZone::Phase &rhs) const
206 {
207  return d == rhs.d || *d == *rhs.d;
208 }
209 
210 int KTimeZone::Phase::utcOffset() const
211 {
212  return d->utcOffset;
213 }
214 
215 QList<QByteArray> KTimeZone::Phase::abbreviations() const
216 {
217  return d->abbreviations.split('\0');
218 }
219 
220 bool KTimeZone::Phase::isDst() const
221 {
222  return d->dst;
223 }
224 
225 QString KTimeZone::Phase::comment() const
226 {
227  return d->comment;
228 }
229 
230 
231 /******************************************************************************/
232 
233 class KTimeZoneTransitionPrivate
234 {
235 public:
236  QDateTime time;
237  KTimeZone::Phase phase;
238 };
239 
240 
241 KTimeZone::Transition::Transition()
242  : d(new KTimeZoneTransitionPrivate)
243 {
244 }
245 
246 KTimeZone::Transition::Transition(const QDateTime &t, const KTimeZone::Phase &p)
247  : d(new KTimeZoneTransitionPrivate)
248 {
249  d->time = t;
250  d->phase = p;
251 }
252 
253 KTimeZone::Transition::Transition(const KTimeZone::Transition &t)
254  : d(new KTimeZoneTransitionPrivate)
255 {
256  d->time = t.d->time;
257  d->phase = t.d->phase;
258 }
259 
260 KTimeZone::Transition::~Transition()
261 {
262  delete d;
263 }
264 
265 KTimeZone::Transition &KTimeZone::Transition::operator=(const KTimeZone::Transition &t)
266 {
267  d->time = t.d->time;
268  d->phase = t.d->phase;
269  return *this;
270 }
271 
272 bool KTimeZone::Transition::operator<(const KTimeZone::Transition &rhs) const
273 {
274  return d->time < rhs.d->time;
275 }
276 
277 QDateTime KTimeZone::Transition::time() const { return d->time; }
278 KTimeZone::Phase KTimeZone::Transition::phase() const { return d->phase; }
279 
280 
281 /******************************************************************************/
282 
283 class KTimeZoneDataPrivate
284 {
285  public:
286  QList<KTimeZone::Phase> phases;
287  QList<KTimeZone::Transition> transitions;
288  QList<KTimeZone::LeapSeconds> leapChanges;
289  QList<int> utcOffsets;
290  QList<QByteArray> abbreviations;
291  KTimeZone::Phase prePhase; // phase to use before the first transition
292 
293  KTimeZoneDataPrivate() {}
294  // Find the last transition before a specified UTC or local date/time.
295  int transitionIndex(const QDateTime &dt) const;
296  bool transitionIndexes(const QDateTime &start, const QDateTime &end, int &ixstart, int &ixend) const;
297  bool isSecondOccurrence(const QDateTime &utcLocalTime, int transitionIndex) const;
298 };
299 
300 
301 /******************************************************************************/
302 
303 class KTimeZonePrivate : public QSharedData
304 {
305 public:
306  KTimeZonePrivate() : source(0), data(0), refCount(1), cachedTransitionIndex(-1) {}
307  KTimeZonePrivate(KTimeZoneSource *src, const QString& nam,
308  const QString &country, float lat, float lon, const QString &cmnt);
309  KTimeZonePrivate(const KTimeZonePrivate &);
310  ~KTimeZonePrivate() { delete data; }
311  KTimeZonePrivate &operator=(const KTimeZonePrivate &);
312  static KTimeZoneSource *utcSource();
313  static void cleanup();
314 
315  KTimeZoneSource *source;
316  QString name;
317  QString countryCode;
318  QString comment;
319  float latitude;
320  float longitude;
321  mutable KTimeZoneData *data;
322  int refCount; // holds the number of KTimeZoneBackend instances using the KTimeZonePrivate instance as a d-pointer.
323  int cachedTransitionIndex;
324  QDateTime cachedTransitionStartZoneTime;
325  QDateTime cachedTransitionEndZoneTime;
326  bool cachedTransitionTimesValid;
327 
328 private:
329  static KTimeZoneSource *mUtcSource;
330 };
331 
332 KTimeZoneSource *KTimeZonePrivate::mUtcSource = 0;
333 
334 
335 KTimeZonePrivate::KTimeZonePrivate(KTimeZoneSource *src, const QString& nam,
336  const QString &country, float lat, float lon, const QString &cmnt)
337  : source(src),
338  name(nam),
339  countryCode(country.toUpper()),
340  comment(cmnt),
341  latitude(lat),
342  longitude(lon),
343  data(0),
344  refCount(1),
345  cachedTransitionIndex(-1)
346 {
347  // Detect duff values.
348  if (latitude > 90 || latitude < -90)
349  latitude = KTimeZone::UNKNOWN;
350  if (longitude > 180 || longitude < -180)
351  longitude = KTimeZone::UNKNOWN;
352 }
353 
354 KTimeZonePrivate::KTimeZonePrivate(const KTimeZonePrivate &rhs)
355  : QSharedData(rhs),
356  source(rhs.source),
357  name(rhs.name),
358  countryCode(rhs.countryCode),
359  comment(rhs.comment),
360  latitude(rhs.latitude),
361  longitude(rhs.longitude),
362  refCount(1),
363  cachedTransitionIndex(rhs.cachedTransitionIndex),
364  cachedTransitionStartZoneTime(rhs.cachedTransitionStartZoneTime),
365  cachedTransitionEndZoneTime(rhs.cachedTransitionEndZoneTime),
366  cachedTransitionTimesValid(rhs.cachedTransitionTimesValid)
367 {
368  if (rhs.data)
369  data = rhs.data->clone();
370  else
371  data = 0;
372 }
373 
374 KTimeZonePrivate &KTimeZonePrivate::operator=(const KTimeZonePrivate &rhs)
375 {
376  // Changing the contents of a KTimeZonePrivate instance by means of operator=() doesn't affect how
377  // many references to it are held.
378  source = rhs.source;
379  name = rhs.name;
380  countryCode = rhs.countryCode;
381  comment = rhs.comment;
382  latitude = rhs.latitude;
383  longitude = rhs.longitude;
384  cachedTransitionIndex = rhs.cachedTransitionIndex;
385  cachedTransitionStartZoneTime = rhs.cachedTransitionStartZoneTime;
386  cachedTransitionEndZoneTime = rhs.cachedTransitionEndZoneTime;
387  cachedTransitionTimesValid = rhs.cachedTransitionTimesValid;
388  delete data;
389  if (rhs.data)
390  data = rhs.data->clone();
391  else
392  data = 0;
393  // refCount is unchanged
394  return *this;
395 }
396 
397 KTimeZoneSource *KTimeZonePrivate::utcSource()
398 {
399  if (!mUtcSource)
400  {
401  mUtcSource = new KTimeZoneSource;
402  qAddPostRoutine(KTimeZonePrivate::cleanup);
403  }
404  return mUtcSource;
405 }
406 
407 void KTimeZonePrivate::cleanup()
408 {
409  delete mUtcSource;
410 }
411 
412 
413 /******************************************************************************/
414 
415 K_GLOBAL_STATIC(KTimeZonePrivate, s_emptyTimeZonePrivate)
416 
417 KTimeZoneBackend::KTimeZoneBackend()
418  : d(&*s_emptyTimeZonePrivate)
419 {
420  ++d->refCount;
421 }
422 
423 KTimeZoneBackend::KTimeZoneBackend(const QString &name)
424  : d(new KTimeZonePrivate(KTimeZonePrivate::utcSource(), name, QString(), KTimeZone::UNKNOWN, KTimeZone::UNKNOWN, QString()))
425 {}
426 
427 KTimeZoneBackend::KTimeZoneBackend(KTimeZoneSource *source, const QString &name,
428  const QString &countryCode, float latitude, float longitude, const QString &comment)
429  : d(new KTimeZonePrivate(source, name, countryCode, latitude, longitude, comment))
430 {}
431 
432 KTimeZoneBackend::KTimeZoneBackend(const KTimeZoneBackend &other)
433  : d(other.d)
434 {
435  ++d->refCount;
436 }
437 
438 KTimeZoneBackend::~KTimeZoneBackend()
439 {
440  if (d && --d->refCount == 0)
441  delete d;
442  d = 0;
443 }
444 
445 KTimeZoneBackend &KTimeZoneBackend::operator=(const KTimeZoneBackend &other)
446 {
447  if (d != other.d)
448  {
449  if (--d->refCount == 0)
450  delete d;
451  d = other.d;
452  ++d->refCount;
453  }
454  return *this;
455 }
456 
457 QByteArray KTimeZoneBackend::type() const
458 {
459  return "KTimeZone";
460 }
461 
462 KTimeZoneBackend *KTimeZoneBackend::clone() const
463 {
464  return new KTimeZoneBackend(*this);
465 }
466 
467 int KTimeZoneBackend::offsetAtZoneTime(const KTimeZone* caller, const QDateTime &zoneDateTime, int *secondOffset) const
468 {
469  if (!zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime) // check for invalid time
470  {
471  if (secondOffset)
472  *secondOffset = 0;
473  return 0;
474  }
475  const QList<KTimeZone::Transition> transitions = caller->transitions();
476  int index = d->cachedTransitionIndex;
477  if (index >= 0 && index < transitions.count())
478  {
479  // There is a cached transition - check whether zoneDateTime uses it.
480  // Caching is used because this method has been found to consume
481  // significant CPU in real life applications.
482  if (!d->cachedTransitionTimesValid)
483  {
484  const int offset = transitions[index].phase().utcOffset();
485  const int preoffset = (index > 0) ? transitions[index - 1].phase().utcOffset() : d->data ? d->data->previousUtcOffset() : 0;
486  d->cachedTransitionStartZoneTime = transitions[index].time().addSecs(qMax(offset, preoffset));
487  if (index + 1 < transitions.count())
488  {
489  const int postoffset = transitions[index + 1].phase().utcOffset();
490  d->cachedTransitionEndZoneTime = transitions[index + 1].time().addSecs(qMin(offset, postoffset));
491  }
492  d->cachedTransitionTimesValid = true;
493  }
494  QDateTime dtutc = zoneDateTime;
495  dtutc.setTimeSpec(Qt::UTC);
496  if (dtutc >= d->cachedTransitionStartZoneTime
497  && (index + 1 >= transitions.count() || dtutc < d->cachedTransitionEndZoneTime))
498  {
499  // The time falls within the cached transition limits, so return its UTC offset
500  const int offset = transitions[index].phase().utcOffset();
501  if (secondOffset)
502  *secondOffset = offset;
503 #ifdef COMPILING_TESTS
504  qDebug("-> Using cache"); // test output requires qDebug instead of kDebug
505 #endif
506  return offset;
507  }
508  }
509 
510  // The time doesn't fall within the cached transition, or there isn't a cached transition
511 #ifdef COMPILING_TESTS
512  qDebug("-> No cache"); // test output requires qDebug instead of kDebug
513 #endif
514  bool validTime;
515  int secondIndex = -1;
516  index = caller->transitionIndex(zoneDateTime, (secondOffset ? &secondIndex : 0), &validTime);
517  const KTimeZone::Transition* tr = (index >= 0) ? &transitions[index] : 0;
518  const int offset = tr ? tr->phase().utcOffset()
519  : validTime ? (d->data ? d->data->previousUtcOffset() : 0)
520  : KTimeZone::InvalidOffset;
521  if (secondOffset)
522  *secondOffset = (secondIndex >= 0) ? transitions.at(secondIndex).phase().utcOffset() : offset;
523 
524  // Cache transition data for subsequent date/time values which occur after the same transition.
525  d->cachedTransitionIndex = index;
526  d->cachedTransitionTimesValid = false;
527  return offset;
528 }
529 
530 int KTimeZoneBackend::offsetAtUtc(const KTimeZone* caller, const QDateTime &utcDateTime) const
531 {
532  if (!utcDateTime.isValid() || utcDateTime.timeSpec() != Qt::UTC) // check for invalid time
533  return 0;
534  const QList<KTimeZone::Transition> transitions = caller->transitions();
535  int index = d->cachedTransitionIndex;
536  if (index >= 0 && index < transitions.count())
537  {
538  // There is a cached transition - check whether utcDateTime uses it.
539  if (utcDateTime >= transitions[index].time()
540  && (index + 1 >= transitions.count()
541  || utcDateTime < transitions[index + 1].time()))
542  {
543  // The time falls within the cached transition, so return its UTC offset
544 #ifdef COMPILING_TESTS
545  qDebug("Using cache"); // test output requires qDebug instead of kDebug
546 #endif
547  return transitions[index].phase().utcOffset();
548  }
549  }
550 
551  // The time doesn't fall within the cached transition, or there isn't a cached transition
552 #ifdef COMPILING_TESTS
553  qDebug("No cache"); // test output requires qDebug instead of kDebug
554 #endif
555  index = caller->transitionIndex(utcDateTime);
556  d->cachedTransitionIndex = index; // cache transition data
557  d->cachedTransitionTimesValid = false;
558  const KTimeZone::Transition* tr = (index >= 0) ? &transitions.at(index) : 0;
559  return tr ? tr->phase().utcOffset() : (d->data ? d->data->previousUtcOffset() : 0);
560 }
561 
562 int KTimeZoneBackend::offset(const KTimeZone* caller, time_t t) const
563 {
564  return offsetAtUtc(caller, KTimeZone::fromTime_t(t));
565 }
566 
567 bool KTimeZoneBackend::isDstAtUtc(const KTimeZone* caller, const QDateTime &utcDateTime) const
568 {
569  if (!utcDateTime.isValid() || utcDateTime.timeSpec() != Qt::UTC) // check for invalid time
570  return false;
571  const KTimeZone::Transition *tr = caller->transition(utcDateTime);
572  if (!tr)
573  return false;
574  return tr->phase().isDst();
575 }
576 
577 bool KTimeZoneBackend::isDst(const KTimeZone* caller, time_t t) const
578 {
579  return isDstAtUtc(caller, KTimeZone::fromTime_t(t));
580 }
581 
582 bool KTimeZoneBackend::hasTransitions(const KTimeZone* caller) const
583 {
584  Q_UNUSED(caller);
585  return false;
586 }
587 
588 
589 /******************************************************************************/
590 
591 #if SIZEOF_TIME_T == 8
592 const time_t KTimeZone::InvalidTime_t = 0x800000000000000LL;
593 #else
594 const time_t KTimeZone::InvalidTime_t = 0x80000000;
595 #endif
596 const int KTimeZone::InvalidOffset = 0x80000000;
597 const float KTimeZone::UNKNOWN = 1000.0;
598 
599 
600 KTimeZone::KTimeZone()
601  : d(new KTimeZoneBackend())
602 {}
603 
604 KTimeZone::KTimeZone(const QString &name)
605  : d(new KTimeZoneBackend(name))
606 {}
607 
608 KTimeZone::KTimeZone(const KTimeZone &tz)
609  : d(tz.d->clone())
610 {}
611 
612 KTimeZone::~KTimeZone()
613 {
614  delete d;
615 }
616 
617 KTimeZone::KTimeZone(KTimeZoneBackend *impl)
618  : d(impl)
619 {
620  // 'impl' should be a newly constructed object, with refCount = 1
621  Q_ASSERT(d->d->refCount == 1 || d->d == &*s_emptyTimeZonePrivate);
622 }
623 
624 KTimeZone &KTimeZone::operator=(const KTimeZone &tz)
625 {
626  if (d != tz.d)
627  {
628  delete d;
629  d = tz.d->clone();
630  }
631  return *this;
632 }
633 
634 bool KTimeZone::operator==(const KTimeZone &rhs) const
635 {
636  return d->d == rhs.d->d;
637 }
638 
639 QByteArray KTimeZone::type() const
640 {
641  return d->type();
642 }
643 
644 bool KTimeZone::isValid() const
645 {
646  return !d->d->name.isEmpty();
647 }
648 
649 QString KTimeZone::countryCode() const
650 {
651  return d->d->countryCode;
652 }
653 
654 float KTimeZone::latitude() const
655 {
656  return d->d->latitude;
657 }
658 
659 float KTimeZone::longitude() const
660 {
661  return d->d->longitude;
662 }
663 
664 QString KTimeZone::comment() const
665 {
666  return d->d->comment;
667 }
668 
669 QString KTimeZone::name() const
670 {
671  return d->d->name;
672 }
673 
674 QList<QByteArray> KTimeZone::abbreviations() const
675 {
676  if (!data(true))
677  return QList<QByteArray>();
678  return d->d->data->abbreviations();
679 }
680 
681 QByteArray KTimeZone::abbreviation(const QDateTime &utcDateTime) const
682 {
683  if (utcDateTime.timeSpec() != Qt::UTC || !data(true))
684  return QByteArray();
685  return d->d->data->abbreviation(utcDateTime);
686 }
687 
688 QList<int> KTimeZone::utcOffsets() const
689 {
690  if (!data(true))
691  return QList<int>();
692  return d->d->data->utcOffsets();
693 }
694 
695 QList<KTimeZone::Phase> KTimeZone::phases() const
696 {
697  if (!data(true))
698  return QList<KTimeZone::Phase>();
699  return d->d->data->phases();
700 }
701 
702 bool KTimeZone::hasTransitions() const
703 {
704  return d->hasTransitions(this);
705 }
706 
707 QList<KTimeZone::Transition> KTimeZone::transitions(const QDateTime &start, const QDateTime &end) const
708 {
709  if (!data(true))
710  return QList<KTimeZone::Transition>();
711  return d->d->data->transitions(start, end);
712 }
713 
714 const KTimeZone::Transition *KTimeZone::transition(const QDateTime &dt, const Transition **secondTransition,
715  bool *validTime) const
716 {
717  if (!data(true))
718  return 0;
719  return d->d->data->transition(dt, secondTransition, validTime);
720 }
721 
722 int KTimeZone::transitionIndex(const QDateTime &dt, int *secondIndex, bool *validTime) const
723 {
724  if (!data(true))
725  return -1;
726  return d->d->data->transitionIndex(dt, secondIndex, validTime);
727 }
728 
729 QList<QDateTime> KTimeZone::transitionTimes(const Phase &phase, const QDateTime &start, const QDateTime &end) const
730 {
731  if (!data(true))
732  return QList<QDateTime>();
733  return d->d->data->transitionTimes(phase, start, end);
734 }
735 
736 QList<KTimeZone::LeapSeconds> KTimeZone::leapSecondChanges() const
737 {
738  if (!data(true))
739  return QList<KTimeZone::LeapSeconds>();
740  return d->d->data->leapSecondChanges();
741 }
742 
743 KTimeZoneSource *KTimeZone::source() const
744 {
745  return d->d->source;
746 }
747 
748 const KTimeZoneData *KTimeZone::data(bool create) const
749 {
750  if (!isValid())
751  return 0;
752  if (create && !d->d->data && d->d->source->useZoneParse())
753  d->d->data = d->d->source->parse(*this);
754  return d->d->data;
755 }
756 
757 void KTimeZone::setData(KTimeZoneData *data, KTimeZoneSource *source)
758 {
759  if (!isValid())
760  return;
761  delete d->d->data;
762  d->d->data = data;
763  if (source)
764  d->d->source = source;
765 }
766 
767 bool KTimeZone::updateBase(const KTimeZone &other)
768 {
769  if (d->d->name.isEmpty() || d->d->name != other.d->d->name)
770  return false;
771  d->d->countryCode = other.d->d->countryCode;
772  d->d->comment = other.d->d->comment;
773  d->d->latitude = other.d->d->latitude;
774  d->d->longitude = other.d->d->longitude;
775  return true;
776 }
777 
778 bool KTimeZone::parse() const
779 {
780  if (!isValid())
781  return false;
782  if (d->d->source->useZoneParse())
783  {
784  delete d->d->data;
785  d->d->data = d->d->source->parse(*this);
786  }
787  return d->d->data;
788 }
789 
790 QDateTime KTimeZone::toUtc(const QDateTime &zoneDateTime) const
791 {
792  if (!zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime)
793  return QDateTime();
794  const int secs = offsetAtZoneTime(zoneDateTime);
795  if (secs == InvalidOffset)
796  return QDateTime();
797  QDateTime dt = zoneDateTime;
798  dt.setTimeSpec(Qt::UTC);
799  return dt.addSecs(-secs);
800 }
801 
802 QDateTime KTimeZone::toZoneTime(const QDateTime &utcDateTime, bool *secondOccurrence) const
803 {
804  if (secondOccurrence)
805  *secondOccurrence = false;
806  if (!utcDateTime.isValid() || utcDateTime.timeSpec() != Qt::UTC) // check for invalid time
807  return QDateTime();
808 
809  // Convert UTC to local time
810  if (hasTransitions())
811  {
812  if (!data(true))
813  {
814  // No data - default to UTC
815  QDateTime dt = utcDateTime;
816  dt.setTimeSpec(Qt::LocalTime);
817  return dt;
818  }
819 
820  const KTimeZoneData *data = d->d->data;
821  const int index = data->transitionIndex(utcDateTime);
822  const int secs = (index >= 0) ? data->transitions().at(index).phase().utcOffset() : data->previousUtcOffset();
823  QDateTime dt = utcDateTime.addSecs(secs);
824  if (secondOccurrence)
825  {
826  // Check whether the local time occurs twice around a daylight savings time
827  // shift, and if so, whether it's the first or second occurrence.
828  *secondOccurrence = data->d->isSecondOccurrence(dt, index);
829  }
830  dt.setTimeSpec(Qt::LocalTime);
831  return dt;
832  }
833  else
834  {
835  const int secs = offsetAtUtc(utcDateTime);
836  QDateTime dt = utcDateTime.addSecs(secs);
837  dt.setTimeSpec(Qt::LocalTime);
838  if (secondOccurrence)
839  {
840  // Check whether the local time occurs twice around a daylight savings time
841  // shift, and if so, whether it's the first or second occurrence.
842  *secondOccurrence = (secs != offsetAtZoneTime(dt));
843  }
844  return dt;
845  }
846 }
847 
848 QDateTime KTimeZone::convert(const KTimeZone &newZone, const QDateTime &zoneDateTime) const
849 {
850  if (newZone == *this)
851  {
852  if (zoneDateTime.timeSpec() != Qt::LocalTime)
853  return QDateTime();
854  return zoneDateTime;
855  }
856  return newZone.toZoneTime(toUtc(zoneDateTime));
857 }
858 
859 int KTimeZone::offsetAtZoneTime(const QDateTime &zoneDateTime, int *secondOffset) const
860 {
861  return d->offsetAtZoneTime(this, zoneDateTime, secondOffset);
862 }
863 
864 int KTimeZone::offsetAtUtc(const QDateTime &utcDateTime) const
865 {
866  return d->offsetAtUtc(this, utcDateTime);
867 }
868 
869 int KTimeZone::offset(time_t t) const
870 {
871  return d->offset(this, t);
872 }
873 
874 int KTimeZone::currentOffset(Qt::TimeSpec basis) const
875 {
876  // Get current offset of this time zone to UTC
877  const time_t now = time(0);
878  const int secs = offset(now);
879 
880  switch (basis)
881  {
882  case Qt::LocalTime:
883  // Return the current offset of this time zone to the local system time
884  return secs - gmtoff(now);
885  case Qt::UTC:
886  // Return the current offset of this time zone to UTC
887  return secs;
888 
889  default:
890  break;
891  }
892  return 0;
893 }
894 
895 bool KTimeZone::isDstAtUtc(const QDateTime &utcDateTime) const
896 {
897  return d->isDstAtUtc(this, utcDateTime);
898 }
899 
900 bool KTimeZone::isDst(time_t t) const
901 {
902  return d->isDst(this, t);
903 }
904 
905 KTimeZone KTimeZone::utc()
906 {
907  static KTimeZone utcZone(QLatin1String("UTC"));
908  return utcZone;
909 }
910 
911 QDateTime KTimeZone::fromTime_t(time_t t)
912 {
913  static const int secondsADay = 86400;
914  static const QDate epochDate(1970,1,1);
915  static const QTime epochTime(0,0,0);
916  int days = t / secondsADay;
917  int secs;
918  if (t >= 0)
919  secs = t % secondsADay;
920  else
921  {
922  secs = secondsADay - (-t % secondsADay);
923  --days;
924  }
925  return QDateTime(epochDate.addDays(days), epochTime.addSecs(secs), Qt::UTC);
926 }
927 
928 time_t KTimeZone::toTime_t(const QDateTime &utcDateTime)
929 {
930  static const QDate epochDate(1970,1,1);
931  static const QTime epochTime(0,0,0);
932  if (utcDateTime.timeSpec() != Qt::UTC)
933  return InvalidTime_t;
934  const qint64 days = epochDate.daysTo(utcDateTime.date());
935  const qint64 secs = epochTime.secsTo(utcDateTime.time());
936  const qint64 t64 = days * 86400 + secs;
937  const time_t t = static_cast<time_t>(t64);
938  if (static_cast<qint64>(t) != t64)
939  return InvalidTime_t;
940  return t;
941 }
942 
943 
944 /******************************************************************************/
945 
946 class KTimeZoneSourcePrivate
947 {
948 public:
949  bool mUseZoneParse;
950 };
951 
952 
953 KTimeZoneSource::KTimeZoneSource()
954  : d(new KTimeZoneSourcePrivate)
955 {
956  d->mUseZoneParse = true;
957 }
958 
959 KTimeZoneSource::KTimeZoneSource(bool useZoneParse)
960  : d(new KTimeZoneSourcePrivate)
961 {
962  d->mUseZoneParse = useZoneParse;
963 }
964 
965 KTimeZoneSource::~KTimeZoneSource()
966 {
967  delete d;
968 }
969 
970 KTimeZoneData *KTimeZoneSource::parse(const KTimeZone &) const
971 {
972  Q_ASSERT(d->mUseZoneParse); // method should never be called if it isn't usable
973  return new KTimeZoneData;
974 }
975 
976 bool KTimeZoneSource::useZoneParse() const
977 {
978  return d->mUseZoneParse;
979 }
980 
981 
982 /******************************************************************************/
983 
984 class KTimeZoneLeapSecondsPrivate
985 {
986  public:
987  QDateTime dt; // UTC time when this change occurred
988  QString comment; // optional comment
989  int seconds; // number of leap seconds
990 };
991 
992 
993 KTimeZone::LeapSeconds::LeapSeconds()
994  : d(new KTimeZoneLeapSecondsPrivate)
995 {
996 }
997 
998 KTimeZone::LeapSeconds::LeapSeconds(const QDateTime &utc, int leap, const QString &cmt)
999  : d(new KTimeZoneLeapSecondsPrivate)
1000 {
1001  if (utc.timeSpec() == Qt::UTC) // invalid if start time is not UTC
1002  {
1003  d->dt = utc;
1004  d->comment = cmt;
1005  d->seconds = leap;
1006  }
1007 }
1008 
1009 KTimeZone::LeapSeconds::LeapSeconds(const KTimeZone::LeapSeconds &c)
1010  : d(new KTimeZoneLeapSecondsPrivate)
1011 {
1012  d->dt = c.d->dt;
1013  d->comment = c.d->comment;
1014  d->seconds = c.d->seconds;
1015 }
1016 
1017 KTimeZone::LeapSeconds::~LeapSeconds()
1018 {
1019  delete d;
1020 }
1021 
1022 KTimeZone::LeapSeconds &KTimeZone::LeapSeconds::operator=(const KTimeZone::LeapSeconds &c)
1023 {
1024  d->dt = c.d->dt;
1025  d->comment = c.d->comment;
1026  d->seconds = c.d->seconds;
1027  return *this;
1028 }
1029 
1030 bool KTimeZone::LeapSeconds::operator<(const KTimeZone::LeapSeconds& c) const
1031 {
1032  return d->dt < c.d->dt;
1033 }
1034 
1035 QDateTime KTimeZone::LeapSeconds::dateTime() const
1036 {
1037  return d->dt;
1038 }
1039 
1040 bool KTimeZone::LeapSeconds::isValid() const
1041 {
1042  return d->dt.isValid();
1043 }
1044 
1045 int KTimeZone::LeapSeconds::leapSeconds() const
1046 {
1047  return d->seconds;
1048 }
1049 
1050 QString KTimeZone::LeapSeconds::comment() const
1051 {
1052  return d->comment;
1053 }
1054 
1055 
1056 /******************************************************************************/
1057 
1058 
1059 int KTimeZoneDataPrivate::transitionIndex(const QDateTime &dt) const
1060 {
1061  // Do a binary search to find the last transition before this date/time
1062  int start = -1;
1063  int end = transitions.count();
1064  if (dt.timeSpec() == Qt::UTC)
1065  {
1066  while (end - start > 1)
1067  {
1068  int i = (start + end) / 2;
1069  if (dt < transitions[i].time())
1070  end = i;
1071  else
1072  start = i;
1073  }
1074  }
1075  else
1076  {
1077  QDateTime dtutc = dt;
1078  dtutc.setTimeSpec(Qt::UTC);
1079  while (end - start > 1)
1080  {
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  return end ? start : -1;
1089 }
1090 
1091 // Find the indexes to the transitions at or after start, and before or at end.
1092 // start and end must be UTC.
1093 // Reply = false if none.
1094 bool KTimeZoneDataPrivate::transitionIndexes(const QDateTime &start, const QDateTime &end, int &ixstart, int &ixend) const
1095 {
1096  ixstart = 0;
1097  if (start.isValid() && start.timeSpec() == Qt::UTC)
1098  {
1099  ixstart = transitionIndex(start);
1100  if (ixstart < 0)
1101  ixstart = 0;
1102  else if (transitions[ixstart].time() < start)
1103  {
1104  if (++ixstart >= transitions.count())
1105  return false; // there are no transitions at/after 'start'
1106  }
1107  }
1108  ixend = -1;
1109  if (end.isValid() && end.timeSpec() == Qt::UTC)
1110  {
1111  ixend = transitionIndex(end);
1112  if (ixend < 0)
1113  return false; // there are no transitions at/before 'end'
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  const int offset = transitions[transitionIndex].phase().utcOffset();
1127  const int prevoffset = (transitionIndex > 0) ? transitions[transitionIndex-1].phase().utcOffset() : prePhase.utcOffset();
1128  const int phaseDiff = prevoffset - offset;
1129  if (phaseDiff <= 0)
1130  return false;
1131  // Find how long after the start of the latest phase 'dt' is
1132  const int afterStart = transitions[transitionIndex].time().secsTo(utcLocalTime) - offset;
1133  return (afterStart < phaseDiff);
1134 }
1135 
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  {
1178  for (int i = 0, end = d->phases.count(); i < end; ++i)
1179  {
1180  const QList<QByteArray> abbrevs = d->phases[i].abbreviations();
1181  for (int j = 0, jend = abbrevs.count(); j < jend; ++j)
1182  if (!d->abbreviations.contains(abbrevs[j]))
1183  d->abbreviations.append(abbrevs[j]);
1184  }
1185  if (d->abbreviations.isEmpty())
1186  d->abbreviations += "UTC";
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  const KTimeZone::Transition *tr = transition(utcDateTime);
1196  const QList<QByteArray> abbrevs = tr ? tr->phase().abbreviations()
1197  : d->prePhase.abbreviations();
1198  if (abbrevs.isEmpty())
1199  return QByteArray();
1200  return abbrevs[0];
1201 }
1202 
1203 QList<int> KTimeZoneData::utcOffsets() const
1204 {
1205  if (d->utcOffsets.isEmpty())
1206  {
1207  for (int i = 0, end = d->phases.count(); i < end; ++i)
1208  {
1209  const int offset = d->phases[i].utcOffset();
1210  if (!d->utcOffsets.contains(offset))
1211  d->utcOffsets.append(offset);
1212  }
1213  if (d->utcOffsets.isEmpty())
1214  d->utcOffsets += 0;
1215  else
1216  qSort(d->utcOffsets);
1217  }
1218  return d->utcOffsets;
1219 }
1220 
1221 QList<KTimeZone::Phase> KTimeZoneData::phases() const
1222 {
1223  return d->phases;
1224 }
1225 
1226 void KTimeZoneData::setPhases(const QList<KTimeZone::Phase> &phases, const KTimeZone::Phase& previousPhase)
1227 {
1228  d->phases = phases;
1229  d->prePhase = previousPhase;
1230 }
1231 
1232 void KTimeZoneData::setPhases(const QList<KTimeZone::Phase> &phases, int previousUtcOffset)
1233 {
1234  d->phases = phases;
1235  d->prePhase = KTimeZone::Phase(previousUtcOffset, QByteArray(), false);
1236 }
1237 
1238 bool KTimeZoneData::hasTransitions() const
1239 {
1240  return false;
1241 }
1242 
1243 QList<KTimeZone::Transition> KTimeZoneData::transitions(const QDateTime &start, const QDateTime &end) const
1244 {
1245  int ixstart, ixend;
1246  if (!d->transitionIndexes(start, end, ixstart, ixend))
1247  return QList<KTimeZone::Transition>(); // there are no transitions within the time period
1248  if (ixend >= 0)
1249  return d->transitions.mid(ixstart, ixend - ixstart + 1);
1250  if (ixstart > 0)
1251  return d->transitions.mid(ixstart);
1252  return d->transitions;
1253 }
1254 
1255 void KTimeZoneData::setTransitions(const QList<KTimeZone::Transition> &transitions)
1256 {
1257  d->transitions = transitions;
1258 }
1259 
1260 int KTimeZoneData::previousUtcOffset() const
1261 {
1262  return d->prePhase.utcOffset();
1263 }
1264 
1265 const KTimeZone::Transition *KTimeZoneData::transition(const QDateTime &dt, const KTimeZone::Transition **secondTransition,
1266  bool *validTime) const
1267 {
1268  int secondIndex;
1269  const int index = transitionIndex(dt, (secondTransition ? &secondIndex : 0), validTime);
1270  if (secondTransition)
1271  *secondTransition = (secondIndex >= 0) ? &d->transitions[secondIndex] : 0;
1272  return (index >= 0) ? &d->transitions[index] : 0;
1273 }
1274 
1275 int KTimeZoneData::transitionIndex(const QDateTime &dt, int *secondIndex, bool *validTime) const
1276 {
1277  if (validTime)
1278  *validTime = true;
1279 
1280  // Find the last transition before this date/time
1281  int index = d->transitionIndex(dt);
1282  if (dt.timeSpec() == Qt::UTC)
1283  {
1284  if (secondIndex)
1285  *secondIndex = index;
1286  return index;
1287  }
1288  else
1289  {
1290  /* Check whether the specified local time actually occurs.
1291  * Find the start of the next phase, and check if it falls in the gap
1292  * between the two phases.
1293  */
1294  QDateTime dtutc = dt;
1295  dtutc.setTimeSpec(Qt::UTC);
1296  const int count = d->transitions.count();
1297  const int next = (index >= 0) ? index + 1 : 0;
1298  if (next < count)
1299  {
1300  KTimeZone::Phase nextPhase = d->transitions[next].phase();
1301  const int offset = (index >= 0) ? d->transitions[index].phase().utcOffset() : d->prePhase.utcOffset();
1302  const int phaseDiff = nextPhase.utcOffset() - offset;
1303  if (phaseDiff > 0)
1304  {
1305  // Get UTC equivalent as if 'dt' was in the next phase
1306  if (dtutc.secsTo(d->transitions[next].time()) + nextPhase.utcOffset() < phaseDiff)
1307  {
1308  // The time falls in the gap between the two phases,
1309  // so return an invalid value.
1310  if (validTime)
1311  *validTime = false;
1312  if (secondIndex)
1313  *secondIndex = -1;
1314  return -1;
1315  }
1316  }
1317  }
1318 
1319  if (index < 0)
1320  {
1321  // The specified time is before the first phase
1322  if (secondIndex)
1323  *secondIndex = -1;
1324  return -1;
1325  }
1326 
1327  /* Check if it's a local time which occurs both before and after the 'latest'
1328  * phase start time (for which it has to span a daylight saving to standard
1329  * time change).
1330  */
1331  bool duplicate = true;
1332  if (d->isSecondOccurrence(dtutc, index))
1333  {
1334  // 'dt' occurs twice
1335  if (secondIndex)
1336  {
1337  *secondIndex = index;
1338  duplicate = false;
1339  }
1340  // Get the transition containing the first occurrence of 'dt'
1341  if (index <= 0)
1342  return -1; // first occurrence of 'dt' is just before the first transition
1343  --index;
1344  }
1345 
1346  if (secondIndex && duplicate)
1347  *secondIndex = index;
1348  return index;
1349  }
1350 }
1351 
1352 QList<QDateTime> KTimeZoneData::transitionTimes(const KTimeZone::Phase &phase, const QDateTime &start, const QDateTime &end) const
1353 {
1354  QList<QDateTime> times;
1355  int ixstart, ixend;
1356  if (d->transitionIndexes(start, end, ixstart, ixend))
1357  {
1358  if (ixend < 0)
1359  ixend = d->transitions.count() - 1;
1360  while (ixstart <= ixend)
1361  {
1362  if (d->transitions[ixstart].phase() == phase)
1363  times += d->transitions[ixstart].time();
1364  }
1365  }
1366  return times;
1367 }
1368 
1369 QList<KTimeZone::LeapSeconds> KTimeZoneData::leapSecondChanges() const
1370 {
1371  return d->leapChanges;
1372 }
1373 
1374 void KTimeZoneData::setLeapSecondChanges(const QList<KTimeZone::LeapSeconds> &adjusts)
1375 {
1376  d->leapChanges = adjusts;
1377 }
1378 
1379 KTimeZone::LeapSeconds KTimeZoneData::leapSecondChange(const QDateTime &utc) const
1380 {
1381  if (utc.timeSpec() != Qt::UTC)
1382  kError() << "KTimeZoneData::leapSecondChange(): non-UTC time specified" << endl;
1383  else
1384  {
1385  for (int i = d->leapChanges.count(); --i >= 0; )
1386  {
1387  if (d->leapChanges[i].dateTime() < utc)
1388  return d->leapChanges[i];
1389  }
1390  }
1391  return KTimeZone::LeapSeconds();
1392 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Fri Nov 16 2012 14:49:06 by doxygen 1.8.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDECore

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

kdelibs-4.8.5 API Reference

Skip menu "kdelibs-4.8.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal