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