KDELibs4Support

ktzfiletimezone.cpp
1 /*
2  This file is part of the KDE libraries
3  Copyright (c) 2005-2008 David Jarvie <[email protected]>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 */
20 
21 #include "ktzfiletimezone.h"
22 
23 #include <config-date.h>
24 
25 #if HAVE_SYS_TIME_H
26 #include <sys/time.h>
27 #endif
28 #if HAVE_TIME_H
29 #include <time.h>
30 #endif
31 
32 #include <QDebug>
33 #include <QFile>
34 #include <QDataStream>
35 #include <QVector>
36 
37 // Use this replacement for QDateTime::setTime_t(uint) since our time
38 // values are signed.
39 static QDateTime fromTime_t(qint32 seconds)
40 {
41  static const QDate epochDate(1970, 1, 1);
42  static const QTime epochTime(0, 0, 0);
43  int days = seconds / 86400;
44  seconds -= days * 86400;
45  if (seconds < 0) {
46  --days;
47  seconds += 86400;
48  }
49  return QDateTime(epochDate.addDays(days), epochTime.addSecs(seconds), Qt::UTC);
50 }
51 
52 /******************************************************************************/
53 
55  const QString &countryCode, float latitude, float longitude, const QString &comment)
56  : KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)
57 {}
58 
59 KTzfileTimeZoneBackend::~KTzfileTimeZoneBackend()
60 {}
61 
63 {
64  return new KTzfileTimeZoneBackend(*this);
65 }
66 
68 {
69  return "KTzfileTimeZone";
70 }
71 
73 {
74  Q_UNUSED(caller)
75  return true;
76 }
77 
78 /******************************************************************************/
79 
81  const QString &countryCode, float latitude, float longitude,
82  const QString &comment)
83  : KTimeZone(new KTzfileTimeZoneBackend(source, name, countryCode, latitude, longitude, comment))
84 {}
85 
86 KTzfileTimeZone::~KTzfileTimeZone()
87 {}
88 
89 /******************************************************************************/
90 
91 class KTzfileTimeZoneDataPrivate
92 {
93 public:
94 };
95 
96 KTzfileTimeZoneData::KTzfileTimeZoneData()
97 // : d(new KTzfileTimeZoneDataPrivate)
98 { }
99 
100 KTzfileTimeZoneData::KTzfileTimeZoneData(const KTzfileTimeZoneData &rhs)
101  : KTimeZoneData(rhs)
102 // d(new KTzfileTimeZoneDataPrivate)
103 {
104 }
105 
106 KTzfileTimeZoneData::~KTzfileTimeZoneData()
107 {
108 // delete d;
109 }
110 
111 KTzfileTimeZoneData &KTzfileTimeZoneData::operator=(const KTzfileTimeZoneData &rhs)
112 {
113  KTimeZoneData::operator=(rhs);
114  return *this;
115 }
116 
118 {
119  return new KTzfileTimeZoneData(*this);
120 }
121 
123 {
124  return true;
125 }
126 
127 /******************************************************************************/
128 
129 class KTzfileTimeZoneSourcePrivate
130 {
131 public:
132  KTzfileTimeZoneSourcePrivate(const QString &loc)
133  : location(loc) {}
134  ~KTzfileTimeZoneSourcePrivate() {}
135 
136  QString location;
137 };
138 
140  : d(new KTzfileTimeZoneSourcePrivate(location))
141 {
142  if (location.length() > 1 && location.endsWith(QLatin1Char('/'))) {
143  d->location.chop(1);
144  }
145 }
146 
147 KTzfileTimeZoneSource::~KTzfileTimeZoneSource()
148 {
149  delete d;
150 }
151 
153 {
154  return d->location;
155 }
156 
158 {
159  quint32 abbrCharCount; // the number of characters of time zone abbreviation strings
160  quint32 ttisgmtcnt;
161  quint8 is;
162  quint8 T_, Z_, i_, f_; // tzfile identifier prefix
163 
164  QString path = zone.name();
165  if (!path.startsWith(QLatin1Char('/'))) {
166  if (d->location == QLatin1String("/")) {
167  path.prepend(d->location);
168  } else {
169  path = d->location + QLatin1Char('/') + path;
170  }
171  }
172  QFile f(path);
173  if (!f.open(QIODevice::ReadOnly)) {
174  qCritical() << "Cannot open " << f.fileName() << endl;
175  return nullptr;
176  }
177  QDataStream str(&f);
178 
179  // Read the file type identifier
180  str >> T_ >> Z_ >> i_ >> f_;
181  if (T_ != 'T' || Z_ != 'Z' || i_ != 'i' || f_ != 'f') {
182  qCritical() << "Not a TZFILE: " << f.fileName() << endl;
183  return nullptr;
184  }
185  // Discard 16 bytes reserved for future use
186  unsigned i;
187  for (i = 0; i < 4; ++i) {
188  str >> ttisgmtcnt;
189  }
190 
192 
193  // Read the sizes of arrays held in the file
194  quint32 nTransitionTimes;
195  quint32 nLocalTimeTypes;
196  quint32 nLeapSecondAdjusts;
197  quint32 nIsStandard;
198  quint32 nIsUtc;
199  str >> nIsUtc
200  >> nIsStandard
201  >> nLeapSecondAdjusts
202  >> nTransitionTimes
203  >> nLocalTimeTypes
204  >> abbrCharCount;
205  // qDebug() << "header: " << nIsUtc << ", " << nIsStandard << ", " << nLeapSecondAdjusts << ", " <<
206  // nTransitionTimes << ", " << nLocalTimeTypes << ", " << abbrCharCount << endl;
207 
208  // Read the transition times, at which the rules for computing local time change
209  struct TransitionTime {
210  qint32 time; // time (as returned by time(2)) at which the rules for computing local time change
211  quint8 localTimeIndex; // index into the LocalTimeType array
212  };
213 //qDebug()<<"Reading zone "<<zone.name();
214  TransitionTime *transitionTimes = new TransitionTime[nTransitionTimes];
215  for (i = 0; i < nTransitionTimes; ++i) {
216  str >> transitionTimes[i].time;
217  }
218  for (i = 0; i < nTransitionTimes; ++i) {
219  str >> transitionTimes[i].localTimeIndex;
220 //qDebug() << "Transition time "<<i<<": "<<transitionTimes[i].time<<" lt index="<<(int)transitionTimes[i].localTimeIndex;
221  }
222 
223  // Read the local time types
224  struct LocalTimeType {
225  qint32 gmtoff; // number of seconds to be added to UTC
226  bool isdst; // whether tm_isdst should be set by localtime(3)
227  quint8 abbrIndex; // index into the list of time zone abbreviations
228  bool isutc; // transition times are in UTC. If UTC, isstd is ignored.
229  bool isstd; // if true, transition times are in standard time;
230  // if false, transition times are in wall clock time,
231  // i.e. standard time or daylight savings time
232  // whichever is current before the transition
233  };
234  LocalTimeType *localTimeTypes = new LocalTimeType[nLocalTimeTypes];
235  LocalTimeType *ltt = localTimeTypes;
236  for (i = 0; i < nLocalTimeTypes; ++ltt, ++i) {
237  str >> ltt->gmtoff;
238  str >> is;
239  ltt->isdst = (is != 0);
240  str >> ltt->abbrIndex;
241  // qDebug() << "local type: " << ltt->gmtoff << ", " << is << ", " << ltt->abbrIndex;
242  ltt->isstd = false; // default if no data
243  ltt->isutc = false; // default if no data
244  }
245 
246  // Read the timezone abbreviations. They are stored as null terminated strings in
247  // a character array.
248  // Make sure we don't fall foul of maliciously coded time zone abbreviations.
249  if (abbrCharCount > 64) {
250  qCritical() << "excessive length for timezone abbreviations: " << abbrCharCount << endl;
251  delete data;
252  delete[] transitionTimes;
253  delete[] localTimeTypes;
254  return nullptr;
255  }
256  QByteArray array(abbrCharCount, 0);
257  str.readRawData(array.data(), array.size());
258  const char *abbrs = array.constData();
259  if (abbrs[abbrCharCount - 1] != 0) {
260  // These abbreviations are corrupt!
261  qCritical() << "timezone abbreviations not null terminated: " << abbrs[abbrCharCount - 1] << endl;
262  delete data;
263  delete[] transitionTimes;
264  delete[] localTimeTypes;
265  return nullptr;
266  }
267  quint8 n = 0;
268  QList<QByteArray> abbreviations;
269  for (i = 0; i < abbrCharCount; ++n, i += strlen(abbrs + i) + 1) {
270  abbreviations += QByteArray(abbrs + i);
271  // Convert the LocalTimeTypes pointer to a sequential index
272  ltt = localTimeTypes;
273  for (unsigned j = 0; j < nLocalTimeTypes; ++ltt, ++j) {
274  if (ltt->abbrIndex == i) {
275  ltt->abbrIndex = n;
276  }
277  }
278  }
279 
280  // Read the leap second adjustments
281  qint32 t;
282  quint32 s;
283  QList<KTimeZone::LeapSeconds> leapChanges;
284  for (i = 0; i < nLeapSecondAdjusts; ++i) {
285  str >> t >> s;
286  // qDebug() << "leap entry: " << t << ", " << s;
287  // Don't use QDateTime::setTime_t() because it takes an unsigned argument
288  leapChanges += KTimeZone::LeapSeconds(fromTime_t(t), static_cast<int>(s));
289  }
290  data->setLeapSecondChanges(leapChanges);
291 
292  // Read the standard/wall time indicators.
293  // These are true if the transition times associated with local time types
294  // are specified as standard time, false if wall clock time.
295  for (i = 0; i < nIsStandard; ++i) {
296  str >> is;
297  localTimeTypes[i].isstd = (is != 0);
298  // qDebug() << "standard: " << is;
299  }
300 
301  // Read the UTC/local time indicators.
302  // These are true if the transition times associated with local time types
303  // are specified as UTC, false if local time.
304  for (i = 0; i < nIsUtc; ++i) {
305  str >> is;
306  localTimeTypes[i].isutc = (is != 0);
307  // qDebug() << "UTC: " << is;
308  }
309 
310  // Find the starting offset from UTC to use before the first transition time.
311  // This is first non-daylight savings local time type, or if there is none,
312  // the first local time type.
313  LocalTimeType *firstLtt = nullptr;
314  ltt = localTimeTypes;
315  for (i = 0; i < nLocalTimeTypes; ++ltt, ++i) {
316  if (!ltt->isdst) {
317  firstLtt = ltt;
318  break;
319  }
320  }
321 
322  // Compile the time type data into a list of KTimeZone::Phase instances.
323  // Also check for local time types which are identical (this does happen)
324  // and use the same Phase instance for each.
325  QByteArray abbrev;
327  QList<QByteArray> phaseAbbrevs;
328  QVector<int> lttLookup(nLocalTimeTypes);
329  ltt = localTimeTypes;
330  for (i = 0; i < nLocalTimeTypes; ++ltt, ++i) {
331  if (ltt->abbrIndex >= abbreviations.count()) {
332  qCritical() << "KTzfileTimeZoneSource::parse(): abbreviation index out of range" << endl;
333  abbrev = "???";
334  } else {
335  abbrev = abbreviations[ltt->abbrIndex];
336  }
337  // Check for an identical Phase
338  int phindex = 0;
339  for (int j = 0, jend = phases.count(); j < jend; ++j, ++phindex) {
340  if (ltt->gmtoff == phases[j].utcOffset()
341  && (bool)ltt->isdst == phases[j].isDst()
342  && abbrev == phaseAbbrevs[j]) {
343  break;
344  }
345  }
346  lttLookup[i] = phindex;
347  if (phindex == phases.count()) {
348  phases += KTimeZone::Phase(ltt->gmtoff, abbrev, ltt->isdst);
349  phaseAbbrevs += abbrev;
350  }
351  }
352  KTimeZone::Phase prePhase(firstLtt->gmtoff,
353  (firstLtt->abbrIndex < abbreviations.count() ? abbreviations[firstLtt->abbrIndex] : ""),
354  false);
355  data->setPhases(phases, prePhase);
356 
357  // Compile the transition list
358  QList<KTimeZone::Transition> transitions;
359  TransitionTime *tt = transitionTimes;
360  for (i = 0; i < nTransitionTimes; ++tt, ++i) {
361  if (tt->localTimeIndex >= nLocalTimeTypes) {
362  qCritical() << "KTzfileTimeZoneSource::parse(): transition ignored: local time type out of range: " << (int)tt->localTimeIndex << " > " << nLocalTimeTypes << endl;
363  continue;
364  }
365 
366  // Convert local transition times to UTC
367  ltt = &localTimeTypes[tt->localTimeIndex];
368  const KTimeZone::Phase phase = phases[lttLookup[tt->localTimeIndex]];
369 //qDebug(161) << "Transition time "<<i<<": "<<fromTime_t(tt->time)<<", offset="<<phase.utcOffset()/60;
370  transitions += KTimeZone::Transition(fromTime_t(tt->time), phase);
371  }
372  data->setTransitions(transitions);
373 //for(int xxx=1;xxx<data->transitions().count();xxx++)
374 //qDebug(161) << "Transition time "<<xxx<<": "<<data->transitions()[xxx].time()<<", offset="<<data->transitions()[xxx].phase().utcOffset()/60;
375  delete[] localTimeTypes;
376  delete[] transitionTimes;
377 
378  return data;
379 }
void setPhases(const QList< KTimeZone::Phase > &phases, const KTimeZone::Phase &previousPhase)
Initialise the daylight savings time phase list.
Definition: ktimezone.cpp:1229
KTzfileTimeZoneBackend(KTzfileTimeZoneSource *source, const QString &name, const QString &countryCode, float latitude, float longitude, const QString &comment)
Implements KTzfileTimeZone::KTzfileTimeZone().
Base class for the parsed data returned by a KTimeZoneSource class.
Definition: ktimezone.h:1302
QString & prepend(QChar ch)
Parsed data from tzfile(5) time zone definition files.
virtual QString fileName() const const override
void setLeapSecondChanges(const QList< KTimeZone::LeapSeconds > &adjusts)
Initialise the leap seconds adjustment list.
Definition: ktimezone.cpp:1379
void setTransitions(const QList< KTimeZone::Transition > &transitions)
Initialise the daylight savings time transition list.
Definition: ktimezone.cpp:1261
KTimeZoneData * clone() const override
Creates a new copy of this object.
QByteArray type() const override
Returns the class name of the data represented by this instance.
int readRawData(char *s, int len)
KTimeZoneData * parse(const KTimeZone &zone) const override
Parses a tzfile file to extract detailed information for one time zone.
Backend class for KTzfileTimeZone class.
int count(const T &value) const const
bool hasTransitions() const override
Return whether daylight saving transitions are available for the time zone.
bool hasTransitions(const KTimeZone *caller) const override
Implements KTzfileTimeZone::hasTransitions().
const char * constData() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
virtual bool open(QIODevice::OpenMode mode) override
KTimeZoneBackend * clone() const override
Creates a copy of this instance.
Base class representing a time zone.
Definition: ktimezone.h:415
KTzfileTimeZoneSource(const QString &location)
Constructs a time zone source.
QString location() const
Returns the local directory containing the time zone definition files.
int length() const const
char * data()
KTzfileTimeZone(KTzfileTimeZoneSource *source, const QString &name, const QString &countryCode=QString(), float latitude=UNKNOWN, float longitude=UNKNOWN, const QString &comment=QString())
Creates a time zone.
QString name() const
Returns the name of the time zone.
Definition: ktimezone.cpp:663
int size() const const
TZFILE time zone functions.
Base backend class for KTimeZone classes.
Definition: ktimezone.h:1121
A class to read and parse tzfile time zone definition files.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Jul 1 2020 22:56:49 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.