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