1 | // Copyright (C) 2022 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 | |
6 | #ifndef QTIMEZONEPRIVATE_P_H |
7 | #define QTIMEZONEPRIVATE_P_H |
8 | |
9 | // |
10 | // W A R N I N G |
11 | // ------------- |
12 | // |
13 | // This file is not part of the Qt API. It exists for the convenience |
14 | // of internal files. This header file may change from version to version |
15 | // without notice, or even be removed. |
16 | // |
17 | // We mean it. |
18 | // |
19 | |
20 | #include "qlist.h" |
21 | #include "qtimezone.h" |
22 | #include "private/qlocale_p.h" |
23 | #include "private/qdatetime_p.h" |
24 | |
25 | #if QT_CONFIG(icu) |
26 | #include <unicode/ucal.h> |
27 | #endif |
28 | |
29 | #ifdef Q_OS_DARWIN |
30 | Q_FORWARD_DECLARE_OBJC_CLASS(NSTimeZone); |
31 | #endif // Q_OS_DARWIN |
32 | |
33 | #ifdef Q_OS_WIN |
34 | #include <qt_windows.h> |
35 | #endif // Q_OS_WIN |
36 | |
37 | #ifdef Q_OS_ANDROID |
38 | #include <QJniObject> |
39 | #endif |
40 | |
41 | QT_REQUIRE_CONFIG(timezone); |
42 | QT_BEGIN_NAMESPACE |
43 | |
44 | class Q_AUTOTEST_EXPORT QTimeZonePrivate : public QSharedData |
45 | { |
46 | public: |
47 | // Version of QTimeZone::OffsetData struct using msecs for efficiency |
48 | struct Data { |
49 | QString abbreviation; |
50 | qint64 atMSecsSinceEpoch; |
51 | int offsetFromUtc; |
52 | int standardTimeOffset; |
53 | int daylightTimeOffset; |
54 | Data() |
55 | : atMSecsSinceEpoch(QTimeZonePrivate::invalidMSecs()), |
56 | offsetFromUtc(QTimeZonePrivate::invalidSeconds()), |
57 | standardTimeOffset(QTimeZonePrivate::invalidSeconds()), |
58 | daylightTimeOffset(QTimeZonePrivate::invalidSeconds()) |
59 | {} |
60 | Data(const QString &name, qint64 when, int offset, int standard) |
61 | : abbreviation(name), |
62 | atMSecsSinceEpoch(when), |
63 | offsetFromUtc(offset), |
64 | standardTimeOffset(standard), |
65 | daylightTimeOffset(offset - standard) |
66 | {} |
67 | }; |
68 | typedef QList<Data> DataList; |
69 | |
70 | // Create null time zone |
71 | QTimeZonePrivate(); |
72 | QTimeZonePrivate(const QTimeZonePrivate &other); |
73 | virtual ~QTimeZonePrivate(); |
74 | |
75 | virtual QTimeZonePrivate *clone() const; |
76 | |
77 | bool operator==(const QTimeZonePrivate &other) const; |
78 | bool operator!=(const QTimeZonePrivate &other) const; |
79 | |
80 | bool isValid() const; |
81 | |
82 | QByteArray id() const; |
83 | virtual QLocale::Territory territory() const; |
84 | virtual QString () const; |
85 | |
86 | virtual QString displayName(qint64 atMSecsSinceEpoch, |
87 | QTimeZone::NameType nameType, |
88 | const QLocale &locale) const; |
89 | virtual QString displayName(QTimeZone::TimeType timeType, |
90 | QTimeZone::NameType nameType, |
91 | const QLocale &locale) const; |
92 | virtual QString abbreviation(qint64 atMSecsSinceEpoch) const; |
93 | |
94 | virtual int offsetFromUtc(qint64 atMSecsSinceEpoch) const; |
95 | virtual int standardTimeOffset(qint64 atMSecsSinceEpoch) const; |
96 | virtual int daylightTimeOffset(qint64 atMSecsSinceEpoch) const; |
97 | |
98 | virtual bool hasDaylightTime() const; |
99 | virtual bool isDaylightTime(qint64 atMSecsSinceEpoch) const; |
100 | |
101 | virtual Data data(qint64 forMSecsSinceEpoch) const; |
102 | virtual Data data(QTimeZone::TimeType timeType) const; |
103 | virtual bool isDataLocale(const QLocale &locale) const; |
104 | QDateTimePrivate::ZoneState stateAtZoneTime(qint64 forLocalMSecs, |
105 | QDateTimePrivate::TransitionOptions resolve) const; |
106 | |
107 | virtual bool hasTransitions() const; |
108 | virtual Data nextTransition(qint64 afterMSecsSinceEpoch) const; |
109 | virtual Data previousTransition(qint64 beforeMSecsSinceEpoch) const; |
110 | DataList transitions(qint64 fromMSecsSinceEpoch, qint64 toMSecsSinceEpoch) const; |
111 | |
112 | virtual QByteArray systemTimeZoneId() const; |
113 | |
114 | virtual bool isTimeZoneIdAvailable(const QByteArray &ianaId) const; |
115 | virtual QList<QByteArray> availableTimeZoneIds() const; |
116 | virtual QList<QByteArray> availableTimeZoneIds(QLocale::Territory territory) const; |
117 | virtual QList<QByteArray> availableTimeZoneIds(int utcOffset) const; |
118 | |
119 | virtual void serialize(QDataStream &ds) const; |
120 | |
121 | // Static Utility Methods |
122 | [[nodiscard]] static constexpr qint64 maxMSecs() |
123 | { return (std::numeric_limits<qint64>::max)(); } |
124 | [[nodiscard]] static constexpr qint64 minMSecs() |
125 | { return (std::numeric_limits<qint64>::min)() + 1; } |
126 | [[nodiscard]] static constexpr qint64 invalidMSecs() |
127 | { return (std::numeric_limits<qint64>::min)(); } |
128 | [[nodiscard]] static constexpr int invalidSeconds() |
129 | { return (std::numeric_limits<int>::min)(); } |
130 | static QTimeZone::OffsetData invalidOffsetData(); |
131 | static QTimeZone::OffsetData toOffsetData(const Data &data); |
132 | static bool isValidId(const QByteArray &ianaId); |
133 | static QString isoOffsetFormat(int offsetFromUtc, |
134 | QTimeZone::NameType mode = QTimeZone::OffsetName); |
135 | |
136 | static QByteArray aliasToIana(QByteArrayView alias); |
137 | static QByteArray ianaIdToWindowsId(const QByteArray &ianaId); |
138 | static QByteArray windowsIdToDefaultIanaId(const QByteArray &windowsId); |
139 | static QByteArray windowsIdToDefaultIanaId(const QByteArray &windowsId, |
140 | QLocale::Territory territory); |
141 | static QList<QByteArray> windowsIdToIanaIds(const QByteArray &windowsId); |
142 | static QList<QByteArray> windowsIdToIanaIds(const QByteArray &windowsId, |
143 | QLocale::Territory territory); |
144 | |
145 | // returns "UTC" QString and QByteArray |
146 | [[nodiscard]] static inline QString utcQString() |
147 | { |
148 | return QStringLiteral("UTC" ); |
149 | } |
150 | |
151 | [[nodiscard]] static inline QByteArray utcQByteArray() |
152 | { |
153 | return QByteArrayLiteral("UTC" ); |
154 | } |
155 | |
156 | #if QT_CONFIG(timezone_locale) |
157 | private: |
158 | // Defined in qtimezonelocale.cpp |
159 | QString localeName(qint64 atMSecsSinceEpoch, int offsetFromUtc, |
160 | QTimeZone::TimeType timeType, |
161 | QTimeZone::NameType nameType, |
162 | const QLocale &locale) const; |
163 | #endif // L10n helpers. |
164 | |
165 | protected: |
166 | QByteArray m_id; |
167 | }; |
168 | Q_DECLARE_TYPEINFO(QTimeZonePrivate::Data, Q_RELOCATABLE_TYPE); |
169 | |
170 | template<> QTimeZonePrivate *QSharedDataPointer<QTimeZonePrivate>::clone(); |
171 | |
172 | class Q_AUTOTEST_EXPORT QUtcTimeZonePrivate final : public QTimeZonePrivate |
173 | { |
174 | public: |
175 | // Create default UTC time zone |
176 | QUtcTimeZonePrivate(); |
177 | // Create named time zone |
178 | QUtcTimeZonePrivate(const QByteArray &utcId); |
179 | // Create offset from UTC |
180 | QUtcTimeZonePrivate(qint32 offsetSeconds); |
181 | // Create custom offset from UTC |
182 | QUtcTimeZonePrivate(const QByteArray &zoneId, int offsetSeconds, const QString &name, |
183 | const QString &abbreviation, QLocale::Territory territory, |
184 | const QString &); |
185 | QUtcTimeZonePrivate(const QUtcTimeZonePrivate &other); |
186 | virtual ~QUtcTimeZonePrivate(); |
187 | |
188 | // Fall-back for UTC[+-]\d+(:\d+){,2} IDs. |
189 | static qint64 offsetFromUtcString(QByteArrayView id); |
190 | |
191 | QUtcTimeZonePrivate *clone() const override; |
192 | |
193 | Data data(qint64 forMSecsSinceEpoch) const override; |
194 | Data data(QTimeZone::TimeType timeType) const override; |
195 | bool isDataLocale(const QLocale &locale) const override; |
196 | |
197 | QLocale::Territory territory() const override; |
198 | QString () const override; |
199 | |
200 | QString displayName(qint64 atMSecsSinceEpoch, |
201 | QTimeZone::NameType nameType, |
202 | const QLocale &locale) const override; |
203 | QString displayName(QTimeZone::TimeType timeType, |
204 | QTimeZone::NameType nameType, |
205 | const QLocale &locale) const override; |
206 | QString abbreviation(qint64 atMSecsSinceEpoch) const override; |
207 | |
208 | int standardTimeOffset(qint64 atMSecsSinceEpoch) const override; |
209 | int daylightTimeOffset(qint64 atMSecsSinceEpoch) const override; |
210 | |
211 | QByteArray systemTimeZoneId() const override; |
212 | |
213 | bool isTimeZoneIdAvailable(const QByteArray &ianaId) const override; |
214 | QList<QByteArray> availableTimeZoneIds() const override; |
215 | QList<QByteArray> availableTimeZoneIds(QLocale::Territory country) const override; |
216 | QList<QByteArray> availableTimeZoneIds(int utcOffset) const override; |
217 | |
218 | void serialize(QDataStream &ds) const override; |
219 | |
220 | private: |
221 | void init(const QByteArray &zoneId, int offsetSeconds, const QString &name, |
222 | const QString &abbreviation, QLocale::Territory territory, |
223 | const QString &); |
224 | |
225 | QString m_name; |
226 | QString m_abbreviation; |
227 | QString ; |
228 | QLocale::Territory m_territory; |
229 | int m_offsetFromUtc; |
230 | }; |
231 | |
232 | // TODO: shuffle (almost reverse) order of and rework #if-ery here to use #elif |
233 | // and match the #if-ery in each of QTZ's newBackendTimeZone() cascades for |
234 | // backend selection. |
235 | #if QT_CONFIG(icu) && !defined(Q_OS_UNIX) |
236 | class Q_AUTOTEST_EXPORT QIcuTimeZonePrivate final : public QTimeZonePrivate |
237 | { |
238 | public: |
239 | // Create default time zone |
240 | QIcuTimeZonePrivate(); |
241 | // Create named time zone |
242 | QIcuTimeZonePrivate(const QByteArray &ianaId); |
243 | QIcuTimeZonePrivate(const QIcuTimeZonePrivate &other); |
244 | ~QIcuTimeZonePrivate(); |
245 | |
246 | QIcuTimeZonePrivate *clone() const override; |
247 | |
248 | using QTimeZonePrivate::displayName; |
249 | QString displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType, |
250 | const QLocale &locale) const override; |
251 | |
252 | int offsetFromUtc(qint64 atMSecsSinceEpoch) const override; |
253 | int standardTimeOffset(qint64 atMSecsSinceEpoch) const override; |
254 | int daylightTimeOffset(qint64 atMSecsSinceEpoch) const override; |
255 | |
256 | bool hasDaylightTime() const override; |
257 | bool isDaylightTime(qint64 atMSecsSinceEpoch) const override; |
258 | |
259 | using QTimeZonePrivate::data; |
260 | Data data(qint64 forMSecsSinceEpoch) const override; |
261 | |
262 | bool hasTransitions() const override; |
263 | Data nextTransition(qint64 afterMSecsSinceEpoch) const override; |
264 | Data previousTransition(qint64 beforeMSecsSinceEpoch) const override; |
265 | |
266 | QByteArray systemTimeZoneId() const override; |
267 | |
268 | bool isTimeZoneIdAvailable(const QByteArray &ianaId) const override; |
269 | QList<QByteArray> availableTimeZoneIds() const override; |
270 | QList<QByteArray> availableTimeZoneIds(QLocale::Territory territory) const override; |
271 | QList<QByteArray> availableTimeZoneIds(int offsetFromUtc) const override; |
272 | |
273 | private: |
274 | void init(const QByteArray &ianaId); |
275 | |
276 | UCalendar *m_ucal; |
277 | }; |
278 | #endif // ICU not on Unix. |
279 | |
280 | #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) |
281 | struct QTzTransitionTime |
282 | { |
283 | qint64 atMSecsSinceEpoch; |
284 | quint8 ruleIndex; |
285 | }; |
286 | Q_DECLARE_TYPEINFO(QTzTransitionTime, Q_PRIMITIVE_TYPE); |
287 | struct QTzTransitionRule |
288 | { |
289 | int stdOffset = 0; |
290 | int dstOffset = 0; |
291 | quint8 abbreviationIndex = 0; |
292 | }; |
293 | Q_DECLARE_TYPEINFO(QTzTransitionRule, Q_PRIMITIVE_TYPE); |
294 | constexpr inline bool operator==(const QTzTransitionRule &lhs, const QTzTransitionRule &rhs) noexcept |
295 | { return lhs.stdOffset == rhs.stdOffset && lhs.dstOffset == rhs.dstOffset && lhs.abbreviationIndex == rhs.abbreviationIndex; } |
296 | constexpr inline bool operator!=(const QTzTransitionRule &lhs, const QTzTransitionRule &rhs) noexcept |
297 | { return !operator==(lhs, rhs); } |
298 | |
299 | // These are stored separately from QTzTimeZonePrivate so that they can be |
300 | // cached, avoiding the need to re-parse them from disk constantly. |
301 | struct QTzTimeZoneCacheEntry |
302 | { |
303 | QList<QTzTransitionTime> m_tranTimes; |
304 | QList<QTzTransitionRule> m_tranRules; |
305 | QList<QByteArray> m_abbreviations; |
306 | QByteArray m_posixRule; |
307 | QTzTransitionRule m_preZoneRule; |
308 | bool m_hasDst = false; |
309 | }; |
310 | |
311 | class Q_AUTOTEST_EXPORT QTzTimeZonePrivate final : public QTimeZonePrivate |
312 | { |
313 | QTzTimeZonePrivate(const QTzTimeZonePrivate &) = default; |
314 | public: |
315 | // Create default time zone |
316 | QTzTimeZonePrivate(); |
317 | // Create named time zone |
318 | QTzTimeZonePrivate(const QByteArray &ianaId); |
319 | ~QTzTimeZonePrivate(); |
320 | |
321 | QTzTimeZonePrivate *clone() const override; |
322 | |
323 | QLocale::Territory territory() const override; |
324 | QString () const override; |
325 | |
326 | using QTimeZonePrivate::displayName; |
327 | QString displayName(QTimeZone::TimeType timeType, |
328 | QTimeZone::NameType nameType, |
329 | const QLocale &locale) const override; |
330 | QString abbreviation(qint64 atMSecsSinceEpoch) const override; |
331 | |
332 | int offsetFromUtc(qint64 atMSecsSinceEpoch) const override; |
333 | int standardTimeOffset(qint64 atMSecsSinceEpoch) const override; |
334 | int daylightTimeOffset(qint64 atMSecsSinceEpoch) const override; |
335 | |
336 | bool hasDaylightTime() const override; |
337 | bool isDaylightTime(qint64 atMSecsSinceEpoch) const override; |
338 | |
339 | Data data(qint64 forMSecsSinceEpoch) const override; |
340 | Data data(QTimeZone::TimeType timeType) const override; |
341 | bool isDataLocale(const QLocale &locale) const override; |
342 | |
343 | bool hasTransitions() const override; |
344 | Data nextTransition(qint64 afterMSecsSinceEpoch) const override; |
345 | Data previousTransition(qint64 beforeMSecsSinceEpoch) const override; |
346 | |
347 | QByteArray systemTimeZoneId() const override; |
348 | |
349 | bool isTimeZoneIdAvailable(const QByteArray &ianaId) const override; |
350 | QList<QByteArray> availableTimeZoneIds() const override; |
351 | QList<QByteArray> availableTimeZoneIds(QLocale::Territory territory) const override; |
352 | |
353 | private: |
354 | static QByteArray staticSystemTimeZoneId(); |
355 | QList<QTimeZonePrivate::Data> getPosixTransitions(qint64 msNear) const; |
356 | |
357 | Data dataForTzTransition(QTzTransitionTime tran) const; |
358 | Data dataFromRule(QTzTransitionRule rule, qint64 msecsSinceEpoch) const; |
359 | QTzTimeZoneCacheEntry cached_data; |
360 | const QList<QTzTransitionTime> &tranCache() const { return cached_data.m_tranTimes; } |
361 | }; |
362 | #endif // Q_OS_UNIX |
363 | |
364 | #ifdef Q_OS_DARWIN |
365 | class Q_AUTOTEST_EXPORT QMacTimeZonePrivate final : public QTimeZonePrivate |
366 | { |
367 | public: |
368 | // Create default time zone |
369 | QMacTimeZonePrivate(); |
370 | // Create named time zone |
371 | QMacTimeZonePrivate(const QByteArray &ianaId); |
372 | QMacTimeZonePrivate(const QMacTimeZonePrivate &other); |
373 | ~QMacTimeZonePrivate(); |
374 | |
375 | QMacTimeZonePrivate *clone() const override; |
376 | |
377 | QString comment() const override; |
378 | |
379 | using QTimeZonePrivate::displayName; |
380 | QString displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType, |
381 | const QLocale &locale) const override; |
382 | QString abbreviation(qint64 atMSecsSinceEpoch) const override; |
383 | |
384 | int offsetFromUtc(qint64 atMSecsSinceEpoch) const override; |
385 | int standardTimeOffset(qint64 atMSecsSinceEpoch) const override; |
386 | int daylightTimeOffset(qint64 atMSecsSinceEpoch) const override; |
387 | |
388 | bool hasDaylightTime() const override; |
389 | bool isDaylightTime(qint64 atMSecsSinceEpoch) const override; |
390 | |
391 | using QTimeZonePrivate::data; |
392 | Data data(qint64 forMSecsSinceEpoch) const override; |
393 | |
394 | bool hasTransitions() const override; |
395 | Data nextTransition(qint64 afterMSecsSinceEpoch) const override; |
396 | Data previousTransition(qint64 beforeMSecsSinceEpoch) const override; |
397 | |
398 | QByteArray systemTimeZoneId() const override; |
399 | bool isTimeZoneIdAvailable(const QByteArray &ianaId) const override; |
400 | QList<QByteArray> availableTimeZoneIds() const override; |
401 | |
402 | NSTimeZone *nsTimeZone() const; |
403 | |
404 | private: |
405 | void init(const QByteArray &zoneId); |
406 | |
407 | NSTimeZone *m_nstz; |
408 | }; |
409 | #endif // Q_OS_DARWIN |
410 | |
411 | #if defined(Q_OS_WIN) && !QT_CONFIG(icu) |
412 | class Q_AUTOTEST_EXPORT QWinTimeZonePrivate final : public QTimeZonePrivate |
413 | { |
414 | public: |
415 | struct QWinTransitionRule { |
416 | int startYear; |
417 | int standardTimeBias; |
418 | int daylightTimeBias; |
419 | SYSTEMTIME standardTimeRule; |
420 | SYSTEMTIME daylightTimeRule; |
421 | }; |
422 | |
423 | // Create default time zone |
424 | QWinTimeZonePrivate(); |
425 | // Create named time zone |
426 | QWinTimeZonePrivate(const QByteArray &ianaId); |
427 | QWinTimeZonePrivate(const QWinTimeZonePrivate &other); |
428 | ~QWinTimeZonePrivate(); |
429 | |
430 | QWinTimeZonePrivate *clone() const override; |
431 | |
432 | QString comment() const override; |
433 | |
434 | using QTimeZonePrivate::displayName; |
435 | QString displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType, |
436 | const QLocale &locale) const override; |
437 | QString abbreviation(qint64 atMSecsSinceEpoch) const override; |
438 | |
439 | int offsetFromUtc(qint64 atMSecsSinceEpoch) const override; |
440 | int standardTimeOffset(qint64 atMSecsSinceEpoch) const override; |
441 | int daylightTimeOffset(qint64 atMSecsSinceEpoch) const override; |
442 | |
443 | bool hasDaylightTime() const override; |
444 | bool isDaylightTime(qint64 atMSecsSinceEpoch) const override; |
445 | |
446 | using QTimeZonePrivate::data; |
447 | Data data(qint64 forMSecsSinceEpoch) const override; |
448 | |
449 | bool hasTransitions() const override; |
450 | Data nextTransition(qint64 afterMSecsSinceEpoch) const override; |
451 | Data previousTransition(qint64 beforeMSecsSinceEpoch) const override; |
452 | |
453 | QByteArray systemTimeZoneId() const override; |
454 | |
455 | QList<QByteArray> availableTimeZoneIds() const override; |
456 | |
457 | // For use within implementation's TransitionTimePair: |
458 | QTimeZonePrivate::Data ruleToData(const QWinTransitionRule &rule, qint64 atMSecsSinceEpoch, |
459 | QTimeZone::TimeType type, bool fakeDst = false) const; |
460 | private: |
461 | void init(const QByteArray &ianaId); |
462 | |
463 | QByteArray m_windowsId; |
464 | QString m_displayName; |
465 | QString m_standardName; |
466 | QString m_daylightName; |
467 | QList<QWinTransitionRule> m_tranRules; |
468 | }; |
469 | #endif // Q_OS_WIN && !icu |
470 | |
471 | #ifdef Q_OS_ANDROID |
472 | class QAndroidTimeZonePrivate final : public QTimeZonePrivate |
473 | { |
474 | public: |
475 | // Create default time zone |
476 | QAndroidTimeZonePrivate(); |
477 | // Create named time zone |
478 | QAndroidTimeZonePrivate(const QByteArray &ianaId); |
479 | QAndroidTimeZonePrivate(const QAndroidTimeZonePrivate &other); |
480 | ~QAndroidTimeZonePrivate(); |
481 | |
482 | QAndroidTimeZonePrivate *clone() const override; |
483 | |
484 | using QTimeZonePrivate::displayName; |
485 | QString displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType, |
486 | const QLocale &locale) const override; |
487 | QString abbreviation(qint64 atMSecsSinceEpoch) const override; |
488 | |
489 | int offsetFromUtc(qint64 atMSecsSinceEpoch) const override; |
490 | int standardTimeOffset(qint64 atMSecsSinceEpoch) const override; |
491 | int daylightTimeOffset(qint64 atMSecsSinceEpoch) const override; |
492 | |
493 | bool hasDaylightTime() const override; |
494 | bool isDaylightTime(qint64 atMSecsSinceEpoch) const override; |
495 | |
496 | using QTimeZonePrivate::data; |
497 | Data data(qint64 forMSecsSinceEpoch) const override; |
498 | |
499 | QByteArray systemTimeZoneId() const override; |
500 | bool isTimeZoneIdAvailable(const QByteArray &ianaId) const override; |
501 | QList<QByteArray> availableTimeZoneIds() const override; |
502 | |
503 | private: |
504 | void init(const QByteArray &zoneId); |
505 | |
506 | QJniObject androidTimeZone; |
507 | |
508 | }; |
509 | #endif // Q_OS_ANDROID |
510 | |
511 | QT_END_NAMESPACE |
512 | |
513 | #endif // QTIMEZONEPRIVATE_P_H |
514 | |