2 This file is part of the KDE libraries
3 Copyright (c) 2005-2007 David Jarvie <djarvie@kde.org>
4 Copyright (c) 2005 S.R.Haque <srhaque@iee.org>.
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.
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.
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.
22 #include "ktimezoned.moc"
31 #include <QStringList>
32 #include <QTextStream>
33 #include <QtDBus/QtDBus>
38 #include <kstandarddirs.h>
39 #include <kstringhandler.h>
40 #include <ktemporaryfile.h>
42 #include <kconfiggroup.h>
44 #include <kpluginfactory.h>
45 #include <kpluginloader.h>
47 K_PLUGIN_FACTORY(KTimeZonedFactory
,
48 registerPlugin
<KTimeZoned
>();
50 K_EXPORT_PLUGIN(KTimeZonedFactory("ktimezoned"))
53 // Config file entry names
54 const char ZONEINFO_DIR
[] = "ZoneinfoDir"; // path to zoneinfo/ directory
55 const char ZONE_TAB
[] = "Zonetab"; // path & name of zone.tab
56 const char ZONE_TAB_CACHE
[] = "ZonetabCache"; // type of cached simulated zone.tab
57 const char LOCAL_ZONE
[] = "LocalZone"; // name of local time zone
60 KTimeZoned::KTimeZoned(QObject
* parent
, const QList
<QVariant
>&)
69 KTimeZoned::~KTimeZoned()
79 void KTimeZoned::initialize(bool reinit
)
81 // If we reach here, the module has already been constructed and therefore
82 // initialized. So only do anything if reinit is true.
87 void KTimeZoned::init(bool restart
)
91 kDebug(1221) << "KTimeZoned::init(restart)";
101 // On Windows, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
102 // holds the time zone database. The TZI binary value is the TIME_ZONE_INFORMATION structure.
104 // For Unix, read zone.tab.
105 KConfig
config(QLatin1String("ktimezonedrc"));
107 config
.reparseConfiguration();
108 KConfigGroup
group(&config
, "TimeZones");
109 mZoneinfoDir
= group
.readEntry(ZONEINFO_DIR
);
110 mZoneTab
= group
.readEntry(ZONE_TAB
);
111 mConfigLocalZone
= group
.readEntry(LOCAL_ZONE
);
112 QString ztc
= group
.readEntry(ZONE_TAB_CACHE
, QString());
113 mZoneTabCache
= (ztc
== "Solaris") ? Solaris
: NoCache
;
114 QString oldZoneinfoDir
= mZoneinfoDir
;
115 QString oldZoneTab
= mZoneTab
;
116 CacheType oldCacheType
= mZoneTabCache
;
118 // Open zone.tab if we already know where it is
120 if (!mZoneTab
.isEmpty() && !mZoneinfoDir
.isEmpty())
122 f
.setFileName(mZoneTab
);
123 if (!f
.open(QIODevice::ReadOnly
))
125 else if (mZoneTabCache
!= NoCache
)
127 // Check whether the cached zone.tab is still up to date
129 #warning Implement checking whether Solaris cached zone.tab is up to date
134 if (mZoneTab
.isEmpty() || mZoneinfoDir
.isEmpty())
136 // Search for zone.tab
139 mZoneTab
= f
.fileName();
141 if (mZoneinfoDir
!= oldZoneinfoDir
142 || mZoneTab
!= oldZoneTab
143 || mZoneTabCache
!= oldCacheType
)
145 // Update config file and notify interested applications
146 group
.writeEntry(ZONEINFO_DIR
, mZoneinfoDir
);
147 group
.writeEntry(ZONE_TAB
, mZoneTab
);
149 switch (mZoneTabCache
)
151 case Solaris
: ztc
= "Solaris"; break;
154 group
.writeEntry(ZONE_TAB_CACHE
, ztc
);
156 QDBusMessage message
= QDBusMessage::createSignal("/Daemon", "org.kde.KTimeZoned", "configChanged");
157 QDBusConnection::sessionBus().send(message
);
161 // Read zone.tab and create a collection of KTimeZone instances
164 mZonetabWatch
= new KDirWatch(this);
165 mZonetabWatch
->addFile(mZoneTab
);
166 connect(mZonetabWatch
, SIGNAL(dirty(const QString
&)), SLOT(zonetab_Changed(const QString
&)));
169 // Find the local system time zone and set up file monitors to detect changes
173 // Check if the local zone has been updated, and if so, write the new
174 // zone to the config file and notify interested parties.
175 void KTimeZoned::updateLocalZone()
177 if (mConfigLocalZone
!= mLocalZone
)
179 KConfig
config(QLatin1String("ktimezonedrc"));
180 KConfigGroup
group(&config
, "TimeZones");
181 mConfigLocalZone
= mLocalZone
;
182 group
.writeEntry(LOCAL_ZONE
, mConfigLocalZone
);
185 QDBusMessage message
= QDBusMessage::createSignal("/Daemon", "org.kde.KTimeZoned", "configChanged");
186 QDBusConnection::sessionBus().send(message
);
191 * Find the location of the zoneinfo files and store in mZoneinfoDir.
192 * Open or if necessary create zone.tab.
194 bool KTimeZoned::findZoneTab(QFile
& f
)
196 #if defined(SOLARIS) || defined(USE_SOLARIS)
197 const QString ZONE_TAB_FILE
= QLatin1String("/tab/zone_sun.tab");
198 const QString ZONE_INFO_DIR
= QLatin1String("/usr/share/lib/zoneinfo");
200 const QString ZONE_TAB_FILE
= QLatin1String("/zone.tab");
201 const QString ZONE_INFO_DIR
= QLatin1String("/usr/share/zoneinfo");
204 mZoneTabCache
= NoCache
;
206 // Find and open zone.tab - it's all easy except knowing where to look.
207 // Try the LSB location first.
209 QString zoneinfoDir
= ZONE_INFO_DIR
;
210 // make a note if the dir exists; whether it contains zone.tab or not
211 if (dir
.exists(zoneinfoDir
))
213 mZoneinfoDir
= zoneinfoDir
;
214 f
.setFileName(zoneinfoDir
+ ZONE_TAB_FILE
);
215 if (f
.open(QIODevice::ReadOnly
))
217 kDebug(1221) << "Can't open " << f
.fileName();
220 zoneinfoDir
= QLatin1String("/usr/lib/zoneinfo");
221 if (dir
.exists(zoneinfoDir
))
223 mZoneinfoDir
= zoneinfoDir
;
224 f
.setFileName(zoneinfoDir
+ ZONE_TAB_FILE
);
225 if (f
.open(QIODevice::ReadOnly
))
227 kDebug(1221) << "Can't open " << f
.fileName();
230 zoneinfoDir
= ::getenv("TZDIR");
231 if (!zoneinfoDir
.isEmpty() && dir
.exists(zoneinfoDir
))
233 mZoneinfoDir
= zoneinfoDir
;
234 f
.setFileName(zoneinfoDir
+ ZONE_TAB_FILE
);
235 if (f
.open(QIODevice::ReadOnly
))
237 kDebug(1221) << "Can't open " << f
.fileName();
240 zoneinfoDir
= QLatin1String("/usr/share/lib/zoneinfo");
241 if (dir
.exists(zoneinfoDir
+ QLatin1String("/src")))
243 mZoneinfoDir
= zoneinfoDir
;
244 // Solaris support. Synthesise something that looks like a zone.tab,
245 // and cache it between sessions.
247 // grep -h ^Zone /usr/share/lib/zoneinfo/src/* | awk '{print "??\t+9999+99999\t" $2}'
249 // where the country code is set to "??" and the latitude/longitude
250 // values are dummies.
252 QDir
d(mZoneinfoDir
+ QLatin1String("/src"));
253 d
.setFilter(QDir::Files
| QDir::Hidden
| QDir::NoSymLinks
);
254 QStringList fileList
= d
.entryList();
256 mZoneTab
= KStandardDirs::locateLocal("cache", QLatin1String("zone.tab"));
257 f
.setFileName(mZoneTab
);
258 if (!f
.open(QIODevice::WriteOnly
))
260 kError(1221) << "Could not create zone.tab cache" << endl
;
265 QList
<QByteArray
> tokens
;
268 QTextStream
tmpStream(&f
);
270 for (int i
= 0, end
= fileList
.count(); i
< end
; ++i
)
272 zoneFile
.setFileName(d
.filePath(fileList
[i
].toLatin1()));
273 if (!zoneFile
.open(QIODevice::ReadOnly
))
275 kDebug(1221) << "Could not open file '" << zoneFile
.fileName().toLatin1() \
276 << "' for reading." << endl
;
279 while (!zoneFile
.atEnd())
281 if ((r
= zoneFile
.readLine(line
.data(), 1023)) > 0
282 && line
.startsWith("Zone"))
284 line
.replace('\t', ' '); // change tabs to spaces
285 tokens
= line
.split(' ');
286 for (int j
= 0, jend
= tokens
.count(); j
< jend
; ++j
)
287 if (tokens
[j
].endsWith(' '))
289 tmpStream
<< "??\t+9999+99999\t" << tokens
[1] << "\n";
295 if (!f
.open(QIODevice::ReadOnly
))
297 kError(1221) << "Could not reopen zone.tab cache file for reading." << endl
;
300 mZoneTabCache
= Solaris
;
306 // Parse zone.tab and for each time zone, create a KSystemTimeZone instance.
307 // Note that only data needed by this module is specified to KSystemTimeZone.
308 void KTimeZoned::readZoneTab(QFile
&f
)
310 // Parse the already open real or fake zone.tab.
311 QRegExp
lineSeparator("[ \t]");
313 mSource
= new KSystemTimeZoneSource
;
318 QString line
= str
.readLine();
319 if (line
.isEmpty() || line
[0] == '#')
321 QStringList tokens
= KStringHandler::perlSplit(lineSeparator
, line
, 4);
322 int n
= tokens
.count();
325 kError(1221) << "readZoneTab(): invalid record: " << line
<< endl
;
329 // Add entry to list.
330 if (tokens
[0] == "??")
332 else if (!tokens
[0].isEmpty())
333 mHaveCountryCodes
= true;
334 mZones
.add(KSystemTimeZone(mSource
, tokens
[2], tokens
[0]));
339 // Find the local time zone, starting from scratch.
340 void KTimeZoned::findLocalZone()
345 mLocalIdFile
.clear();
346 mLocalZoneDataFile
.clear();
348 // SOLUTION 1: DEFINITIVE.
349 // First try the simplest solution of checking for well-formed TZ setting.
350 const char *envtz
= ::getenv("TZ");
354 if (!mLocalZone
.isEmpty()) kDebug(1221)<<"TZ: "<<mLocalZone
;
357 if (mLocalZone
.isEmpty())
359 // SOLUTION 2: DEFINITIVE.
360 // BSD & Linux support: local time zone id in /etc/timezone.
362 if (!mLocalZone
.isEmpty()) kDebug(1221)<<"/etc/timezone: "<<mLocalZone
;
364 if (mLocalZone
.isEmpty() && !mZoneinfoDir
.isEmpty())
366 // SOLUTION 3: DEFINITIVE.
367 // Try to follow any /etc/localtime symlink to a zoneinfo file.
368 // SOLUTION 4: DEFINITIVE.
369 // Try to match /etc/localtime against the list of zoneinfo files.
370 matchZoneFile(QLatin1String("/etc/localtime"));
371 if (!mLocalZone
.isEmpty()) kDebug(1221)<<"/etc/localtime: "<<mLocalZone
;
373 if (mLocalZone
.isEmpty())
375 // SOLUTION 5: DEFINITIVE.
376 // Solaris support using /etc/default/init.
378 if (!mLocalZone
.isEmpty()) kDebug(1221)<<"/etc/default/init: "<<mLocalZone
;
381 if (!mLocalZone
.isEmpty())
383 // The local time zone is defined by a file.
384 // Watch for changes in the file so as to be notified of any change
385 // in local time zone.
386 mDirWatch
= new KDirWatch(this);
387 mDirWatch
->addFile(mLocalIdFile
);
388 if (!mLocalZoneDataFile
.isEmpty())
389 mDirWatch
->addFile(mLocalZoneDataFile
);
390 connect(mDirWatch
, SIGNAL(dirty(const QString
&)), SLOT(localChanged(const QString
&)));
392 else if (!mZoneinfoDir
.isEmpty())
394 // SOLUTION 6: HEURISTIC.
395 // None of the deterministic stuff above has worked: try a heuristic. We
396 // try to find a pair of matching time zone abbreviations...that way, we'll
397 // likely return a value in the user's own country.
399 QByteArray
tzname0(tzname
[0]); // store copies, because zone.parse() will change them
400 QByteArray
tzname1(tzname
[1]);
401 int bestOffset
= INT_MAX
;
402 KSystemTimeZoneSource::startParseBlock();
403 const KTimeZones::ZoneMap zmap
= mZones
.zones();
404 for (KTimeZones::ZoneMap::ConstIterator it
= zmap
.constBegin(), end
= zmap
.constEnd(); it
!= end
; ++it
)
406 KTimeZone zone
= it
.value();
407 int candidateOffset
= qAbs(zone
.currentOffset(Qt::LocalTime
));
408 if (candidateOffset
< bestOffset
411 QList
<QByteArray
> abbrs
= zone
.abbreviations();
412 if (abbrs
.contains(tzname0
) && abbrs
.contains(tzname1
))
414 // kDebug(1221) << "local=" << zone.name();
415 mLocalZone
= zone
.name();
416 bestOffset
= candidateOffset
;
422 KSystemTimeZoneSource::endParseBlock();
423 if (!mLocalZone
.isEmpty())
424 mLocalMethod
= TzName
;
425 if (!mLocalZone
.isEmpty()) kDebug(1221)<<"tzname: "<<mLocalZone
;
427 if (mLocalZone
.isEmpty())
429 // SOLUTION 7: FAILSAFE.
430 mLocalZone
= KTimeZone::utc().name();
432 if (!mLocalZone
.isEmpty()) kDebug(1221)<<"Failsafe: "<<mLocalZone
;
435 // Finally, if the local zone identity has changed, store
436 // the new one in the config file.
440 // Called when KDirWatch detects a change in zone.tab
441 void KTimeZoned::zonetab_Changed(const QString
& path
)
443 kDebug(1221) << "zone.tab changed";
444 if (path
!= mZoneTab
)
446 kError(1221) << "Wrong path (" << path
<< ") for zone.tab";
449 QDBusMessage message
= QDBusMessage::createSignal("/Daemon", "org.kde.KTimeZoned", "zonetabChanged");
450 QList
<QVariant
> args
;
452 message
.setArguments(args
);
453 QDBusConnection::sessionBus().send(message
);
455 // Reread zone.tab and recreate the collection of KTimeZone instances,
456 // in case any zones have been created or deleted and one of them
457 // subsequently becomes the local zone.
459 f
.setFileName(mZoneTab
);
460 if (!f
.open(QIODevice::ReadOnly
))
461 kError(1221) << "Could not open zone.tab (" << mZoneTab
<< ") to reread";
466 // Called when KDirWatch detects a change
467 void KTimeZoned::localChanged(const QString
& path
)
469 if (path
== mLocalZoneDataFile
)
471 // Only need to update the definition of the local zone,
473 QDBusMessage message
= QDBusMessage::createSignal("/Daemon", "org.kde.KTimeZoned", "zoneDefinitionChanged");
474 QList
<QVariant
> args
;
476 message
.setArguments(args
);
477 QDBusConnection::sessionBus().send(message
);
480 QString oldDataFile
= mLocalZoneDataFile
;
481 switch (mLocalMethod
)
486 const char *envtz
= ::getenv("TZ");
487 if (mSavedTZ
!= envtz
)
489 // TZ has changed - start from scratch again
493 // The contents of the file pointed to by TZ has changed.
495 // Fall through to LocaltimeLink
498 matchZoneFile(mLocalIdFile
);
509 if (oldDataFile
!= mLocalZoneDataFile
)
511 if (!oldDataFile
.isEmpty())
512 mDirWatch
->removeFile(oldDataFile
);
513 if (!mLocalZoneDataFile
.isEmpty())
514 mDirWatch
->addFile(mLocalZoneDataFile
);
519 bool KTimeZoned::checkTZ(const char *envZone
)
521 // SOLUTION 1: DEFINITIVE.
522 // First try the simplest solution of checking for well-formed TZ setting.
525 if (envZone
[0] == '\0')
527 mLocalMethod
= EnvTz
;
528 mLocalZone
= KTimeZone::utc().name();
529 mLocalIdFile
.clear();
530 mLocalZoneDataFile
.clear();
533 if (envZone
[0] == ':')
535 // TZ specifies a file name, either relative to zoneinfo/ or absolute.
536 QString TZfile
= QFile::decodeName(envZone
+ 1);
537 if (TZfile
.startsWith(mZoneinfoDir
))
539 // It's an absolute file name in the zoneinfo directory.
540 // Convert it to a file name relative to zoneinfo/.
541 TZfile
= TZfile
.mid(mZoneinfoDir
.length());
543 if (TZfile
.startsWith(QLatin1Char('/')))
545 // It's an absolute file name.
547 if (matchZoneFile(TZfile
))
549 mLocalMethod
= static_cast<LocalMethod
>(EnvTz
| (mLocalMethod
& TypeMask
));
553 else if (!TZfile
.isEmpty())
555 // It's a file name relative to zoneinfo/
557 if (!mLocalZone
.isEmpty())
559 mLocalMethod
= EnvTz
;
560 mLocalZoneDataFile
= mZoneinfoDir
+ '/' + TZfile
;
561 mLocalIdFile
.clear();
570 bool KTimeZoned::checkTimezone()
572 // SOLUTION 2: DEFINITIVE.
574 kDebug(1221)<<"checkTimezone()";
576 f
.setFileName(QLatin1String("/etc/timezone"));
577 if (!f
.open(QIODevice::ReadOnly
))
579 kDebug(1221)<<"checkTimezone(): /etc/timezone opened";
580 // Read the first line of the file.
582 ts
.setCodec("ISO-8859-1");
585 zoneName
= ts
.readLine();
587 if (!zoneName
.isEmpty())
589 KTimeZone local
= mZones
.zone(zoneName
);
590 kDebug(1221)<<"checkTimezone(): local="<<local
.isValid()<<", name="<<zoneName
;
593 mLocalZone
= zoneName
;
594 mLocalMethod
= Timezone
;
595 mLocalIdFile
= f
.fileName();
596 mLocalZoneDataFile
= mZoneinfoDir
.isEmpty() ? QString() : mZoneinfoDir
+ '/' + zoneName
;
603 bool KTimeZoned::matchZoneFile(const QString
&path
)
605 // SOLUTION 3: DEFINITIVE.
606 // Try to follow any symlink to a zoneinfo file.
607 // Get the path of the file which the symlink points to.
613 // The file is a symlink.
614 QString zoneInfoFileName
= fi
.canonicalFilePath();
615 QFileInfo
fiz(zoneInfoFileName
);
616 if (fiz
.exists() && fiz
.isReadable())
618 if (zoneInfoFileName
.startsWith(mZoneinfoDir
))
620 // We've got the zoneinfo file path.
621 // The time zone name is the part of the path after the zoneinfo directory.
622 QString name
= zoneInfoFileName
.mid(mZoneinfoDir
.length() + 1);
623 // kDebug(1221) << "local=" << name;
624 KTimeZone local
= mZones
.zone(name
);
625 if (!local
.isValid())
631 // It isn't a zoneinfo file or a copy thereof.
632 // Use the absolute path as the time zone name.
633 mLocalZone
= f
.fileName();
635 mLocalMethod
= LocaltimeLink
;
636 mLocalIdFile
= f
.fileName();
637 mLocalZoneDataFile
= zoneInfoFileName
;
641 else if (f
.open(QIODevice::ReadOnly
))
643 // SOLUTION 4: DEFINITIVE.
644 // Try to match the file against the list of zoneinfo files.
646 // Compute the file's MD5 sum.
650 qlonglong referenceSize
= f
.size();
651 QString referenceMd5Sum
= context
.hexDigest();
652 MD5Map::ConstIterator it5
, end5
;
656 if (!mConfigLocalZone
.isEmpty())
658 // We know the local zone from last time.
659 // Check whether the file still matches it.
660 KTimeZone tzone
= mZones
.zone(mConfigLocalZone
);
663 local
= compareChecksum(tzone
, referenceMd5Sum
, referenceSize
);
665 zoneName
= local
.name();
669 if (!local
.isValid() && mHaveCountryCodes
)
671 /* Look for time zones with the user's country code.
672 * This has two advantages: 1) it shortens the search;
673 * 2) it increases the chance of the correctly titled time zone
674 * being found, since multiple time zones can have identical
675 * definitions. For example, Europe/Guernsey is identical to
676 * Europe/London, but the latter is more likely to be the right
677 * zone name for a user with 'gb' country code.
679 QString country
= KGlobal::locale()->country().toUpper();
680 const KTimeZones::ZoneMap zmap
= mZones
.zones();
681 for (KTimeZones::ZoneMap::ConstIterator zit
= zmap
.constBegin(), zend
= zmap
.constEnd(); zit
!= zend
; ++zit
)
683 KTimeZone tzone
= zit
.value();
684 if (tzone
.countryCode() == country
)
686 local
= compareChecksum(tzone
, referenceMd5Sum
, referenceSize
);
689 zoneName
= local
.name();
696 if (!local
.isValid())
698 // Look for a checksum match with the cached checksum values
699 MD5Map oldChecksums
= mMd5Sums
; // save a copy of the existing checksums
700 for (it5
= mMd5Sums
.constBegin(), end5
= mMd5Sums
.constEnd(); it5
!= end5
; ++it5
)
702 if (it5
.value() == referenceMd5Sum
)
704 // The cached checksum matches. Ensure that the file hasn't changed.
705 if (compareChecksum(it5
, referenceMd5Sum
, referenceSize
))
707 zoneName
= it5
.key();
708 local
= mZones
.zone(zoneName
);
712 oldChecksums
.clear(); // the cache has been cleared
717 if (!local
.isValid())
719 // The checksum didn't match any in the cache.
720 // Continue building missing entries in the cache on the assumption that
721 // we haven't previously looked at the zoneinfo file which matches.
722 const KTimeZones::ZoneMap zmap
= mZones
.zones();
723 for (KTimeZones::ZoneMap::ConstIterator zit
= zmap
.constBegin(), zend
= zmap
.constEnd(); zit
!= zend
; ++zit
)
725 KTimeZone zone
= zit
.value();
726 zoneName
= zone
.name();
727 if (!mMd5Sums
.contains(zoneName
))
729 QString candidateMd5Sum
= calcChecksum(zoneName
, referenceSize
);
730 if (candidateMd5Sum
== referenceMd5Sum
)
732 // kDebug(1221) << "local=" << zone.name();
740 if (!local
.isValid())
742 // Didn't find the file, so presumably a previously cached checksum must
743 // have changed. Delete all the old checksums.
744 MD5Map::ConstIterator mit
;
745 MD5Map::ConstIterator mend
= oldChecksums
.constEnd();
746 for (mit
= oldChecksums
.constBegin(); mit
!= mend
; ++mit
)
747 mMd5Sums
.remove(mit
.key());
749 // And recalculate the old checksums
750 for (mit
= oldChecksums
.constBegin(); mit
!= mend
; ++mit
)
752 zoneName
= mit
.key();
753 QString candidateMd5Sum
= calcChecksum(zoneName
, referenceSize
);
754 if (candidateMd5Sum
== referenceMd5Sum
)
756 // kDebug(1221) << "local=" << zoneName;
757 local
= mZones
.zone(zoneName
);
763 bool success
= false;
766 // The file matches a zoneinfo file
767 mLocalZone
= zoneName
;
768 mLocalZoneDataFile
= mZoneinfoDir
+ '/' + zoneName
;
773 // The file doesn't match a zoneinfo file. If it's a TZfile, use it directly.
774 // Read the file type identifier.
778 if (str
.readRawData(buff
, 4) == 4
779 && buff
[0] == 'T' && buff
[1] == 'Z' && buff
[2] == 'i' && buff
[3] == 'f')
781 // Use its absolute path as the zone name.
782 mLocalZone
= f
.fileName();
783 mLocalZoneDataFile
.clear();
790 mLocalMethod
= LocaltimeCopy
;
791 mLocalIdFile
= f
.fileName();
798 bool KTimeZoned::checkDefaultInit()
800 // SOLUTION 5: DEFINITIVE.
801 // Solaris support using /etc/default/init.
803 f
.setFileName(QLatin1String("/etc/default/init"));
804 if (!f
.open(QIODevice::ReadOnly
))
806 // Read the last line starting "TZ=".
809 ts
.setCodec("ISO-8859-1");
812 zoneName
= ts
.readLine();
813 if (zoneName
.startsWith("TZ="))
815 zoneName
= zoneName
.mid(3);
816 // kDebug(1221) << "local=" << zoneName;
821 KTimeZone local
= mZones
.zone(zoneName
);
822 if (!local
.isValid())
824 mLocalZone
= zoneName
;
825 mLocalMethod
= DefaultInit
;
826 mLocalIdFile
= f
.fileName();
827 mLocalZoneDataFile
= mZoneinfoDir
.isEmpty() ? QString() : mZoneinfoDir
+ '/' + zoneName
;
831 // Check whether the checksum for a time zone matches a given saved checksum.
832 KTimeZone
KTimeZoned::compareChecksum(const KTimeZone
&zone
, const QString
&referenceMd5Sum
, qlonglong size
)
834 MD5Map::ConstIterator it5
= mMd5Sums
.constFind(zone
.name());
835 if (it5
== mMd5Sums
.constEnd())
837 // No checksum has been computed yet for this zone file.
839 QString candidateMd5Sum
= calcChecksum(zone
.name(), size
);
840 if (candidateMd5Sum
== referenceMd5Sum
)
842 // kDebug(1221) << "local=" << zone.name();
847 if (it5
.value() == referenceMd5Sum
)
849 // The cached checksum matches. Ensure that the file hasn't changed.
850 if (compareChecksum(it5
, referenceMd5Sum
, size
))
851 return mZones
.zone(it5
.key());
856 // Check whether a checksum matches a given saved checksum.
857 // Returns false if the file no longer matches and cache was cleared.
858 bool KTimeZoned::compareChecksum(MD5Map::ConstIterator it5
, const QString
&referenceMd5Sum
, qlonglong size
)
860 // The cached checksum matches. Ensure that the file hasn't changed.
861 QString zoneName
= it5
.key();
862 QString candidateMd5Sum
= calcChecksum(zoneName
, size
);
863 if (candidateMd5Sum
.isNull())
864 mMd5Sums
.remove(zoneName
); // no match - wrong file size
865 else if (candidateMd5Sum
== referenceMd5Sum
)
868 // File(s) have changed, so clear the cache
870 mMd5Sums
[zoneName
] = candidateMd5Sum
; // reinsert the newly calculated checksum
874 // Calculate the MD5 checksum for the given zone file, provided that its size matches.
875 // The calculated checksum is cached.
876 QString
KTimeZoned::calcChecksum(const QString
&zoneName
, qlonglong size
)
878 QString path
= mZoneinfoDir
+ '/' + zoneName
;
880 if (static_cast<qlonglong
>(fi
.size()) == size
)
882 // Only do the heavy lifting for file sizes which match.
885 if (f
.open(QIODevice::ReadOnly
))
890 QString candidateMd5Sum
= context
.hexDigest();
892 mMd5Sums
[zoneName
] = candidateMd5Sum
; // cache the new checksum
893 return candidateMd5Sum
;