KItinerary

timezonedb.cpp
1 /*
2  SPDX-FileCopyrightText: 2018 Volker Krause <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "timezonedb.h"
8 #include "timezonedb_p.h"
9 #include "timezonedb_data.cpp"
10 #include "timezone_zindex.cpp"
11 
12 #include <QTimeZone>
13 
14 using namespace KItinerary;
15 
17 {
18  return timezone_names + timezone_names_offsets[static_cast<std::underlying_type<KnowledgeDb::Tz>::type>(tz)];
19 }
20 
22 {
23  if (tz == Tz::Undefined) {
24  return {};
25  }
26  return QTimeZone(tzId(tz));
27 }
28 
30 {
31  const auto it = std::lower_bound(std::begin(country_timezone_map), std::end(country_timezone_map), country);
32  if (it != std::end(country_timezone_map) && (*it).country == country) {
33  return (*it).timezone;
34  }
35 
36  return Tz::Undefined;
37 }
38 
40 {
41  return timezone_country_map[static_cast<std::underlying_type<KnowledgeDb::Tz>::type>(tz)];
42 }
43 
44 KnowledgeDb::Tz KnowledgeDb::timezoneForCoordinate(float lat, float lon, bool *ambiguous)
45 {
46  // see arctic latitude filter in the generator script, we only cover 65°S to 80°N
47  if (lat < timezone_index_params.yStart || lat > timezone_index_params.yEnd()) {
48  return Tz::Undefined;
49  }
50 
51  const uint32_t x = ((lon - timezone_index_params.xStart) / timezone_index_params.xRange) * (1 << timezone_index_params.zDepth);
52  const uint32_t y = ((lat - timezone_index_params.yStart) / timezone_index_params.yRange) * (1 << timezone_index_params.zDepth);
53  uint32_t z = 0;
54  for (int i = timezone_index_params.zDepth - 1; i >= 0; --i) {
55  z <<= 1;
56  z += (y & (1 << i)) ? 1 : 0;
57  z <<= 1;
58  z += (x & (1 << i)) ? 1 : 0;
59  }
60 
61  const auto it = std::upper_bound(std::begin(timezone_index), std::end(timezone_index), z);
62  if (it == std::begin(timezone_index)) {
63  return Tz::Undefined;
64  }
65  if (ambiguous) {
66  *ambiguous = (*std::prev(it)).isAmbiguous;
67  }
68  return (*std::prev(it)).tz;
69 }
70 
71 static bool compareOffsetData(const QTimeZone::OffsetData &lhs, const QTimeZone::OffsetData &rhs)
72 {
73  return lhs.offsetFromUtc == rhs.offsetFromUtc
74  && lhs.standardTimeOffset == rhs.standardTimeOffset
75  && lhs.daylightTimeOffset == rhs.daylightTimeOffset
76  && lhs.atUtc == rhs.atUtc
77  && lhs.abbreviation == rhs.abbreviation;
78 }
79 
80 static bool isEquivalentTimezone(const QTimeZone &lhs, const QTimeZone &rhs)
81 {
83  if (lhs.offsetFromUtc(dt) != rhs.offsetFromUtc(dt) || lhs.hasTransitions() != rhs.hasTransitions()) {
84  return false;
85  }
86 
87  for (int i = 0; i < 2 && dt.isValid(); ++i) {
88  const auto lhsOff = lhs.nextTransition(dt);
89  const auto rhsOff = rhs.nextTransition(dt);
90  if (!compareOffsetData(lhsOff, rhsOff)) {
91  return false;
92  }
93  dt = lhsOff.atUtc;
94  }
95 
96  return true;
97 }
98 
100 {
101  bool ambiguous = false;
102  const auto coordTz = timezoneForCoordinate(lat, lon, &ambiguous);
103  const auto countryTz = timezoneForCountry(country);
104  const auto countryFromCoord = countryForTimezone(coordTz);
105 
106  // if we determine a different country than was provided, search for an equivalent timezone
107  // in the requested country
108  // example: Tijuana airport ending up in America/Los Angeles, and America/Tijuna being the only MX timezone equivalent to that
109  if (coordTz != Tz::Undefined && countryFromCoord.isValid() && countryFromCoord != country) {
110  bool nonUnique = false;
111  Tz foundTz = Tz::Undefined;
112  const auto coordZone = toQTimeZone(coordTz);
113 
114  constexpr const int timezone_count = sizeof(timezone_country_map) / sizeof(timezone_country_map[0]);
115  for (int i = 1; i < timezone_count; ++i) {
116  if (timezone_country_map[i] != country) {
117  continue;
118  }
119  const auto t = static_cast<Tz>(i);
120  if (!isEquivalentTimezone(toQTimeZone(t), coordZone)) {
121  continue;
122  }
123  if (foundTz != Tz::Undefined) {
124  nonUnique = true;
125  break;
126  }
127  foundTz = t;
128  }
129 
130  if (!nonUnique && foundTz != Tz::Undefined) {
131  return foundTz;
132  }
133  }
134 
135  // only one method found a result, let's use that one
136  if (coordTz == Tz::Undefined || coordTz == countryTz) {
137  return countryTz;
138  }
139  if (countryTz == Tz::Undefined) {
140  return coordTz;
141  }
142 
143  // if the coordinate-based timezone is also in @p country, that takes precedence
144  // example: the various AR sub-zones, or the MY sub-zone
145  if (country == countryFromCoord || !ambiguous) {
146  return coordTz;
147  }
148 
149  // if both timezones are equivalent, the country-based one wins, otherwise we use the coordinate one
150  return isEquivalentTimezone(toQTimeZone(coordTz), toQTimeZone(countryTz)) ? countryTz : coordTz;
151 }
152 
154 {
155  bool ambiguous = false;
156  const auto tz = timezoneForCoordinate(lat, lon, &ambiguous);
157  if (!ambiguous) {
158  return countryForTimezone(tz);
159  }
160  return {};
161 }
Classes for reservation/travel data models, data extraction and data augmentation.
bool hasTransitions() const const
int offsetFromUtc(const QDateTime &atDateTime) const const
Tz timezoneForLocation(float lat, float lon, CountryId country)
Returns the timezone for the given location consisting of coordinates and country.
Definition: timezonedb.cpp:99
const char * tzId(Tz tz)
Returns the IANA timezone id for tz.
Definition: timezonedb.cpp:16
CountryId countryForCoordinate(float lat, float lon)
Returns the country for the given coordinate.
Definition: timezonedb.cpp:153
Tz timezoneForCoordinate(float lat, float lon, bool *ambiguous=nullptr)
Returns the timezone for the given coordinate.
Definition: timezonedb.cpp:44
QTimeZone::OffsetData nextTransition(const QDateTime &afterDateTime) const const
CountryId countryForTimezone(Tz tz)
Returns the country for a given timezone.
Definition: timezonedb.cpp:39
Tz
Enum representing all timezones.
Tz timezoneForCountry(CountryId country)
Returns the timezone for the given country, as long as there is exactly one timezone used in that cou...
Definition: timezonedb.cpp:29
QDateTime currentDateTimeUtc()
QTimeZone toQTimeZone(Tz tz)
Returns the corresponding QTimeZone.
Definition: timezonedb.cpp:21
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Jul 8 2020 23:12:32 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.