25 #include "ksystemtimezone.moc"
28 #include <config-date.h>
30 #ifdef HAVE_SYS_TIME_H
39 #include <QtCore/QCoreApplication>
40 #include <QtCore/QFile>
41 #include <QtCore/QFileInfo>
42 #include <QtCore/QDir>
43 #include <QtCore/QRegExp>
44 #include <QtCore/QStringList>
45 #include <QtCore/QTextStream>
46 #include <QtDBus/QDBusConnection>
47 #include <QtDBus/QDBusInterface>
48 #include <QtDBus/QDBusConnectionInterface>
49 #include <QtDBus/QDBusReply>
64 #define KTIMEZONED_DBUS_IFACE "org.kde.KTimeZoned"
72 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS
74 if (!localtime_r(&t, &tmtime))
77 return tmtime.tm_gmtoff;
79 int lwday = tmtime.tm_wday;
80 int lt = 3600*tmtime.tm_hour + 60*tmtime.tm_min + tmtime.tm_sec;
81 if (!gmtime_r(&t, &tmtime))
83 int uwday = tmtime.tm_wday;
84 int ut = 3600*tmtime.tm_hour + 60*tmtime.tm_min + tmtime.tm_sec;
87 tm *tmtime = localtime(&t);
91 return tmtime->tm_gmtoff;
93 int lwday = tmtime->tm_wday;
94 int lt = 3600*tmtime->tm_hour + 60*tmtime->tm_min + tmtime->tm_sec;
96 int uwday = tmtime->tm_wday;
97 int ut = 3600*tmtime->tm_hour + 60*tmtime->tm_min + tmtime->tm_sec;
100 #ifndef HAVE_TM_GMTOFF
104 if (lwday == uwday + 1 || (lwday == 0 && uwday == 6))
116 class KSystemTimeZonesPrivate :
public KTimeZones
119 static KSystemTimeZonesPrivate *instance();
121 static void setLocalZone();
122 static void cleanup();
125 static void updateTimezoneInformation()
127 instance()->updateTimezoneInformation(
true);
130 static void updateZonetab() { instance()->readZoneTab(
true); }
134 static QString m_localZoneName;
138 static bool m_ktimezonedError;
141 KSystemTimeZonesPrivate() {}
143 void updateTimezoneInformation(
bool update);
145 void readZoneTab(
bool update);
146 static float convertCoordinate(
const QString &coordinate);
150 static KSystemTimeZonesPrivate *m_instance;
154 KTimeZone KSystemTimeZonesPrivate::m_localZone;
155 QString KSystemTimeZonesPrivate::m_localZoneName;
156 QString KSystemTimeZonesPrivate::m_zoneinfoDir;
157 QString KSystemTimeZonesPrivate::m_zonetab;
159 bool KSystemTimeZonesPrivate::m_ktimezonedError =
true;
162 KSystemTimeZonesPrivate *KSystemTimeZonesPrivate::m_instance = 0;
171 return m_tzfileSource;
180 KSystemTimeZones::KSystemTimeZones()
183 QDBusConnection dbus = QDBusConnection::sessionBus();
185 dbus.connect(
QString(),
QString(), dbusIface, QLatin1String(
"configChanged"),
this, SLOT(configChanged()));
186 dbus.connect(
QString(),
QString(), dbusIface, QLatin1String(
"zonetabChanged"),
this, SLOT(zonetabChanged(
QString)));
198 if (simulatedLocalZone->isValid())
199 return *simulatedLocalZone;
201 KSystemTimeZonesPrivate::instance();
202 return KSystemTimeZonesPrivate::m_localZone;
207 KSystemTimeZonesPrivate::instance();
208 return KSystemTimeZonesPrivate::m_localZone;
215 *simulatedLocalZone = tz;
222 return simulatedLocalZone->isValid();
230 KSystemTimeZonesPrivate::instance();
231 return KSystemTimeZonesPrivate::m_zoneinfoDir;
236 KSystemTimeZonesPrivate::instance();
237 return !KSystemTimeZonesPrivate::m_ktimezonedError;
242 return KSystemTimeZonesPrivate::instance();
252 return KSystemTimeZonesPrivate::instance()->zones();
257 return KSystemTimeZonesPrivate::instance()->zone(name);
260 void KSystemTimeZones::configChanged()
262 kDebug(161) <<
"KSystemTimeZones::configChanged()";
263 KSystemTimeZonesPrivate::m_ktimezonedError =
false;
267 void KSystemTimeZones::zonetabChanged(
const QString &zonetab)
271 kDebug(161) <<
"KSystemTimeZones::zonetabChanged()";
272 KSystemTimeZonesPrivate::m_ktimezonedError =
false;
275 KSystemTimeZonesPrivate::updateZonetab();
279 void KSystemTimeZones::zoneDefinitionChanged(
const QString &zone)
301 if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(QLatin1String(
"org.kde.kded")))
304 QDBusInterface *ktimezoned =
new QDBusInterface(QLatin1String(
"org.kde.kded"), QLatin1String(
"/modules/ktimezoned"), dbusIface);
305 QDBusReply<void> reply = ktimezoned->call(QLatin1String(
"initialize"),
false);
306 m_ktimezonedError = !reply.isValid();
307 if (m_ktimezonedError)
308 kError(161) <<
"KSystemTimeZones: ktimezoned initialize() D-Bus call failed: " << reply.error().message() << endl;
309 kDebug(161)<<
"instance(): ... initialised";
319 m_instance->updateTimezoneInformation(
false);
322 if (!m_zonetab.isEmpty())
323 m_instance->readZoneTab(
false);
326 if (!m_localZone.isValid())
329 qAddPostRoutine(KSystemTimeZonesPrivate::cleanup);
338 config.reparseConfiguration();
342 kError(161) <<
"No time zone information obtained from ktimezoned";
343 m_ktimezonedError =
true;
345 m_zoneinfoDir =
group.readEntry(
"ZoneinfoDir");
346 m_zonetab =
group.readEntry(
"Zonetab");
347 m_localZoneName =
group.readEntry(
"LocalZone");
348 if (m_zoneinfoDir.length() > 1 && m_zoneinfoDir.endsWith(QLatin1Char(
'/')))
349 m_zoneinfoDir.truncate(m_zoneinfoDir.length() - 1);
352 kDebug(161) <<
"readConfig(): local zone=" << m_localZoneName;
355 void KSystemTimeZonesPrivate::setLocalZone()
358 if (m_localZoneName.startsWith(QLatin1Char(
'/'))) {
360 filename = m_localZoneName;
364 m_localZone = m_instance->zone(m_localZoneName);
365 if (m_localZone.isValid())
368 filename = m_zoneinfoDir + QLatin1Char(
'/') + m_localZoneName;
373 if (zonename.startsWith(m_zoneinfoDir + QLatin1Char(
'/')))
374 zonename = zonename.mid(m_zoneinfoDir.length() + 1);
375 m_localZone =
KTzfileTimeZone(KSystemTimeZonesPrivate::tzfileSource(), zonename);
376 if (m_localZone.isValid() && m_instance)
379 const KTimeZone oldzone = m_instance->zone(zonename);
380 if (!oldzone.
isValid() || oldzone.
type() !=
"KTzfileTimeZone")
382 m_instance->remove(oldzone);
383 m_instance->add(m_localZone);
388 void KSystemTimeZonesPrivate::cleanup()
393 delete m_tzfileSource;
398 void KSystemTimeZonesPrivate::updateTimezoneInformation(
bool update)
411 newZones += stz.name();
424 const ZoneMap oldZones =
zones();
425 for (ZoneMap::const_iterator it = oldZones.begin(); it != oldZones.end(); ++it)
427 if (newZones.indexOf(it.key()) < 0)
438 void KSystemTimeZonesPrivate::readZoneTab(
bool update)
440 kDebug(161) <<
"readZoneTab(" << m_zonetab<<
")";
443 f.setFileName(m_zonetab);
444 if (!f.open(QIODevice::ReadOnly))
447 const QRegExp lineSeparator(QLatin1String(
"[ \t]"));
448 const QRegExp ordinateSeparator(QLatin1String(
"[+-]"));
453 const QString line = str.readLine();
454 if (line.isEmpty() || line[0] == QLatin1Char(
'#'))
457 const int n = tokens.count();
460 kError(161) <<
"readZoneTab(): invalid record: " << line << endl;
465 const int i = tokens[1].indexOf(ordinateSeparator, 1);
468 kError(161) <<
"readZoneTab() " << tokens[2] <<
": invalid coordinates: " << tokens[1] << endl;
472 const float latitude = convertCoordinate(tokens[1].left(i));
473 const float longitude = convertCoordinate(tokens[1].mid(i));
476 if (tokens[0] == QLatin1String(
"??"))
477 tokens[0] = QString::fromLatin1(
"");
480 if (n > 3 && tokens[3] == QLatin1String(
"-"))
481 tokens[3] = QString::fromLatin1(
"");
485 const KTzfileTimeZone tz(tzfileSource(), tokens[2], tokens[0], latitude, longitude, (n > 3 ? tokens[3] :
QString()));
489 newZones += tz.name();
504 const ZoneMap oldZones =
zones();
505 for (ZoneMap::ConstIterator it = oldZones.constBegin(); it != oldZones.constEnd(); ++it)
507 if (newZones.indexOf(it.key()) < 0)
516 float KSystemTimeZonesPrivate::convertCoordinate(
const QString &coordinate)
518 int value = coordinate.toInt();
523 if (coordinate.length() > 6)
525 degrees = value / 10000;
526 value -= degrees * 10000;
527 minutes = value / 100;
528 value -= minutes * 100;
533 degrees = value / 100;
534 value -= degrees * 100;
537 value = degrees * 3600 + minutes * 60 + seconds;
538 return value / 3600.0;
547 const QString &countryCode,
float latitude,
float longitude,
const QString &comment)
561 return "KSystemTimeZone";
566 if (!caller->
isValid() || !zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime)
569 const QByteArray originalZone = qgetenv(
"TZ");
570 QByteArray tz = caller->
name().toUtf8();
572 const bool change = (tz != originalZone);
575 ::setenv(
"TZ", tz, 1);
581 tmtime.tm_sec = zoneDateTime.time().second();
582 tmtime.tm_min = zoneDateTime.time().minute();
583 tmtime.tm_hour = zoneDateTime.time().hour();
584 tmtime.tm_mday = zoneDateTime.date().day();
585 tmtime.tm_mon = zoneDateTime.date().month() - 1;
586 tmtime.tm_year = zoneDateTime.date().year() - 1900;
587 tmtime.tm_isdst = -1;
588 const time_t t = mktime(&tmtime);
592 int offset2 = offset1;
598 const int maxShift = 3600;
599 offset2 =
gmtoff(t + maxShift);
600 if (offset2 < offset1)
603 if (offset1 - offset2 < maxShift)
604 offset2 =
gmtoff(t + (offset1 - offset2));
606 else if ((offset2 =
gmtoff(t - maxShift)) > offset1)
609 if (offset2 - offset1 < maxShift)
610 offset2 =
gmtoff(t - (offset2 - offset1));
612 const int o = offset1;
616 else offset2 = offset1;
618 *secondOffset = offset2;
624 if (originalZone.isEmpty())
627 ::setenv(
"TZ", originalZone, 1);
644 const QByteArray originalZone = qgetenv(
"TZ");
645 QByteArray tz = caller->
name().toUtf8();
647 const bool change = (tz != originalZone);
650 ::setenv(
"TZ", tz, 1);
654 const int secs =
gmtoff(t);
659 if (originalZone.isEmpty())
662 ::setenv(
"TZ", originalZone, 1);
678 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS
680 if (localtime_r(&t, &tmtime))
681 return tmtime.tm_isdst > 0;
683 const tm *tmtime = localtime(&t);
685 return tmtime->tm_isdst > 0;
695 const QString &countryCode,
float latitude,
float longitude,
const QString &comment)
707 class KSystemTimeZoneDataPrivate
716 class KSystemTimeZoneSourcePrivate
719 static void setTZ(
const QByteArray &zoneName);
720 static void restoreTZ();
721 static QByteArray savedTZ;
722 static QByteArray originalTZ;
723 static bool TZIsSaved;
724 static bool multiParse;
727 QByteArray KSystemTimeZoneSourcePrivate::savedTZ;
728 QByteArray KSystemTimeZoneSourcePrivate::originalTZ;
729 bool KSystemTimeZoneSourcePrivate::TZIsSaved =
false;
730 bool KSystemTimeZoneSourcePrivate::multiParse =
false;
746 const QByteArray tz = zone.
name().toUtf8();
747 KSystemTimeZoneSourcePrivate::setTZ(tz);
752 data->d->abbreviations.append(tzname[0]);
753 data->d->abbreviations.append(tzname[1]);
758 KSystemTimeZoneSourcePrivate::restoreTZ();
764 KSystemTimeZoneSourcePrivate::originalTZ = qgetenv(
"TZ");
765 KSystemTimeZoneSourcePrivate::multiParse =
true;
770 if (KSystemTimeZoneSourcePrivate::multiParse)
773 if (KSystemTimeZoneSourcePrivate::originalTZ.isEmpty())
776 ::setenv(
"TZ", KSystemTimeZoneSourcePrivate::originalTZ, 1);
778 KSystemTimeZoneSourcePrivate::multiParse =
false;
784 void KSystemTimeZoneSourcePrivate::setTZ(
const QByteArray &zoneName)
786 QByteArray tz = zoneName;
788 bool setTZ = multiParse;
791 savedTZ = qgetenv(
"TZ");
793 setTZ = (tz != savedTZ);
797 ::setenv(
"TZ", tz, 1);
803 void KSystemTimeZoneSourcePrivate::restoreTZ()
807 if (savedTZ.isEmpty())
810 ::setenv(
"TZ", savedTZ, 1);
820 : d(new KSystemTimeZoneDataPrivate)
825 d(new KSystemTimeZoneDataPrivate)
838 d->abbreviations = rhs.d->abbreviations;
849 return d->abbreviations;
855 if (utcDateTime.timeSpec() != Qt::UTC)
857 time_t t = utcDateTime.toTime_t();
860 KSystemTimeZoneSourcePrivate::setTZ(d->TZ);
866 #ifdef _POSIX_THREAD_SAFE_FUNCTIONS
868 if (localtime_r(&t, &tmtime))
869 #ifdef HAVE_STRUCT_TM_TM_ZONE
870 abbr = tmtime.tm_zone;
872 abbr = tzname[(tmtime.tm_isdst > 0) ? 1 : 0];
875 const tm *tmtime = localtime(&t);
877 #ifdef HAVE_STRUCT_TM_TM_ZONE
878 abbr = tmtime->tm_zone;
880 abbr = tzname[(tmtime->tm_isdst > 0) ? 1 : 0];
883 KSystemTimeZoneSourcePrivate::restoreTZ();