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)) && !defined(QT_NO_SYSTEMLOCALE)
17Q_FORWARD_DECLARE_CF_TYPE(CFTimeZone);
18Q_FORWARD_DECLARE_OBJC_CLASS(NSTimeZone);
19#endif
20
21QT_BEGIN_NAMESPACE
22
23class QTimeZonePrivate;
24
25class 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==(const ShortData &lhs, const 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
85public:
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 &comment = 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 QByteArray id() const;
170 QLocale::Territory territory() const;
171# if QT_DEPRECATED_SINCE(6, 6)
172 QT_DEPRECATED_VERSION_X_6_6("Use territory() instead")
173 QLocale::Country country() const;
174# endif
175 QString comment() const;
176
177 QString displayName(const QDateTime &atDateTime,
178 QTimeZone::NameType nameType = QTimeZone::DefaultName,
179 const QLocale &locale = QLocale()) const;
180 QString displayName(QTimeZone::TimeType timeType,
181 QTimeZone::NameType nameType = QTimeZone::DefaultName,
182 const QLocale &locale = QLocale()) const;
183 QString abbreviation(const QDateTime &atDateTime) const;
184
185 int offsetFromUtc(const QDateTime &atDateTime) const;
186 int standardTimeOffset(const QDateTime &atDateTime) const;
187 int daylightTimeOffset(const QDateTime &atDateTime) const;
188
189 bool hasDaylightTime() const;
190 bool isDaylightTime(const QDateTime &atDateTime) const;
191
192 OffsetData offsetData(const QDateTime &forDateTime) const;
193
194 bool hasTransitions() const;
195 OffsetData nextTransition(const QDateTime &afterDateTime) const;
196 OffsetData previousTransition(const QDateTime &beforeDateTime) const;
197 OffsetDataList transitions(const QDateTime &fromDateTime, const QDateTime &toDateTime) const;
198
199 static QByteArray systemTimeZoneId();
200 static QTimeZone systemTimeZone();
201 static QTimeZone utc();
202
203 static bool isTimeZoneIdAvailable(const QByteArray &ianaId);
204
205 static QList<QByteArray> availableTimeZoneIds();
206 static QList<QByteArray> availableTimeZoneIds(QLocale::Territory territory);
207 static QList<QByteArray> availableTimeZoneIds(int offsetSeconds);
208
209 static QByteArray ianaIdToWindowsId(const QByteArray &ianaId);
210 static QByteArray windowsIdToDefaultIanaId(const QByteArray &windowsId);
211 static QByteArray windowsIdToDefaultIanaId(const QByteArray &windowsId,
212 QLocale::Territory territory);
213 static QList<QByteArray> windowsIdToIanaIds(const QByteArray &windowsId);
214 static QList<QByteArray> windowsIdToIanaIds(const QByteArray &windowsId,
215 QLocale::Territory territory);
216
217# if (defined(Q_OS_DARWIN) || defined(Q_QDOC)) && !defined(QT_NO_SYSTEMLOCALE)
218 static QTimeZone fromCFTimeZone(CFTimeZoneRef timeZone);
219 CFTimeZoneRef toCFTimeZone() const Q_DECL_CF_RETURNS_RETAINED;
220 static QTimeZone fromNSTimeZone(const NSTimeZone *timeZone);
221 NSTimeZone *toNSTimeZone() const Q_DECL_NS_RETURNS_AUTORELEASED;
222# endif
223
224# if __cpp_lib_chrono >= 201907L || defined(Q_QDOC)
225 QT_POST_CXX17_API_IN_EXPORTED_CLASS
226 static QTimeZone fromStdTimeZonePtr(const std::chrono::time_zone *timeZone)
227 {
228 if (!timeZone)
229 return QTimeZone();
230 const std::string_view timeZoneName = timeZone->name();
231 return QTimeZone(QByteArrayView(timeZoneName).toByteArray());
232 }
233# endif
234#endif // feature timezone
235private:
236 friend Q_CORE_EXPORT bool comparesEqual(const QTimeZone &lhs, const QTimeZone &rhs) noexcept;
237 Q_DECLARE_EQUALITY_COMPARABLE(QTimeZone)
238
239#ifndef QT_NO_DATASTREAM
240 friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz);
241#endif
242#ifndef QT_NO_DEBUG_STREAM
243 friend Q_CORE_EXPORT QDebug operator<<(QDebug dbg, const QTimeZone &tz);
244#endif
245 QTimeZone(QTimeZonePrivate &dd);
246 friend class QTimeZonePrivate;
247 friend class QDateTime;
248 friend class QDateTimePrivate;
249 Data d;
250};
251
252#if QT_CONFIG(timezone)
253Q_DECLARE_TYPEINFO(QTimeZone::OffsetData, Q_RELOCATABLE_TYPE);
254#endif
255Q_DECLARE_SHARED(QTimeZone)
256
257#ifndef QT_NO_DATASTREAM
258Q_CORE_EXPORT QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz);
259Q_CORE_EXPORT QDataStream &operator>>(QDataStream &ds, QTimeZone &tz);
260#endif
261
262#ifndef QT_NO_DEBUG_STREAM
263Q_CORE_EXPORT QDebug operator<<(QDebug dbg, const QTimeZone &tz);
264#endif
265
266#if QT_CONFIG(timezone) && __cpp_lib_chrono >= 201907L
267// zoned_time
268template <typename> // QT_POST_CXX17_API_IN_EXPORTED_CLASS
269inline QDateTime QDateTime::fromStdZonedTime(const std::chrono::zoned_time<
270 std::chrono::milliseconds,
271 const std::chrono::time_zone *
272 > &time)
273{
274 const auto sysTime = time.get_sys_time();
275 const QTimeZone timeZone = QTimeZone::fromStdTimeZonePtr(timeZone: time.get_time_zone());
276 return fromMSecsSinceEpoch(msecs: sysTime.time_since_epoch().count(), timeZone);
277}
278#endif
279
280QT_END_NAMESPACE
281
282#endif // QTIMEZONE_H
283