Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtimezoneprivate_chrono.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <chrono>
8#include <optional>
9
11
12using namespace std::chrono;
13using namespace std::chrono_literals;
14
15#define EXCEPTION_CHECKED(expression, fallback)
16 QT_TRY {
17 expression;
18 } QT_CATCH (const std::runtime_error &) {
19 fallback;
20 }
21
24{
25 return sys_time<milliseconds>(milliseconds(millis));
26}
27
29infoAtEpochMillis(const std::chrono::time_zone *zone, qint64 millis)
30{
31 if (!zone)
32 return std::nullopt;
33 EXCEPTION_CHECKED(return zone->get_info(chronoForEpochMillis(millis)), return std::nullopt);
34}
35
37{
38 EXCEPTION_CHECKED(return std::chrono::current_zone(), return std::nullptr);
39}
40
41static const std::chrono::time_zone *idToZone(std::string_view id)
42{
43 EXCEPTION_CHECKED(return get_tzdb().locate_zone(id), return nullptr);
44}
45
46static const std::vector<std::string_view> getAllZoneIds(std::optional<int> offset = std::nullopt)
47{
48 const tzdb &db = get_tzdb(); // May throw std::runtime_error.
49 std::vector<std::string_view> raw, links, all;
50 auto now = system_clock::now();
51 for (const time_zone &zone : db.zones) {
52 if (offset) {
53 const sys_info info = zone.get_info(now);
54 if (info.offset.count() != *offset)
55 continue;
56 }
57 raw.push_back(zone.name());
58 }
59 for (const time_zone_link &link : db.links) {
60 if (offset) {
61 const std::string_view target = link.target();
62 const time_zone *zone = db.locate_zone(target);
63 const sys_info info = zone->get_info(now);
64 if (info.offset.count() != *offset)
65 continue;
66 }
67 links.push_back(link.name());
68 }
69 // Each is sorted, so we can unite them; and the result shall be sorted.
70 all.reserve(raw.size() + links.size());
71 std::set_union(raw.begin(), raw.end(), links.begin(), links.end(), std::back_inserter(all));
72 // We could in principle merge in also such of matchingTimeZoneIds(offset)
73 // as are known zones - our CLDR data says those also have this offset - but
74 // hopefully that's redundant.
75 return all;
76}
77
78static const std::optional<std::vector<std::string_view>>
79allZoneIds(std::optional<int> offset = std::nullopt)
80{
81 EXCEPTION_CHECKED(return getAllZoneIds(offset), return std::nullopt);
82}
83
85fromSysInfo(std::chrono::sys_info info, qint64 atMSecsSinceEpoch)
86{
87 QString abbreviation = QString::fromLatin1(info.abbrev);
88 int offsetFromUtc = info.offset.count();
89 // offset is in seconds, save is in minutes
90 int standardTimeOffset = offsetFromUtc - seconds(info.save).count();
91 return QChronoTimeZonePrivate::Data(abbreviation, atMSecsSinceEpoch,
92 offsetFromUtc, standardTimeOffset);
93}
94
95QChronoTimeZonePrivate::QChronoTimeZonePrivate()
96 : m_timeZone(currentZone())
97{
98 if (m_timeZone)
99 m_id.assign(m_timeZone->name());
100}
101
102QChronoTimeZonePrivate::QChronoTimeZonePrivate(QByteArrayView id)
103 : m_timeZone(idToZone(std::string_view(id.data(), id.size())))
104{
105 if (m_timeZone)
106 m_id.assign(m_timeZone->name());
107}
108
109QChronoTimeZonePrivate *QChronoTimeZonePrivate::clone() const
110{
111 return new QChronoTimeZonePrivate(*this);
112}
113
114QChronoTimeZonePrivate::~QChronoTimeZonePrivate()
115 = default;
116
117QByteArray QChronoTimeZonePrivate::systemTimeZoneId() const
118{
119 if (const time_zone *zone = currentZone()) {
120 std::string_view name = zone->name();
121 return {name.data(), qsizetype(name.size())};
122 }
123 return {};
124}
125
126bool QChronoTimeZonePrivate::isTimeZoneIdAvailable(QByteArrayView ianaId) const
127{
128 return idToZone(std::string_view(ianaId.data(), ianaId.size())) != nullptr;
129}
130
131QList<QByteArray> QChronoTimeZonePrivate::availableTimeZoneIds() const
132{
133 QList<QByteArray> result;
134 if (auto ids = allZoneIds()) {
135 result.reserve(ids->size());
136 for (std::string_view id : *ids)
137 result << QByteArray{id.data(), qsizetype(id.size())};
138 // Order preserved; they were already sorted and unique.
139 }
140 return result;
141}
142
143QList<QByteArray> QChronoTimeZonePrivate::availableTimeZoneIds(int utcOffset) const
144{
145 QList<QByteArray> result;
146 if (auto ids = allZoneIds(utcOffset)) {
147 result.reserve(ids->size());
148 for (std::string_view id : *ids)
149 result << QByteArray{id.data(), qsizetype(id.size())};
150 // Order preserved; they were already sorted and unique.
151 }
152 return padSortedWithAliases(result);
153}
154
155QString QChronoTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
156{
157 if (auto info = infoAtEpochMillis(m_timeZone, atMSecsSinceEpoch))
158 return fromSysInfo(*info, atMSecsSinceEpoch).abbreviation;
159 return {};
160}
161
162int QChronoTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const
163{
164 if (auto info = infoAtEpochMillis(m_timeZone, atMSecsSinceEpoch))
165 return fromSysInfo(*info, atMSecsSinceEpoch).offsetFromUtc;
166 return invalidSeconds();
167}
168
169int QChronoTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const
170{
171 // Subtracting minutes from seconds will convert the minutes to seconds.
172 if (auto info = infoAtEpochMillis(m_timeZone, atMSecsSinceEpoch))
173 return int((info->offset - info->save).count());
174 return invalidSeconds();
175}
176
177int QChronoTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const
178{
179 if (auto info = infoAtEpochMillis(m_timeZone, atMSecsSinceEpoch))
180 return int(std::chrono::seconds(info->save).count());
181 return invalidSeconds();
182}
183
184bool QChronoTimeZonePrivate::hasDaylightTime() const
185{
186 Data data = QTimeZonePrivate::data(QTimeZone::DaylightTime);
187 return data.daylightTimeOffset != 0
188 && data.daylightTimeOffset != invalidSeconds();
189}
190
191bool QChronoTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
192{
193 if (auto info = infoAtEpochMillis(m_timeZone, atMSecsSinceEpoch))
194 return info->save != 0min;
195 return false;
196}
197
198QChronoTimeZonePrivate::Data
199QChronoTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
200{
201 if (auto info = infoAtEpochMillis(m_timeZone, forMSecsSinceEpoch))
202 return fromSysInfo(*info, forMSecsSinceEpoch);
203 return {};
204}
205
206bool QChronoTimeZonePrivate::hasTransitions() const
207{
208 return true;
209}
210
211QChronoTimeZonePrivate::Data
212QChronoTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const
213{
214 if (const auto info = infoAtEpochMillis(m_timeZone, afterMSecsSinceEpoch)) {
215 const auto tran = info->end;
216 qint64 when = milliseconds(tran.time_since_epoch()).count();
217 if (when > afterMSecsSinceEpoch)
218 return fromSysInfo(m_timeZone->get_info(tran), when);
219 // else we were already at (or after) the end-of-time "transition"
220 }
221 return {};
222}
223
224QChronoTimeZonePrivate::Data
225QChronoTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
226{
227 if (const auto info = infoAtEpochMillis(m_timeZone, beforeMSecsSinceEpoch - 1)) {
228 qint64 when = milliseconds(info->begin.time_since_epoch()).count();
229 if (when < beforeMSecsSinceEpoch)
230 return fromSysInfo(*info, when);
231 // else we were already at (or before) the start-of-time "transition"
232 }
233 return {};
234}
235
236QT_END_NAMESPACE
Combined button and popup list for selecting options.
#define EXCEPTION_CHECKED(expression, fallback)
static const std::chrono::time_zone * currentZone()
static const std::vector< std::string_view > getAllZoneIds(std::optional< int > offset=std::nullopt)
static const std::optional< std::vector< std::string_view > > allZoneIds(std::optional< int > offset=std::nullopt)
static std::chrono::sys_time< std::chrono::milliseconds > chronoForEpochMillis(qint64 millis)
static const std::chrono::time_zone * idToZone(std::string_view id)
static QChronoTimeZonePrivate::Data fromSysInfo(std::chrono::sys_info info, qint64 atMSecsSinceEpoch)
static std::optional< std::chrono::sys_info > infoAtEpochMillis(const std::chrono::time_zone *zone, qint64 millis)