1 | // Copyright (C) 2017 The Qt Company Ltd. |
2 | // Copyright (C) 2013 John Layt <jlayt@kde.org> |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #ifndef QTIMEZONE_H |
6 | #define QTIMEZONE_H |
7 | |
8 | #include <QtCore/qcompare.h> |
9 | #include <QtCore/qdatetime.h> |
10 | #include <QtCore/qlocale.h> |
11 | #include <QtCore/qswap.h> |
12 | #include <QtCore/qtclasshelpermacros.h> |
13 | |
14 | #include <chrono> |
15 | |
16 | #if QT_CONFIG(timezone) && (defined(Q_OS_DARWIN) || defined(Q_QDOC)) |
17 | Q_FORWARD_DECLARE_CF_TYPE(CFTimeZone); |
18 | Q_FORWARD_DECLARE_OBJC_CLASS(NSTimeZone); |
19 | #endif |
20 | |
21 | QT_BEGIN_NAMESPACE |
22 | |
23 | class QTimeZonePrivate; |
24 | |
25 | class Q_CORE_EXPORT QTimeZone |
26 | { |
27 | struct ShortData |
28 | { |
29 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
30 | quintptr mode : 2; |
31 | #endif |
32 | qintptr offset : sizeof(void *) * 8 - 2; |
33 | |
34 | #if Q_BYTE_ORDER == Q_BIG_ENDIAN |
35 | quintptr mode : 2; |
36 | #endif |
37 | |
38 | // mode is a cycled Qt::TimeSpec, (int(spec) + 1) % 4, so that zero |
39 | // (lowest bits of a pointer) matches spec being Qt::TimeZone, for which |
40 | // Data holds a QTZP pointer instead of ShortData. |
41 | // Passing Qt::TimeZone gets the equivalent of a null QTZP; it is not short. |
42 | constexpr ShortData(Qt::TimeSpec spec, int secondsAhead = 0) |
43 | #if Q_BYTE_ORDER == Q_BIG_ENDIAN |
44 | : offset(spec == Qt::OffsetFromUTC ? secondsAhead : 0), |
45 | mode((int(spec) + 1) & 3) |
46 | #else |
47 | : mode((int(spec) + 1) & 3), |
48 | offset(spec == Qt::OffsetFromUTC ? secondsAhead : 0) |
49 | #endif |
50 | { |
51 | } |
52 | friend constexpr bool operator==(ShortData lhs, ShortData rhs) |
53 | { return lhs.mode == rhs.mode && lhs.offset == rhs.offset; } |
54 | constexpr Qt::TimeSpec spec() const { return Qt::TimeSpec((mode + 3) & 3); } |
55 | }; |
56 | |
57 | union Data |
58 | { |
59 | Data() noexcept; |
60 | Data(ShortData sd) : s(sd) {} |
61 | Data(const Data &other) noexcept; |
62 | Data(Data &&other) noexcept : d(std::exchange(obj&: other.d, new_val: nullptr)) {} |
63 | Data &operator=(const Data &other) noexcept; |
64 | Data &operator=(Data &&other) noexcept { swap(other); return *this; } |
65 | ~Data(); |
66 | |
67 | void swap(Data &other) noexcept { qt_ptr_swap(lhs&: d, rhs&: other.d); } |
68 | // isShort() is equivalent to s.spec() != Qt::TimeZone |
69 | bool isShort() const { return s.mode; } // a.k.a. quintptr(d) & 3 |
70 | |
71 | // Typse must support: out << wrap("C-strings"); |
72 | template <typename Stream, typename Wrap> |
73 | void serialize(Stream &out, const Wrap &wrap) const; |
74 | |
75 | Data(QTimeZonePrivate *dptr) noexcept; |
76 | Data &operator=(QTimeZonePrivate *dptr) noexcept; |
77 | const QTimeZonePrivate *operator->() const { Q_ASSERT(!isShort()); return d; } |
78 | QTimeZonePrivate *operator->() { Q_ASSERT(!isShort()); return d; } |
79 | |
80 | QTimeZonePrivate *d = nullptr; |
81 | ShortData s; |
82 | }; |
83 | QTimeZone(ShortData sd) : d(sd) {} |
84 | |
85 | public: |
86 | // Sane UTC offsets range from -16 to +16 hours: |
87 | static constexpr int MinUtcOffsetSecs = -16 * 3600; |
88 | // No known modern zone > 12 hrs West of Greenwich. |
89 | // Until 1844, Asia/Manila (in The Philippines) was at 15:56 West. |
90 | static constexpr int MaxUtcOffsetSecs = +16 * 3600; |
91 | // No known modern zone > 14 hrs East of Greenwich. |
92 | // Until 1867, America/Metlakatla (in Alaska) was at 15:13:42 East. |
93 | |
94 | enum Initialization { LocalTime, UTC }; |
95 | |
96 | QTimeZone() noexcept; |
97 | Q_IMPLICIT QTimeZone(Initialization spec) noexcept |
98 | : d(ShortData(spec == UTC ? Qt::UTC : Qt::LocalTime)) {} |
99 | |
100 | #if QT_CONFIG(timezone) |
101 | explicit QTimeZone(int offsetSeconds); |
102 | explicit QTimeZone(const QByteArray &ianaId); |
103 | QTimeZone(const QByteArray &zoneId, int offsetSeconds, const QString &name, |
104 | const QString &abbreviation, QLocale::Territory territory = QLocale::AnyTerritory, |
105 | const QString & = QString()); |
106 | #endif // timezone backends |
107 | |
108 | QTimeZone(const QTimeZone &other) noexcept; |
109 | QTimeZone(QTimeZone &&other) noexcept : d(std::move(other.d)) {} |
110 | ~QTimeZone(); |
111 | |
112 | QTimeZone &operator=(const QTimeZone &other); |
113 | QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QTimeZone) |
114 | |
115 | void swap(QTimeZone &other) noexcept |
116 | { d.swap(other&: other.d); } |
117 | |
118 | #if QT_CORE_REMOVED_SINCE(6, 7) |
119 | bool operator==(const QTimeZone &other) const; |
120 | bool operator!=(const QTimeZone &other) const; |
121 | #endif |
122 | |
123 | bool isValid() const; |
124 | |
125 | static QTimeZone fromDurationAheadOfUtc(std::chrono::seconds offset) |
126 | { |
127 | return QTimeZone((offset.count() >= MinUtcOffsetSecs && offset.count() <= MaxUtcOffsetSecs) |
128 | ? ShortData(offset.count() ? Qt::OffsetFromUTC : Qt::UTC, |
129 | int(offset.count())) |
130 | : ShortData(Qt::TimeZone)); |
131 | } |
132 | static QTimeZone fromSecondsAheadOfUtc(int offset) |
133 | { |
134 | return fromDurationAheadOfUtc(offset: std::chrono::seconds{offset}); |
135 | } |
136 | constexpr Qt::TimeSpec timeSpec() const noexcept { return d.s.spec(); } |
137 | constexpr int fixedSecondsAheadOfUtc() const noexcept |
138 | { return timeSpec() == Qt::OffsetFromUTC ? int(d.s.offset) : 0; } |
139 | |
140 | static constexpr bool isUtcOrFixedOffset(Qt::TimeSpec spec) noexcept |
141 | { return spec == Qt::UTC || spec == Qt::OffsetFromUTC; } |
142 | constexpr bool isUtcOrFixedOffset() const noexcept { return isUtcOrFixedOffset(spec: timeSpec()); } |
143 | |
144 | #if QT_CONFIG(timezone) |
145 | QTimeZone asBackendZone() const; |
146 | |
147 | enum TimeType { |
148 | StandardTime = 0, |
149 | DaylightTime = 1, |
150 | GenericTime = 2 |
151 | }; |
152 | |
153 | enum NameType { |
154 | DefaultName = 0, |
155 | LongName = 1, |
156 | ShortName = 2, |
157 | OffsetName = 3 |
158 | }; |
159 | |
160 | struct OffsetData { |
161 | QString abbreviation; |
162 | QDateTime atUtc; |
163 | int offsetFromUtc; |
164 | int standardTimeOffset; |
165 | int daylightTimeOffset; |
166 | }; |
167 | typedef QList<OffsetData> OffsetDataList; |
168 | |
169 | bool hasAlternativeName(QByteArrayView alias) const; |
170 | QByteArray id() const; |
171 | QLocale::Territory territory() const; |
172 | # if QT_DEPRECATED_SINCE(6, 6) |
173 | QT_DEPRECATED_VERSION_X_6_6("Use territory() instead" ) |
174 | QLocale::Country country() const; |
175 | # endif |
176 | QString () const; |
177 | |
178 | QString displayName(const QDateTime &atDateTime, |
179 | QTimeZone::NameType nameType = QTimeZone::DefaultName, |
180 | const QLocale &locale = QLocale()) const; |
181 | QString displayName(QTimeZone::TimeType timeType, |
182 | QTimeZone::NameType nameType = QTimeZone::DefaultName, |
183 | const QLocale &locale = QLocale()) const; |
184 | QString abbreviation(const QDateTime &atDateTime) const; |
185 | |
186 | int offsetFromUtc(const QDateTime &atDateTime) const; |
187 | int standardTimeOffset(const QDateTime &atDateTime) const; |
188 | int daylightTimeOffset(const QDateTime &atDateTime) const; |
189 | |
190 | bool hasDaylightTime() const; |
191 | bool isDaylightTime(const QDateTime &atDateTime) const; |
192 | |
193 | OffsetData offsetData(const QDateTime &forDateTime) const; |
194 | |
195 | bool hasTransitions() const; |
196 | OffsetData nextTransition(const QDateTime &afterDateTime) const; |
197 | OffsetData previousTransition(const QDateTime &beforeDateTime) const; |
198 | OffsetDataList transitions(const QDateTime &fromDateTime, const QDateTime &toDateTime) const; |
199 | |
200 | static QByteArray systemTimeZoneId(); |
201 | static QTimeZone systemTimeZone(); |
202 | static QTimeZone utc(); |
203 | |
204 | static bool isTimeZoneIdAvailable(const QByteArray &ianaId); |
205 | |
206 | static QList<QByteArray> availableTimeZoneIds(); |
207 | static QList<QByteArray> availableTimeZoneIds(QLocale::Territory territory); |
208 | static QList<QByteArray> availableTimeZoneIds(int offsetSeconds); |
209 | |
210 | static QByteArray ianaIdToWindowsId(const QByteArray &ianaId); |
211 | static QByteArray windowsIdToDefaultIanaId(const QByteArray &windowsId); |
212 | static QByteArray windowsIdToDefaultIanaId(const QByteArray &windowsId, |
213 | QLocale::Territory territory); |
214 | static QList<QByteArray> windowsIdToIanaIds(const QByteArray &windowsId); |
215 | static QList<QByteArray> windowsIdToIanaIds(const QByteArray &windowsId, |
216 | QLocale::Territory territory); |
217 | |
218 | # if defined(Q_OS_DARWIN) || defined(Q_QDOC) |
219 | static QTimeZone fromCFTimeZone(CFTimeZoneRef timeZone); |
220 | CFTimeZoneRef toCFTimeZone() const Q_DECL_CF_RETURNS_RETAINED; |
221 | static QTimeZone fromNSTimeZone(const NSTimeZone *timeZone); |
222 | NSTimeZone *toNSTimeZone() const Q_DECL_NS_RETURNS_AUTORELEASED; |
223 | # endif |
224 | |
225 | # if __cpp_lib_chrono >= 201907L || defined(Q_QDOC) |
226 | QT_POST_CXX17_API_IN_EXPORTED_CLASS |
227 | static QTimeZone fromStdTimeZonePtr(const std::chrono::time_zone *timeZone) |
228 | { |
229 | if (!timeZone) |
230 | return QTimeZone(); |
231 | const std::string_view timeZoneName = timeZone->name(); |
232 | return QTimeZone(QByteArrayView(timeZoneName).toByteArray()); |
233 | } |
234 | # endif |
235 | #endif // feature timezone |
236 | private: |
237 | friend Q_CORE_EXPORT bool comparesEqual(const QTimeZone &lhs, const QTimeZone &rhs) noexcept; |
238 | Q_DECLARE_EQUALITY_COMPARABLE(QTimeZone) |
239 | |
240 | #ifndef QT_NO_DATASTREAM |
241 | friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz); |
242 | #endif |
243 | #ifndef QT_NO_DEBUG_STREAM |
244 | friend Q_CORE_EXPORT QDebug operator<<(QDebug dbg, const QTimeZone &tz); |
245 | #endif |
246 | QTimeZone(QTimeZonePrivate &dd); |
247 | friend class QTimeZonePrivate; |
248 | friend class QDateTime; |
249 | friend class QDateTimePrivate; |
250 | Data d; |
251 | }; |
252 | |
253 | #if QT_CONFIG(timezone) |
254 | Q_DECLARE_TYPEINFO(QTimeZone::OffsetData, Q_RELOCATABLE_TYPE); |
255 | #endif |
256 | Q_DECLARE_SHARED(QTimeZone) |
257 | |
258 | #ifndef QT_NO_DATASTREAM |
259 | Q_CORE_EXPORT QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz); |
260 | Q_CORE_EXPORT QDataStream &operator>>(QDataStream &ds, QTimeZone &tz); |
261 | #endif |
262 | |
263 | #ifndef QT_NO_DEBUG_STREAM |
264 | Q_CORE_EXPORT QDebug operator<<(QDebug dbg, const QTimeZone &tz); |
265 | #endif |
266 | |
267 | #if QT_CONFIG(timezone) && __cpp_lib_chrono >= 201907L |
268 | // zoned_time |
269 | template <typename> // QT_POST_CXX17_API_IN_EXPORTED_CLASS |
270 | inline QDateTime QDateTime::fromStdZonedTime(const std::chrono::zoned_time< |
271 | std::chrono::milliseconds, |
272 | const std::chrono::time_zone * |
273 | > &time) |
274 | { |
275 | const auto sysTime = time.get_sys_time(); |
276 | const QTimeZone timeZone = QTimeZone::fromStdTimeZonePtr(time.get_time_zone()); |
277 | return fromMSecsSinceEpoch(sysTime.time_since_epoch().count(), timeZone); |
278 | } |
279 | #endif |
280 | |
281 | QT_END_NAMESPACE |
282 | |
283 | #endif // QTIMEZONE_H |
284 | |