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 | #include "qtimezone.h" |
6 | #if QT_CONFIG(timezone) |
7 | # include "qtimezoneprivate_p.h" |
8 | #endif |
9 | |
10 | #include <QtCore/qdatastream.h> |
11 | #include <QtCore/qdatetime.h> |
12 | |
13 | #include <qdebug.h> |
14 | |
15 | #include <algorithm> |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | using namespace Qt::StringLiterals; |
20 | |
21 | #if QT_CONFIG(timezone) |
22 | // Create default time zone using appropriate backend |
23 | static QTimeZonePrivate *newBackendTimeZone() |
24 | { |
25 | #ifdef QT_NO_SYSTEMLOCALE |
26 | #if QT_CONFIG(icu) |
27 | return new QIcuTimeZonePrivate(); |
28 | #else |
29 | return new QUtcTimeZonePrivate(); |
30 | #endif |
31 | #else |
32 | #if defined(Q_OS_DARWIN) |
33 | return new QMacTimeZonePrivate(); |
34 | #elif defined(Q_OS_ANDROID) |
35 | return new QAndroidTimeZonePrivate(); |
36 | #elif defined(Q_OS_UNIX) |
37 | return new QTzTimeZonePrivate(); |
38 | #elif QT_CONFIG(icu) |
39 | return new QIcuTimeZonePrivate(); |
40 | #elif defined(Q_OS_WIN) |
41 | return new QWinTimeZonePrivate(); |
42 | #else |
43 | return new QUtcTimeZonePrivate(); |
44 | #endif // System Locales |
45 | #endif // QT_NO_SYSTEMLOCALE |
46 | } |
47 | |
48 | // Create named time zone using appropriate backend |
49 | static QTimeZonePrivate *newBackendTimeZone(const QByteArray &ianaId) |
50 | { |
51 | Q_ASSERT(!ianaId.isEmpty()); |
52 | #ifdef QT_NO_SYSTEMLOCALE |
53 | #if QT_CONFIG(icu) |
54 | return new QIcuTimeZonePrivate(ianaId); |
55 | #else |
56 | return new QUtcTimeZonePrivate(ianaId); |
57 | #endif |
58 | #else |
59 | #if defined(Q_OS_DARWIN) |
60 | return new QMacTimeZonePrivate(ianaId); |
61 | #elif defined(Q_OS_ANDROID) |
62 | return new QAndroidTimeZonePrivate(ianaId); |
63 | #elif defined(Q_OS_UNIX) |
64 | return new QTzTimeZonePrivate(ianaId); |
65 | #elif QT_CONFIG(icu) |
66 | return new QIcuTimeZonePrivate(ianaId); |
67 | #elif defined(Q_OS_WIN) |
68 | return new QWinTimeZonePrivate(ianaId); |
69 | #else |
70 | return new QUtcTimeZonePrivate(ianaId); |
71 | #endif // System Locales |
72 | #endif // QT_NO_SYSTEMLOCALE |
73 | } |
74 | |
75 | class QTimeZoneSingleton |
76 | { |
77 | public: |
78 | QTimeZoneSingleton() : backend(newBackendTimeZone()) {} |
79 | |
80 | // The global_tz is the tz to use in static methods such as availableTimeZoneIds() and |
81 | // isTimeZoneIdAvailable() and to create named IANA time zones. This is usually the host |
82 | // system, but may be different if the host resources are insufficient or if |
83 | // QT_NO_SYSTEMLOCALE is set. A simple UTC backend is used if no alternative is available. |
84 | QExplicitlySharedDataPointer<QTimeZonePrivate> backend; |
85 | }; |
86 | |
87 | Q_GLOBAL_STATIC(QTimeZoneSingleton, global_tz); |
88 | #endif // feature timezone |
89 | |
90 | /*! |
91 | \class QTimeZone |
92 | \inmodule QtCore |
93 | \since 5.2 |
94 | \threadsafe |
95 | |
96 | \brief QTimeZone identifies how a time representation relates to UTC. |
97 | |
98 | When dates and times are combined, the meaning of the result depends on how |
99 | time is being represented. There are various international standards for |
100 | representing time; one of these, UTC, corresponds to the traditional |
101 | standard of solar mean time at Greenwich (a.k.a. GMT). All other time |
102 | systems supported by Qt are ultimately specified in relation to UTC. An |
103 | instance of this class provides a stateless calculator for conversions |
104 | between UTC and other time representations. |
105 | |
106 | Some time representations are simply defined at a fixed offset to UTC. |
107 | Others are defined by governments for use within their jurisdictions. The |
108 | latter are properly known as time zones, but QTimeZone (since Qt 6.5) is |
109 | unifies their representation with that of general time systems. One time |
110 | zone generally supported on most operating systems is designated local time; |
111 | this is presumed to correspond to the time zone within which the user is |
112 | living. |
113 | |
114 | For time zones other than local time, UTC and those at fixed offsets from |
115 | UTC, Qt can only provide support when the operating system provides some way |
116 | to access that information. When Qt is built, the \c timezone feature |
117 | controls whether such information is available. When it is not, some |
118 | constructors and methods of QTimeZone are excluded from its API; these are |
119 | documented as depending on feature \c timezone. Note that, even when Qt is |
120 | built with this feature enabled, it may be unavailable to users whose |
121 | systems are misconfigured, or where some standard packages (for example, the |
122 | \c tzdata package on Linux) are not installed. This feature is enabled by |
123 | default when time zone information is available. |
124 | |
125 | This class is primarily designed for use in QDateTime; most applications |
126 | will not need to access this class directly and should instead use an |
127 | instance of it when constructing a QDateTime. |
128 | |
129 | \note For consistency with QDateTime, QTimeZone does not account for leap |
130 | seconds. |
131 | |
132 | \section1 Remarks |
133 | |
134 | QTimeZone, like QDateTime, measures offsets from UTC in seconds. This |
135 | contrasts with their measurement of time generally, which they do in |
136 | milliseconds. Real-world time zones generally have UTC offsets that are |
137 | whole-number multiples of five minutes (300 seconds), at least since well |
138 | before 1970. A positive offset from UTC gives a time representation puts |
139 | noon on any given day before UTC noon on that day; a negative offset puts |
140 | noon after UTC noon on the same day. |
141 | |
142 | \section2 Lightweight Time Representations |
143 | |
144 | QTimeZone can represent UTC, local time and fixed offsets from UTC even when |
145 | feature \c timezone is disabled. The form in which it does so is also |
146 | available when the feature is enabled; it is a more lightweight form and |
147 | processing using it will typically be more efficient, unless methods only |
148 | available when feature \c timezone is enabled are being exercised. See \l |
149 | Initialization and \l QTimeZone::fromSecondsAheadOfUtc(int) for how to |
150 | construct these representations. |
151 | |
152 | This documentation distinguishes between "time zone", used to describe a |
153 | time representation described by system-supplied or standard information, |
154 | and time representations more generally, which include these lightweight |
155 | forms. The methods available only when feature \c timezone is enabled are |
156 | apt to be cheaper for time zones than for lightweight time representations, |
157 | for which these methods may construct a suitable transient time zone object |
158 | to which to forward the query. |
159 | |
160 | \section2 IANA Time Zone IDs |
161 | |
162 | QTimeZone uses the IANA time zone IDs as defined in the IANA Time Zone |
163 | Database (http://www.iana.org/time-zones). This is to ensure a standard ID |
164 | across all supported platforms. Most platforms support the IANA IDs |
165 | and the IANA Database natively, but for Windows a mapping is required to |
166 | the native IDs. See below for more details. |
167 | |
168 | The IANA IDs can and do change on a regular basis, and can vary depending |
169 | on how recently the host system data was updated. As such you cannot rely |
170 | on any given ID existing on any host system. You must use |
171 | availableTimeZoneIds() to determine what IANA IDs are available. |
172 | |
173 | The IANA IDs and database are also know as the Olson IDs and database, |
174 | named after the original compiler of the database. |
175 | |
176 | \section2 UTC Offset Time Zones |
177 | |
178 | A default UTC time zone backend is provided which is always available when |
179 | feature \c timezone is enabled. This provides a set of generic Offset From |
180 | UTC time zones in the range UTC-16:00 to UTC+16:00. These time zones can be |
181 | created using either the standard ISO format names, such as "UTC+00:00", as |
182 | listed by availableTimeZoneIds(), or using a name of similar form in |
183 | combination with the number of offset seconds. |
184 | |
185 | \section2 Windows Time Zones |
186 | |
187 | Windows native time zone support is severely limited compared to the |
188 | standard IANA TZ Database. Windows time zones cover larger geographic |
189 | areas and are thus less accurate in their conversions. They also do not |
190 | support as much historical data and so may only be accurate for the |
191 | current year. In particular, when MS's zone data claims that DST was |
192 | observed prior to 1900 (this is historically known to be untrue), the |
193 | claim is ignored and the standard time (allegedly) in force in 1900 is |
194 | taken to have always been in effect. |
195 | |
196 | QTimeZone uses a conversion table derived from the Unicode CLDR data to map |
197 | between IANA IDs and Windows IDs. Depending on your version of Windows |
198 | and Qt, this table may not be able to provide a valid conversion, in which |
199 | "UTC" will be returned. |
200 | |
201 | QTimeZone provides a public API to use this conversion table. The Windows ID |
202 | used is the Windows Registry Key for the time zone which is also the MS |
203 | Exchange EWS ID as well, but is different to the Time Zone Name (TZID) and |
204 | COD code used by MS Exchange in versions before 2007. |
205 | |
206 | \note When Qt is built with the ICU library, it is used in preference to the |
207 | Windows system APIs, bypassing all problems with those APIs using different |
208 | names. |
209 | |
210 | \section2 System Time Zone |
211 | |
212 | The method systemTimeZoneId() returns the current system IANA time zone |
213 | ID which on Unix-like systems will always be correct. On Windows this ID is |
214 | translated from the Windows system ID using an internal translation |
215 | table and the user's selected country. As a consequence there is a small |
216 | chance any Windows install may have IDs not known by Qt, in which case |
217 | "UTC" will be returned. |
218 | |
219 | Creating a new QTimeZone instance using the system time zone ID will only |
220 | produce a fixed named copy of the time zone, it will not change if the |
221 | system time zone changes. QTimeZone::systemTimeZone() will return an |
222 | instance representing the zone named by this system ID. Note that |
223 | constructing a QDateTime using this system zone may behave differently than |
224 | constructing a QDateTime that uses Qt::LocalTime as its Qt::TimeSpec, as the |
225 | latter directly uses system APIs for accessing local time information, which |
226 | may behave differently (and, in particular, might adapt if the user adjusts |
227 | the system zone setting). |
228 | |
229 | \section2 Time Zone Offsets |
230 | |
231 | The difference between UTC and the local time in a time zone is expressed |
232 | as an offset in seconds from UTC, i.e. the number of seconds to add to UTC |
233 | to obtain the local time. The total offset is comprised of two component |
234 | parts, the standard time offset and the daylight-saving time offset. The |
235 | standard time offset is the number of seconds to add to UTC to obtain |
236 | standard time in the time zone. The daylight-saving time offset is the |
237 | number of seconds to add to the standard time offset to obtain |
238 | daylight-saving time (abbreviated DST and sometimes called "daylight time" |
239 | or "summer time") in the time zone. The usual case for DST (using |
240 | standard time in winter, DST in summer) has a positive daylight-saving |
241 | time offset. However, some zones have negative DST offsets, used in |
242 | winter, with summer using standard time. |
243 | |
244 | Note that the standard and DST offsets for a time zone may change over time |
245 | as countries have changed DST laws or even their standard time offset. |
246 | |
247 | \section2 License |
248 | |
249 | This class includes data obtained from the CLDR data files under the terms |
250 | of the Unicode Data Files and Software License. See |
251 | \l{unicode-cldr}{Unicode Common Locale Data Repository (CLDR)} for details. |
252 | |
253 | \sa QDateTime, QCalendar |
254 | */ |
255 | |
256 | /*! |
257 | \variable QTimeZone::MinUtcOffsetSecs |
258 | \brief Timezone offsets from UTC are expected to be no lower than this. |
259 | |
260 | The lowest UTC offset of any early 21st century timezone is -12 hours (Baker |
261 | Island, USA), or 12 hours west of Greenwich. |
262 | |
263 | Historically, until 1844, The Philippines (then controlled by Spain) used |
264 | the same date as Spain's American holdings, so had offsets close to 16 hours |
265 | west of Greenwich. As The Philippines was using local solar mean time, it is |
266 | possible some outlying territory of it may have been operating at more than |
267 | 16 hours west of Greenwich, but no early 21st century timezone traces its |
268 | history back to such an extreme. |
269 | |
270 | \sa MaxUtcOffsetSecs |
271 | */ |
272 | /*! |
273 | \variable QTimeZone::MaxUtcOffsetSecs |
274 | \brief Timezone offsets from UTC are expected to be no higher than this. |
275 | |
276 | The highest UTC offset of any early 21st century timezone is +14 hours |
277 | (Christmas Island, Kiribati, Kiritimati), or 14 hours east of Greenwich. |
278 | |
279 | Historically, before 1867, when Russia sold Alaska to America, Alaska used |
280 | the same date as Russia, so had offsets over 15 hours east of Greenwich. As |
281 | Alaska was using local solar mean time, its offsets varied, but all were |
282 | less than 16 hours east of Greenwich. |
283 | |
284 | \sa MinUtcOffsetSecs |
285 | */ |
286 | |
287 | #if QT_CONFIG(timezone) |
288 | /*! |
289 | \enum QTimeZone::TimeType |
290 | |
291 | The type of time zone time, for example when requesting the name. In time |
292 | zones that do not apply DST, all three values may return the same result. |
293 | |
294 | \value StandardTime |
295 | The standard time in a time zone, i.e. when Daylight-Saving is not |
296 | in effect. |
297 | For example when formatting a display name this will show something |
298 | like "Pacific Standard Time". |
299 | \value DaylightTime |
300 | A time when Daylight-Saving is in effect. |
301 | For example when formatting a display name this will show something |
302 | like "Pacific daylight-saving time". |
303 | \value GenericTime |
304 | A time which is not specifically Standard or Daylight-Saving time, |
305 | either an unknown time or a neutral form. |
306 | For example when formatting a display name this will show something |
307 | like "Pacific Time". |
308 | |
309 | This type is only available when feature \c timezone is enabled. |
310 | */ |
311 | |
312 | /*! |
313 | \enum QTimeZone::NameType |
314 | |
315 | The type of time zone name. |
316 | |
317 | \value DefaultName |
318 | The default form of the time zone name, e.g. LongName, ShortName or OffsetName |
319 | \value LongName |
320 | The long form of the time zone name, e.g. "Central European Time" |
321 | \value ShortName |
322 | The short form of the time zone name, usually an abbreviation, e.g. "CET" |
323 | \value OffsetName |
324 | The standard ISO offset form of the time zone name, e.g. "UTC+01:00" |
325 | |
326 | This type is only available when feature \c timezone is enabled. |
327 | */ |
328 | |
329 | /*! |
330 | \class QTimeZone::OffsetData |
331 | \inmodule QtCore |
332 | |
333 | The time zone offset data for a given moment in time. |
334 | |
335 | This provides the time zone offsets and abbreviation to use at that moment |
336 | in time. When a function returns this type, it may use an invalid datetime |
337 | to indicate that the query it is answering has no valid answer, so check |
338 | \c{atUtc.isValid()} before using the results. |
339 | |
340 | \list |
341 | \li OffsetData::atUtc The datetime of the offset data in UTC time. |
342 | \li OffsetData::offsetFromUtc The total offset from UTC in effect at the datetime. |
343 | \li OffsetData::standardTimeOffset The standard time offset component of the total offset. |
344 | \li OffsetData::daylightTimeOffset The DST offset component of the total offset. |
345 | \li OffsetData::abbreviation The abbreviation in effect at the datetime. |
346 | \endlist |
347 | |
348 | For example, for time zone "Europe/Berlin" the OffsetDate in standard and DST might be: |
349 | |
350 | \list |
351 | \li atUtc = QDateTime(QDate(2013, 1, 1), QTime(0, 0), QTimeZone::UTC) |
352 | \li offsetFromUtc = 3600 |
353 | \li standardTimeOffset = 3600 |
354 | \li daylightTimeOffset = 0 |
355 | \li abbreviation = "CET" |
356 | \endlist |
357 | |
358 | \list |
359 | \li atUtc = QDateTime(QDate(2013, 6, 1), QTime(0, 0), QTimeZone::UTC) |
360 | \li offsetFromUtc = 7200 |
361 | \li standardTimeOffset = 3600 |
362 | \li daylightTimeOffset = 3600 |
363 | \li abbreviation = "CEST" |
364 | \endlist |
365 | |
366 | This type is only available when feature \c timezone is enabled. |
367 | */ |
368 | |
369 | /*! |
370 | \typedef QTimeZone::OffsetDataList |
371 | |
372 | Synonym for QList<OffsetData>. |
373 | |
374 | This type is only available when feature \c timezone is enabled. |
375 | */ |
376 | #endif // timezone backends |
377 | |
378 | QTimeZone::Data::Data() noexcept : d(nullptr) |
379 | { |
380 | // Assumed by the conversion between spec and mode: |
381 | static_assert(int(Qt::TimeZone) == 3); |
382 | } |
383 | |
384 | QTimeZone::Data::Data(const Data &other) noexcept |
385 | { |
386 | #if QT_CONFIG(timezone) |
387 | if (!other.isShort() && other.d) |
388 | other.d->ref.ref(); |
389 | #endif |
390 | d = other.d; |
391 | } |
392 | |
393 | QTimeZone::Data::Data(QTimeZonePrivate *dptr) noexcept |
394 | : d(dptr) |
395 | { |
396 | #if QT_CONFIG(timezone) |
397 | if (d) |
398 | d->ref.ref(); |
399 | #endif |
400 | } |
401 | |
402 | QTimeZone::Data::~Data() |
403 | { |
404 | #if QT_CONFIG(timezone) |
405 | if (!isShort() && d && !d->ref.deref()) |
406 | delete d; |
407 | d = nullptr; |
408 | #endif |
409 | } |
410 | |
411 | QTimeZone::Data &QTimeZone::Data::operator=(const QTimeZone::Data &other) noexcept |
412 | { |
413 | #if QT_CONFIG(timezone) |
414 | if (!other.isShort()) |
415 | return *this = other.d; |
416 | if (!isShort() && d && !d->ref.deref()) |
417 | delete d; |
418 | #endif |
419 | d = other.d; |
420 | return *this; |
421 | } |
422 | |
423 | /*! |
424 | Create a null/invalid time zone instance. |
425 | */ |
426 | |
427 | QTimeZone::QTimeZone() noexcept |
428 | { |
429 | // Assumed by (at least) Data::swap() and {copy,move} {assign,construct}: |
430 | static_assert(sizeof(ShortData) <= sizeof(Data::d)); |
431 | // Needed for ShortData::offset to represent all valid offsets: |
432 | static_assert(qintptr(1) << (sizeof(void *) * 8 - 2) >= MaxUtcOffsetSecs); |
433 | } |
434 | |
435 | #if QT_CONFIG(timezone) |
436 | QTimeZone::Data &QTimeZone::Data::operator=(QTimeZonePrivate *dptr) noexcept |
437 | { |
438 | if (!isShort()) { |
439 | if (d == dptr) |
440 | return *this; |
441 | if (d && !d->ref.deref()) |
442 | delete d; |
443 | } |
444 | if (dptr) |
445 | dptr->ref.ref(); |
446 | d = dptr; |
447 | Q_ASSERT(!isShort()); |
448 | return *this; |
449 | } |
450 | |
451 | /*! |
452 | Creates a time zone instance with the requested IANA ID \a ianaId. |
453 | |
454 | The ID must be one of the available system IDs or a valid UTC-with-offset |
455 | ID, otherwise an invalid time zone will be returned. |
456 | |
457 | This constructor is only available when feature \c timezone is enabled. |
458 | |
459 | \sa availableTimeZoneIds() |
460 | */ |
461 | |
462 | QTimeZone::QTimeZone(const QByteArray &ianaId) |
463 | { |
464 | // Try and see if it's a CLDR UTC offset ID - just as quick by creating as |
465 | // by looking up. |
466 | d = new QUtcTimeZonePrivate(ianaId); |
467 | // If not a CLDR UTC offset ID then try creating it with the system backend. |
468 | // Relies on backend not creating valid TZ with invalid name. |
469 | if (!d->isValid()) { |
470 | if (ianaId.isEmpty()) |
471 | d = newBackendTimeZone(); |
472 | else if (global_tz->backend->isTimeZoneIdAvailable(ianaId)) |
473 | d = newBackendTimeZone(ianaId); |
474 | // else: No such ID, avoid creating a TZ cache entry for it. |
475 | } |
476 | // Can also handle UTC with arbitrary (valid) offset, but only do so as |
477 | // fall-back, since either of the above may handle it more informatively. |
478 | if (!d->isValid()) { |
479 | qint64 offset = QUtcTimeZonePrivate::offsetFromUtcString(id: ianaId); |
480 | if (offset != QTimeZonePrivate::invalidSeconds()) { |
481 | // Should have abs(offset) < 24 * 60 * 60 = 86400. |
482 | qint32 seconds = qint32(offset); |
483 | Q_ASSERT(qint64(seconds) == offset); |
484 | // NB: this canonicalises the name, so it might not match ianaId |
485 | d = new QUtcTimeZonePrivate(seconds); |
486 | } |
487 | } |
488 | } |
489 | |
490 | /*! |
491 | Creates a time zone instance with the given offset, \a offsetSeconds, from UTC. |
492 | |
493 | The \a offsetSeconds from UTC must be in the range -16 hours to +16 hours |
494 | otherwise an invalid time zone will be returned. |
495 | |
496 | This constructor is only available when feature \c timezone is enabled. The |
497 | returned instance is equivalent to the lightweight time representation |
498 | \c{QTimeZone::fromSecondsAfterUtc(offsetSeconds)}, albeit implemented as a |
499 | time zone. |
500 | |
501 | \sa MinUtcOffsetSecs, MaxUtcOffsetSecs |
502 | */ |
503 | |
504 | QTimeZone::QTimeZone(int offsetSeconds) |
505 | : d((offsetSeconds >= MinUtcOffsetSecs && offsetSeconds <= MaxUtcOffsetSecs) |
506 | ? new QUtcTimeZonePrivate(offsetSeconds) : nullptr) |
507 | { |
508 | } |
509 | |
510 | /*! |
511 | Creates a custom time zone instance at fixed offset from UTC. |
512 | |
513 | The returned time zone has an ID of \a ianaId and an offset from UTC of \a |
514 | offsetSeconds. The \a name will be the name used by displayName() for the |
515 | LongName, the \a abbreviation will be used by displayName() for the |
516 | ShortName and by abbreviation(), and the optional \a territory will be used |
517 | by territory(). The \a comment is an optional note that may be displayed in |
518 | a GUI to assist users in selecting a time zone. |
519 | |
520 | The \a ianaId must not be one of the available system IDs returned by |
521 | availableTimeZoneIds(). The \a offsetSeconds from UTC must be in the range |
522 | -16 hours to +16 hours. |
523 | |
524 | If the custom time zone does not have a specific territory then set it to the |
525 | default value of QLocale::AnyTerritory. |
526 | |
527 | This constructor is only available when feature \c timezone is enabled. |
528 | |
529 | \sa id(), offsetFromUtc(), displayName(), abbreviation(), territory(), comment(), |
530 | MinUtcOffsetSecs, MaxUtcOffsetSecs |
531 | */ |
532 | |
533 | QTimeZone::QTimeZone(const QByteArray &ianaId, int offsetSeconds, const QString &name, |
534 | const QString &abbreviation, QLocale::Territory territory, const QString &) |
535 | : d(isTimeZoneIdAvailable(ianaId) ? nullptr // Don't let client code hijack a real zone name. |
536 | : new QUtcTimeZonePrivate(ianaId, offsetSeconds, name, abbreviation, territory, comment)) |
537 | { |
538 | } |
539 | |
540 | /*! |
541 | \internal |
542 | |
543 | Private. Create time zone with given private backend |
544 | |
545 | This constructor is only available when feature \c timezone is enabled. |
546 | */ |
547 | |
548 | QTimeZone::QTimeZone(QTimeZonePrivate &dd) |
549 | : d(&dd) |
550 | { |
551 | } |
552 | |
553 | /*! |
554 | \since 6.5 |
555 | Converts this QTimeZone to one whose timeSpec() is Qt::TimeZone. |
556 | |
557 | In all cases, the result's \l timeSpec() is Qt::TimeZone. When this |
558 | QTimeZone's timeSpec() is Qt::TimeZone, this QTimeZone itself is returned. |
559 | |
560 | If timeSpec() is Qt::UTC, QTimeZone::utc() is returned. If it is |
561 | Qt::OffsetFromUTC then QTimeZone(int) is passed its offset and the result is |
562 | returned. |
563 | |
564 | If timeSpec() is Qt::LocalTime then an instance of the current system time |
565 | zone will be returned. This will not change to reflect any subsequent change |
566 | to the system time zone. It represents the local time that was in effect |
567 | when asBackendZone() was called. |
568 | |
569 | When using a lightweight time representation - local time, UTC time or time |
570 | at a fixed offset from UTC - using methods only supported when feature \c |
571 | timezone is enabled may be more expensive than using a corresponding time |
572 | zone. This method maps a lightweight time representation to a corresponding |
573 | time zone - that is, an instance based on system-supplied or standard data. |
574 | |
575 | This method is only available when feature \c timezone is enabled. |
576 | |
577 | \sa QTimeZone(QTimeZone::Initialization), fromSecondsAheadOfUtc() |
578 | */ |
579 | |
580 | QTimeZone QTimeZone::asBackendZone() const |
581 | { |
582 | switch (timeSpec()) { |
583 | case Qt::TimeZone: |
584 | return *this; |
585 | case Qt::LocalTime: |
586 | return systemTimeZone(); |
587 | case Qt::UTC: |
588 | return utc(); |
589 | case Qt::OffsetFromUTC: |
590 | return QTimeZone(*new QUtcTimeZonePrivate(int(d.s.offset))); |
591 | } |
592 | return QTimeZone(); |
593 | } |
594 | #endif // timezone backends |
595 | |
596 | /*! |
597 | \since 6.5 |
598 | \enum QTimeZone::Initialization |
599 | |
600 | The type of the simplest lightweight time representations. |
601 | |
602 | This enumeration identifies a type of lightweight time representation to |
603 | pass to a QTimeZone constructor, where no further data are required. They |
604 | correspond to the like-named members of Qt::TimeSpec. |
605 | |
606 | \value LocalTime This time representation corresponds to the one implicitly |
607 | used by system functions using \c time_t and \c {struct tm} |
608 | value to map between local time and UTC time. |
609 | |
610 | \value UTC This time representation, Coordinated Universal Time, is the base |
611 | representation to which civil time is referred in all supported |
612 | time representations. It is defined by the International |
613 | Telecommunication Union. |
614 | */ |
615 | |
616 | /*! |
617 | \since 6.5 |
618 | \fn QTimeZone::QTimeZone(Initialization spec) noexcept |
619 | |
620 | Creates a lightweight instance describing UTC or local time. |
621 | |
622 | \sa fromSecondsAheadOfUtc(), asBackendZone(), utc(), systemTimeZone() |
623 | */ |
624 | |
625 | /*! |
626 | \since 6.5 |
627 | \fn QTimeZone::fromSecondsAheadOfUtc(int offset) |
628 | \fn QTimeZone::fromDurationAheadOfUtc(std::chrono::seconds offset) |
629 | |
630 | Returns a time representation at a fixed \a offset, in seconds, ahead of |
631 | UTC. |
632 | |
633 | The \a offset from UTC must be in the range -16 hours to +16 hours otherwise |
634 | an invalid time zone will be returned. The returned QTimeZone is a |
635 | lightweight time representation, not a time zone (backed by system-supplied |
636 | or standard data). |
637 | |
638 | If the offset is 0, the \l timeSpec() of the returned instance will be |
639 | Qt::UTC. Otherwise, if \a offset is valid, timeSpec() is |
640 | Qt::OffsetFromUTC. An invalid time zone, when returned, has Qt::TimeZone as |
641 | its timeSpec(). |
642 | |
643 | \sa QTimeZone(int), asBackendZone(), fixedSecondsAheadOfUtc(), |
644 | MinUtcOffsetSecs, MaxUtcOffsetSecs |
645 | */ |
646 | |
647 | /*! |
648 | \since 6.5 |
649 | \fn Qt::TimeSpec QTimeZone::timeSpec() const noexcept |
650 | |
651 | Returns a Qt::TimeSpec identifying the type of time representation. |
652 | |
653 | If the result is Qt::TimeZone, this time description is a time zone (backed |
654 | by system-supplied or standard data); otherwise, it is a lightweight time |
655 | representation. If the result is Qt::LocalTime it describes local time: see |
656 | Qt::TimeSpec for details. |
657 | |
658 | \sa fixedSecondsAheadOfUtc(), asBackendZone() |
659 | */ |
660 | |
661 | /*! |
662 | \since 6.5 |
663 | \fn int QTimeZone::fixedSecondsAheadOfUtc() const noexcept |
664 | |
665 | For a lightweight time representation whose \l timeSpec() is Qt::OffsetFromUTC, |
666 | this returns the fixed offset from UTC that it describes. For any other time |
667 | representation it returns 0, even if that time representation does have a |
668 | constant offset from UTC. |
669 | */ |
670 | |
671 | /*! |
672 | \since 6.5 |
673 | \fn QTimeZone::isUtcOrFixedOffset(Qt::TimeSpec spec) noexcept |
674 | |
675 | Returns \c true if \a spec is Qt::UTC or Qt::OffsetFromUTC. |
676 | */ |
677 | |
678 | /*! |
679 | \since 6.5 |
680 | \fn QTimeZone::isUtcOrFixedOffset() const noexcept |
681 | |
682 | Returns \c true if \l timeSpec() is Qt::UTC or Qt::OffsetFromUTC. |
683 | |
684 | When it is true, the time description does not change over time, such as |
685 | having seasonal daylight-saving changes, as may happen for local time or a |
686 | time zone. Knowing this may save the calling code to need for various other |
687 | checks. |
688 | */ |
689 | |
690 | /*! |
691 | Copy constructor: copy \a other to this. |
692 | */ |
693 | |
694 | QTimeZone::QTimeZone(const QTimeZone &other) noexcept |
695 | : d(other.d) |
696 | { |
697 | } |
698 | |
699 | /*! |
700 | \fn QTimeZone::QTimeZone(QTimeZone &&other) noexcept |
701 | |
702 | Move constructor of this from \a other. |
703 | */ |
704 | |
705 | /*! |
706 | Destroys the time zone. |
707 | */ |
708 | |
709 | QTimeZone::~QTimeZone() |
710 | { |
711 | } |
712 | |
713 | /*! |
714 | \fn QTimeZone::swap(QTimeZone &other) noexcept |
715 | |
716 | Swaps this time zone instance with \a other. This function is very |
717 | fast and never fails. |
718 | */ |
719 | |
720 | /*! |
721 | Assignment operator, assign \a other to this. |
722 | */ |
723 | |
724 | QTimeZone &QTimeZone::operator=(const QTimeZone &other) |
725 | { |
726 | d = other.d; |
727 | return *this; |
728 | } |
729 | |
730 | /*! |
731 | \fn QTimeZone &QTimeZone::operator=(QTimeZone &&other) |
732 | |
733 | Move-assigns \a other to this QTimeZone instance, transferring the ownership |
734 | of its data to this instance. |
735 | */ |
736 | |
737 | /*! |
738 | Returns \c true if this time representation is equal to the \a other. |
739 | |
740 | Two representations are different if they are internally described |
741 | differently, even if they agree in their representation of all moments of |
742 | time. In particular, a lightweight time representation may coincide with a |
743 | time zone but the two will not be equal. |
744 | */ |
745 | |
746 | bool QTimeZone::operator==(const QTimeZone &other) const |
747 | { |
748 | if (d.isShort()) |
749 | return other.d.isShort() && d.s == other.d.s; |
750 | |
751 | if (!other.d.isShort()) { |
752 | if (d.d == other.d.d) |
753 | return true; |
754 | #if QT_CONFIG(timezone) |
755 | return d.d && other.d.d && *d.d == *other.d.d; |
756 | #endif |
757 | } |
758 | |
759 | return false; |
760 | } |
761 | |
762 | /*! |
763 | Returns \c true if this time zone is not equal to the \a other time zone. |
764 | |
765 | Two representations are different if they are internally described |
766 | differently, even if they agree in their representation of all moments of |
767 | time. In particular, a lightweight time representation may coincide with a |
768 | time zone but the two will not be equal. |
769 | */ |
770 | bool QTimeZone::operator!=(const QTimeZone &other) const // ### Qt 7: inline |
771 | { |
772 | return !(*this == other); |
773 | } |
774 | |
775 | /*! |
776 | Returns \c true if this time zone is valid. |
777 | */ |
778 | |
779 | bool QTimeZone::isValid() const |
780 | { |
781 | #if QT_CONFIG(timezone) |
782 | if (!d.isShort()) |
783 | return d.d && d->isValid(); |
784 | #endif |
785 | return d.isShort(); |
786 | } |
787 | |
788 | #if QT_CONFIG(timezone) |
789 | /*! |
790 | Returns the IANA ID for the time zone. |
791 | |
792 | IANA IDs are used on all platforms. On Windows these are translated from |
793 | the Windows ID into the best match IANA ID for the time zone and territory. |
794 | |
795 | This method is only available when feature \c timezone is enabled. |
796 | */ |
797 | |
798 | QByteArray QTimeZone::id() const |
799 | { |
800 | if (d.isShort()) { |
801 | switch (d.s.spec()) { |
802 | case Qt::UTC: |
803 | return QTimeZonePrivate::utcQByteArray(); |
804 | case Qt::LocalTime: |
805 | return systemTimeZoneId(); |
806 | case Qt::OffsetFromUTC: |
807 | return QUtcTimeZonePrivate(d.s.offset).id(); |
808 | case Qt::TimeZone: |
809 | Q_UNREACHABLE(); |
810 | break; |
811 | } |
812 | } else if (d.d) { |
813 | return d->id(); |
814 | } |
815 | return QByteArray(); |
816 | } |
817 | |
818 | /*! |
819 | \since 6.2 |
820 | |
821 | Returns the territory for the time zone. |
822 | |
823 | A return of \l {QLocale::}{AnyTerritory} means the zone has no known |
824 | territorial association. In some cases this may be because the zone has no |
825 | associated territory - for example, UTC - or because the zone is used in |
826 | several territories - for example, CET. In other cases, the QTimeZone |
827 | backend may not know which territory the zone is associated with - for |
828 | example, because it is not the primary zone of the territory in which it is |
829 | used. |
830 | |
831 | This method is only available when feature \c timezone is enabled. |
832 | */ |
833 | QLocale::Territory QTimeZone::territory() const |
834 | { |
835 | if (d.isShort()) { |
836 | if (d.s.spec() == Qt::LocalTime) |
837 | return systemTimeZone().territory(); |
838 | } else if (isValid()) { |
839 | return d->territory(); |
840 | } |
841 | return QLocale::AnyTerritory; |
842 | } |
843 | |
844 | #if QT_DEPRECATED_SINCE(6, 6) |
845 | /*! |
846 | \deprecated [6.6] Use territory() instead. |
847 | |
848 | Returns the territory for the time zone. |
849 | |
850 | This method is only available when feature \c timezone is enabled. |
851 | */ |
852 | |
853 | QLocale::Country QTimeZone::country() const |
854 | { |
855 | return territory(); |
856 | } |
857 | #endif |
858 | |
859 | /*! |
860 | Returns any comment for the time zone. |
861 | |
862 | A comment may be provided by the host platform to assist users in |
863 | choosing the correct time zone. Depending on the platform this may not |
864 | be localized. |
865 | |
866 | This method is only available when feature \c timezone is enabled. |
867 | */ |
868 | |
869 | QString QTimeZone::() const |
870 | { |
871 | if (d.isShort()) { |
872 | // TODO: anything ? Or just stick with empty string ? |
873 | } else if (isValid()) { |
874 | return d->comment(); |
875 | } |
876 | return QString(); |
877 | } |
878 | |
879 | /*! |
880 | Returns the localized time zone display name at the given \a atDateTime |
881 | for the given \a nameType in the given \a locale. The \a nameType and |
882 | \a locale requested may not be supported on all platforms, in which case |
883 | the best available option will be returned. |
884 | |
885 | If the \a locale is not provided then the application default locale will |
886 | be used. |
887 | |
888 | The display name may change depending on DST or historical events. |
889 | |
890 | This method is only available when feature \c timezone is enabled. |
891 | |
892 | \sa abbreviation() |
893 | */ |
894 | |
895 | QString QTimeZone::displayName(const QDateTime &atDateTime, NameType nameType, |
896 | const QLocale &locale) const |
897 | { |
898 | if (d.isShort()) { |
899 | switch (d.s.spec()) { |
900 | case Qt::LocalTime: |
901 | return systemTimeZone().displayName(atDateTime, nameType, locale); |
902 | case Qt::UTC: |
903 | case Qt::OffsetFromUTC: |
904 | return QUtcTimeZonePrivate(d.s.offset).QTimeZonePrivate::displayName( |
905 | atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch(), nameType, locale); |
906 | case Qt::TimeZone: |
907 | Q_UNREACHABLE(); |
908 | break; |
909 | } |
910 | } else if (isValid()) { |
911 | return d->displayName(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch(), nameType, locale); |
912 | } |
913 | |
914 | return QString(); |
915 | } |
916 | |
917 | /*! |
918 | Returns the localized time zone display name for the given \a timeType |
919 | and \a nameType in the given \a locale. The \a nameType and \a locale |
920 | requested may not be supported on all platforms, in which case the best |
921 | available option will be returned. |
922 | |
923 | If the \a locale is not provided then the application default locale will |
924 | be used. |
925 | |
926 | Where the time zone display names have changed over time then the most |
927 | recent names will be used. |
928 | |
929 | This method is only available when feature \c timezone is enabled. |
930 | |
931 | \sa abbreviation() |
932 | */ |
933 | |
934 | QString QTimeZone::displayName(TimeType timeType, NameType nameType, |
935 | const QLocale &locale) const |
936 | { |
937 | if (d.isShort()) { |
938 | switch (d.s.spec()) { |
939 | case Qt::LocalTime: |
940 | return systemTimeZone().displayName(timeType, nameType, locale); |
941 | case Qt::UTC: |
942 | case Qt::OffsetFromUTC: |
943 | return QUtcTimeZonePrivate(d.s.offset).displayName(timeType, nameType, locale); |
944 | case Qt::TimeZone: |
945 | Q_UNREACHABLE(); |
946 | break; |
947 | } |
948 | } else if (isValid()) { |
949 | return d->displayName(timeType, nameType, locale); |
950 | } |
951 | |
952 | return QString(); |
953 | } |
954 | |
955 | /*! |
956 | Returns the time zone abbreviation at the given \a atDateTime. The |
957 | abbreviation may change depending on DST or even historical events. |
958 | |
959 | Note that the abbreviation is not guaranteed to be unique to this time zone |
960 | and should not be used in place of the ID or display name. |
961 | |
962 | This method is only available when feature \c timezone is enabled. |
963 | |
964 | \sa displayName() |
965 | */ |
966 | |
967 | QString QTimeZone::abbreviation(const QDateTime &atDateTime) const |
968 | { |
969 | if (d.isShort()) { |
970 | switch (d.s.spec()) { |
971 | case Qt::LocalTime: |
972 | return systemTimeZone().abbreviation(atDateTime); |
973 | case Qt::UTC: |
974 | case Qt::OffsetFromUTC: |
975 | return QUtcTimeZonePrivate(d.s.offset).abbreviation(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch()); |
976 | case Qt::TimeZone: |
977 | Q_UNREACHABLE(); |
978 | break; |
979 | } |
980 | } else if (isValid()) { |
981 | return d->abbreviation(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch()); |
982 | } |
983 | |
984 | return QString(); |
985 | } |
986 | |
987 | /*! |
988 | Returns the total effective offset at the given \a atDateTime, i.e. the |
989 | number of seconds to add to UTC to obtain the local time. This includes |
990 | any DST offset that may be in effect, i.e. it is the sum of |
991 | standardTimeOffset() and daylightTimeOffset() for the given datetime. |
992 | |
993 | For example, for the time zone "Europe/Berlin" the standard time offset is |
994 | +3600 seconds and the DST offset is +3600 seconds. During standard time |
995 | offsetFromUtc() will return +3600 (UTC+01:00), and during DST it will |
996 | return +7200 (UTC+02:00). |
997 | |
998 | This method is only available when feature \c timezone is enabled. |
999 | |
1000 | \sa standardTimeOffset(), daylightTimeOffset() |
1001 | */ |
1002 | |
1003 | int QTimeZone::offsetFromUtc(const QDateTime &atDateTime) const |
1004 | { |
1005 | if (d.isShort()) { |
1006 | switch (d.s.spec()) { |
1007 | case Qt::LocalTime: |
1008 | return systemTimeZone().offsetFromUtc(atDateTime); |
1009 | case Qt::UTC: |
1010 | case Qt::OffsetFromUTC: |
1011 | return d.s.offset; |
1012 | case Qt::TimeZone: |
1013 | Q_UNREACHABLE(); |
1014 | break; |
1015 | } |
1016 | } else if (isValid()) { |
1017 | const int offset = d->offsetFromUtc(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch()); |
1018 | if (offset != QTimeZonePrivate::invalidSeconds()) |
1019 | return offset; |
1020 | } |
1021 | return 0; |
1022 | } |
1023 | |
1024 | /*! |
1025 | Returns the standard time offset at the given \a atDateTime, i.e. the |
1026 | number of seconds to add to UTC to obtain the local Standard Time. This |
1027 | excludes any DST offset that may be in effect. |
1028 | |
1029 | For example, for the time zone "Europe/Berlin" the standard time offset is |
1030 | +3600 seconds. During both standard and DST offsetFromUtc() will return |
1031 | +3600 (UTC+01:00). |
1032 | |
1033 | This method is only available when feature \c timezone is enabled. |
1034 | |
1035 | \sa offsetFromUtc(), daylightTimeOffset() |
1036 | */ |
1037 | |
1038 | int QTimeZone::standardTimeOffset(const QDateTime &atDateTime) const |
1039 | { |
1040 | if (d.isShort()) { |
1041 | switch (d.s.spec()) { |
1042 | case Qt::LocalTime: |
1043 | return systemTimeZone().standardTimeOffset(atDateTime); |
1044 | case Qt::UTC: |
1045 | case Qt::OffsetFromUTC: |
1046 | return d.s.offset; |
1047 | case Qt::TimeZone: |
1048 | Q_UNREACHABLE(); |
1049 | break; |
1050 | } |
1051 | } else if (isValid()) { |
1052 | const int offset = d->standardTimeOffset(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch()); |
1053 | if (offset != QTimeZonePrivate::invalidSeconds()) |
1054 | return offset; |
1055 | } |
1056 | return 0; |
1057 | } |
1058 | |
1059 | /*! |
1060 | Returns the daylight-saving time offset at the given \a atDateTime, |
1061 | i.e. the number of seconds to add to the standard time offset to obtain the |
1062 | local daylight-saving time. |
1063 | |
1064 | For example, for the time zone "Europe/Berlin" the DST offset is +3600 |
1065 | seconds. During standard time daylightTimeOffset() will return 0, and when |
1066 | daylight-saving is in effect it will return +3600. |
1067 | |
1068 | This method is only available when feature \c timezone is enabled. |
1069 | |
1070 | \sa offsetFromUtc(), standardTimeOffset() |
1071 | */ |
1072 | |
1073 | int QTimeZone::daylightTimeOffset(const QDateTime &atDateTime) const |
1074 | { |
1075 | if (d.isShort()) { |
1076 | switch (d.s.spec()) { |
1077 | case Qt::LocalTime: |
1078 | return systemTimeZone().daylightTimeOffset(atDateTime); |
1079 | case Qt::UTC: |
1080 | case Qt::OffsetFromUTC: |
1081 | return 0; |
1082 | case Qt::TimeZone: |
1083 | Q_UNREACHABLE(); |
1084 | break; |
1085 | } |
1086 | } else if (hasDaylightTime()) { |
1087 | const int offset = d->daylightTimeOffset(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch()); |
1088 | if (offset != QTimeZonePrivate::invalidSeconds()) |
1089 | return offset; |
1090 | } |
1091 | return 0; |
1092 | } |
1093 | |
1094 | /*! |
1095 | Returns \c true if the time zone has practiced daylight-saving at any time. |
1096 | |
1097 | This method is only available when feature \c timezone is enabled. |
1098 | |
1099 | \sa isDaylightTime(), daylightTimeOffset() |
1100 | */ |
1101 | |
1102 | bool QTimeZone::hasDaylightTime() const |
1103 | { |
1104 | if (d.isShort()) { |
1105 | switch (d.s.spec()) { |
1106 | case Qt::LocalTime: |
1107 | return systemTimeZone().hasDaylightTime(); |
1108 | case Qt::UTC: |
1109 | case Qt::OffsetFromUTC: |
1110 | return false; |
1111 | case Qt::TimeZone: |
1112 | Q_UNREACHABLE(); |
1113 | break; |
1114 | } |
1115 | } else if (isValid()) { |
1116 | return d->hasDaylightTime(); |
1117 | } |
1118 | return false; |
1119 | } |
1120 | |
1121 | /*! |
1122 | Returns \c true if daylight-saving was in effect at the given \a atDateTime. |
1123 | |
1124 | This method is only available when feature \c timezone is enabled. |
1125 | |
1126 | \sa hasDaylightTime(), daylightTimeOffset() |
1127 | */ |
1128 | |
1129 | bool QTimeZone::isDaylightTime(const QDateTime &atDateTime) const |
1130 | { |
1131 | if (d.isShort()) { |
1132 | switch (d.s.spec()) { |
1133 | case Qt::LocalTime: |
1134 | return systemTimeZone().isDaylightTime(atDateTime); |
1135 | case Qt::UTC: |
1136 | case Qt::OffsetFromUTC: |
1137 | return false; |
1138 | case Qt::TimeZone: |
1139 | Q_UNREACHABLE(); |
1140 | break; |
1141 | } |
1142 | } else if (hasDaylightTime()) { |
1143 | return d->isDaylightTime(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch()); |
1144 | } |
1145 | return false; |
1146 | } |
1147 | |
1148 | /*! |
1149 | Returns the effective offset details at the given \a forDateTime. |
1150 | |
1151 | This is the equivalent of calling abbreviation() and all three offset |
1152 | functions individually but is more efficient. If this data is not available |
1153 | for the given datetime, an invalid OffsetData will be returned with an |
1154 | invalid QDateTime as its \c atUtc. |
1155 | |
1156 | This method is only available when feature \c timezone is enabled. |
1157 | |
1158 | \sa offsetFromUtc(), standardTimeOffset(), daylightTimeOffset(), abbreviation() |
1159 | */ |
1160 | |
1161 | QTimeZone::OffsetData QTimeZone::offsetData(const QDateTime &forDateTime) const |
1162 | { |
1163 | if (d.isShort()) { |
1164 | switch (d.s.spec()) { |
1165 | case Qt::LocalTime: |
1166 | return systemTimeZone().offsetData(forDateTime); |
1167 | case Qt::UTC: |
1168 | case Qt::OffsetFromUTC: |
1169 | return { .abbreviation: abbreviation(atDateTime: forDateTime), .atUtc: forDateTime, .offsetFromUtc: int(d.s.offset), .standardTimeOffset: int(d.s.offset), .daylightTimeOffset: 0 }; |
1170 | case Qt::TimeZone: |
1171 | Q_UNREACHABLE(); |
1172 | break; |
1173 | } |
1174 | } |
1175 | if (isValid()) |
1176 | return QTimeZonePrivate::toOffsetData(data: d->data(forMSecsSinceEpoch: forDateTime.toMSecsSinceEpoch())); |
1177 | |
1178 | return QTimeZonePrivate::invalidOffsetData(); |
1179 | } |
1180 | |
1181 | /*! |
1182 | Returns \c true if the system backend supports obtaining transitions. |
1183 | |
1184 | Transitions are changes in the time-zone: these happen when DST turns on or |
1185 | off and when authorities alter the offsets for the time-zone. |
1186 | |
1187 | This method is only available when feature \c timezone is enabled. |
1188 | |
1189 | \sa nextTransition(), previousTransition(), transitions() |
1190 | */ |
1191 | |
1192 | bool QTimeZone::hasTransitions() const |
1193 | { |
1194 | if (d.isShort()) { |
1195 | switch (d.s.spec()) { |
1196 | case Qt::LocalTime: |
1197 | return systemTimeZone().hasTransitions(); |
1198 | case Qt::UTC: |
1199 | case Qt::OffsetFromUTC: |
1200 | return false; |
1201 | case Qt::TimeZone: |
1202 | Q_UNREACHABLE(); |
1203 | break; |
1204 | } |
1205 | } else if (isValid()) { |
1206 | return d->hasTransitions(); |
1207 | } |
1208 | return false; |
1209 | } |
1210 | |
1211 | /*! |
1212 | Returns the first time zone Transition after the given \a afterDateTime. |
1213 | This is most useful when you have a Transition time and wish to find the |
1214 | Transition after it. |
1215 | |
1216 | If there is no transition after the given \a afterDateTime then an invalid |
1217 | OffsetData will be returned with an invalid QDateTime as its \c atUtc. |
1218 | |
1219 | The given \a afterDateTime is exclusive. |
1220 | |
1221 | This method is only available when feature \c timezone is enabled. |
1222 | |
1223 | \sa hasTransitions(), previousTransition(), transitions() |
1224 | */ |
1225 | |
1226 | QTimeZone::OffsetData QTimeZone::nextTransition(const QDateTime &afterDateTime) const |
1227 | { |
1228 | if (d.isShort()) { |
1229 | switch (d.s.spec()) { |
1230 | case Qt::LocalTime: |
1231 | return systemTimeZone().nextTransition(afterDateTime); |
1232 | case Qt::UTC: |
1233 | case Qt::OffsetFromUTC: |
1234 | break; |
1235 | case Qt::TimeZone: |
1236 | Q_UNREACHABLE(); |
1237 | break; |
1238 | } |
1239 | } else if (hasTransitions()) { |
1240 | return QTimeZonePrivate::toOffsetData(data: d->nextTransition(afterMSecsSinceEpoch: afterDateTime.toMSecsSinceEpoch())); |
1241 | } |
1242 | |
1243 | return QTimeZonePrivate::invalidOffsetData(); |
1244 | } |
1245 | |
1246 | /*! |
1247 | Returns the first time zone Transition before the given \a beforeDateTime. |
1248 | This is most useful when you have a Transition time and wish to find the |
1249 | Transition before it. |
1250 | |
1251 | If there is no transition before the given \a beforeDateTime then an invalid |
1252 | OffsetData will be returned with an invalid QDateTime as its \c atUtc. |
1253 | |
1254 | The given \a beforeDateTime is exclusive. |
1255 | |
1256 | This method is only available when feature \c timezone is enabled. |
1257 | |
1258 | \sa hasTransitions(), nextTransition(), transitions() |
1259 | */ |
1260 | |
1261 | QTimeZone::OffsetData QTimeZone::previousTransition(const QDateTime &beforeDateTime) const |
1262 | { |
1263 | if (d.isShort()) { |
1264 | switch (d.s.spec()) { |
1265 | case Qt::LocalTime: |
1266 | return systemTimeZone().previousTransition(beforeDateTime); |
1267 | case Qt::UTC: |
1268 | case Qt::OffsetFromUTC: |
1269 | break; |
1270 | case Qt::TimeZone: |
1271 | Q_UNREACHABLE(); |
1272 | break; |
1273 | } |
1274 | } else if (hasTransitions()) { |
1275 | return QTimeZonePrivate::toOffsetData( |
1276 | data: d->previousTransition(beforeMSecsSinceEpoch: beforeDateTime.toMSecsSinceEpoch())); |
1277 | } |
1278 | |
1279 | return QTimeZonePrivate::invalidOffsetData(); |
1280 | } |
1281 | |
1282 | /*! |
1283 | Returns a list of all time zone transitions between the given datetimes. |
1284 | |
1285 | The given \a fromDateTime and \a toDateTime are inclusive. |
1286 | |
1287 | This method is only available when feature \c timezone is enabled. |
1288 | |
1289 | \sa hasTransitions(), nextTransition(), previousTransition() |
1290 | */ |
1291 | |
1292 | QTimeZone::OffsetDataList QTimeZone::transitions(const QDateTime &fromDateTime, |
1293 | const QDateTime &toDateTime) const |
1294 | { |
1295 | OffsetDataList list; |
1296 | if (d.isShort()) { |
1297 | switch (d.s.spec()) { |
1298 | case Qt::LocalTime: |
1299 | return systemTimeZone().transitions(fromDateTime, toDateTime); |
1300 | case Qt::UTC: |
1301 | case Qt::OffsetFromUTC: |
1302 | break; |
1303 | case Qt::TimeZone: |
1304 | Q_UNREACHABLE(); |
1305 | break; |
1306 | } |
1307 | } else if (hasTransitions()) { |
1308 | const QTimeZonePrivate::DataList plist = d->transitions(fromMSecsSinceEpoch: fromDateTime.toMSecsSinceEpoch(), |
1309 | toMSecsSinceEpoch: toDateTime.toMSecsSinceEpoch()); |
1310 | list.reserve(asize: plist.size()); |
1311 | for (const QTimeZonePrivate::Data &pdata : plist) |
1312 | list.append(t: QTimeZonePrivate::toOffsetData(data: pdata)); |
1313 | } |
1314 | return list; |
1315 | } |
1316 | |
1317 | // Static methods |
1318 | |
1319 | /*! |
1320 | Returns the current system time zone IANA ID. |
1321 | |
1322 | On Windows this ID is translated from the Windows ID using an internal |
1323 | translation table and the user's selected country. As a consequence there |
1324 | is a small chance any Windows install may have IDs not known by Qt, in |
1325 | which case "UTC" will be returned. |
1326 | |
1327 | This method is only available when feature \c timezone is enabled. |
1328 | |
1329 | \sa systemTimeZone() |
1330 | */ |
1331 | |
1332 | QByteArray QTimeZone::systemTimeZoneId() |
1333 | { |
1334 | QByteArray sys = global_tz->backend->systemTimeZoneId(); |
1335 | if (!sys.isEmpty()) |
1336 | return sys; |
1337 | // The system zone, despite the empty ID, may know its real ID anyway: |
1338 | auto zone = systemTimeZone(); |
1339 | if (zone.isValid() && !zone.id().isEmpty()) |
1340 | return zone.id(); |
1341 | // TODO: "-00:00", meaning "unspecified local zone" in some RFC, may be more apt. |
1342 | // If all else fails, guess UTC. |
1343 | return QTimeZonePrivate::utcQByteArray(); |
1344 | } |
1345 | |
1346 | /*! |
1347 | \since 5.5 |
1348 | |
1349 | Returns a QTimeZone object that describes local system time. |
1350 | |
1351 | This method is only available when feature \c timezone is enabled. The |
1352 | returned instance is usually equivalent to the lightweight time |
1353 | representation \c {QTimeZone(QTimeZone::LocalTime)}, albeit implemented as a |
1354 | time zone. |
1355 | |
1356 | \sa utc(), Initialization, asBackendZone() |
1357 | */ |
1358 | QTimeZone QTimeZone::systemTimeZone() |
1359 | { |
1360 | return QTimeZone(global_tz->backend->systemTimeZoneId()); |
1361 | } |
1362 | |
1363 | /*! |
1364 | \since 5.5 |
1365 | Returns a QTimeZone object that describes UTC as a time zone. |
1366 | |
1367 | This method is only available when feature \c timezone is enabled. It is |
1368 | equivalent to passing 0 to QTimeZone(int offsetSeconds) and to the |
1369 | lightweight time representation QTimeZone(QTimeZone::UTC), albeit |
1370 | implemented as a time zone, unlike the latter. |
1371 | |
1372 | \sa systemTimeZone(), Initialization, asBackendZone() |
1373 | */ |
1374 | QTimeZone QTimeZone::utc() |
1375 | { |
1376 | return QTimeZone(QTimeZonePrivate::utcQByteArray()); |
1377 | } |
1378 | |
1379 | /*! |
1380 | Returns \c true if a given time zone \a ianaId is available on this system. |
1381 | |
1382 | This method is only available when feature \c timezone is enabled. |
1383 | |
1384 | \sa availableTimeZoneIds() |
1385 | */ |
1386 | |
1387 | bool QTimeZone::isTimeZoneIdAvailable(const QByteArray &ianaId) |
1388 | { |
1389 | #if defined(Q_OS_UNIX) && !(defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN)) |
1390 | // Keep #if-ery consistent with selection of QTzTimeZonePrivate in |
1391 | // newBackendTimeZone(). Skip the pre-check, as the TZ backend accepts POSIX |
1392 | // zone IDs, which need not be valid IANA IDs. |
1393 | #else |
1394 | // isValidId is not strictly required, but faster to weed out invalid |
1395 | // IDs as availableTimeZoneIds() may be slow |
1396 | if (!QTimeZonePrivate::isValidId(ianaId)) |
1397 | return false; |
1398 | #endif |
1399 | return QUtcTimeZonePrivate().isTimeZoneIdAvailable(ianaId) |
1400 | || global_tz->backend->isTimeZoneIdAvailable(ianaId); |
1401 | } |
1402 | |
1403 | static QList<QByteArray> set_union(const QList<QByteArray> &l1, const QList<QByteArray> &l2) |
1404 | { |
1405 | QList<QByteArray> result; |
1406 | result.reserve(asize: l1.size() + l2.size()); |
1407 | std::set_union(first1: l1.begin(), last1: l1.end(), |
1408 | first2: l2.begin(), last2: l2.end(), |
1409 | result: std::back_inserter(x&: result)); |
1410 | return result; |
1411 | } |
1412 | |
1413 | /*! |
1414 | Returns a list of all available IANA time zone IDs on this system. |
1415 | |
1416 | This method is only available when feature \c timezone is enabled. |
1417 | |
1418 | \sa isTimeZoneIdAvailable() |
1419 | */ |
1420 | |
1421 | QList<QByteArray> QTimeZone::availableTimeZoneIds() |
1422 | { |
1423 | return set_union(l1: QUtcTimeZonePrivate().availableTimeZoneIds(), |
1424 | l2: global_tz->backend->availableTimeZoneIds()); |
1425 | } |
1426 | |
1427 | /*! |
1428 | Returns a list of all available IANA time zone IDs for a given \a territory. |
1429 | |
1430 | As a special case, a \a territory of \l {QLocale::}{AnyTerritory} selects |
1431 | those time zones that have no kown territorial association, such as UTC. If |
1432 | you require a list of all time zone IDs for all territories then use the |
1433 | standard availableTimeZoneIds() method. |
1434 | |
1435 | This method is only available when feature \c timezone is enabled. |
1436 | |
1437 | \sa isTimeZoneIdAvailable(), territory() |
1438 | */ |
1439 | |
1440 | QList<QByteArray> QTimeZone::availableTimeZoneIds(QLocale::Territory territory) |
1441 | { |
1442 | return set_union(l1: QUtcTimeZonePrivate().availableTimeZoneIds(country: territory), |
1443 | l2: global_tz->backend->availableTimeZoneIds(territory)); |
1444 | } |
1445 | |
1446 | /*! |
1447 | Returns a list of all available IANA time zone IDs with a given standard |
1448 | time offset of \a offsetSeconds. |
1449 | |
1450 | This method is only available when feature \c timezone is enabled. |
1451 | |
1452 | \sa isTimeZoneIdAvailable() |
1453 | */ |
1454 | |
1455 | QList<QByteArray> QTimeZone::availableTimeZoneIds(int offsetSeconds) |
1456 | { |
1457 | return set_union(l1: QUtcTimeZonePrivate().availableTimeZoneIds(utcOffset: offsetSeconds), |
1458 | l2: global_tz->backend->availableTimeZoneIds(utcOffset: offsetSeconds)); |
1459 | } |
1460 | |
1461 | /*! |
1462 | Returns the Windows ID equivalent to the given \a ianaId. |
1463 | |
1464 | This method is only available when feature \c timezone is enabled. |
1465 | |
1466 | \sa windowsIdToDefaultIanaId(), windowsIdToIanaIds() |
1467 | */ |
1468 | |
1469 | QByteArray QTimeZone::ianaIdToWindowsId(const QByteArray &ianaId) |
1470 | { |
1471 | return QTimeZonePrivate::ianaIdToWindowsId(ianaId); |
1472 | } |
1473 | |
1474 | /*! |
1475 | Returns the default IANA ID for a given \a windowsId. |
1476 | |
1477 | Because a Windows ID can cover several IANA IDs in several different |
1478 | territories, this function returns the most frequently used IANA ID with no |
1479 | regard for the territory and should thus be used with care. It is usually |
1480 | best to request the default for a specific territory. |
1481 | |
1482 | This method is only available when feature \c timezone is enabled. |
1483 | |
1484 | \sa ianaIdToWindowsId(), windowsIdToIanaIds() |
1485 | */ |
1486 | |
1487 | QByteArray QTimeZone::windowsIdToDefaultIanaId(const QByteArray &windowsId) |
1488 | { |
1489 | return QTimeZonePrivate::windowsIdToDefaultIanaId(windowsId); |
1490 | } |
1491 | |
1492 | /*! |
1493 | Returns the default IANA ID for a given \a windowsId and \a territory. |
1494 | |
1495 | Because a Windows ID can cover several IANA IDs within a given territory, |
1496 | the most frequently used IANA ID in that territory is returned. |
1497 | |
1498 | As a special case, \l{QLocale::}{AnyTerritory} returns the default of those |
1499 | IANA IDs that have no known territorial association. |
1500 | |
1501 | This method is only available when feature \c timezone is enabled. |
1502 | |
1503 | \sa ianaIdToWindowsId(), windowsIdToIanaIds(), territory() |
1504 | */ |
1505 | |
1506 | QByteArray QTimeZone::windowsIdToDefaultIanaId(const QByteArray &windowsId, |
1507 | QLocale::Territory territory) |
1508 | { |
1509 | return QTimeZonePrivate::windowsIdToDefaultIanaId(windowsId, territory); |
1510 | } |
1511 | |
1512 | /*! |
1513 | Returns all the IANA IDs for a given \a windowsId. |
1514 | |
1515 | The returned list is sorted alphabetically. |
1516 | |
1517 | This method is only available when feature \c timezone is enabled. |
1518 | |
1519 | \sa ianaIdToWindowsId(), windowsIdToDefaultIanaId() |
1520 | */ |
1521 | |
1522 | QList<QByteArray> QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId) |
1523 | { |
1524 | return QTimeZonePrivate::windowsIdToIanaIds(windowsId); |
1525 | } |
1526 | |
1527 | /*! |
1528 | Returns all the IANA IDs for a given \a windowsId and \a territory. |
1529 | |
1530 | As a special case, \l{QLocale::}{AnyTerritory} selects those IANA IDs that |
1531 | have no known territorial association. |
1532 | |
1533 | The returned list is in order of frequency of usage, i.e. larger zones |
1534 | within a territory are listed first. |
1535 | |
1536 | This method is only available when feature \c timezone is enabled. |
1537 | |
1538 | \sa ianaIdToWindowsId(), windowsIdToDefaultIanaId(), territory() |
1539 | */ |
1540 | |
1541 | QList<QByteArray> QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId, |
1542 | QLocale::Territory territory) |
1543 | { |
1544 | return QTimeZonePrivate::windowsIdToIanaIds(windowsId, territory); |
1545 | } |
1546 | |
1547 | /*! |
1548 | \fn QTimeZone QTimeZone::fromStdTimeZonePtr(const std::chrono::time_zone *timeZone) |
1549 | \since 6.4 |
1550 | |
1551 | Returns a QTimeZone object representing the same time zone as \a timeZone. |
1552 | The IANA ID of \a timeZone must be one of the available system IDs, |
1553 | otherwise an invalid time zone will be returned. |
1554 | |
1555 | This method is only available when feature \c timezone is enabled. |
1556 | */ |
1557 | #endif // feature timezone |
1558 | |
1559 | template <typename Stream, typename Wrap> |
1560 | void QTimeZone::Data::serialize(Stream &out, const Wrap &wrap) const |
1561 | { |
1562 | if (isShort()) { |
1563 | switch (s.spec()) { |
1564 | case Qt::UTC: |
1565 | out << wrap("QTimeZone::UTC" ); |
1566 | break; |
1567 | case Qt::LocalTime: |
1568 | out << wrap("QTimeZone::LocalTime" ); |
1569 | break; |
1570 | case Qt::OffsetFromUTC: |
1571 | out << wrap("AheadOfUtcBy" ) << int(s.offset); |
1572 | break; |
1573 | case Qt::TimeZone: |
1574 | Q_UNREACHABLE(); |
1575 | break; |
1576 | } |
1577 | return; |
1578 | } |
1579 | #if QT_CONFIG(timezone) |
1580 | if constexpr (std::is_same<Stream, QDataStream>::value) { |
1581 | if (d) |
1582 | d->serialize(ds&: out); |
1583 | } else { |
1584 | // QDebug, traditionally gets a QString, hence quotes round the (possibly empty) ID: |
1585 | out << QString::fromUtf8(utf8: d ? QByteArrayView(d->id()) : QByteArrayView()); |
1586 | } |
1587 | #endif |
1588 | } |
1589 | |
1590 | #ifndef QT_NO_DATASTREAM |
1591 | // Invalid, as an IANA ID: too long, starts with - and has other invalid characters in it |
1592 | static inline QString invalidId() { return QStringLiteral("-No Time Zone Specified!" ); } |
1593 | |
1594 | QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz) |
1595 | { |
1596 | const auto toQString = [](const char *text) { |
1597 | return QString(QLatin1StringView(text)); |
1598 | }; |
1599 | if (tz.isValid()) |
1600 | tz.d.serialize(out&: ds, wrap: toQString); |
1601 | else |
1602 | ds << invalidId(); |
1603 | return ds; |
1604 | } |
1605 | |
1606 | QDataStream &operator>>(QDataStream &ds, QTimeZone &tz) |
1607 | { |
1608 | QString ianaId; |
1609 | ds >> ianaId; |
1610 | // That may be various things other than actual IANA IDs: |
1611 | if (ianaId == invalidId()) { |
1612 | tz = QTimeZone(); |
1613 | } else if (ianaId == "OffsetFromUtc"_L1 ) { |
1614 | int utcOffset; |
1615 | QString name; |
1616 | QString abbreviation; |
1617 | int territory; |
1618 | QString ; |
1619 | ds >> ianaId >> utcOffset >> name >> abbreviation >> territory >> comment; |
1620 | #if QT_CONFIG(timezone) |
1621 | // Try creating as a system timezone, which succeeds (producing a valid |
1622 | // zone) iff ianaId is valid; use this if it is a plain offset from UTC |
1623 | // zone, with the right offset, ignoring the other data: |
1624 | tz = QTimeZone(ianaId.toUtf8()); |
1625 | if (!tz.isValid() || tz.hasDaylightTime() |
1626 | || tz.offsetFromUtc(atDateTime: QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC)) != utcOffset) { |
1627 | // Construct a custom timezone using the saved values: |
1628 | tz = QTimeZone(ianaId.toUtf8(), utcOffset, name, abbreviation, |
1629 | QLocale::Territory(territory), comment); |
1630 | } |
1631 | #else |
1632 | tz = QTimeZone::fromSecondsAheadOfUtc(utcOffset); |
1633 | #endif |
1634 | } else if (ianaId == "AheadOfUtcBy"_L1 ) { |
1635 | int utcOffset; |
1636 | ds >> utcOffset; |
1637 | tz = QTimeZone::fromSecondsAheadOfUtc(offset: utcOffset); |
1638 | } else if (ianaId == "QTimeZone::UTC"_L1 ) { |
1639 | tz = QTimeZone(QTimeZone::UTC); |
1640 | } else if (ianaId == "QTimeZone::LocalTime"_L1 ) { |
1641 | tz = QTimeZone(QTimeZone::LocalTime); |
1642 | #if QT_CONFIG(timezone) |
1643 | } else { |
1644 | tz = QTimeZone(ianaId.toUtf8()); |
1645 | #endif |
1646 | } |
1647 | return ds; |
1648 | } |
1649 | #endif // QT_NO_DATASTREAM |
1650 | |
1651 | #ifndef QT_NO_DEBUG_STREAM |
1652 | QDebug operator<<(QDebug dbg, const QTimeZone &tz) |
1653 | { |
1654 | QDebugStateSaver saver(dbg); |
1655 | const auto asIs = [](const char *text) { return text; }; |
1656 | // TODO Include backend and data version details? |
1657 | dbg.nospace() << "QTimeZone(" ; |
1658 | tz.d.serialize(out&: dbg, wrap: asIs); |
1659 | dbg.nospace() << ')'; |
1660 | return dbg; |
1661 | } |
1662 | #endif |
1663 | |
1664 | QT_END_NAMESPACE |
1665 | |