const KstTimezones::ZoneMap KstTimezones::allZones()
{
    // Have we already done all the hard work? If not, create the cache.
    if (m_zones)
        return *m_zones;
    m_zones = new ZoneMap();

    // Go read the database.
    //
    // On Windows, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
    // is the place to look. The TZI binary value is the TIME_ZONE_INFORMATION structure.
    //
    // For Unix its all easy except knowing where to look. Try the LSB location first.
    QFile f;
    m_zoneinfoDir = "/usr/share/zoneinfo";
    f.setName(m_zoneinfoDir + "/zone.tab");
    if (!f.open(IO_ReadOnly))
    {
        m_zoneinfoDir = "/usr/lib/zoneinfo";
        f.setName(m_zoneinfoDir + "/zone.tab");
        if (!f.open(IO_ReadOnly))
        {
            m_zoneinfoDir = ::getenv("TZDIR");
            f.setName(m_zoneinfoDir + "/zone.tab");
            if (m_zoneinfoDir.isEmpty() || !f.open(IO_ReadOnly))
            {
                // Solaris support. Synthesise something that looks like a zone.tab.
                //
                // /bin/grep -h ^Zone /usr/share/lib/zoneinfo/src/* | /bin/awk '{print "??\t+9999+99999\t" $2}'
                //
                // where the country code is set to "??" and the lattitude/longitude
                // values are dummies.
                m_zoneinfoDir = "/usr/share/lib/zoneinfo";
                KTempFile temp;
                KShellProcess reader;
                reader << "/bin/grep" << "-h" << "^Zone" << m_zoneinfoDir << "/src/*" << temp.name() << "|" <<
                    "/bin/awk" << "'{print \"??\\t+9999+99999\\t\" $2}'";
                // Note the use of blocking here...it is a trivial amount of data!
                temp.close();
                reader.start(KProcess::Block);
                f.setName(temp.name());
                if (!temp.status() || !f.open(IO_ReadOnly))
                {
                    return *m_zones;
                }
            }
        }
    }

    // Parse the zone.tab.
    QTextStream str(&f);
    QRegExp lineSeparator("[ \t]");
    QRegExp ordinateSeparator("[+-]");
    KSharedPtr<KstTimezoneSource> db(new KstTimezoneSource(m_zoneinfoDir));
    while (!str.atEnd())
    {
        QString line = str.readLine();
        if (line.isEmpty() || '#' == line[0])
            continue;
        QStringList tokens = KStringHandler::perlSplit(lineSeparator, line, 4);
        if (tokens.count() < 3)
        {
            continue;
        }

        // Got three tokens. Now check for two ordinates plus first one is "".
        QStringList ordinates = KStringHandler::perlSplit(ordinateSeparator, tokens[1], 2);
        if (ordinates.count() < 2)
        {
            continue;
        }

        float latitude = convertCoordinate(ordinates[1]);
        float longitude = convertCoordinate(ordinates[2]);

        // Add entry to list.
        if (tokens[0] == "??")
            tokens[0] = "";
        KstTimezone *timezone = new KstTimezone(db, tokens[2], tokens[0], latitude, longitude, tokens[3]);
        add(timezone);
    }
    f.close();
    return *m_zones;
}
Example #2
0
/*
 * 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);
    QRegExp lineSeparator("[ \t]");
    QRegExp ordinateSeparator("[+-]");
    if (!m_source)
        m_source = new KSystemTimeZoneSource;
    while (!str.atEnd())
    {
        QString line = str.readLine();
        if (line.isEmpty() || line[0] == '#')
            continue;
        QStringList tokens = KStringHandler::perlSplit(lineSeparator, line, 4);
        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 "".
        int i = tokens[1].indexOf(ordinateSeparator, 1);
        if (i < 0)
        {
            kError(161) << "readZoneTab() " << tokens[2] << ": invalid coordinates: " << tokens[1] << endl;
            continue;
        }

        float latitude = convertCoordinate(tokens[1].left(i));
        float longitude = convertCoordinate(tokens[1].mid(i));

        // Add entry to list.
        if (tokens[0] == "??")
            tokens[0] = "";
        // Solaris sets the empty Comments field to '-', making it not empty.
        // Clean it up.
        if (n > 3  &&  tokens[3] == "-")
            tokens[3] = "";
        KSystemTimeZone tz(m_source, 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());
        }
    }
}