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