kdelibs/kdecore/date/ksystemtimezone.cpp
2014-11-19 15:19:19 +00:00

831 lines
25 KiB
C++

/*
This file is part of the KDE libraries
Copyright (c) 2005-2010 David Jarvie <djarvie@kde.org>
Copyright (c) 2005 S.R.Haque <srhaque@iee.org>.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// This file requires HAVE_STRUCT_TM_TM_ZONE to be defined if struct tm member tm_zone is available.
// This file requires HAVE_TM_GMTOFF to be defined if struct tm member tm_gmtoff is available.
#include "ksystemtimezone.moc"
#include <config.h>
#include <config-date.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <climits>
#include <cstdlib>
#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtCore/QRegExp>
#include <QtCore/QStringList>
#include <QtCore/QTextStream>
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusConnectionInterface>
#include <QtDBus/QDBusReply>
#include <kglobal.h>
#include <klocale.h>
#include <kcodecs.h>
#include <kstringhandler.h>
#include <ktemporaryfile.h>
#include <ktoolinvocation.h>
#include <kdebug.h>
#include <kconfiggroup.h>
#include "ktzfiletimezone.h"
#define KTIMEZONED_DBUS_IFACE "org.kde.KTimeZoned"
/* Return the offset to UTC in the current time zone at the specified UTC time.
* The thread-safe function localtime_r() is used in preference if available.
*/
int gmtoff(time_t t)
{
#ifdef _POSIX_THREAD_SAFE_FUNCTIONS
tm tmtime;
if (!localtime_r(&t, &tmtime))
return 0;
#ifdef HAVE_TM_GMTOFF
return tmtime.tm_gmtoff;
#else
int lwday = tmtime.tm_wday;
int lt = 3600*tmtime.tm_hour + 60*tmtime.tm_min + tmtime.tm_sec;
if (!gmtime_r(&t, &tmtime))
return 0;
int uwday = tmtime.tm_wday;
int ut = 3600*tmtime.tm_hour + 60*tmtime.tm_min + tmtime.tm_sec;
#endif
#else
tm *tmtime = localtime(&t);
if (!tmtime)
return 0;
#ifdef HAVE_TM_GMTOFF
return tmtime->tm_gmtoff;
#else
int lwday = tmtime->tm_wday;
int lt = 3600*tmtime->tm_hour + 60*tmtime->tm_min + tmtime->tm_sec;
tmtime = gmtime(&t);
int uwday = tmtime->tm_wday;
int ut = 3600*tmtime->tm_hour + 60*tmtime->tm_min + tmtime->tm_sec;
#endif
#endif
#ifndef HAVE_TM_GMTOFF
if (lwday != uwday)
{
// Adjust for different day
if (lwday == uwday + 1 || (lwday == 0 && uwday == 6))
lt += 24*3600;
else
lt -= 24*3600;
}
return lt - ut;
#endif
}
/******************************************************************************/
class KSystemTimeZonesPrivate : public KTimeZones
{
public:
static KSystemTimeZonesPrivate *instance();
static KTzfileTimeZoneSource *tzfileSource();
static void setLocalZone();
static void cleanup();
static void readConfig(bool init);
static void updateZonetab() { instance()->readZoneTab(true); }
static KTimeZone m_localZone;
static QString m_localZoneName;
static QString m_zoneinfoDir;
static QString m_zonetab;
static KSystemTimeZoneSource *m_source;
static bool m_ktimezonedError;
private:
KSystemTimeZonesPrivate() {}
void readZoneTab(bool update);
static float convertCoordinate(const QString &coordinate);
static KSystemTimeZones *m_parent;
static KSystemTimeZonesPrivate *m_instance;
static KTzfileTimeZoneSource *m_tzfileSource;
};
KTimeZone KSystemTimeZonesPrivate::m_localZone;
QString KSystemTimeZonesPrivate::m_localZoneName;
QString KSystemTimeZonesPrivate::m_zoneinfoDir;
QString KSystemTimeZonesPrivate::m_zonetab;
KSystemTimeZoneSource *KSystemTimeZonesPrivate::m_source = 0;
bool KSystemTimeZonesPrivate::m_ktimezonedError = true;
KTzfileTimeZoneSource *KSystemTimeZonesPrivate::m_tzfileSource = 0;
KSystemTimeZones *KSystemTimeZonesPrivate::m_parent = 0;
KSystemTimeZonesPrivate *KSystemTimeZonesPrivate::m_instance = 0;
KTzfileTimeZoneSource *KSystemTimeZonesPrivate::tzfileSource()
{
if (!m_tzfileSource)
{
instance();
m_tzfileSource = new KTzfileTimeZoneSource(m_zoneinfoDir);
}
return m_tzfileSource;
}
#ifndef NDEBUG
K_GLOBAL_STATIC(KTimeZone, simulatedLocalZone)
#endif
KSystemTimeZones::KSystemTimeZones()
: d(0)
{
QDBusConnection dbus = QDBusConnection::sessionBus();
const QString dbusIface = QString::fromLatin1(KTIMEZONED_DBUS_IFACE);
dbus.connect(QString(), QString(), dbusIface, QLatin1String("configChanged"), this, SLOT(configChanged()));
dbus.connect(QString(), QString(), dbusIface, QLatin1String("zonetabChanged"), this, SLOT(zonetabChanged(QString)));
// No need to connect to definitionChanged() - see comments in zoneDefinitionChanged()
//dbus.connect(QString(), QString(), dbusIface, QLatin1String("definitionChanged"), this, SLOT(zoneDefinitionChanged(QString)));
}
KSystemTimeZones::~KSystemTimeZones()
{
}
KTimeZone KSystemTimeZones::local()
{
#ifndef NDEBUG
if (simulatedLocalZone->isValid())
return *simulatedLocalZone;
#endif
KSystemTimeZonesPrivate::instance();
return KSystemTimeZonesPrivate::m_localZone;
}
KTimeZone KSystemTimeZones::realLocalZone()
{
KSystemTimeZonesPrivate::instance();
return KSystemTimeZonesPrivate::m_localZone;
}
void KSystemTimeZones::setLocalZone(const KTimeZone& tz)
{
Q_UNUSED(tz);
#ifndef NDEBUG
*simulatedLocalZone = tz;
#endif
}
bool KSystemTimeZones::isSimulated()
{
#ifndef NDEBUG
return simulatedLocalZone->isValid();
#else
return false;
#endif
}
QString KSystemTimeZones::zoneinfoDir()
{
KSystemTimeZonesPrivate::instance();
return KSystemTimeZonesPrivate::m_zoneinfoDir;
}
bool KSystemTimeZones::isTimeZoneDaemonAvailable()
{
KSystemTimeZonesPrivate::instance();
return !KSystemTimeZonesPrivate::m_ktimezonedError;
}
KTimeZones *KSystemTimeZones::timeZones()
{
return KSystemTimeZonesPrivate::instance();
}
KTimeZone KSystemTimeZones::readZone(const QString &name)
{
return KTzfileTimeZone(KSystemTimeZonesPrivate::tzfileSource(), name);
}
const KTimeZones::ZoneMap KSystemTimeZones::zones()
{
return KSystemTimeZonesPrivate::instance()->zones();
}
KTimeZone KSystemTimeZones::zone(const QString& name)
{
return KSystemTimeZonesPrivate::instance()->zone(name);
}
void KSystemTimeZones::configChanged()
{
kDebug(161) << "KSystemTimeZones::configChanged()";
KSystemTimeZonesPrivate::m_ktimezonedError = false;
KSystemTimeZonesPrivate::readConfig(false);
}
void KSystemTimeZones::zonetabChanged(const QString &zonetab)
{
Q_UNUSED(zonetab)
kDebug(161) << "KSystemTimeZones::zonetabChanged()";
KSystemTimeZonesPrivate::m_ktimezonedError = false;
// Re-read zone.tab and update our collection, removing any deleted
// zones and adding any new zones.
KSystemTimeZonesPrivate::updateZonetab();
}
void KSystemTimeZones::zoneDefinitionChanged(const QString &zone)
{
// No need to do anything when the definition (as opposed to the
// identity) of the local zone changes, since the updated details
// will always be accessed by the system library calls to fetch
// local zone information.
Q_UNUSED(zone)
KSystemTimeZonesPrivate::m_ktimezonedError = false;
}
// Perform initialization, create the unique KSystemTimeZones instance,
// whose only function is to receive D-Bus signals from KTimeZoned,
// and create the unique KSystemTimeZonesPrivate instance.
KSystemTimeZonesPrivate *KSystemTimeZonesPrivate::instance()
{
if (!m_instance)
{
m_instance = new KSystemTimeZonesPrivate;
// A KSystemTimeZones instance is required only to catch D-Bus signals.
m_parent = new KSystemTimeZones;
// Ensure that the KDED time zones module has initialized. The call loads the module on demand.
if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(QLatin1String("org.kde.kded")))
KToolInvocation::klauncher(); // this calls startKdeinit, and blocks until it returns
const QString dbusIface = QString::fromLatin1(KTIMEZONED_DBUS_IFACE);
QDBusInterface *ktimezoned = new QDBusInterface(QLatin1String("org.kde.kded"), QLatin1String("/modules/ktimezoned"), dbusIface);
QDBusReply<void> reply = ktimezoned->call(QLatin1String("initialize"), false);
m_ktimezonedError = !reply.isValid();
if (m_ktimezonedError)
kError(161) << "KSystemTimeZones: ktimezoned initialize() D-Bus call failed: " << reply.error().message() << endl;
kDebug(161)<<"instance(): ... initialised";
delete ktimezoned;
// Read the time zone config written by ktimezoned
readConfig(true);
// Go read the database.
// For Unix, read zone.tab.
if (!m_zonetab.isEmpty())
m_instance->readZoneTab(false);
setLocalZone();
if (!m_localZone.isValid())
m_localZone = KTimeZone::utc(); // ensure a time zone is always returned
qAddPostRoutine(KSystemTimeZonesPrivate::cleanup);
}
return m_instance;
}
void KSystemTimeZonesPrivate::readConfig(bool init)
{
KConfig config(QLatin1String("ktimezonedrc"));
if (!init)
config.reparseConfiguration();
KConfigGroup group(&config, "TimeZones");
if (!group.exists())
{
kError(161) << "No time zone information obtained from ktimezoned";
m_ktimezonedError = true;
}
m_zoneinfoDir = group.readEntry("ZoneinfoDir");
m_zonetab = group.readEntry("Zonetab");
m_localZoneName = group.readEntry("LocalZone");
if (m_zoneinfoDir.length() > 1 && m_zoneinfoDir.endsWith(QLatin1Char('/')))
m_zoneinfoDir.truncate(m_zoneinfoDir.length() - 1); // strip trailing '/'
if (!init)
setLocalZone();
kDebug(161) << "readConfig(): local zone=" << m_localZoneName;
}
void KSystemTimeZonesPrivate::setLocalZone()
{
QString filename;
if (m_localZoneName.startsWith(QLatin1Char('/'))) {
// The time zone is specified by a file outside the zoneinfo directory
filename = m_localZoneName;
} else {
// The zone name is either a known zone, or it's a relative file name
// in zoneinfo directory which isn't in zone.tab.
m_localZone = m_instance->zone(m_localZoneName);
if (m_localZone.isValid())
return;
// It's a relative file name
filename = m_zoneinfoDir + QLatin1Char('/') + m_localZoneName;
}
// Parse the specified time zone data file
QString zonename = filename;
if (zonename.startsWith(m_zoneinfoDir + QLatin1Char('/')))
zonename = zonename.mid(m_zoneinfoDir.length() + 1);
m_localZone = KTzfileTimeZone(KSystemTimeZonesPrivate::tzfileSource(), zonename);
if (m_localZone.isValid() && m_instance)
{
// Add the new time zone to the list
const KTimeZone oldzone = m_instance->zone(zonename);
if (!oldzone.isValid() || oldzone.type() != "KTzfileTimeZone")
{
m_instance->remove(oldzone);
m_instance->add(m_localZone);
}
}
}
void KSystemTimeZonesPrivate::cleanup()
{
delete m_parent;
delete m_instance;
delete m_source;
delete m_tzfileSource;
}
/*
* Find the location of the zoneinfo files and store in mZoneinfoDir.
* Parse zone.tab and for each time zone, create a KSystemTimeZone instance.
*/
void KSystemTimeZonesPrivate::readZoneTab(bool update)
{
kDebug(161) << "readZoneTab(" << m_zonetab<< ")";
QStringList newZones;
QFile f;
f.setFileName(m_zonetab);
if (!f.open(QIODevice::ReadOnly))
return;
QTextStream str(&f);
const QRegExp lineSeparator(QLatin1String("[ \t]"));
const QRegExp ordinateSeparator(QLatin1String("[+-]"));
if (!m_source)
m_source = new KSystemTimeZoneSource;
while (!str.atEnd())
{
const QString line = str.readLine();
if (line.isEmpty() || line[0] == QLatin1Char('#'))
continue;
QStringList tokens = KStringHandler::perlSplit(lineSeparator, line, 4);
const int n = tokens.count();
if (n < 3)
{
kError(161) << "readZoneTab(): invalid record: " << line << endl;
continue;
}
// Got three tokens. Now check for two ordinates plus first one is "".
const int i = tokens[1].indexOf(ordinateSeparator, 1);
if (i < 0)
{
kError(161) << "readZoneTab() " << tokens[2] << ": invalid coordinates: " << tokens[1] << endl;
continue;
}
const float latitude = convertCoordinate(tokens[1].left(i));
const float longitude = convertCoordinate(tokens[1].mid(i));
// Add entry to list.
if (tokens[0] == QLatin1String("??"))
tokens[0] = QString::fromLatin1("");
// Solaris sets the empty Comments field to '-', making it not empty.
// Clean it up.
if (n > 3 && tokens[3] == QLatin1String("-"))
tokens[3] = QString::fromLatin1("");
// Note: KTzfileTimeZone is used in preference to KSystemTimeZone because of
// the large overhead incurred by tzset() - see KSystemTimeZones class
// description for details.
const KTzfileTimeZone tz(tzfileSource(), tokens[2], tokens[0], latitude, longitude, (n > 3 ? tokens[3] : QString()));
if (update)
{
// Update the existing collection with the new zone definition
newZones += tz.name();
KTimeZone oldTz = zone(tz.name());
if (oldTz.isValid())
oldTz.updateBase(tz); // the zone previously existed, so update its definition
else
add(tz); // the zone didn't previously exist, so add it
}
else
add(tz);
}
f.close();
if (update)
{
// Remove any zones from the collection which no longer exist
const ZoneMap oldZones = zones();
for (ZoneMap::ConstIterator it = oldZones.constBegin(); it != oldZones.constEnd(); ++it)
{
if (newZones.indexOf(it.key()) < 0)
remove(it.value());
}
}
}
/**
* Convert sHHMM or sHHMMSS to a floating point number of degrees.
*/
float KSystemTimeZonesPrivate::convertCoordinate(const QString &coordinate)
{
int value = coordinate.toInt();
int degrees = 0;
int minutes = 0;
int seconds = 0;
if (coordinate.length() > 6)
{
degrees = value / 10000;
value -= degrees * 10000;
minutes = value / 100;
value -= minutes * 100;
seconds = value;
}
else
{
degrees = value / 100;
value -= degrees * 100;
minutes = value;
}
value = degrees * 3600 + minutes * 60 + seconds;
return value / 3600.0;
}
/******************************************************************************/
KSystemTimeZoneBackend::KSystemTimeZoneBackend(KSystemTimeZoneSource *source, const QString &name,
const QString &countryCode, float latitude, float longitude, const QString &comment)
: KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)
{}
KSystemTimeZoneBackend::~KSystemTimeZoneBackend()
{}
KTimeZoneBackend *KSystemTimeZoneBackend::clone() const
{
return new KSystemTimeZoneBackend(*this);
}
QByteArray KSystemTimeZoneBackend::type() const
{
return "KSystemTimeZone";
}
int KSystemTimeZoneBackend::offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const
{
if (!caller->isValid() || !zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime)
return 0;
// Make this time zone the current local time zone
const QByteArray originalZone = qgetenv("TZ"); // save the original local time zone
QByteArray tz = caller->name().toUtf8();
tz.prepend(":");
const bool change = (tz != originalZone);
if (change)
{
::setenv("TZ", tz, 1);
::tzset();
}
// Convert zone time to UTC, and then get the offset to UTC
tm tmtime;
tmtime.tm_sec = zoneDateTime.time().second();
tmtime.tm_min = zoneDateTime.time().minute();
tmtime.tm_hour = zoneDateTime.time().hour();
tmtime.tm_mday = zoneDateTime.date().day();
tmtime.tm_mon = zoneDateTime.date().month() - 1;
tmtime.tm_year = zoneDateTime.date().year() - 1900;
tmtime.tm_isdst = -1;
const time_t t = mktime(&tmtime);
int offset1 = (t == (time_t)-1) ? KTimeZone::InvalidOffset : gmtoff(t);
if (secondOffset)
{
int offset2 = offset1;
if (t != (time_t)-1)
{
// Check if there is a backward DST change near to this time, by
// checking if the UTC offset is different 1 hour later or earlier.
// ASSUMPTION: DST SHIFTS ARE NEVER GREATER THAN 1 HOUR.
const int maxShift = 3600;
offset2 = gmtoff(t + maxShift);
if (offset2 < offset1)
{
// There is a backward DST shift during the following hour
if (offset1 - offset2 < maxShift)
offset2 = gmtoff(t + (offset1 - offset2));
}
else if ((offset2 = gmtoff(t - maxShift)) > offset1)
{
// There is a backward DST shift during the previous hour
if (offset2 - offset1 < maxShift)
offset2 = gmtoff(t - (offset2 - offset1));
// Put UTC offsets into the correct order
const int o = offset1;
offset1 = offset2;
offset2 = o;
}
else offset2 = offset1;
}
*secondOffset = offset2;
}
if (change)
{
// Restore the original local time zone
if (originalZone.isEmpty())
::unsetenv("TZ");
else
::setenv("TZ", originalZone, 1);
::tzset();
}
return offset1;
}
int KSystemTimeZoneBackend::offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
{
return offset(caller, KTimeZone::toTime_t(utcDateTime));
}
int KSystemTimeZoneBackend::offset(const KTimeZone *caller, time_t t) const
{
if (!caller->isValid() || t == KTimeZone::InvalidTime_t)
return 0;
// Make this time zone the current local time zone
const QByteArray originalZone = qgetenv("TZ"); // save the original local time zone
QByteArray tz = caller->name().toUtf8();
tz.prepend(":");
const bool change = (tz != originalZone);
if (change)
{
::setenv("TZ", tz, 1);
::tzset();
}
const int secs = gmtoff(t);
if (change)
{
// Restore the original local time zone
if (originalZone.isEmpty())
::unsetenv("TZ");
else
::setenv("TZ", originalZone, 1);
::tzset();
}
return secs;
}
bool KSystemTimeZoneBackend::isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
{
return isDst(caller, KTimeZone::toTime_t(utcDateTime));
}
bool KSystemTimeZoneBackend::isDst(const KTimeZone *caller, time_t t) const
{
Q_UNUSED(caller)
if (t != (time_t)-1)
{
#ifdef _POSIX_THREAD_SAFE_FUNCTIONS
tm tmtime;
if (localtime_r(&t, &tmtime))
return tmtime.tm_isdst > 0;
#else
const tm *tmtime = localtime(&t);
if (tmtime)
return tmtime->tm_isdst > 0;
#endif
}
return false;
}
/******************************************************************************/
KSystemTimeZone::KSystemTimeZone(KSystemTimeZoneSource *source, const QString &name,
const QString &countryCode, float latitude, float longitude, const QString &comment)
: KTimeZone(new KSystemTimeZoneBackend(source, name, countryCode, latitude, longitude, comment))
{
}
KSystemTimeZone::~KSystemTimeZone()
{
}
/******************************************************************************/
class KSystemTimeZoneDataPrivate
{
public:
QByteArray TZ;
QList<QByteArray> abbreviations;
};
// N.B. KSystemTimeZoneSourcePrivate is also used by KSystemTimeZoneData
class KSystemTimeZoneSourcePrivate
{
public:
static void setTZ(const QByteArray &zoneName);
static void restoreTZ();
static QByteArray savedTZ; // temporary value of TZ environment variable saved by setTZ()
static QByteArray originalTZ; // saved value of TZ environment variable during multiple parse() calls
static bool TZIsSaved; // TZ has been saved in savedTZ
static bool multiParse; // true if performing multiple parse() calls
};
QByteArray KSystemTimeZoneSourcePrivate::savedTZ;
QByteArray KSystemTimeZoneSourcePrivate::originalTZ;
bool KSystemTimeZoneSourcePrivate::TZIsSaved = false;
bool KSystemTimeZoneSourcePrivate::multiParse = false;
KSystemTimeZoneSource::KSystemTimeZoneSource()
: d(0)
// : d(new KSystemTimeZoneSourcePrivate)
{
}
KSystemTimeZoneSource::~KSystemTimeZoneSource()
{
// delete d;
}
KTimeZoneData* KSystemTimeZoneSource::parse(const KTimeZone &zone) const
{
const QByteArray tz = zone.name().toUtf8();
KSystemTimeZoneSourcePrivate::setTZ(tz); // make this time zone the current local time zone
tzset(); // initialize the tzname array
KSystemTimeZoneData* data = new KSystemTimeZoneData;
data->d->TZ = tz;
data->d->abbreviations.append(tzname[0]);
data->d->abbreviations.append(tzname[1]);
// There is no easy means to access the sequence of daylight savings time
// changes, or leap seconds adjustments, so leave that data empty.
KSystemTimeZoneSourcePrivate::restoreTZ(); // restore the original local time zone if necessary
return data;
}
void KSystemTimeZoneSource::startParseBlock()
{
KSystemTimeZoneSourcePrivate::originalTZ = qgetenv("TZ"); // save the original local time zone
KSystemTimeZoneSourcePrivate::multiParse = true;
}
void KSystemTimeZoneSource::endParseBlock()
{
if (KSystemTimeZoneSourcePrivate::multiParse)
{
// Restore the original local time zone
if (KSystemTimeZoneSourcePrivate::originalTZ.isEmpty())
::unsetenv("TZ");
else
::setenv("TZ", KSystemTimeZoneSourcePrivate::originalTZ, 1);
::tzset();
KSystemTimeZoneSourcePrivate::multiParse = false;
}
}
// Set the TZ environment variable to the specified time zone,
// saving its current setting first if necessary.
void KSystemTimeZoneSourcePrivate::setTZ(const QByteArray &zoneName)
{
QByteArray tz = zoneName;
tz.prepend(":");
bool setTZ = multiParse;
if (!setTZ)
{
savedTZ = qgetenv("TZ"); // save the original local time zone
TZIsSaved = true;
setTZ = (tz != savedTZ);
}
if (setTZ)
{
::setenv("TZ", tz, 1);
::tzset();
}
}
// Restore the TZ environment variable if it was saved by setTz()
void KSystemTimeZoneSourcePrivate::restoreTZ()
{
if (TZIsSaved)
{
if (savedTZ.isEmpty())
::unsetenv("TZ");
else
::setenv("TZ", savedTZ, 1);
::tzset();
TZIsSaved = false;
}
}
/******************************************************************************/
KSystemTimeZoneData::KSystemTimeZoneData()
: d(new KSystemTimeZoneDataPrivate)
{ }
KSystemTimeZoneData::KSystemTimeZoneData(const KSystemTimeZoneData &rhs)
: KTimeZoneData(),
d(new KSystemTimeZoneDataPrivate)
{
operator=(rhs);
}
KSystemTimeZoneData::~KSystemTimeZoneData()
{
delete d;
}
KSystemTimeZoneData &KSystemTimeZoneData::operator=(const KSystemTimeZoneData &rhs)
{
d->TZ = rhs.d->TZ;
d->abbreviations = rhs.d->abbreviations;
return *this;
}
KTimeZoneData *KSystemTimeZoneData::clone() const
{
return new KSystemTimeZoneData(*this);
}
QList<QByteArray> KSystemTimeZoneData::abbreviations() const
{
return d->abbreviations;
}
QByteArray KSystemTimeZoneData::abbreviation(const QDateTime &utcDateTime) const
{
QByteArray abbr;
if (utcDateTime.timeSpec() != Qt::UTC)
return abbr;
time_t t = utcDateTime.toTime_t();
if (t != KTimeZone::InvalidTime_t)
{
KSystemTimeZoneSourcePrivate::setTZ(d->TZ); // make this time zone the current local time zone
/* Use tm.tm_zone if available because it returns the abbreviation
* in use at the time specified. Otherwise, use tzname[] which
* returns the appropriate current abbreviation instead.
*/
#ifdef _POSIX_THREAD_SAFE_FUNCTIONS
tm tmtime;
if (localtime_r(&t, &tmtime))
#ifdef HAVE_STRUCT_TM_TM_ZONE
abbr = tmtime.tm_zone;
#else
abbr = tzname[(tmtime.tm_isdst > 0) ? 1 : 0];
#endif
#else
const tm *tmtime = localtime(&t);
if (tmtime)
#ifdef HAVE_STRUCT_TM_TM_ZONE
abbr = tmtime->tm_zone;
#else
abbr = tzname[(tmtime->tm_isdst > 0) ? 1 : 0];
#endif
#endif
KSystemTimeZoneSourcePrivate::restoreTZ(); // restore the original local time zone if necessary
}
return abbr;
}
QList<int> KSystemTimeZoneData::utcOffsets() const
{
return QList<int>();
}