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::fromSecondsAheadOfUtc(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 \memberswap{time zone instance}
713*/
714
715/*!
716 Assignment operator, assign \a other to this.
717*/
718
719QTimeZone &QTimeZone::operator=(const QTimeZone &other)
720{
721 d = other.d;
722 return *this;
723}
724
725/*!
726 \fn QTimeZone &QTimeZone::operator=(QTimeZone &&other)
727
728 Move-assigns \a other to this QTimeZone instance, transferring the ownership
729 of its data to this instance.
730*/
731
732/*!
733 \fn bool QTimeZone::operator==(const QTimeZone &lhs, const QTimeZone &rhs)
734
735 Returns \c true if \a lhs time zone is equal to the \a rhs time zone.
736
737 Two representations are different if they are internally described
738 differently, even if they agree in their representation of all moments of
739 time. In particular, a lightweight time representation may coincide with a
740 time zone but the two will not be equal.
741*/
742
743/*!
744 \fn bool QTimeZone::operator!=(const QTimeZone &lhs, const QTimeZone &rhs)
745
746 Returns \c true if \a lhs time zone is not equal to the \a rhs time zone.
747
748 Two representations are different if they are internally described
749 differently, even if they agree in their representation of all moments of
750 time. In particular, a lightweight time representation may coincide with a
751 time zone but the two will not be equal.
752*/
753
754bool comparesEqual(const QTimeZone &lhs, const QTimeZone &rhs) noexcept
755{
756 if (lhs.d.isShort())
757 return rhs.d.isShort() && lhs.d.s == rhs.d.s;
758
759 if (!rhs.d.isShort()) {
760 if (lhs.d.d == rhs.d.d)
761 return true;
762#if QT_CONFIG(timezone)
763 return lhs.d.d && rhs.d.d && *lhs.d.d == *rhs.d.d;
764#endif
765 }
766
767 return false;
768}
769
770/*!
771 Returns \c true if this time zone is valid.
772*/
773
774bool QTimeZone::isValid() const
775{
776#if QT_CONFIG(timezone)
777 if (!d.isShort())
778 return d.d && d->isValid();
779#endif
780 return d.isShort();
781}
782
783#if QT_CONFIG(timezone)
784/*!
785 Returns the IANA ID for the time zone.
786
787 IANA IDs are used on all platforms. On Windows these are translated from
788 the Windows ID into the best match IANA ID for the time zone and territory.
789
790 If this timezone instance was not constructed from an IANA ID, its ID is
791 determined by how it was constructed. In most cases, the ID passed when
792 constructing the instance is used. (The constructor for a custom zone uses
793 the ID it is passed, which must not be an IANA ID.) There are two
794 exceptions.
795 \list
796 \li Instances constructed by passing only a UTC offset in seconds have no ID
797 passed when constructing.
798 \li The constructor taking only an IANA ID will also accept some UTC-offset
799 IDs that are not in fact IANA IDs: its handling of these is equivalent
800 to passing the corresponding offset in seconds, as for the first
801 exception.
802 \endlist
803
804 In the two exceptional cases, if there is an IANA UTC-offset zone with the
805 specified offset, the instance constructed uses that IANA zone's ID, even
806 though this may differ from the (non-IANA) UTC-offset ID passed to the
807 constructor. Otherwise, the instance uses an ID synthesized from its offset,
808 with the form UTC±hh:mm:ss, omitting any trailing :00 for zero seconds or
809 minutes. Again, this may differ from the UTC-offset ID passed to the
810 constructor.
811
812 This method is only available when feature \c timezone is enabled.
813*/
814
815QByteArray QTimeZone::id() const
816{
817 if (d.isShort()) {
818 switch (d.s.spec()) {
819 case Qt::UTC:
820 return QTimeZonePrivate::utcQByteArray();
821 case Qt::LocalTime:
822 return systemTimeZoneId();
823 case Qt::OffsetFromUTC:
824 return QUtcTimeZonePrivate(d.s.offset).id();
825 case Qt::TimeZone:
826 Q_UNREACHABLE();
827 break;
828 }
829 } else if (d.d) {
830 return d->id();
831 }
832 return QByteArray();
833}
834
835/*!
836 \since 6.8
837 Returns \c true if \a alias is an alternative name for this timezone.
838
839 The IANA (formerly Olson) database has renamed some zones during its
840 history. There are also some zones that only differed prior to 1970 but are
841 now treated as synonymous. Some backends may have data reaching to before
842 1970 and produce distinct zones in the latter case. Others may produce zones
843 indistinguishable except by id(). This method determines whether an ID
844 refers (at least since 1970) to the same zone that this timezone object
845 describes.
846
847 This method is only available when feature \c timezone is enabled.
848*/
849bool QTimeZone::hasAlternativeName(QByteArrayView alias) const
850{
851 if (alias == id())
852 return true;
853 QByteArray mine = QTimeZonePrivate::aliasToIana(alias: id());
854 // Empty if id() aliases to itself, which we've already checked:
855 if (!mine.isEmpty() && alias == mine)
856 return true;
857 QByteArray its = QTimeZonePrivate::aliasToIana(alias);
858 // Empty if alias aliases to itself, which we've already compared to id()
859 // and, where relevant, mine.
860 return !its.isEmpty() && its == (mine.isEmpty() ? id() : mine);
861}
862
863/*!
864 \since 6.2
865
866 Returns the territory for the time zone.
867
868 A return of \l {QLocale::}{AnyTerritory} means the zone has no known
869 territorial association. In some cases this may be because the zone has no
870 associated territory - for example, UTC - or because the zone is used in
871 several territories - for example, CET. In other cases, the QTimeZone
872 backend may not know which territory the zone is associated with - for
873 example, because it is not the primary zone of the territory in which it is
874 used.
875
876 This method is only available when feature \c timezone is enabled.
877*/
878QLocale::Territory QTimeZone::territory() const
879{
880 if (d.isShort()) {
881 if (d.s.spec() == Qt::LocalTime)
882 return systemTimeZone().territory();
883 } else if (isValid()) {
884 return d->territory();
885 }
886 return QLocale::AnyTerritory;
887}
888
889#if QT_DEPRECATED_SINCE(6, 6)
890/*!
891 \deprecated [6.6] Use territory() instead.
892
893 Returns the territory for the time zone.
894
895 This method is only available when feature \c timezone is enabled.
896*/
897
898QLocale::Country QTimeZone::country() const
899{
900 return territory();
901}
902#endif
903
904/*!
905 Returns any comment for the time zone.
906
907 A comment may be provided by the host platform to assist users in
908 choosing the correct time zone. Depending on the platform this may not
909 be localized.
910
911 This method is only available when feature \c timezone is enabled.
912*/
913
914QString QTimeZone::comment() const
915{
916 if (d.isShort()) {
917 // TODO: anything ? Or just stick with empty string ?
918 } else if (isValid()) {
919 return d->comment();
920 }
921 return QString();
922}
923
924/*!
925 Returns the localized time zone display name.
926
927 The name returned is the one for the given \a locale, applicable at the
928 given \a atDateTime, and of the form indicated by \a nameType. The display
929 name may change depending on DST or historical events.
930//! [display-name-caveats]
931 If no suitably localized name of the given type is available, another name
932 type may be used, or an empty string may be returned.
933
934 If the \a locale is not provided, then the application default locale will
935 be used. For custom timezones created by client code, the data supplied to
936 the constructor are used, as no localization data will be available for it.
937 If this timezone is invalid, an empty string is returned. This may also
938 arise for the representation of local time if determining the system time
939 zone fails.
940
941 This method is only available when feature \c timezone is enabled.
942//! [display-name-caveats]
943
944 \sa abbreviation()
945*/
946
947QString QTimeZone::displayName(const QDateTime &atDateTime, NameType nameType,
948 const QLocale &locale) const
949{
950 if (d.isShort()) {
951 switch (d.s.spec()) {
952 case Qt::LocalTime:
953 return systemTimeZone().displayName(atDateTime, nameType, locale);
954 case Qt::UTC:
955 case Qt::OffsetFromUTC:
956 return QUtcTimeZonePrivate(d.s.offset).displayName(
957 atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch(), nameType, locale);
958 case Qt::TimeZone:
959 Q_UNREACHABLE();
960 break;
961 }
962 } else if (isValid()) {
963 return d->displayName(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch(), nameType, locale);
964 }
965
966 return QString();
967}
968
969/*!
970 Returns the localized time zone display name.
971
972 The name returned is the one for the given \a locale, applicable when the
973 given \a timeType is in effect and of the form indicated by \a nameType.
974 Where the time zone display names have changed over time, the current names
975 will be used.
976 \include qtimezone.cpp display-name-caveats
977
978 \sa abbreviation()
979*/
980
981QString QTimeZone::displayName(TimeType timeType, NameType nameType,
982 const QLocale &locale) const
983{
984 if (d.isShort()) {
985 switch (d.s.spec()) {
986 case Qt::LocalTime:
987 return systemTimeZone().displayName(timeType, nameType, locale);
988 case Qt::UTC:
989 case Qt::OffsetFromUTC:
990 return QUtcTimeZonePrivate(d.s.offset).displayName(timeType, nameType, locale);
991 case Qt::TimeZone:
992 Q_UNREACHABLE();
993 break;
994 }
995 } else if (isValid()) {
996 return d->displayName(timeType, nameType, locale);
997 }
998
999 return QString();
1000}
1001
1002/*!
1003 Returns the time zone abbreviation at the given \a atDateTime.
1004
1005 The abbreviation may change depending on DST or even historical events.
1006
1007 \note The abbreviation is not guaranteed to be unique to this time zone and
1008 should not be used in place of the ID or display name. The abbreviation may
1009 be localized, depending on the underlying operating system. To get consistent
1010 localization, use \c {displayName(atDateTime, QTimeZone::ShortName, locale)}.
1011
1012 This method is only available when feature \c timezone is enabled.
1013
1014 \sa displayName()
1015*/
1016
1017QString QTimeZone::abbreviation(const QDateTime &atDateTime) const
1018{
1019 if (d.isShort()) {
1020 switch (d.s.spec()) {
1021 case Qt::LocalTime:
1022 return systemTimeZone().abbreviation(atDateTime);
1023 case Qt::UTC:
1024 case Qt::OffsetFromUTC:
1025 return QUtcTimeZonePrivate(d.s.offset).abbreviation(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch());
1026 case Qt::TimeZone:
1027 Q_UNREACHABLE();
1028 break;
1029 }
1030 } else if (isValid()) {
1031 return d->abbreviation(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch());
1032 }
1033
1034 return QString();
1035}
1036
1037/*!
1038 Returns the total effective offset at the given \a atDateTime, i.e. the
1039 number of seconds to add to UTC to obtain the local time. This includes
1040 any DST offset that may be in effect, i.e. it is the sum of
1041 standardTimeOffset() and daylightTimeOffset() for the given datetime.
1042
1043 For example, for the time zone "Europe/Berlin" the standard time offset is
1044 +3600 seconds and the DST offset is +3600 seconds. During standard time
1045 offsetFromUtc() will return +3600 (UTC+01:00), and during DST it will
1046 return +7200 (UTC+02:00).
1047
1048 This method is only available when feature \c timezone is enabled.
1049
1050 \sa standardTimeOffset(), daylightTimeOffset()
1051*/
1052
1053int QTimeZone::offsetFromUtc(const QDateTime &atDateTime) const
1054{
1055 if (d.isShort()) {
1056 switch (d.s.spec()) {
1057 case Qt::LocalTime:
1058 return systemTimeZone().offsetFromUtc(atDateTime);
1059 case Qt::UTC:
1060 case Qt::OffsetFromUTC:
1061 return d.s.offset;
1062 case Qt::TimeZone:
1063 Q_UNREACHABLE();
1064 break;
1065 }
1066 } else if (isValid()) {
1067 const int offset = d->offsetFromUtc(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch());
1068 if (offset != QTimeZonePrivate::invalidSeconds())
1069 return offset;
1070 }
1071 return 0;
1072}
1073
1074/*!
1075 Returns the standard time offset at the given \a atDateTime, i.e. the
1076 number of seconds to add to UTC to obtain the local Standard Time. This
1077 excludes any DST offset that may be in effect.
1078
1079 For example, for the time zone "Europe/Berlin" the standard time offset is
1080 +3600 seconds. During both standard and DST offsetFromUtc() will return
1081 +3600 (UTC+01:00).
1082
1083 This method is only available when feature \c timezone is enabled.
1084
1085 \sa offsetFromUtc(), daylightTimeOffset()
1086*/
1087
1088int QTimeZone::standardTimeOffset(const QDateTime &atDateTime) const
1089{
1090 if (d.isShort()) {
1091 switch (d.s.spec()) {
1092 case Qt::LocalTime:
1093 return systemTimeZone().standardTimeOffset(atDateTime);
1094 case Qt::UTC:
1095 case Qt::OffsetFromUTC:
1096 return d.s.offset;
1097 case Qt::TimeZone:
1098 Q_UNREACHABLE();
1099 break;
1100 }
1101 } else if (isValid()) {
1102 const int offset = d->standardTimeOffset(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch());
1103 if (offset != QTimeZonePrivate::invalidSeconds())
1104 return offset;
1105 }
1106 return 0;
1107}
1108
1109/*!
1110 Returns the daylight-saving time offset at the given \a atDateTime,
1111 i.e. the number of seconds to add to the standard time offset to obtain the
1112 local daylight-saving time.
1113
1114 For example, for the time zone "Europe/Berlin" the DST offset is +3600
1115 seconds. During standard time daylightTimeOffset() will return 0, and when
1116 daylight-saving is in effect it will return +3600.
1117
1118 This method is only available when feature \c timezone is enabled.
1119
1120 \sa offsetFromUtc(), standardTimeOffset()
1121*/
1122
1123int QTimeZone::daylightTimeOffset(const QDateTime &atDateTime) const
1124{
1125 if (d.isShort()) {
1126 switch (d.s.spec()) {
1127 case Qt::LocalTime:
1128 return systemTimeZone().daylightTimeOffset(atDateTime);
1129 case Qt::UTC:
1130 case Qt::OffsetFromUTC:
1131 return 0;
1132 case Qt::TimeZone:
1133 Q_UNREACHABLE();
1134 break;
1135 }
1136 } else if (hasDaylightTime()) {
1137 const int offset = d->daylightTimeOffset(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch());
1138 if (offset != QTimeZonePrivate::invalidSeconds())
1139 return offset;
1140 }
1141 return 0;
1142}
1143
1144/*!
1145 Returns \c true if the time zone has practiced daylight-saving at any time.
1146
1147 This method is only available when feature \c timezone is enabled.
1148
1149 \sa isDaylightTime(), daylightTimeOffset()
1150*/
1151
1152bool QTimeZone::hasDaylightTime() const
1153{
1154 if (d.isShort()) {
1155 switch (d.s.spec()) {
1156 case Qt::LocalTime:
1157 return systemTimeZone().hasDaylightTime();
1158 case Qt::UTC:
1159 case Qt::OffsetFromUTC:
1160 return false;
1161 case Qt::TimeZone:
1162 Q_UNREACHABLE();
1163 break;
1164 }
1165 } else if (isValid()) {
1166 return d->hasDaylightTime();
1167 }
1168 return false;
1169}
1170
1171/*!
1172 Returns \c true if daylight-saving was in effect at the given \a atDateTime.
1173
1174 This method is only available when feature \c timezone is enabled.
1175
1176 \sa hasDaylightTime(), daylightTimeOffset()
1177*/
1178
1179bool QTimeZone::isDaylightTime(const QDateTime &atDateTime) const
1180{
1181 if (d.isShort()) {
1182 switch (d.s.spec()) {
1183 case Qt::LocalTime:
1184 return systemTimeZone().isDaylightTime(atDateTime);
1185 case Qt::UTC:
1186 case Qt::OffsetFromUTC:
1187 return false;
1188 case Qt::TimeZone:
1189 Q_UNREACHABLE();
1190 break;
1191 }
1192 } else if (hasDaylightTime()) {
1193 return d->isDaylightTime(atMSecsSinceEpoch: atDateTime.toMSecsSinceEpoch());
1194 }
1195 return false;
1196}
1197
1198/*!
1199 Returns the effective offset details at the given \a forDateTime.
1200
1201 This is the equivalent of calling abbreviation() and all three offset
1202 functions individually but may be more efficient and may get a different
1203 localization for the abbreviation. If this data is not available for the
1204 given datetime, an invalid OffsetData will be returned with an invalid
1205 QDateTime as its \c atUtc.
1206
1207 This method is only available when feature \c timezone is enabled.
1208
1209 \sa offsetFromUtc(), standardTimeOffset(), daylightTimeOffset(), abbreviation()
1210*/
1211
1212QTimeZone::OffsetData QTimeZone::offsetData(const QDateTime &forDateTime) const
1213{
1214 if (d.isShort()) {
1215 switch (d.s.spec()) {
1216 case Qt::LocalTime:
1217 return systemTimeZone().offsetData(forDateTime);
1218 case Qt::UTC:
1219 case Qt::OffsetFromUTC:
1220 return { .abbreviation: abbreviation(atDateTime: forDateTime), .atUtc: forDateTime, .offsetFromUtc: int(d.s.offset), .standardTimeOffset: int(d.s.offset), .daylightTimeOffset: 0 };
1221 case Qt::TimeZone:
1222 Q_UNREACHABLE();
1223 break;
1224 }
1225 }
1226 if (isValid())
1227 return QTimeZonePrivate::toOffsetData(data: d->data(forMSecsSinceEpoch: forDateTime.toMSecsSinceEpoch()));
1228
1229 return QTimeZonePrivate::invalidOffsetData();
1230}
1231
1232/*!
1233 Returns \c true if the system backend supports obtaining transitions.
1234
1235 Transitions are changes in the time-zone: these happen when DST turns on or
1236 off and when authorities alter the offsets for the time-zone.
1237
1238 This method is only available when feature \c timezone is enabled.
1239
1240 \sa nextTransition(), previousTransition(), transitions()
1241*/
1242
1243bool QTimeZone::hasTransitions() const
1244{
1245 if (d.isShort()) {
1246 switch (d.s.spec()) {
1247 case Qt::LocalTime:
1248 return systemTimeZone().hasTransitions();
1249 case Qt::UTC:
1250 case Qt::OffsetFromUTC:
1251 return false;
1252 case Qt::TimeZone:
1253 Q_UNREACHABLE();
1254 break;
1255 }
1256 } else if (isValid()) {
1257 return d->hasTransitions();
1258 }
1259 return false;
1260}
1261
1262/*!
1263 Returns the first time zone Transition after the given \a afterDateTime.
1264 This is most useful when you have a Transition time and wish to find the
1265 Transition after it.
1266
1267 If there is no transition after the given \a afterDateTime then an invalid
1268 OffsetData will be returned with an invalid QDateTime as its \c atUtc.
1269
1270 The given \a afterDateTime is exclusive.
1271
1272 This method is only available when feature \c timezone is enabled.
1273
1274 \sa hasTransitions(), previousTransition(), transitions()
1275*/
1276
1277QTimeZone::OffsetData QTimeZone::nextTransition(const QDateTime &afterDateTime) const
1278{
1279 if (d.isShort()) {
1280 switch (d.s.spec()) {
1281 case Qt::LocalTime:
1282 return systemTimeZone().nextTransition(afterDateTime);
1283 case Qt::UTC:
1284 case Qt::OffsetFromUTC:
1285 break;
1286 case Qt::TimeZone:
1287 Q_UNREACHABLE();
1288 break;
1289 }
1290 } else if (hasTransitions()) {
1291 return QTimeZonePrivate::toOffsetData(data: d->nextTransition(afterMSecsSinceEpoch: afterDateTime.toMSecsSinceEpoch()));
1292 }
1293
1294 return QTimeZonePrivate::invalidOffsetData();
1295}
1296
1297/*!
1298 Returns the first time zone Transition before the given \a beforeDateTime.
1299 This is most useful when you have a Transition time and wish to find the
1300 Transition before it.
1301
1302 If there is no transition before the given \a beforeDateTime then an invalid
1303 OffsetData will be returned with an invalid QDateTime as its \c atUtc.
1304
1305 The given \a beforeDateTime is exclusive.
1306
1307 This method is only available when feature \c timezone is enabled.
1308
1309 \sa hasTransitions(), nextTransition(), transitions()
1310*/
1311
1312QTimeZone::OffsetData QTimeZone::previousTransition(const QDateTime &beforeDateTime) const
1313{
1314 if (d.isShort()) {
1315 switch (d.s.spec()) {
1316 case Qt::LocalTime:
1317 return systemTimeZone().previousTransition(beforeDateTime);
1318 case Qt::UTC:
1319 case Qt::OffsetFromUTC:
1320 break;
1321 case Qt::TimeZone:
1322 Q_UNREACHABLE();
1323 break;
1324 }
1325 } else if (hasTransitions()) {
1326 return QTimeZonePrivate::toOffsetData(
1327 data: d->previousTransition(beforeMSecsSinceEpoch: beforeDateTime.toMSecsSinceEpoch()));
1328 }
1329
1330 return QTimeZonePrivate::invalidOffsetData();
1331}
1332
1333/*!
1334 Returns a list of all time zone transitions between the given datetimes.
1335
1336 The given \a fromDateTime and \a toDateTime are inclusive. The \c atUtc
1337 member of each entry describes the moment of the transition, at which the
1338 offsets and abbreviation given by other members take effect.
1339
1340 This method is only available when feature \c timezone is enabled.
1341
1342 \sa hasTransitions(), nextTransition(), previousTransition()
1343*/
1344
1345QTimeZone::OffsetDataList QTimeZone::transitions(const QDateTime &fromDateTime,
1346 const QDateTime &toDateTime) const
1347{
1348 OffsetDataList list;
1349 if (d.isShort()) {
1350 switch (d.s.spec()) {
1351 case Qt::LocalTime:
1352 return systemTimeZone().transitions(fromDateTime, toDateTime);
1353 case Qt::UTC:
1354 case Qt::OffsetFromUTC:
1355 break;
1356 case Qt::TimeZone:
1357 Q_UNREACHABLE();
1358 break;
1359 }
1360 } else if (hasTransitions()) {
1361 const QTimeZonePrivate::DataList plist = d->transitions(fromMSecsSinceEpoch: fromDateTime.toMSecsSinceEpoch(),
1362 toMSecsSinceEpoch: toDateTime.toMSecsSinceEpoch());
1363 list.reserve(asize: plist.size());
1364 for (const QTimeZonePrivate::Data &pdata : plist)
1365 list.append(t: QTimeZonePrivate::toOffsetData(data: pdata));
1366 }
1367 return list;
1368}
1369
1370// Static methods
1371
1372/*!
1373 Returns the current system time zone IANA ID.
1374
1375 Equivalent to calling systemTimeZone().id(), but may bypass some computation
1376 to obtain it. Constructing a QTimeZone from the returned byte array will
1377 produce the same result as systemTimeZone().
1378
1379 If the backend is unable to determine the correct system zone, the result is
1380 empty. In this case, systemTimeZone().isValid() is false and a warning is
1381 output if either this method of systemTimeZone() is called.
1382
1383 If the backend is able to determine the correct system zone but not its
1384 name, an empty byte array is returned. For example, on Windows, the system
1385 native ID is converted to an IANA ID - if the system ID isn't known to the
1386 internal translation code, the result shall be empty. In this case,
1387 systemTimeZone().isValid() shall be true.
1388
1389 This method is only available when feature \c timezone is enabled.
1390
1391 \note Prior to Qt 6.7, when the result could not be determined, the
1392 misleading result "UTC" was returned.
1393
1394 \sa systemTimeZone()
1395*/
1396
1397QByteArray QTimeZone::systemTimeZoneId()
1398{
1399 QByteArray sys = global_tz->backend->systemTimeZoneId();
1400 if (!sys.isEmpty())
1401 return sys;
1402 // The system zone, despite the empty ID, may know its real ID anyway:
1403 return global_tz->backend->id();
1404}
1405
1406/*!
1407 \since 5.5
1408
1409 Returns a QTimeZone object that describes local system time.
1410
1411 This method is only available when feature \c timezone is enabled. The
1412 returned instance is usually equivalent to the lightweight time
1413 representation \c {QTimeZone(QTimeZone::LocalTime)}, albeit implemented as a
1414 time zone.
1415
1416 The returned object will not change to reflect any subsequent change to the
1417 system time zone. It represents the local time that was in effect when
1418 asBackendZone() was called. On misconfigured systems, such as those that
1419 lack the timezone data relied on by the backend for which Qt was compiled,
1420 it may be invalid. In such a case, a warning is output.
1421
1422 \sa utc(), Initialization, asBackendZone(), systemTimeZoneId()
1423*/
1424QTimeZone QTimeZone::systemTimeZone()
1425{
1426 // Short-cut constructor's handling of empty ID:
1427 const QByteArray sysId = global_tz->backend->systemTimeZoneId();
1428 const auto sys = sysId.isEmpty() ? QTimeZone(global_tz->backend) : QTimeZone(sysId);
1429 if (!sys.isValid()) {
1430 static bool neverWarned = true;
1431 if (neverWarned) {
1432 // Racey but, at worst, merely repeats the warning.
1433 neverWarned = false;
1434 qWarning(msg: "Unable to determine system time zone: "
1435 "please check your system configuration.");
1436 }
1437 }
1438 return sys;
1439}
1440
1441/*!
1442 \since 5.5
1443 Returns a QTimeZone object that describes UTC as a time zone.
1444
1445 This method is only available when feature \c timezone is enabled. It is
1446 equivalent to passing 0 to QTimeZone(int offsetSeconds) and to the
1447 lightweight time representation QTimeZone(QTimeZone::UTC), albeit
1448 implemented as a time zone, unlike the latter.
1449
1450 \sa systemTimeZone(), Initialization, asBackendZone()
1451*/
1452QTimeZone QTimeZone::utc()
1453{
1454 return QTimeZone(0);
1455}
1456
1457/*!
1458 Returns \c true if a given time zone \a ianaId is available on this system.
1459
1460 This may include some non-IANA IDs, notably UTC-offset IDs, that are not
1461 listed in \l availableTimeZoneIds().
1462
1463 This method is only available when feature \c timezone is enabled.
1464
1465 \sa availableTimeZoneIds()
1466*/
1467
1468bool QTimeZone::isTimeZoneIdAvailable(const QByteArray &ianaId)
1469{
1470#if defined(Q_OS_UNIX) && !(defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN))
1471 // Keep #if-ery consistent with selection of QTzTimeZonePrivate in
1472 // newBackendTimeZone(). Skip the pre-check, as the TZ backend accepts POSIX
1473 // zone IDs, which need not be valid IANA IDs. See also QTBUG-112006.
1474#else
1475 // isValidId is not strictly required, but faster to weed out invalid
1476 // IDs as availableTimeZoneIds() may be slow
1477 if (!QTimeZonePrivate::isValidId(ianaId))
1478 return false;
1479#endif
1480 return QUtcTimeZonePrivate().isTimeZoneIdAvailable(ianaId)
1481 || QUtcTimeZonePrivate::offsetFromUtcString(id: ianaId) != QTimeZonePrivate::invalidSeconds()
1482 || global_tz->backend->isTimeZoneIdAvailable(ianaId);
1483}
1484
1485static QList<QByteArray> set_union(const QList<QByteArray> &l1, const QList<QByteArray> &l2)
1486{
1487 QList<QByteArray> result;
1488 result.reserve(asize: l1.size() + l2.size());
1489 std::set_union(first1: l1.begin(), last1: l1.end(),
1490 first2: l2.begin(), last2: l2.end(),
1491 result: std::back_inserter(x&: result));
1492 return result;
1493}
1494
1495/*!
1496 Returns a list of all available IANA time zone IDs on this system.
1497
1498 This method is only available when feature \c timezone is enabled.
1499
1500 \note the QTimeZone constructor will also accept some UTC-offset IDs that
1501 are not in the list returned - it would be impractical to list all possible
1502 UTC-offset IDs.
1503
1504 \sa isTimeZoneIdAvailable()
1505*/
1506
1507QList<QByteArray> QTimeZone::availableTimeZoneIds()
1508{
1509 return set_union(l1: QUtcTimeZonePrivate().availableTimeZoneIds(),
1510 l2: global_tz->backend->availableTimeZoneIds());
1511}
1512
1513/*!
1514 Returns a list of all available IANA time zone IDs for a given \a territory.
1515
1516 As a special case, a \a territory of \l {QLocale::} {AnyTerritory} selects
1517 those time zones that have a non-territorial association, such as UTC, while
1518 \l {QLocale::}{World} selects those time-zones for which there is a global
1519 default IANA ID. If you require a list of all time zone IDs for all
1520 territories then use the standard availableTimeZoneIds() method.
1521
1522 This method is only available when feature \c timezone is enabled.
1523
1524 \sa isTimeZoneIdAvailable(), territory()
1525*/
1526
1527QList<QByteArray> QTimeZone::availableTimeZoneIds(QLocale::Territory territory)
1528{
1529 return set_union(l1: QUtcTimeZonePrivate().availableTimeZoneIds(country: territory),
1530 l2: global_tz->backend->availableTimeZoneIds(territory));
1531}
1532
1533/*!
1534 Returns a list of all available IANA time zone IDs with a given standard
1535 time offset of \a offsetSeconds.
1536
1537 Where the given offset is supported, \c{QTimeZone(offsetSeconds).id()} is
1538 included in the list, even if it is not an IANA ID. This only arises when
1539 there is no IANA UTC-offset ID with the given offset.
1540
1541 This method is only available when feature \c timezone is enabled.
1542
1543 \sa isTimeZoneIdAvailable(), QTimeZone(int)
1544*/
1545
1546QList<QByteArray> QTimeZone::availableTimeZoneIds(int offsetSeconds)
1547{
1548 return set_union(l1: QUtcTimeZonePrivate().availableTimeZoneIds(utcOffset: offsetSeconds),
1549 l2: global_tz->backend->availableTimeZoneIds(utcOffset: offsetSeconds));
1550}
1551
1552/*!
1553 Returns the Windows ID equivalent to the given \a ianaId.
1554
1555 This method is only available when feature \c timezone is enabled.
1556
1557 \sa windowsIdToDefaultIanaId(), windowsIdToIanaIds()
1558*/
1559
1560QByteArray QTimeZone::ianaIdToWindowsId(const QByteArray &ianaId)
1561{
1562 return QTimeZonePrivate::ianaIdToWindowsId(ianaId);
1563}
1564
1565/*!
1566 Returns the default IANA ID for a given \a windowsId.
1567
1568 Because a Windows ID can cover several IANA IDs in several different
1569 territories, this function returns the most frequently used IANA ID with no
1570 regard for the territory and should thus be used with care. It is usually
1571 best to request the default for a specific territory.
1572
1573 This method is only available when feature \c timezone is enabled.
1574
1575 \sa ianaIdToWindowsId(), windowsIdToIanaIds()
1576*/
1577
1578QByteArray QTimeZone::windowsIdToDefaultIanaId(const QByteArray &windowsId)
1579{
1580 return QTimeZonePrivate::windowsIdToDefaultIanaId(windowsId);
1581}
1582
1583/*!
1584 Returns the default IANA ID for a given \a windowsId and \a territory.
1585
1586 Because a Windows ID can cover several IANA IDs within a given territory,
1587 the most frequently used IANA ID in that territory is returned.
1588
1589 As a special case, \l {QLocale::} {AnyTerritory} returns the default of
1590 those IANA IDs that have a non-territorial association, while \l {QLocale::}
1591 {World} returns the default for the given \a windowsId in territories that
1592 have no specific association with it.
1593
1594 If the return is empty, there is no IANA ID specific to the given \a
1595 territory for this \a windowsId. It is reasonable, in this case, to fall
1596 back to \c{windowsIdToDefaultIanaId(windowsId)}.
1597
1598 This method is only available when feature \c timezone is enabled.
1599
1600 \sa ianaIdToWindowsId(), windowsIdToIanaIds(), territory()
1601*/
1602
1603QByteArray QTimeZone::windowsIdToDefaultIanaId(const QByteArray &windowsId,
1604 QLocale::Territory territory)
1605{
1606 return QTimeZonePrivate::windowsIdToDefaultIanaId(windowsId, territory);
1607}
1608
1609/*!
1610 Returns all the IANA IDs for a given \a windowsId.
1611
1612 The returned list is sorted alphabetically.
1613
1614 This method is only available when feature \c timezone is enabled.
1615
1616 \sa ianaIdToWindowsId(), windowsIdToDefaultIanaId()
1617*/
1618
1619QList<QByteArray> QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId)
1620{
1621 return QTimeZonePrivate::windowsIdToIanaIds(windowsId);
1622}
1623
1624/*!
1625 Returns all the IANA IDs for a given \a windowsId and \a territory.
1626
1627 As a special case, \l{QLocale::} {AnyTerritory} selects those IANA IDs that
1628 have a non-territorial association, while \l {QLocale::} {World} selects the
1629 default for the given \a windowsId in territories that have no specific
1630 association with it.
1631
1632 The returned list is in order of frequency of usage, i.e. larger zones
1633 within a territory are listed first.
1634
1635 This method is only available when feature \c timezone is enabled.
1636
1637 \sa ianaIdToWindowsId(), windowsIdToDefaultIanaId(), territory()
1638*/
1639
1640QList<QByteArray> QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId,
1641 QLocale::Territory territory)
1642{
1643 return QTimeZonePrivate::windowsIdToIanaIds(windowsId, territory);
1644}
1645
1646/*!
1647 \fn QTimeZone QTimeZone::fromStdTimeZonePtr(const std::chrono::time_zone *timeZone)
1648 \since 6.4
1649
1650 Returns a QTimeZone object representing the same time zone as \a timeZone.
1651 The IANA ID of \a timeZone must be one of the available system IDs,
1652 otherwise an invalid time zone will be returned.
1653
1654 This method is only available when feature \c timezone is enabled.
1655*/
1656#endif // feature timezone
1657
1658template <typename Stream, typename Wrap>
1659void QTimeZone::Data::serialize(Stream &out, const Wrap &wrap) const
1660{
1661 if (isShort()) {
1662 switch (s.spec()) {
1663 case Qt::UTC:
1664 out << wrap("QTimeZone::UTC");
1665 break;
1666 case Qt::LocalTime:
1667 out << wrap("QTimeZone::LocalTime");
1668 break;
1669 case Qt::OffsetFromUTC:
1670 out << wrap("AheadOfUtcBy") << int(s.offset);
1671 break;
1672 case Qt::TimeZone:
1673 Q_UNREACHABLE();
1674 break;
1675 }
1676 return;
1677 }
1678#if QT_CONFIG(timezone)
1679 if constexpr (std::is_same<Stream, QDataStream>::value) {
1680 if (d)
1681 d->serialize(ds&: out);
1682 } else {
1683 // QDebug, traditionally gets a QString, hence quotes round the (possibly empty) ID:
1684 out << QString::fromUtf8(utf8: d ? QByteArrayView(d->id()) : QByteArrayView());
1685 }
1686#endif
1687}
1688
1689#ifndef QT_NO_DATASTREAM
1690// Invalid, as an IANA ID: too long, starts with - and has other invalid characters in it
1691static inline QString invalidId() { return QStringLiteral("-No Time Zone Specified!"); }
1692
1693QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz)
1694{
1695 const auto toQString = [](const char *text) {
1696 return QString(QLatin1StringView(text));
1697 };
1698 if (tz.isValid())
1699 tz.d.serialize(out&: ds, wrap: toQString);
1700 else
1701 ds << invalidId();
1702 return ds;
1703}
1704
1705QDataStream &operator>>(QDataStream &ds, QTimeZone &tz)
1706{
1707 QString ianaId;
1708 ds >> ianaId;
1709 // That may be various things other than actual IANA IDs:
1710 if (ianaId == invalidId()) {
1711 tz = QTimeZone();
1712 } else if (ianaId == "OffsetFromUtc"_L1) {
1713 int utcOffset;
1714 QString name;
1715 QString abbreviation;
1716 int territory;
1717 QString comment;
1718 ds >> ianaId >> utcOffset >> name >> abbreviation >> territory >> comment;
1719#if QT_CONFIG(timezone)
1720 // Try creating as a system timezone, which succeeds (producing a valid
1721 // zone) iff ianaId is valid; use this if it is a plain offset from UTC
1722 // zone, with the right offset, ignoring the other data:
1723 tz = QTimeZone(ianaId.toUtf8());
1724 if (!tz.isValid() || tz.hasDaylightTime()
1725 || tz.offsetFromUtc(atDateTime: QDateTime::fromMSecsSinceEpoch(msecs: 0, timeZone: QTimeZone::UTC)) != utcOffset) {
1726 // Construct a custom timezone using the saved values:
1727 tz = QTimeZone(ianaId.toUtf8(), utcOffset, name, abbreviation,
1728 QLocale::Territory(territory), comment);
1729 }
1730#else
1731 tz = QTimeZone::fromSecondsAheadOfUtc(utcOffset);
1732#endif
1733 } else if (ianaId == "AheadOfUtcBy"_L1) {
1734 int utcOffset;
1735 ds >> utcOffset;
1736 tz = QTimeZone::fromSecondsAheadOfUtc(offset: utcOffset);
1737 } else if (ianaId == "QTimeZone::UTC"_L1) {
1738 tz = QTimeZone(QTimeZone::UTC);
1739 } else if (ianaId == "QTimeZone::LocalTime"_L1) {
1740 tz = QTimeZone(QTimeZone::LocalTime);
1741#if QT_CONFIG(timezone)
1742 } else {
1743 tz = QTimeZone(ianaId.toUtf8());
1744#endif
1745 }
1746 return ds;
1747}
1748#endif // QT_NO_DATASTREAM
1749
1750#ifndef QT_NO_DEBUG_STREAM
1751QDebug operator<<(QDebug dbg, const QTimeZone &tz)
1752{
1753 QDebugStateSaver saver(dbg);
1754 const auto asIs = [](const char *text) { return text; };
1755 // TODO Include backend and data version details?
1756 dbg.nospace() << "QTimeZone(";
1757 tz.d.serialize(out&: dbg, wrap: asIs);
1758 dbg.nospace() << ')';
1759 return dbg;
1760}
1761#endif
1762
1763QT_END_NAMESPACE
1764

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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