ktimezones.cpp
00001 /* 00002 This file is part of the KDE libraries 00003 Copyright (c) 2005 S.R.Haque <srhaque@iee.org>. 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include <config.h> 00021 00022 #include <ktimezones.h> 00023 #include <kdebug.h> 00024 #include <kmdcodec.h> 00025 #include <kprocess.h> 00026 #include <kstringhandler.h> 00027 #include <ktempfile.h> 00028 00029 #include <qdatetime.h> 00030 #include <qfile.h> 00031 #include <qregexp.h> 00032 #include <qstringlist.h> 00033 #include <qtextstream.h> 00034 00035 #include <cerrno> 00036 #include <climits> 00037 #include <cstdlib> 00038 #include <cstring> 00039 #include <ctime> 00040 00041 #define UTC_ZONE "UTC" 00042 00051 class AbbreviationsMatch : 00052 public KTimezoneDetails 00053 { 00054 public: 00055 AbbreviationsMatch(const QString &stdZone, const QString &dstZone = "") 00056 { 00057 m_stdZone = stdZone; 00058 m_dstZone = dstZone; 00059 } 00060 00061 void parseStarted() 00062 { 00063 m_foundStd = false; 00064 m_foundDst = m_dstZone.isEmpty(); 00065 } 00066 00067 bool test() 00068 { 00069 return (m_foundStd && m_foundDst); 00070 } 00071 00072 private: 00073 bool m_foundStd; 00074 bool m_foundDst; 00075 QString m_stdZone; 00076 QString m_dstZone; 00077 00078 virtual void gotAbbreviation(int /*index*/, const QString &value) 00079 { 00080 if (m_stdZone == value) 00081 { 00082 m_foundStd = true; 00083 } 00084 if (m_dstZone == value) 00085 { 00086 m_foundDst = true; 00087 } 00088 } 00089 }; 00090 00094 class DummySource : 00095 public KTimezoneSource 00096 { 00097 public: 00098 DummySource() : 00099 KTimezoneSource("") 00100 { 00101 } 00102 00103 virtual bool parse(const QString &/*zone*/, KTimezoneDetails &/*dataReceiver*/) const 00104 { 00105 return true; 00106 } 00107 }; 00108 00112 class OffsetFind : 00113 public KTimezoneDetails 00114 { 00115 public: 00116 OffsetFind(unsigned dateTime) 00117 { 00118 m_dateTime = dateTime; 00119 } 00120 00121 void parseStarted() 00122 { 00123 m_transitionTimeIndex = 0; 00124 m_localTimeIndex = -1; 00125 m_abbrIndex = -1; 00126 m_offset = 0; 00127 m_isDst = false; 00128 m_abbr = UTC_ZONE; 00129 } 00130 00131 int offset() 00132 { 00133 return m_offset; 00134 } 00135 00136 bool isDst() 00137 { 00138 return m_isDst; 00139 } 00140 00141 QString abbreviation() 00142 { 00143 return m_abbr; 00144 } 00145 00146 private: 00147 unsigned m_dateTime; 00148 int m_transitionTimeIndex; 00149 int m_localTimeIndex; 00150 int m_abbrIndex; 00151 int m_offset; 00152 bool m_isDst; 00153 QString m_abbr; 00154 00155 virtual void gotTransitionTime(int index, unsigned transitionTime) 00156 { 00157 if (transitionTime <= m_dateTime) 00158 { 00159 // Remember the index of the transition time that relates to dateTime. 00160 m_transitionTimeIndex = index; 00161 } 00162 } 00163 00164 virtual void gotLocalTimeIndex(int index, unsigned localTimeIndex) 00165 { 00166 if (index == m_transitionTimeIndex) 00167 { 00168 // Remember the index of the local time that relates to dateTime. 00169 m_localTimeIndex = localTimeIndex; 00170 } 00171 } 00172 00173 virtual void gotLocalTime(int index, int gmtOff, bool isDst, unsigned abbrInd) 00174 { 00175 if (index == m_localTimeIndex) 00176 { 00177 // Remember the results that relate to gmtOffset. 00178 m_offset = gmtOff; 00179 m_isDst = isDst; 00180 m_abbrIndex = abbrInd; 00181 } 00182 } 00183 00184 virtual void gotAbbreviation(int index, const QString &value) 00185 { 00186 if (index == m_abbrIndex) 00187 { 00188 m_abbr = value; 00189 } 00190 } 00191 }; 00192 00193 const float KTimezone::UNKNOWN = 1000.0; 00194 00195 bool KTimezone::isValidLatitude(float latitude) 00196 { 00197 return (latitude >= -90.0) && (latitude <= 90.0); 00198 } 00199 00200 bool KTimezone::isValidLongitude(float longitude) 00201 { 00202 return (longitude >= -180.0) && (longitude <= 180.0); 00203 } 00204 00205 KTimezone::KTimezone( 00206 KSharedPtr<KTimezoneSource> db, const QString& name, 00207 const QString &countryCode, float latitude, float longitude, 00208 const QString &comment) : 00209 m_db(db), 00210 m_name(name), 00211 m_countryCode(countryCode), 00212 m_latitude(latitude), 00213 m_longitude(longitude), 00214 m_comment(comment), 00215 d(0) 00216 { 00217 // Detect duff values. 00218 if (m_latitude * m_latitude > 90 * 90) 00219 m_latitude = UNKNOWN; 00220 if (m_longitude * m_longitude > 180 * 180) 00221 m_longitude = UNKNOWN; 00222 } 00223 00224 KTimezone::~KTimezone() 00225 { 00226 // FIXME when needed: 00227 // delete d; 00228 } 00229 00230 QString KTimezone::comment() const 00231 { 00232 return m_comment; 00233 } 00234 00235 QDateTime KTimezone::convert(const KTimezone *newZone, const QDateTime &dateTime) const 00236 { 00237 char *originalZone = ::getenv("TZ"); 00238 00239 // Convert the given localtime to UTC. 00240 ::setenv("TZ", m_name.utf8(), 1); 00241 tzset(); 00242 unsigned utc = dateTime.toTime_t(); 00243 00244 // Set the timezone and convert UTC to localtime. 00245 ::setenv("TZ", newZone->name().utf8(), 1); 00246 tzset(); 00247 QDateTime remoteTime; 00248 remoteTime.setTime_t(utc, Qt::LocalTime); 00249 00250 // Now restore things 00251 if (!originalZone) 00252 { 00253 ::unsetenv("TZ"); 00254 } 00255 else 00256 { 00257 ::setenv("TZ", originalZone, 1); 00258 } 00259 tzset(); 00260 return remoteTime; 00261 } 00262 00263 QString KTimezone::countryCode() const 00264 { 00265 return m_countryCode; 00266 } 00267 00268 float KTimezone::latitude() const 00269 { 00270 return m_latitude; 00271 } 00272 00273 float KTimezone::longitude() const 00274 { 00275 return m_longitude; 00276 } 00277 00278 QString KTimezone::name() const 00279 { 00280 return m_name; 00281 } 00282 00283 int KTimezone::offset(Qt::TimeSpec basisSpec) const 00284 { 00285 char *originalZone = ::getenv("TZ"); 00286 00287 // Get the time in the current timezone. 00288 QDateTime basisTime = QDateTime::currentDateTime(basisSpec); 00289 00290 // Set the timezone and find out what time it is there compared to the basis. 00291 ::setenv("TZ", m_name.utf8(), 1); 00292 tzset(); 00293 QDateTime remoteTime = QDateTime::currentDateTime(Qt::LocalTime); 00294 int offset = remoteTime.secsTo(basisTime); 00295 00296 // Now restore things 00297 if (!originalZone) 00298 { 00299 ::unsetenv("TZ"); 00300 } 00301 else 00302 { 00303 ::setenv("TZ", originalZone, 1); 00304 } 00305 tzset(); 00306 return offset; 00307 } 00308 00309 int KTimezone::offset(const QDateTime &dateTime) const 00310 { 00311 OffsetFind finder(dateTime.toTime_t()); 00312 int result = 0; 00313 if (parse(finder)) 00314 { 00315 result = finder.offset(); 00316 } 00317 return result; 00318 } 00319 00320 bool KTimezone::parse(KTimezoneDetails &dataReceiver) const 00321 { 00322 dataReceiver.parseStarted(); 00323 bool result = m_db->parse(m_name, dataReceiver); 00324 dataReceiver.parseEnded(); 00325 return result; 00326 } 00327 00328 KTimezones::KTimezones() : 00329 m_zoneinfoDir(), 00330 m_zones(0), 00331 d(0) 00332 { 00333 // Create the database (and resolve m_zoneinfoDir!). 00334 allZones(); 00335 m_UTC = new KTimezone(new DummySource(), UTC_ZONE); 00336 add(m_UTC); 00337 } 00338 00339 KTimezones::~KTimezones() 00340 { 00341 // FIXME when needed: 00342 // delete d; 00343 00344 // Autodelete behavior. 00345 if (m_zones) 00346 { 00347 for (ZoneMap::ConstIterator it = m_zones->begin(); it != m_zones->end(); ++it) 00348 { 00349 delete it.data(); 00350 } 00351 } 00352 delete m_zones; 00353 } 00354 00355 void KTimezones::add(KTimezone *zone) 00356 { 00357 m_zones->insert(zone->name(), zone); 00358 } 00359 00360 const KTimezones::ZoneMap KTimezones::allZones() 00361 { 00362 // Have we already done all the hard work? If not, create the cache. 00363 if (m_zones) 00364 return *m_zones; 00365 m_zones = new ZoneMap(); 00366 00367 // Go read the database. 00368 // 00369 // On Windows, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones 00370 // is the place to look. The TZI binary value is the TIME_ZONE_INFORMATION structure. 00371 // 00372 // For Unix its all easy except knowing where to look. Try the LSB location first. 00373 QFile f; 00374 m_zoneinfoDir = "/usr/share/zoneinfo"; 00375 f.setName(m_zoneinfoDir + "/zone.tab"); 00376 if (!f.open(IO_ReadOnly)) 00377 { 00378 kdDebug() << "Can't open " << f.name() << endl; 00379 m_zoneinfoDir = "/usr/lib/zoneinfo"; 00380 f.setName(m_zoneinfoDir + "/zone.tab"); 00381 if (!f.open(IO_ReadOnly)) 00382 { 00383 kdDebug() << "Can't open " << f.name() << endl; 00384 m_zoneinfoDir = ::getenv("TZDIR"); 00385 f.setName(m_zoneinfoDir + "/zone.tab"); 00386 if (m_zoneinfoDir.isEmpty() || !f.open(IO_ReadOnly)) 00387 { 00388 kdDebug() << "Can't open " << f.name() << endl; 00389 00390 // Solaris support. Synthesise something that looks like a zone.tab. 00391 // 00392 // /bin/grep -h ^Zone /usr/share/lib/zoneinfo/src/* | /bin/awk '{print "??\t+9999+99999\t" $2}' 00393 // 00394 // where the country code is set to "??" and the lattitude/longitude 00395 // values are dummies. 00396 m_zoneinfoDir = "/usr/share/lib/zoneinfo"; 00397 KTempFile temp; 00398 KShellProcess reader; 00399 reader << "/bin/grep" << "-h" << "^Zone" << m_zoneinfoDir << "/src/*" << temp.name() << "|" << 00400 "/bin/awk" << "'{print \"??\\t+9999+99999\\t\" $2}'"; 00401 // Note the use of blocking here...it is a trivial amount of data! 00402 temp.close(); 00403 reader.start(KProcess::Block); 00404 f.setName(temp.name()); 00405 if (!temp.status() || !f.open(IO_ReadOnly)) 00406 { 00407 kdDebug() << "Can't open " << f.name() << endl; 00408 return *m_zones; 00409 } 00410 } 00411 } 00412 } 00413 00414 // Parse the zone.tab. 00415 QTextStream str(&f); 00416 QRegExp lineSeparator("[ \t]"); 00417 QRegExp ordinateSeparator("[+-]"); 00418 KSharedPtr<KTimezoneSource> db(new KTimezoneSource(m_zoneinfoDir)); 00419 while (!str.atEnd()) 00420 { 00421 QString line = str.readLine(); 00422 if (line.isEmpty() || '#' == line[0]) 00423 continue; 00424 QStringList tokens = KStringHandler::perlSplit(lineSeparator, line, 4); 00425 if (tokens.count() < 3) 00426 { 00427 kdError() << "invalid record: " << line << endl; 00428 continue; 00429 } 00430 00431 // Got three tokens. Now check for two ordinates plus first one is "". 00432 QStringList ordinates = KStringHandler::perlSplit(ordinateSeparator, tokens[1], 2); 00433 if (ordinates.count() < 2) 00434 { 00435 kdError() << "invalid coordinates: " << tokens[1] << endl; 00436 continue; 00437 } 00438 00439 float latitude = convertCoordinate(ordinates[1]); 00440 float longitude = convertCoordinate(ordinates[2]); 00441 00442 // Add entry to list. 00443 if (tokens[0] == "??") 00444 tokens[0] = ""; 00445 KTimezone *timezone = new KTimezone(db, tokens[2], tokens[0], latitude, longitude, tokens[3]); 00446 add(timezone); 00447 } 00448 f.close(); 00449 return *m_zones; 00450 } 00451 00455 float KTimezones::convertCoordinate(const QString &coordinate) 00456 { 00457 int value = coordinate.toInt(); 00458 int degrees = 0; 00459 int minutes = 0; 00460 int seconds = 0; 00461 00462 if (coordinate.length() > 11) 00463 { 00464 degrees = value / 10000; 00465 value -= degrees * 10000; 00466 minutes = value / 100; 00467 value -= minutes * 100; 00468 seconds = value; 00469 } 00470 else 00471 { 00472 degrees = value / 100; 00473 value -= degrees * 100; 00474 minutes = value; 00475 } 00476 value = degrees * 3600 + minutes * 60 + seconds; 00477 return value / 3600.0; 00478 } 00479 00480 const KTimezone *KTimezones::local() 00481 { 00482 const KTimezone *local = 0; 00483 00484 // First try the simplest solution of checking for well-formed TZ setting. 00485 char *envZone = ::getenv("TZ"); 00486 if (envZone) 00487 { 00488 if (envZone[0] == '\0') 00489 { 00490 return m_UTC; 00491 } 00492 else 00493 if (envZone[0] == ':') 00494 { 00495 envZone++; 00496 } 00497 local = zone(envZone); 00498 } 00499 if (local) 00500 return local; 00501 00502 // Try to match /etc/localtime against the list of zoneinfo files. 00503 QFile f; 00504 f.setName("/etc/localtime"); 00505 if (f.open(IO_ReadOnly)) 00506 { 00507 // Compute the MD5 sum of /etc/localtime. 00508 KMD5 context(""); 00509 context.reset(); 00510 context.update(f); 00511 QIODevice::Offset referenceSize = f.size(); 00512 QString referenceMd5Sum = context.hexDigest(); 00513 f.close(); 00514 if (!m_zoneinfoDir.isEmpty()) 00515 { 00516 // Compare it with each zoneinfo file. 00517 for (ZoneMap::Iterator it = m_zones->begin(); it != m_zones->end(); ++it) 00518 { 00519 KTimezone *zone = it.data(); 00520 f.setName(m_zoneinfoDir + '/' + zone->name()); 00521 if (f.open(IO_ReadOnly)) 00522 { 00523 QIODevice::Offset candidateSize = f.size(); 00524 QString candidateMd5Sum; 00525 if (candidateSize == referenceSize) 00526 { 00527 // Only do the heavy lifting for file sizes which match. 00528 context.reset(); 00529 context.update(f); 00530 candidateMd5Sum = context.hexDigest(); 00531 } 00532 f.close(); 00533 if (candidateMd5Sum == referenceMd5Sum) 00534 { 00535 // kdError() << "local=" << zone->name() << endl; 00536 local = zone; 00537 break; 00538 } 00539 } 00540 } 00541 } 00542 } 00543 if (local) 00544 return local; 00545 00546 // BSD support. 00547 QString fileZone; 00548 f.setName("/etc/timezone"); 00549 if (!f.open(IO_ReadOnly)) 00550 { 00551 kdDebug() << "Can't open " << f.name() << endl; 00552 00553 // Solaris support using /etc/default/init. 00554 f.setName("/etc/default/init"); 00555 if (!f.open(IO_ReadOnly)) 00556 { 00557 kdDebug() << "Can't open " << f.name() << endl; 00558 } 00559 else 00560 { 00561 QTextStream ts(&f); 00562 ts.setEncoding(QTextStream::Latin1); 00563 00564 // Read the last line starting "TZ=". 00565 while (!ts.atEnd()) 00566 { 00567 fileZone = ts.readLine(); 00568 if (fileZone.startsWith("TZ=")) 00569 { 00570 fileZone = fileZone.mid(3); 00571 00572 // kdError() << "local=" << fileZone << endl; 00573 local = zone(fileZone); 00574 } 00575 } 00576 f.close(); 00577 } 00578 } 00579 else 00580 { 00581 QTextStream ts(&f); 00582 ts.setEncoding(QTextStream::Latin1); 00583 00584 // Read the first line. 00585 if (!ts.atEnd()) 00586 { 00587 fileZone = ts.readLine(); 00588 00589 // kdError() << "local=" << fileZone << endl; 00590 local = zone(fileZone); 00591 } 00592 f.close(); 00593 } 00594 if (local) 00595 return local; 00596 00597 // None of the deterministic stuff above has worked: try a heuristic. We 00598 // try to find a pair of matching timezone abbreviations...that way, we'll 00599 // likely return a value in the user's own country. 00600 if (!m_zoneinfoDir.isEmpty()) 00601 { 00602 tzset(); 00603 AbbreviationsMatch matcher(tzname[0], tzname[1]); 00604 int bestOffset = INT_MAX; 00605 for (ZoneMap::Iterator it = m_zones->begin(); it != m_zones->end(); ++it) 00606 { 00607 KTimezone *zone = it.data(); 00608 int candidateOffset = QABS(zone->offset(Qt::LocalTime)); 00609 if (zone->parse(matcher) && matcher.test() && (candidateOffset < bestOffset)) 00610 { 00611 // kdError() << "local=" << zone->name() << endl; 00612 bestOffset = candidateOffset; 00613 local = zone; 00614 } 00615 } 00616 } 00617 if (local) 00618 return local; 00619 return m_UTC; 00620 } 00621 00622 const KTimezone *KTimezones::zone(const QString &name) 00623 { 00624 if (name.isEmpty()) 00625 return m_UTC; 00626 ZoneMap::ConstIterator it = m_zones->find(name); 00627 if (it != m_zones->end()) 00628 return it.data(); 00629 00630 // Error. 00631 return 0; 00632 } 00633 00634 KTimezoneDetails::KTimezoneDetails() 00635 { 00636 } 00637 00638 KTimezoneDetails::~KTimezoneDetails() 00639 { 00640 } 00641 00642 void KTimezoneDetails::gotAbbreviation(int /*index*/, const QString &) 00643 {} 00644 00645 void KTimezoneDetails::gotHeader( 00646 unsigned, unsigned, unsigned, 00647 unsigned, unsigned, unsigned) 00648 {} 00649 00650 void KTimezoneDetails::gotLeapAdjustment(int /*index*/, unsigned, unsigned) 00651 {} 00652 00653 void KTimezoneDetails::gotLocalTime(int /*index*/, int, bool, unsigned) 00654 {} 00655 00656 void KTimezoneDetails::gotLocalTimeIndex(int /*index*/, unsigned) 00657 {} 00658 00659 void KTimezoneDetails::gotIsStandard(int /*index*/, bool) 00660 {} 00661 00662 void KTimezoneDetails::gotTransitionTime(int /*index*/, unsigned) 00663 {} 00664 00665 void KTimezoneDetails::gotIsUTC(int /*index*/, bool) 00666 {} 00667 00668 void KTimezoneDetails::parseEnded() 00669 {} 00670 00671 void KTimezoneDetails::parseStarted() 00672 {} 00673 00674 KTimezoneSource::KTimezoneSource(const QString &db) : 00675 m_db(db) 00676 { 00677 } 00678 00679 KTimezoneSource::~KTimezoneSource() 00680 { 00681 } 00682 00683 QString KTimezoneSource::db() 00684 { 00685 return m_db; 00686 } 00687 00688 bool KTimezoneSource::parse(const QString &zone, KTimezoneDetails &dataReceiver) const 00689 { 00690 QFile f(m_db + '/' + zone); 00691 if (!f.open(IO_ReadOnly)) 00692 { 00693 kdError() << "Cannot open " << f.name() << endl; 00694 return false; 00695 } 00696 00697 // Structures that represent the zoneinfo file. 00698 Q_UINT8 T, z, i_, f_; 00699 struct 00700 { 00701 Q_UINT32 ttisgmtcnt; 00702 Q_UINT32 ttisstdcnt; 00703 Q_UINT32 leapcnt; 00704 Q_UINT32 timecnt; 00705 Q_UINT32 typecnt; 00706 Q_UINT32 charcnt; 00707 } tzh; 00708 Q_UINT32 transitionTime; 00709 Q_UINT8 localTimeIndex; 00710 struct 00711 { 00712 Q_INT32 gmtoff; 00713 Q_INT8 isdst; 00714 Q_UINT8 abbrind; 00715 } tt; 00716 Q_UINT32 leapTime; 00717 Q_UINT32 leapSeconds; 00718 Q_UINT8 isStandard; 00719 Q_UINT8 isUTC; 00720 00721 QDataStream str(&f); 00722 str >> T >> z >> i_ >> f_; 00723 // kdError() << "signature: " << QChar(T) << QChar(z) << QChar(i_) << QChar(f_) << endl; 00724 unsigned i; 00725 for (i = 0; i < 4; i++) 00726 str >> tzh.ttisgmtcnt; 00727 str >> tzh.ttisgmtcnt >> tzh.ttisstdcnt >> tzh.leapcnt >> tzh.timecnt >> tzh.typecnt >> tzh.charcnt; 00728 // kdError() << "header: " << tzh.ttisgmtcnt << ", " << tzh.ttisstdcnt << ", " << tzh.leapcnt << ", " << 00729 // tzh.timecnt << ", " << tzh.typecnt << ", " << tzh.charcnt << endl; 00730 dataReceiver.gotHeader(tzh.ttisgmtcnt, tzh.ttisstdcnt, tzh.leapcnt, tzh.timecnt, tzh.typecnt, tzh.charcnt); 00731 for (i = 0; i < tzh.timecnt; i++) 00732 { 00733 str >> transitionTime; 00734 dataReceiver.gotTransitionTime(i, transitionTime); 00735 } 00736 for (i = 0; i < tzh.timecnt; i++) 00737 { 00738 // NB: these appear to be 1-based, not zero-based! 00739 str >> localTimeIndex; 00740 dataReceiver.gotLocalTimeIndex(i, localTimeIndex); 00741 } 00742 for (i = 0; i < tzh.typecnt; i++) 00743 { 00744 str >> tt.gmtoff >> tt.isdst >> tt.abbrind; 00745 // kdError() << "local type: " << tt.gmtoff << ", " << tt.isdst << ", " << tt.abbrind << endl; 00746 dataReceiver.gotLocalTime(i, tt.gmtoff, (tt.isdst != 0), tt.abbrind); 00747 } 00748 00749 // Make sure we don't run foul of maliciously coded timezone abbreviations. 00750 if (tzh.charcnt > 64) 00751 { 00752 kdError() << "excessive length for timezone abbreviations: " << tzh.charcnt << endl; 00753 return false; 00754 } 00755 QByteArray array(tzh.charcnt); 00756 str.readRawBytes(array.data(), array.size()); 00757 char *abbrs = array.data(); 00758 if (abbrs[tzh.charcnt - 1] != 0) 00759 { 00760 // These abbrevations are corrupt! 00761 kdError() << "timezone abbreviations not terminated: " << abbrs[tzh.charcnt - 1] << endl; 00762 return false; 00763 } 00764 char *abbr = abbrs; 00765 while (abbr < abbrs + tzh.charcnt) 00766 { 00767 // kdError() << "abbr: " << abbr << endl; 00768 dataReceiver.gotAbbreviation((abbr - abbrs), abbr); 00769 abbr += strlen(abbr) + 1; 00770 } 00771 for (i = 0; i < tzh.leapcnt; i++) 00772 { 00773 str >> leapTime >> leapSeconds; 00774 // kdError() << "leap entry: " << leapTime << ", " << leapSeconds << endl; 00775 dataReceiver.gotLeapAdjustment(i, leapTime, leapSeconds); 00776 } 00777 for (i = 0; i < tzh.ttisstdcnt; i++) 00778 { 00779 str >> isStandard; 00780 // kdError() << "standard: " << isStandard << endl; 00781 dataReceiver.gotIsStandard(i, (isStandard != 0)); 00782 } 00783 for (i = 0; i < tzh.ttisgmtcnt; i++) 00784 { 00785 str >> isUTC; 00786 // kdError() << "UTC: " << isUTC << endl; 00787 dataReceiver.gotIsUTC(i, (isUTC != 0)); 00788 } 00789 return true; 00790 }