File indexing completed on 2024-11-24 04:45:05
0001 /* 0002 SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "timezones.h" 0008 0009 #include <QDebug> 0010 #include <QFile> 0011 0012 #include <cassert> 0013 #include <limits> 0014 0015 using namespace KItinerary::Generator; 0016 0017 Timezones::Timezones() 0018 { 0019 // load zone.tab for country mapping 0020 QFile zoneTab(QStringLiteral("/usr/share/zoneinfo/zone.tab")); 0021 if (!zoneTab.open(QFile::ReadOnly)) { 0022 qCritical() << "Unable to open zonetab file: " << zoneTab.errorString(); 0023 exit(1); 0024 } 0025 0026 const auto lines = QString::fromUtf8(zoneTab.readAll()).split(QLatin1Char('\n')); 0027 for (const auto &line : lines) { 0028 if (line.startsWith(QLatin1Char('#'))) { 0029 continue; 0030 } 0031 0032 const auto cols = line.split(QLatin1Char('\t')); 0033 if (cols.size() < 3) { 0034 continue; 0035 } 0036 0037 const auto countries = cols.at(0).split(QLatin1Char(',')); 0038 const auto tzName = cols.at(2).toUtf8(); 0039 0040 m_zones.push_back(tzName); 0041 for (const auto &country : countries) { 0042 m_countryZones[country].push_back(tzName); 0043 } 0044 if (countries.size() == 1) { 0045 if (m_countryForZone.find(tzName) != m_countryForZone.end()) { 0046 m_countryForZone[tzName] = QString(); 0047 } else { 0048 m_countryForZone[tzName] = countries[0]; 0049 } 0050 } 0051 } 0052 0053 /* Remove non-official zones that openSUSE patches into their zonetab. */ 0054 removeZone("Asia/Beijing"); 0055 0056 /* Manual overrides for countries that de-facto only have a single timezone, 0057 * even if the IANA database doesn't reflect that. 0058 */ 0059 m_countryZones[QStringLiteral("AR")] = { "America/Argentina/Buenos_Aires" }; 0060 m_countryZones[QStringLiteral("CN")] = { "Asia/Shanghai" }; 0061 m_countryZones[QStringLiteral("CY")] = { "Asia/Nicosia" }; 0062 m_countryZones[QStringLiteral("DE")] = { "Europe/Berlin" }; 0063 m_countryZones[QStringLiteral("MY")] = { "Asia/Kuala_Lumpur" }; 0064 0065 /* Manual overrides for timezones that do not belong to a unique country, contrary what zonetab claims. */ 0066 setCountryForZone("Asia/Bangkok", {}); // also used in northern Vietnam 0067 setCountryForZone("Europe/Simferopol", {}); // disputed area 0068 0069 // create offset index 0070 std::sort(m_zones.begin(), m_zones.end()); 0071 m_zoneOffsets.reserve(m_zones.size()); 0072 uint16_t offset = 0; 0073 for (const auto &tz : m_zones) { 0074 m_zoneOffsets.push_back(offset); 0075 offset += tz.size() + 1; // +1 of the trailing null byte 0076 } 0077 0078 } 0079 0080 Timezones::~Timezones() = default; 0081 0082 uint16_t Timezones::offset(const QByteArray& tz) const 0083 { 0084 const auto it = std::lower_bound(m_zones.begin(), m_zones.end(), tz); 0085 if (it == m_zones.end() || (*it) != tz) { 0086 return std::numeric_limits<uint16_t>::max(); 0087 } 0088 return m_zoneOffsets[std::distance(m_zones.begin(), it)]; 0089 } 0090 0091 void Timezones::setCountryForZone(const QByteArray &tz, const QString &country) 0092 { 0093 auto it = m_countryForZone.find(tz); 0094 if (it == m_countryForZone.end()) { 0095 return; 0096 } 0097 (*it).second = country; 0098 } 0099 0100 void Timezones::removeZone(const QByteArray &tz) 0101 { 0102 m_zones.erase(std::remove(m_zones.begin(), m_zones.end(), tz), m_zones.end()); 0103 m_countryForZone.erase(tz); 0104 for (auto &it : m_countryZones) { 0105 it.second.erase(std::remove(it.second.begin(), it.second.end(), tz), it.second.end()); 0106 } 0107 }