KDELibs4Support

ksystemtimezone.cpp
1 /*
2  This file is part of the KDE libraries
3  Copyright (c) 2005-2010 David Jarvie <[email protected]>
4  Copyright (c) 2005 S.R.Haque <[email protected]>.
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 // This file requires HAVE_STRUCT_TM_TM_ZONE to be defined if struct tm member tm_zone is available.
23 // This file requires HAVE_TM_GMTOFF to be defined if struct tm member tm_gmtoff is available.
24 
25 #include "moc_ksystemtimezone.cpp"
26 
27 #include <config-date.h>
28 
29 #if HAVE_SYS_TIME_H
30 #include <sys/time.h>
31 #endif
32 #if HAVE_TIME_H
33 #include <time.h>
34 #endif
35 #include <climits>
36 #include <cstdlib>
37 
38 #include <QDebug>
39 #include <QCoreApplication>
40 #include <QFile>
41 #include <QFileInfo>
42 #include <QDir>
43 #include <QRegExp>
44 #include <QStringList>
45 #include <QTextStream>
46 #include <QDBusConnection>
47 #include <QDBusInterface>
48 #include <QDBusConnectionInterface>
49 #include <QDBusReply>
50 
51 #include <kconfig.h>
52 #include <klocalizedstring.h>
53 #include <kstringhandler.h>
54 #include <kconfiggroup.h>
55 #include "ktzfiletimezone.h"
56 #ifdef Q_OS_WIN
57 #include "ktimezone_win.h"
58 #endif
59 
60 #define KTIMEZONED_DBUS_IFACE "org.kde.KTimeZoned"
61 
62 /* Return the offset to UTC in the current time zone at the specified UTC time.
63  * The thread-safe function localtime_r() is used in preference if available.
64  */
65 int gmtoff(time_t t)
66 {
67 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS
68  tm tmtime;
69  if (!localtime_r(&t, &tmtime)) {
70  return 0;
71  }
72 #if HAVE_TM_GMTOFF
73  return tmtime.tm_gmtoff;
74 #else
75  int lwday = tmtime.tm_wday;
76  int lt = 3600 * tmtime.tm_hour + 60 * tmtime.tm_min + tmtime.tm_sec;
77  if (!gmtime_r(&t, &tmtime)) {
78  return 0;
79  }
80  int uwday = tmtime.tm_wday;
81  int ut = 3600 * tmtime.tm_hour + 60 * tmtime.tm_min + tmtime.tm_sec;
82 #endif
83 #else
84  tm *tmtime = localtime(&t);
85  if (!tmtime) {
86  return 0;
87  }
88 #if HAVE_TM_GMTOFF
89  return tmtime->tm_gmtoff;
90 #else
91  int lwday = tmtime->tm_wday;
92  int lt = 3600 * tmtime->tm_hour + 60 * tmtime->tm_min + tmtime->tm_sec;
93  tmtime = gmtime(&t);
94  int uwday = tmtime->tm_wday;
95  int ut = 3600 * tmtime->tm_hour + 60 * tmtime->tm_min + tmtime->tm_sec;
96 #endif
97 #endif
98 #if ! HAVE_TM_GMTOFF
99  if (lwday != uwday) {
100  // Adjust for different day
101  if (lwday == uwday + 1 || (lwday == 0 && uwday == 6)) {
102  lt += 24 * 3600;
103  } else {
104  lt -= 24 * 3600;
105  }
106  }
107  return lt - ut;
108 #endif
109 }
110 
111 /******************************************************************************/
112 
113 class KSystemTimeZonesPrivate : public KTimeZones
114 {
115 public:
116  static KSystemTimeZonesPrivate *instance();
117  static KTzfileTimeZoneSource *tzfileSource();
118  static void setLocalZone();
119  static void cleanup();
120  static void readConfig(bool init);
121 #ifdef Q_OS_WIN
122  static void updateTimezoneInformation()
123  {
124  instance()->updateTimezoneInformation(true);
125  }
126 #else
127  static void updateZonetab()
128  {
129  instance()->readZoneTab(true);
130  }
131 #endif
132 
133  static KTimeZone m_localZone;
134  static QString m_localZoneName;
135  static QString m_zoneinfoDir;
136  static QString m_zonetab;
137  static KSystemTimeZoneSource *m_source;
138  static bool m_ktimezonedError;
139 
140 private:
141  KSystemTimeZonesPrivate() {}
142 #ifdef Q_OS_WIN
143  void updateTimezoneInformation(bool update);
144 #else
145  void readZoneTab(bool update);
146  static float convertCoordinate(const QString &coordinate);
147 #endif
148 
149  static KSystemTimeZones *m_parent;
150  static KSystemTimeZonesPrivate *m_instance;
151  static KTzfileTimeZoneSource *m_tzfileSource;
152 };
153 
154 KTimeZone KSystemTimeZonesPrivate::m_localZone;
155 QString KSystemTimeZonesPrivate::m_localZoneName;
156 QString KSystemTimeZonesPrivate::m_zoneinfoDir;
157 QString KSystemTimeZonesPrivate::m_zonetab;
158 KSystemTimeZoneSource *KSystemTimeZonesPrivate::m_source = nullptr;
159 bool KSystemTimeZonesPrivate::m_ktimezonedError = true;
160 KTzfileTimeZoneSource *KSystemTimeZonesPrivate::m_tzfileSource = nullptr;
161 KSystemTimeZones *KSystemTimeZonesPrivate::m_parent = nullptr;
162 KSystemTimeZonesPrivate *KSystemTimeZonesPrivate::m_instance = nullptr;
163 
164 KTzfileTimeZoneSource *KSystemTimeZonesPrivate::tzfileSource()
165 {
166  if (!m_tzfileSource) {
167  instance();
168  m_tzfileSource = new KTzfileTimeZoneSource(m_zoneinfoDir);
169  }
170  return m_tzfileSource;
171 }
172 
173 // for the benefit of KTimeZoneTest
174 KDELIBS4SUPPORT_EXPORT void k_system_time_zone_private_reset_config()
175 {
176  // Remove any old zones from the collection
177  const KTimeZones::ZoneMap oldZones = KSystemTimeZonesPrivate::instance()->zones();
178  for (KTimeZones::ZoneMap::ConstIterator it = oldZones.constBegin(); it != oldZones.constEnd(); ++it) {
179  KSystemTimeZonesPrivate::instance()->remove(it.value());
180  }
181 
182  // Read new config file
183  KSystemTimeZonesPrivate::readConfig(false);
184 }
185 
186 #ifndef NDEBUG
187 Q_GLOBAL_STATIC(KTimeZone, simulatedLocalZone)
188 #endif
189 
190 KSystemTimeZones::KSystemTimeZones()
191  : d(nullptr)
192 {
194  const QString dbusIface = QString::fromLatin1(KTIMEZONED_DBUS_IFACE);
195  dbus.connect(QString(), QString(), dbusIface, QLatin1String("timeZoneChanged"), this, SLOT(configChanged()));
196  dbus.connect(QString(), QString(), dbusIface, QLatin1String("timeZoneDatabaseUpdated"), this, SLOT(zonetabChanged(QString)));
197  // No need to connect to definitionChanged() - see comments in zoneDefinitionChanged()
198  //dbus.connect(QString(), QString(), dbusIface, QLatin1String("definitionChanged"), this, SLOT(zoneDefinitionChanged(QString)));
199 }
200 
201 KSystemTimeZones::~KSystemTimeZones()
202 {
203 }
204 
206 {
207 #ifndef NDEBUG
208  if (simulatedLocalZone()->isValid()) {
209  return *simulatedLocalZone();
210  }
211 #endif
212  KSystemTimeZonesPrivate::instance();
213  return KSystemTimeZonesPrivate::m_localZone;
214 }
215 
217 {
218  KSystemTimeZonesPrivate::instance();
219  return KSystemTimeZonesPrivate::m_localZone;
220 }
221 
223 {
224  Q_UNUSED(tz);
225 #ifndef NDEBUG
226  *simulatedLocalZone() = tz;
227 #endif
228 }
229 
231 {
232 #ifndef NDEBUG
233  return simulatedLocalZone()->isValid();
234 #else
235  return false;
236 #endif
237 }
238 
240 {
241  KSystemTimeZonesPrivate::instance();
242  return KSystemTimeZonesPrivate::m_zoneinfoDir;
243 }
244 
246 {
247  KSystemTimeZonesPrivate::instance();
248  return !KSystemTimeZonesPrivate::m_ktimezonedError;
249 }
250 
252 {
253  return KSystemTimeZonesPrivate::instance();
254 }
255 
257 {
258  return KTzfileTimeZone(KSystemTimeZonesPrivate::tzfileSource(), name);
259 }
260 
262 {
263  return KSystemTimeZonesPrivate::instance()->zones();
264 }
265 
267 {
268  return KSystemTimeZonesPrivate::instance()->zone(name);
269 }
270 
271 void KSystemTimeZones::configChanged()
272 {
273  //qDebug() << "KSystemTimeZones::configChanged()";
274  KSystemTimeZonesPrivate::m_ktimezonedError = false;
275  KSystemTimeZonesPrivate::readConfig(false);
276 }
277 
278 void KSystemTimeZones::zonetabChanged(const QString &zonetab)
279 {
280  Q_UNUSED(zonetab)
281 #ifndef Q_OS_WIN
282  //qDebug() << "KSystemTimeZones::zonetabChanged()";
283  KSystemTimeZonesPrivate::m_ktimezonedError = false;
284  // Re-read zone.tab and update our collection, removing any deleted
285  // zones and adding any new zones.
286  KSystemTimeZonesPrivate::updateZonetab();
287 #endif
288 }
289 
290 void KSystemTimeZones::zoneDefinitionChanged(const QString &zone)
291 {
292  // No need to do anything when the definition (as opposed to the
293  // identity) of the local zone changes, since the updated details
294  // will always be accessed by the system library calls to fetch
295  // local zone information.
296  Q_UNUSED(zone)
297  KSystemTimeZonesPrivate::m_ktimezonedError = false;
298 }
299 
300 // Perform initialization, create the unique KSystemTimeZones instance,
301 // whose only function is to receive D-Bus signals from KTimeZoned,
302 // and create the unique KSystemTimeZonesPrivate instance.
303 KSystemTimeZonesPrivate *KSystemTimeZonesPrivate::instance()
304 {
305  if (!m_instance) {
306  m_instance = new KSystemTimeZonesPrivate;
307 
308  // A KSystemTimeZones instance is required only to catch D-Bus signals.
309  m_parent = new KSystemTimeZones;
310  // Ensure that the KDED time zones module has initialized. The call loads the module on demand.
312  if (!bus->isServiceRegistered(QLatin1String("org.kde.kded5"))) {
313  // kded isn't even running: start it
314  QDBusReply<void> reply = bus->startService(QLatin1String("org.kde.kded5"));
315  if (!reply.isValid()) {
316  qWarning() << "Couldn't start kded5 from org.kde.kded5.service:" << reply.error();
317  }
318  }
319  const QString dbusIface = QString::fromLatin1(KTIMEZONED_DBUS_IFACE);
320  QDBusInterface *ktimezoned = new QDBusInterface(QLatin1String("org.kde.kded5"), QLatin1String("/modules/ktimezoned"), dbusIface);
321  QDBusReply<void> reply = ktimezoned->call(QLatin1String("initialize"), false);
322  m_ktimezonedError = !reply.isValid();
323  if (m_ktimezonedError) {
324  qCritical() << "KSystemTimeZones: ktimezoned initialize() D-Bus call failed: " << reply.error().message() << endl;
325  }
326 //qDebug()<<"instance(): ... initialised";
327  delete ktimezoned;
328 
329  // Read the time zone config written by ktimezoned
330  readConfig(true);
331 
332  // Go read the database.
333 #ifdef Q_OS_WIN
334  // On Windows, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
335  // is the place to look. The TZI binary value is the TIME_ZONE_INFORMATION structure.
336  m_instance->updateTimezoneInformation(false);
337 #else
338  // For Unix, read zone.tab.
339  if (!m_zonetab.isEmpty()) {
340  m_instance->readZoneTab(false);
341  }
342 #endif
343  setLocalZone();
344 
345  qAddPostRoutine(KSystemTimeZonesPrivate::cleanup);
346  }
347  return m_instance;
348 }
349 
350 void KSystemTimeZonesPrivate::readConfig(bool init)
351 {
352  KConfig config(QLatin1String("ktimezonedrc"));
353  if (!init) {
354  config.reparseConfiguration();
355  }
356  KConfigGroup group(&config, "TimeZones");
357  if (!group.exists()) {
358  qCritical() << "No time zone information obtained from ktimezoned";
359  m_ktimezonedError = true;
360  }
361  m_zoneinfoDir = group.readEntry("ZoneinfoDir");
362  m_zonetab = group.readEntry("Zonetab");
363  m_localZoneName = group.readEntry("LocalZone");
364  if (m_zoneinfoDir.length() > 1 && m_zoneinfoDir.endsWith(QLatin1Char('/'))) {
365  m_zoneinfoDir.truncate(m_zoneinfoDir.length() - 1); // strip trailing '/'
366  }
367  if (!init) {
368 #ifdef Q_OS_WIN
369  updateTimezoneInformation();
370 #else
371  updateZonetab();
372 #endif
373  setLocalZone();
374  }
375  //qDebug() << "readConfig(): local zone=" << m_localZoneName;
376 }
377 
378 void KSystemTimeZonesPrivate::setLocalZone()
379 {
380  // Check whether the local zone name is set at all
381  if (m_localZoneName.isEmpty()) {
382  m_localZone = KTimeZone::utc();
383  return;
384  }
385 
386  // Check if the zone name is a known zone
387  if (m_instance) {
388  m_localZone = m_instance->zone(m_localZoneName);
389  if (m_localZone.isValid()) {
390  return;
391  }
392  }
393 
394  QString filename;
395  if (m_localZoneName.startsWith(QLatin1Char('/'))) {
396  // The time zone is specified by a file outside the zoneinfo directory
397  filename = m_localZoneName;
398  } else {
399  // The zone name is specified by a relative file name in zoneinfo directory.
400  filename = m_zoneinfoDir + QLatin1Char('/') + m_localZoneName;
401  }
402 
403  // Verify that the time zone file actually exists
404  if (!QFile::exists(filename)) {
405  m_localZone = KTimeZone::utc();
406  return;
407  }
408 
409  // Parse the specified time zone data file
410  QString zonename = filename;
411  if (zonename.startsWith(m_zoneinfoDir + QLatin1Char('/'))) {
412  zonename = zonename.mid(m_zoneinfoDir.length() + 1);
413  }
414  m_localZone = KTzfileTimeZone(KSystemTimeZonesPrivate::tzfileSource(), zonename);
415 
416  // Add the new time zone to the list of known time zones
417  if (m_instance) {
418  const KTimeZone oldzone = m_instance->zone(zonename);
419  if (!oldzone.isValid() || oldzone.type() != "KTzfileTimeZone") {
420  m_instance->remove(oldzone);
421  m_instance->add(m_localZone);
422  }
423  }
424 }
425 
426 void KSystemTimeZonesPrivate::cleanup()
427 {
428  delete m_parent;
429  delete m_instance;
430  delete m_source;
431  delete m_tzfileSource;
432 }
433 
434 #ifdef Q_OS_WIN
435 
436 void KSystemTimeZonesPrivate::updateTimezoneInformation(bool update)
437 {
438  if (!m_source) {
439  m_source = new KSystemTimeZoneSourceWindows;
440  }
441  QStringList newZones;
442  Q_FOREACH (const QString &tz, KSystemTimeZoneWindows::listTimeZones()) {
443  // const std::wstring wstr = tz.toStdWString();
444  // const KTimeZone info = make_time_zone( wstr.c_str() );
445  KSystemTimeZoneWindows stz(m_source, tz);
446  if (update) {
447  // Update the existing collection with the new zone definition
448  newZones += stz.name();
449  KTimeZone oldTz = zone(stz.name());
450  if (oldTz.isValid()) {
451  oldTz.updateBase(stz); // the zone previously existed, so update its definition
452  } else {
453  add(stz); // the zone didn't previously exist, so add it
454  }
455  } else {
456  add(stz);
457  }
458  }
459  if (update) {
460  // Remove any zones from the collection which no longer exist
461  const ZoneMap oldZones = zones();
462  for (ZoneMap::const_iterator it = oldZones.begin(); it != oldZones.end(); ++it) {
463  if (newZones.indexOf(it.key()) < 0) {
464  remove(it.value());
465  }
466  }
467  }
468 }
469 
470 #else
471 /*
472  * Find the location of the zoneinfo files and store in mZoneinfoDir.
473  * Parse zone.tab and for each time zone, create a KSystemTimeZone instance.
474  */
475 void KSystemTimeZonesPrivate::readZoneTab(bool update)
476 {
477  //qDebug() << "readZoneTab(" << m_zonetab<< ")";
478  QStringList newZones;
479  QFile f;
480  f.setFileName(m_zonetab);
481  if (!f.open(QIODevice::ReadOnly)) {
482  return;
483  }
484  QTextStream str(&f);
485  const QRegExp lineSeparator(QLatin1String("[ \t]"));
486  const QRegExp ordinateSeparator(QLatin1String("[+-]"));
487  if (!m_source) {
488  m_source = new KSystemTimeZoneSource;
489  }
490  while (!str.atEnd()) {
491  const QString line = str.readLine();
492  if (line.isEmpty() || line[0] == QLatin1Char('#')) {
493  continue;
494  }
495  QStringList tokens = KStringHandler::perlSplit(lineSeparator, line, 4);
496  const int n = tokens.count();
497  if (n < 3) {
498  qCritical() << "readZoneTab(): invalid record: " << line << endl;
499  continue;
500  }
501 
502  // Got three tokens. Now check for two ordinates plus first one is "".
503  const int i = tokens[1].indexOf(ordinateSeparator, 1);
504  if (i < 0) {
505  qCritical() << "readZoneTab() " << tokens[2] << ": invalid coordinates: " << tokens[1] << endl;
506  continue;
507  }
508 
509  const float latitude = convertCoordinate(tokens[1].left(i));
510  const float longitude = convertCoordinate(tokens[1].mid(i));
511 
512  // Add entry to list.
513  if (tokens[0] == QLatin1String("??")) {
514  tokens[0] = QString::fromLatin1("");
515  }
516  // Solaris sets the empty Comments field to '-', making it not empty.
517  // Clean it up.
518  if (n > 3 && tokens[3] == QLatin1String("-")) {
519  tokens[3] = QString::fromLatin1("");
520  }
521  // Note: KTzfileTimeZone is used in preference to KSystemTimeZone because of
522  // the large overhead incurred by tzset() - see KSystemTimeZones class
523  // description for details.
524  const KTzfileTimeZone tz(tzfileSource(), tokens[2], tokens[0], latitude, longitude, (n > 3 ? tokens[3] : QString()));
525  if (update) {
526  // Update the existing collection with the new zone definition
527  newZones += tz.name();
528  KTimeZone oldTz = zone(tz.name());
529  if (oldTz.isValid()) {
530  oldTz.updateBase(tz); // the zone previously existed, so update its definition
531  } else {
532  add(tz); // the zone didn't previously exist, so add it
533  }
534  } else {
535  add(tz);
536  }
537  }
538  f.close();
539 
540  if (update) {
541  // Remove any zones from the collection which no longer exist
542  const ZoneMap oldZones = zones();
543  for (ZoneMap::ConstIterator it = oldZones.constBegin(); it != oldZones.constEnd(); ++it) {
544  if (newZones.indexOf(it.key()) < 0) {
545  remove(it.value());
546  }
547  }
548  }
549 }
550 
554 float KSystemTimeZonesPrivate::convertCoordinate(const QString &coordinate)
555 {
556  int value = coordinate.toInt();
557  int degrees = 0;
558  int minutes = 0;
559  int seconds = 0;
560 
561  if (coordinate.length() > 6) {
562  degrees = value / 10000;
563  value -= degrees * 10000;
564  minutes = value / 100;
565  value -= minutes * 100;
566  seconds = value;
567  } else {
568  degrees = value / 100;
569  value -= degrees * 100;
570  minutes = value;
571  }
572  value = degrees * 3600 + minutes * 60 + seconds;
573  return value / 3600.0;
574 }
575 #endif
576 
577 /******************************************************************************/
578 
580  const QString &countryCode, float latitude, float longitude, const QString &comment)
581  : KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)
582 {}
583 
584 KSystemTimeZoneBackend::~KSystemTimeZoneBackend()
585 {}
586 
588 {
589  return new KSystemTimeZoneBackend(*this);
590 }
591 
593 {
594  return "KSystemTimeZone";
595 }
596 
597 int KSystemTimeZoneBackend::offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const
598 {
599  if (!caller->isValid() || !zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime) {
600  return 0;
601  }
602  // Make this time zone the current local time zone
603  const QByteArray originalZone = qgetenv("TZ"); // save the original local time zone
604  QByteArray tz = caller->name().toUtf8();
605  tz.prepend(":");
606  const bool change = (tz != originalZone);
607  if (change) {
608  qputenv("TZ", tz);
609  ::tzset();
610  }
611 
612  // Convert zone time to UTC, and then get the offset to UTC
613  tm tmtime;
614  tmtime.tm_sec = zoneDateTime.time().second();
615  tmtime.tm_min = zoneDateTime.time().minute();
616  tmtime.tm_hour = zoneDateTime.time().hour();
617  tmtime.tm_mday = zoneDateTime.date().day();
618  tmtime.tm_mon = zoneDateTime.date().month() - 1;
619  tmtime.tm_year = zoneDateTime.date().year() - 1900;
620  tmtime.tm_isdst = -1;
621  const time_t t = mktime(&tmtime);
622  int offset1 = (t == (time_t) - 1) ? KTimeZone::InvalidOffset : gmtoff(t);
623  if (secondOffset) {
624  int offset2 = offset1;
625  if (t != (time_t) - 1) {
626  // Check if there is a backward DST change near to this time, by
627  // checking if the UTC offset is different 1 hour later or earlier.
628  // ASSUMPTION: DST SHIFTS ARE NEVER GREATER THAN 1 HOUR.
629  const int maxShift = 3600;
630  offset2 = gmtoff(t + maxShift);
631  if (offset2 < offset1) {
632  // There is a backward DST shift during the following hour
633  if (offset1 - offset2 < maxShift) {
634  offset2 = gmtoff(t + (offset1 - offset2));
635  }
636  } else if ((offset2 = gmtoff(t - maxShift)) > offset1) {
637  // There is a backward DST shift during the previous hour
638  if (offset2 - offset1 < maxShift) {
639  offset2 = gmtoff(t - (offset2 - offset1));
640  }
641  // Put UTC offsets into the correct order
642  const int o = offset1;
643  offset1 = offset2;
644  offset2 = o;
645  } else {
646  offset2 = offset1;
647  }
648  }
649  *secondOffset = offset2;
650  }
651 
652  if (change) {
653  // Restore the original local time zone
654  if (originalZone.isEmpty()) {
655  ::unsetenv("TZ");
656  } else {
657  qputenv("TZ", originalZone);
658  }
659  ::tzset();
660  }
661  return offset1;
662 }
663 
664 int KSystemTimeZoneBackend::offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
665 {
666  return offset(caller, KTimeZone::toTime_t(utcDateTime));
667 }
668 
669 int KSystemTimeZoneBackend::offset(const KTimeZone *caller, time_t t) const
670 {
671  if (!caller->isValid() || t == KTimeZone::InvalidTime_t) {
672  return 0;
673  }
674 
675  // Make this time zone the current local time zone
676  const QByteArray originalZone = qgetenv("TZ"); // save the original local time zone
677  QByteArray tz = caller->name().toUtf8();
678  tz.prepend(":");
679  const bool change = (tz != originalZone);
680  if (change) {
681  qputenv("TZ", tz);
682  ::tzset();
683  }
684 
685  const int secs = gmtoff(t);
686 
687  if (change) {
688  // Restore the original local time zone
689  if (originalZone.isEmpty()) {
690  ::unsetenv("TZ");
691  } else {
692  qputenv("TZ", originalZone);
693  }
694  ::tzset();
695  }
696  return secs;
697 }
698 
699 bool KSystemTimeZoneBackend::isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
700 {
701  return isDst(caller, KTimeZone::toTime_t(utcDateTime));
702 }
703 
704 bool KSystemTimeZoneBackend::isDst(const KTimeZone *caller, time_t t) const
705 {
706  Q_UNUSED(caller)
707  if (t != (time_t) - 1) {
708 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS
709  tm tmtime;
710  if (localtime_r(&t, &tmtime)) {
711  return tmtime.tm_isdst > 0;
712  }
713 #else
714  const tm *tmtime = localtime(&t);
715  if (tmtime) {
716  return tmtime->tm_isdst > 0;
717  }
718 #endif
719  }
720  return false;
721 }
722 
723 /******************************************************************************/
724 
726  const QString &countryCode, float latitude, float longitude, const QString &comment)
727  : KTimeZone(new KSystemTimeZoneBackend(source, name, countryCode, latitude, longitude, comment))
728 {
729 }
730 
731 KSystemTimeZone::~KSystemTimeZone()
732 {
733 }
734 
735 /******************************************************************************/
736 
737 class KSystemTimeZoneDataPrivate
738 {
739 public:
740  QByteArray TZ;
742 };
743 
744 // N.B. KSystemTimeZoneSourcePrivate is also used by KSystemTimeZoneData
745 class KSystemTimeZoneSourcePrivate
746 {
747 public:
748  static void setTZ(const QByteArray &zoneName);
749  static void restoreTZ();
750  static QByteArray savedTZ; // temporary value of TZ environment variable saved by setTZ()
751  static QByteArray originalTZ; // saved value of TZ environment variable during multiple parse() calls
752  static bool TZIsSaved; // TZ has been saved in savedTZ
753  static bool multiParse; // true if performing multiple parse() calls
754 };
755 
756 QByteArray KSystemTimeZoneSourcePrivate::savedTZ;
757 QByteArray KSystemTimeZoneSourcePrivate::originalTZ;
758 bool KSystemTimeZoneSourcePrivate::TZIsSaved = false;
759 bool KSystemTimeZoneSourcePrivate::multiParse = false;
760 
762  : d(nullptr)
763 // : d(new KSystemTimeZoneSourcePrivate)
764 {
765 }
766 
767 KSystemTimeZoneSource::~KSystemTimeZoneSource()
768 {
769 // delete d;
770 }
771 
773 {
774  const QByteArray tz = zone.name().toUtf8();
775  KSystemTimeZoneSourcePrivate::setTZ(tz); // make this time zone the current local time zone
776 
777  tzset(); // initialize the tzname array
779  data->d->TZ = tz;
780  data->d->abbreviations.append(tzname[0]);
781  data->d->abbreviations.append(tzname[1]);
782 
783  // There is no easy means to access the sequence of daylight savings time
784  // changes, or leap seconds adjustments, so leave that data empty.
785 
786  KSystemTimeZoneSourcePrivate::restoreTZ(); // restore the original local time zone if necessary
787  return data;
788 }
789 
791 {
792  KSystemTimeZoneSourcePrivate::originalTZ = qgetenv("TZ"); // save the original local time zone
793  KSystemTimeZoneSourcePrivate::multiParse = true;
794 }
795 
797 {
798  if (KSystemTimeZoneSourcePrivate::multiParse) {
799  // Restore the original local time zone
800  if (KSystemTimeZoneSourcePrivate::originalTZ.isEmpty()) {
801  ::unsetenv("TZ");
802  } else {
803  qputenv("TZ", KSystemTimeZoneSourcePrivate::originalTZ);
804  }
805  ::tzset();
806  KSystemTimeZoneSourcePrivate::multiParse = false;
807  }
808 }
809 
810 // Set the TZ environment variable to the specified time zone,
811 // saving its current setting first if necessary.
812 void KSystemTimeZoneSourcePrivate::setTZ(const QByteArray &zoneName)
813 {
814  QByteArray tz = zoneName;
815  tz.prepend(":");
816  bool setTZ = multiParse;
817  if (!setTZ) {
818  savedTZ = qgetenv("TZ"); // save the original local time zone
819  TZIsSaved = true;
820  setTZ = (tz != savedTZ);
821  }
822  if (setTZ) {
823  qputenv("TZ", tz);
824  ::tzset();
825  }
826 }
827 
828 // Restore the TZ environment variable if it was saved by setTz()
829 void KSystemTimeZoneSourcePrivate::restoreTZ()
830 {
831  if (TZIsSaved) {
832  if (savedTZ.isEmpty()) {
833  ::unsetenv("TZ");
834  } else {
835  qputenv("TZ", savedTZ);
836  }
837  ::tzset();
838  TZIsSaved = false;
839  }
840 }
841 
842 /******************************************************************************/
843 
844 KSystemTimeZoneData::KSystemTimeZoneData()
845  : d(new KSystemTimeZoneDataPrivate)
846 { }
847 
848 KSystemTimeZoneData::KSystemTimeZoneData(const KSystemTimeZoneData &rhs)
849  : KTimeZoneData(),
850  d(new KSystemTimeZoneDataPrivate)
851 {
852  operator=(rhs);
853 }
854 
855 KSystemTimeZoneData::~KSystemTimeZoneData()
856 {
857  delete d;
858 }
859 
861 {
862  d->TZ = rhs.d->TZ;
863  d->abbreviations = rhs.d->abbreviations;
864  return *this;
865 }
866 
868 {
869  return new KSystemTimeZoneData(*this);
870 }
871 
873 {
874  return d->abbreviations;
875 }
876 
878 {
879  QByteArray abbr;
880  if (utcDateTime.timeSpec() != Qt::UTC) {
881  return abbr;
882  }
883  time_t t = utcDateTime.toTime_t();
884  if (t != KTimeZone::InvalidTime_t) {
885  KSystemTimeZoneSourcePrivate::setTZ(d->TZ); // make this time zone the current local time zone
886 
887  /* Use tm.tm_zone if available because it returns the abbreviation
888  * in use at the time specified. Otherwise, use tzname[] which
889  * returns the appropriate current abbreviation instead.
890  */
891 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS
892  tm tmtime;
893  if (localtime_r(&t, &tmtime))
894 #if HAVE_STRUCT_TM_TM_ZONE
895  abbr = tmtime.tm_zone;
896 #else
897  abbr = tzname[(tmtime.tm_isdst > 0) ? 1 : 0];
898 #endif
899 #else
900  const tm *tmtime = localtime(&t);
901  if (tmtime)
902 #if HAVE_STRUCT_TM_TM_ZONE
903  abbr = tmtime->tm_zone;
904 #else
905  abbr = tzname[(tmtime->tm_isdst > 0) ? 1 : 0];
906 #endif
907 #endif
908  KSystemTimeZoneSourcePrivate::restoreTZ(); // restore the original local time zone if necessary
909  }
910  return abbr;
911 }
912 
914 {
915  return QList<int>();
916 }
917 
static bool isSimulated()
Check whether there is a simulated local system time zone.
int minute() const const
QDBusReply< void > startService(const QString &name)
static bool isTimeZoneDaemonAvailable()
Return whether the KDE time zone daemon, ktimezoned, appears to be available and working.
KSystemTimeZone(KSystemTimeZoneSource *source, const QString &name, const QString &countryCode=QString(), float latitude=UNKNOWN, float longitude=UNKNOWN, const QString &comment=QString())
Creates a time zone.
QString readLine(qint64 maxlen)
static KTimeZone zone(const QString &name)
Returns the time zone with the given name.
Base class for the parsed data returned by a KTimeZoneSource class.
Definition: ktimezone.h:1302
QList< QByteArray > abbreviations() const override
Returns the complete list of time zone abbreviations.
QDBusConnectionInterface * interface() const const
bool isValid() const
Checks whether the instance is valid.
Definition: ktimezone.cpp:638
QMap::const_iterator constBegin() const const
static QStringList listTimeZones()
Static helper method that lists all availalbe timezones on the system as per the information in the W...
bool isEmpty() const const
void setFileName(const QString &name)
static const KTimeZones::ZoneMap zones()
Returns all the time zones defined in this collection.
static QString zoneinfoDir()
Returns the location of the system time zone zoneinfo database.
QString message() const const
QTime time() const const
bool isValid() const const
QDBusConnection sessionBus()
int day() const const
The KSystemTimeZoneWindows class represents a time zone defined in the Windows registry.
Definition: ktimezone_win.h:76
static void setLocalZone(const KTimeZone &tz)
Set or clear the simulated local system time zone.
bool exists() const const
QDBusReply< bool > isServiceRegistered(const QString &serviceName) const const
The KTimeZones class represents a time zone database which consists of a collection of individual tim...
Definition: ktimezone.h:308
QList< int > utcOffsets() const override
Returns the complete list of UTC offsets for the time zone.
bool isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const override
Implements KSystemTimeZone::isDstAtUtc().
int second() const const
LocalTime
static KTimeZones * timeZones()
Returns the unique KTimeZones instance containing the system time zones collection.
int offset(const KTimeZone *caller, time_t t) const override
Implements KSystemTimeZone::offset().
QByteArray abbreviation(const QDateTime &utcDateTime) const override
Returns the time zone abbreviation current at a specified time.
int offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const override
Implements KSystemTimeZone::offsetAtZoneTime().
KSystemTimeZoneData & operator=(const KSystemTimeZoneData &)
Assignment; no special ownership assumed.
int count(const T &value) const const
bool atEnd() const const
int offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const override
Implements KSystemTimeZone::offsetAtUtc().
static time_t toTime_t(const QDateTime &utcDateTime)
Converts a UTC QDateTime to a UTC time, measured in seconds since 00:00:00 UTC 1st January 1970 (as r...
Definition: ktimezone.cpp:938
QByteArray type() const
Returns the class name of the data represented by this instance.
Definition: ktimezone.cpp:633
bool exists() const
static KTimeZone readZone(const QString &name)
Returns the time zone with the given name, containing the full time zone definition read directly fro...
QByteArray & prepend(char ch)
Backend class for KSystemTimeZone class.
int toInt(bool *ok, int base) const const
bool isDst(const KTimeZone *caller, time_t t) const override
Implements KSystemTimeZone::isDst().
bool isEmpty() const const
QMap::const_iterator constEnd() const const
The KTzfileTimeZone class represents a time zone defined in tzfile(5) format.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
KSystemTimeZoneBackend(KSystemTimeZoneSource *source, const QString &name, const QString &countryCode, float latitude, float longitude, const QString &comment)
Implements KSystemTimeZone::KSystemTimeZone().
static KTimeZone local()
Returns the current local system time zone.
static const time_t InvalidTime_t
Indicates an invalid time_t value.
Definition: ktimezone.h:1079
bool updateBase(const KTimeZone &other)
Update the definition of the time zone to be identical to another KTimeZone instance.
Definition: ktimezone.cpp:780
static const int InvalidOffset
Indicates an invalid UTC offset.
Definition: ktimezone.h:1075
virtual bool open(QIODevice::OpenMode mode) override
int indexOf(QStringView str, int from) const const
A class to read and parse system time zone data.
int hour() const const
A class to read and parse the timezone information from the Windows registry.
Definition: ktimezone_win.h:43
static KTimeZone realLocalZone()
Return the real (not simulated) local system time zone.
Qt::TimeSpec timeSpec() const const
Base class representing a time zone.
Definition: ktimezone.h:415
QCA_EXPORT void init()
bool isValid() const const
const QDBusError & error()
void readConfig()
Reads the file share configuration file.
Definition: kfileshare.cpp:108
QByteArray type() const override
Returns the class name of the data represented by this instance.
static void startParseBlock()
Use in conjunction with endParseBlock() to improve efficiency when calling parse() for a group of KSy...
QDBusMessage call(const QString &method, Args &&...args)
QString mid(int position, int n) const const
QDate date() const const
virtual void close() override
void reparseConfiguration()
int length() const const
KTimeZoneBackend * clone() const override
Creates a copy of this instance.
KTimeZoneData * parse(const KTimeZone &zone) const override
Extract detailed information for one time zone, via the system time zone library functions.
QString fromLatin1(const char *str, int size)
QString name() const
Returns the name of the time zone.
Definition: ktimezone.cpp:663
KSystemTimeZoneSource()
Constructs a system time zone source.
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
int year() const const
QList< QByteArray > abbreviations() const
Returns the list of time zone abbreviations used by the time zone.
Definition: ktimezone.cpp:668
T readEntry(const QString &key, const T &aDefault) const
int month() const const
KTimeZoneData * clone() const override
Creates a new copy of this object.
static KTimeZone utc()
Returns a standard UTC time zone, with name "UTC".
Definition: ktimezone.cpp:916
Parsed system time zone data.
The KSystemTimeZones class represents the system time zone database, consisting of a collection of in...
TZFILE time zone functions.
uint toTime_t() const const
Base backend class for KTimeZone classes.
Definition: ktimezone.h:1121
int remove(const Key &key)
A class to read and parse tzfile time zone definition files.
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue Jul 7 2020 22:56:52 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.