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)
16Q_FORWARD_DECLARE_CF_TYPE(CFTimeZone);
17Q_FORWARD_DECLARE_OBJC_CLASS(NSTimeZone);
18#endif
19
20QT_BEGIN_NAMESPACE
21
22class QTimeZonePrivate;
23
24class 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
84public:
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 &comment = 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 comment() 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
232private:
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)
247Q_DECLARE_TYPEINFO(QTimeZone::OffsetData, Q_RELOCATABLE_TYPE);
248#endif
249Q_DECLARE_SHARED(QTimeZone)
250
251#ifndef QT_NO_DATASTREAM
252Q_CORE_EXPORT QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz);
253Q_CORE_EXPORT QDataStream &operator>>(QDataStream &ds, QTimeZone &tz);
254#endif
255
256#ifndef QT_NO_DEBUG_STREAM
257Q_CORE_EXPORT QDebug operator<<(QDebug dbg, const QTimeZone &tz);
258#endif
259
260#if QT_CONFIG(timezone) && __cpp_lib_chrono >= 201907L
261// zoned_time
262template <typename> // QT_POST_CXX17_API_IN_EXPORTED_CLASS
263inline 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
274QT_END_NAMESPACE
275
276#endif // QTIMEZONE_H
277

source code of qtbase/src/corelib/time/qtimezone.h