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

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