1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 John Layt <jlayt@kde.org>
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 
41 #include "qtimezone.h"
42 #include "qtimezoneprivate_p.h"
43 
44 #include <QtCore/qdatastream.h>
45 #include <QtCore/qdatetime.h>
46 
47 #include <qdebug.h>
48 
49 #include <algorithm>
50 
51 QT_BEGIN_NAMESPACE
52 
53 // Create default time zone using appropriate backend
newBackendTimeZone()54 static QTimeZonePrivate *newBackendTimeZone()
55 {
56 #ifdef QT_NO_SYSTEMLOCALE
57 #if QT_CONFIG(icu)
58     return new QIcuTimeZonePrivate();
59 #else
60     return new QUtcTimeZonePrivate();
61 #endif
62 #else
63 #if defined Q_OS_MAC
64     return new QMacTimeZonePrivate();
65 #elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
66     return new QAndroidTimeZonePrivate();
67 #elif defined(Q_OS_UNIX) || defined(Q_OS_ANDROID_EMBEDDED)
68     return new QTzTimeZonePrivate();
69 #elif QT_CONFIG(icu)
70     return new QIcuTimeZonePrivate();
71 #elif defined Q_OS_WIN
72     return new QWinTimeZonePrivate();
73 #else
74     return new QUtcTimeZonePrivate();
75 #endif // System Locales
76 #endif // QT_NO_SYSTEMLOCALE
77 }
78 
79 // Create named time zone using appropriate backend
newBackendTimeZone(const QByteArray & ianaId)80 static QTimeZonePrivate *newBackendTimeZone(const QByteArray &ianaId)
81 {
82     Q_ASSERT(!ianaId.isEmpty());
83 #ifdef QT_NO_SYSTEMLOCALE
84 #if QT_CONFIG(icu)
85     return new QIcuTimeZonePrivate(ianaId);
86 #else
87     return new QUtcTimeZonePrivate(ianaId);
88 #endif
89 #else
90 #if defined Q_OS_MAC
91     return new QMacTimeZonePrivate(ianaId);
92 #elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
93     return new QAndroidTimeZonePrivate(ianaId);
94 #elif defined(Q_OS_UNIX) || defined(Q_OS_ANDROID_EMBEDDED)
95     return new QTzTimeZonePrivate(ianaId);
96 #elif QT_CONFIG(icu)
97     return new QIcuTimeZonePrivate(ianaId);
98 #elif defined Q_OS_WIN
99     return new QWinTimeZonePrivate(ianaId);
100 #else
101     return new QUtcTimeZonePrivate(ianaId);
102 #endif // System Locales
103 #endif // QT_NO_SYSTEMLOCALE
104 }
105 
106 class QTimeZoneSingleton
107 {
108 public:
QTimeZoneSingleton()109     QTimeZoneSingleton() : backend(newBackendTimeZone()) {}
110 
111     // The backend_tz is the tz to use in static methods such as availableTimeZoneIds() and
112     // isTimeZoneIdAvailable() and to create named IANA time zones.  This is usually the host
113     // system, but may be different if the host resources are insufficient or if
114     // QT_NO_SYSTEMLOCALE is set.  A simple UTC backend is used if no alternative is available.
115     QSharedDataPointer<QTimeZonePrivate> backend;
116 };
117 
118 Q_GLOBAL_STATIC(QTimeZoneSingleton, global_tz);
119 
120 /*!
121     \class QTimeZone
122     \inmodule QtCore
123     \since 5.2
124 
125     \brief The QTimeZone class converts between UTC and local time in a specific
126            time zone.
127 
128     \threadsafe
129 
130     This class provides a stateless calculator for time zone conversions
131     between UTC and the local time in a specific time zone.  By default it uses
132     the host system time zone data to perform these conversions.
133 
134     This class is primarily designed for use in QDateTime; most applications
135     will not need to access this class directly and should instead use
136     QDateTime with a Qt::TimeSpec of Qt::TimeZone.
137 
138     \note For consistency with QDateTime, QTimeZone does not account for leap
139     seconds.
140 
141     \section1 Remarks
142 
143     \section2 IANA Time Zone IDs
144 
145     QTimeZone uses the IANA time zone IDs as defined in the IANA Time Zone
146     Database (http://www.iana.org/time-zones). This is to ensure a standard ID
147     across all supported platforms.  Most platforms support the IANA IDs
148     and the IANA Database natively, but for Windows a mapping is required to
149     the native IDs.  See below for more details.
150 
151     The IANA IDs can and do change on a regular basis, and can vary depending
152     on how recently the host system data was updated.  As such you cannot rely
153     on any given ID existing on any host system.  You must use
154     availableTimeZoneIds() to determine what IANA IDs are available.
155 
156     The IANA IDs and database are also know as the Olson IDs and database,
157     named after their creator.
158 
159     \section2 UTC Offset Time Zones
160 
161     A default UTC time zone backend is provided which is always guaranteed to
162     be available.  This provides a set of generic Offset From UTC time zones
163     in the range UTC-14:00 to UTC+14:00.  These time zones can be created
164     using either the standard ISO format names "UTC+00:00" as listed by
165     availableTimeZoneIds(), or using the number of offset seconds.
166 
167     \section2 Windows Time Zones
168 
169     Windows native time zone support is severely limited compared to the
170     standard IANA TZ Database.  Windows time zones cover larger geographic
171     areas and are thus less accurate in their conversions.  They also do not
172     support as much historic conversion data and so may only be accurate for
173     the current year.
174 
175     QTimeZone uses a conversion table derived form the Unicode CLDR data to map
176     between IANA IDs and Windows IDs.  Depending on your version of Windows
177     and Qt, this table may not be able to provide a valid conversion, in which
178     "UTC" will be returned.
179 
180     QTimeZone provides a public API to use this conversion table.  The Windows ID
181     used is the Windows Registry Key for the time zone which is also the MS
182     Exchange EWS ID as well, but is different to the Time Zone Name (TZID) and
183     COD code used by MS Exchange in versions before 2007.
184 
185     \section2 System Time Zone
186 
187     QTimeZone does not support any concept of a system or default time zone.
188     If you require a QDateTime that uses the current system time zone at any
189     given moment then you should use a Qt::TimeSpec of Qt::LocalTime.
190 
191     The method systemTimeZoneId() returns the current system IANA time zone
192     ID which on Unix-like systems will always be correct.  On Windows this ID is
193     translated from the Windows system ID using an internal translation
194     table and the user's selected country.  As a consequence there is a small
195     chance any Windows install may have IDs not known by Qt, in which case
196     "UTC" will be returned.
197 
198     Creating a new QTimeZone instance using the system time zone ID will only
199     produce a fixed named copy of the time zone, it will not change if the
200     system time zone changes.
201 
202     \section2 Time Zone Offsets
203 
204     The difference between UTC and the local time in a time zone is expressed
205     as an offset in seconds from UTC, i.e. the number of seconds to add to UTC
206     to obtain the local time.  The total offset is comprised of two component
207     parts, the standard time offset and the daylight-saving time offset.  The
208     standard time offset is the number of seconds to add to UTC to obtain
209     standard time in the time zone.  The daylight-saving time offset is the
210     number of seconds to add to the standard time offset to obtain
211     daylight-saving time (abbreviated DST and sometimes called "daylight time"
212     or "summer time") in the time zone.
213 
214     Note that the standard and DST offsets for a time zone may change over time
215     as countries have changed DST laws or even their standard time offset.
216 
217     \section2 License
218 
219     This class includes data obtained from the CLDR data files under the terms
220     of the Unicode Data Files and Software License. See
221     \l{unicode-cldr}{Unicode Common Locale Data Repository (CLDR)} for details.
222 
223     \sa QDateTime
224 */
225 
226 /*!
227   \enum QTimeZone::anonymous
228 
229   Sane UTC offsets range from -14 to +14 hours.
230   No known zone > 12 hrs West of Greenwich (Baker Island, USA).
231   No known zone > 14 hrs East of Greenwich (Kiritimati, Christmas Island, Kiribati).
232 
233   \value MinUtcOffsetSecs
234           -14 * 3600,
235 
236   \value MaxUtcOffsetSecs
237           +14 * 3600
238 */
239 
240 /*!
241     \enum QTimeZone::TimeType
242 
243     The type of time zone time, for example when requesting the name.  In time
244     zones that do not apply DST, all three values may return the same result.
245 
246     \value StandardTime
247            The standard time in a time zone, i.e. when Daylight-Saving is not
248            in effect.
249            For example when formatting a display name this will show something
250            like "Pacific Standard Time".
251     \value DaylightTime
252            A time when Daylight-Saving is in effect.
253            For example when formatting a display name this will show something
254            like "Pacific daylight-saving time".
255     \value GenericTime
256            A time which is not specifically Standard or Daylight-Saving time,
257            either an unknown time or a neutral form.
258            For example when formatting a display name this will show something
259            like "Pacific Time".
260 */
261 
262 /*!
263     \enum QTimeZone::NameType
264 
265     The type of time zone name.
266 
267     \value DefaultName
268            The default form of the time zone name, e.g. LongName, ShortName or OffsetName
269     \value LongName
270            The long form of the time zone name, e.g. "Central European Time"
271     \value ShortName
272            The short form of the time zone name, usually an abbreviation, e.g. "CET"
273     \value OffsetName
274            The standard ISO offset form of the time zone name, e.g. "UTC+01:00"
275 */
276 
277 /*!
278     \class QTimeZone::OffsetData
279     \inmodule QtCore
280 
281     The time zone offset data for a given moment in time, i.e. the time zone
282     offsets and abbreviation to use at that moment in time.
283 
284     \list
285     \li OffsetData::atUtc  The datetime of the offset data in UTC time.
286     \li OffsetData::offsetFromUtc  The total offset from UTC in effect at the datetime.
287     \li OffsetData::standardTimeOffset  The standard time offset component of the total offset.
288     \li OffsetData::daylightTimeOffset  The DST offset component of the total offset.
289     \li OffsetData::abbreviation  The abbreviation in effect at the datetime.
290     \endlist
291 
292     For example, for time zone "Europe/Berlin" the OffsetDate in standard and DST might be:
293 
294     \list
295     \li atUtc = QDateTime(QDate(2013, 1, 1), QTime(0, 0, 0), Qt::UTC)
296     \li offsetFromUtc = 3600
297     \li standardTimeOffset = 3600
298     \li daylightTimeOffset = 0
299     \li abbreviation = "CET"
300     \endlist
301 
302     \list
303     \li atUtc = QDateTime(QDate(2013, 6, 1), QTime(0, 0, 0), Qt::UTC)
304     \li offsetFromUtc = 7200
305     \li standardTimeOffset = 3600
306     \li daylightTimeOffset = 3600
307     \li abbreviation = "CEST"
308     \endlist
309 */
310 
311 /*!
312     \typedef QTimeZone::OffsetDataList
313 
314     Synonym for QVector<OffsetData>.
315 */
316 
317 /*!
318     Create a null/invalid time zone instance.
319 */
320 
QTimeZone()321 QTimeZone::QTimeZone() noexcept
322     : d(nullptr)
323 {
324 }
325 
326 /*!
327     Creates an instance of the requested time zone \a ianaId.
328 
329     The ID must be one of the available system IDs or a valid UTC-with-offset
330     ID, otherwise an invalid time zone will be returned.
331 
332     \sa availableTimeZoneIds()
333 */
334 
QTimeZone(const QByteArray & ianaId)335 QTimeZone::QTimeZone(const QByteArray &ianaId)
336 {
337     // Try and see if it's a CLDR UTC offset ID - just as quick by creating as
338     // by looking up.
339     d = new QUtcTimeZonePrivate(ianaId);
340     // If not a CLDR UTC offset ID then try creating it with the system backend.
341     // Relies on backend not creating valid TZ with invalid name.
342     if (!d->isValid())
343         d = ianaId.isEmpty() ? newBackendTimeZone() : newBackendTimeZone(ianaId);
344     // Can also handle UTC with arbitrary (valid) offset, but only do so as
345     // fall-back, since either of the above may handle it more informatively.
346     if (!d->isValid()) {
347         qint64 offset = QUtcTimeZonePrivate::offsetFromUtcString(ianaId);
348         if (offset != QTimeZonePrivate::invalidSeconds()) {
349             // Should have abs(offset) < 24 * 60 * 60 = 86400.
350             qint32 seconds = qint32(offset);
351             Q_ASSERT(qint64(seconds) == offset);
352             // NB: this canonicalises the name, so it might not match ianaId
353             d = new QUtcTimeZonePrivate(seconds);
354         }
355     }
356 }
357 
358 /*!
359     Creates an instance of a time zone with the requested Offset from UTC of
360     \a offsetSeconds.
361 
362     The \a offsetSeconds from UTC must be in the range -14 hours to +14 hours
363     otherwise an invalid time zone will be returned.
364 */
365 
QTimeZone(int offsetSeconds)366 QTimeZone::QTimeZone(int offsetSeconds)
367     : d((offsetSeconds >= MinUtcOffsetSecs && offsetSeconds <= MaxUtcOffsetSecs)
368         ? new QUtcTimeZonePrivate(offsetSeconds) : nullptr)
369 {
370 }
371 
372 /*!
373     Creates a custom time zone with an ID of \a ianaId and an offset from UTC
374     of \a offsetSeconds.  The \a name will be the name used by displayName()
375     for the LongName, the \a abbreviation will be used by displayName() for the
376     ShortName and by abbreviation(), and the optional \a country will be used
377     by country().  The \a comment is an optional note that may be displayed in
378     a GUI to assist users in selecting a time zone.
379 
380     The \a ianaId must not be one of the available system IDs returned by
381     availableTimeZoneIds().  The \a offsetSeconds from UTC must be in the range
382     -14 hours to +14 hours.
383 
384     If the custom time zone does not have a specific country then set it to the
385     default value of QLocale::AnyCountry.
386 */
387 
QTimeZone(const QByteArray & ianaId,int offsetSeconds,const QString & name,const QString & abbreviation,QLocale::Country country,const QString & comment)388 QTimeZone::QTimeZone(const QByteArray &ianaId, int offsetSeconds, const QString &name,
389                      const QString &abbreviation, QLocale::Country country, const QString &comment)
390     : d()
391 {
392     if (!isTimeZoneIdAvailable(ianaId))
393         d = new QUtcTimeZonePrivate(ianaId, offsetSeconds, name, abbreviation, country, comment);
394 }
395 
396 /*!
397     \internal
398 
399     Private. Create time zone with given private backend
400 */
401 
QTimeZone(QTimeZonePrivate & dd)402 QTimeZone::QTimeZone(QTimeZonePrivate &dd)
403     : d(&dd)
404 {
405 }
406 
407 /*!
408     Copy constructor, copy \a other to this.
409 */
410 
QTimeZone(const QTimeZone & other)411 QTimeZone::QTimeZone(const QTimeZone &other)
412     : d(other.d)
413 {
414 }
415 
416 /*!
417     Destroys the time zone.
418 */
419 
~QTimeZone()420 QTimeZone::~QTimeZone()
421 {
422 }
423 
424 /*!
425     \fn QTimeZone::swap(QTimeZone &other)
426 
427     Swaps this time zone instance with \a other. This function is very
428     fast and never fails.
429 */
430 
431 /*!
432     Assignment operator, assign \a other to this.
433 */
434 
operator =(const QTimeZone & other)435 QTimeZone &QTimeZone::operator=(const QTimeZone &other)
436 {
437     d = other.d;
438     return *this;
439 }
440 
441 /*
442     \fn void QTimeZone::swap(QTimeZone &other)
443 
444     Swaps this timezone with \a other. This function is very fast and
445     never fails.
446 */
447 
448 /*!
449     \fn QTimeZone &QTimeZone::operator=(QTimeZone &&other)
450 
451     Move-assigns \a other to this QTimeZone instance, transferring the
452     ownership of the managed pointer to this instance.
453 */
454 
455 /*!
456     Returns \c true if this time zone is equal to the \a other time zone.
457 */
458 
operator ==(const QTimeZone & other) const459 bool QTimeZone::operator==(const QTimeZone &other) const
460 {
461     if (d && other.d)
462         return (*d == *other.d);
463     else
464         return (d == other.d);
465 }
466 
467 /*!
468     Returns \c true if this time zone is not equal to the \a other time zone.
469 */
470 
operator !=(const QTimeZone & other) const471 bool QTimeZone::operator!=(const QTimeZone &other) const
472 {
473     if (d && other.d)
474         return (*d != *other.d);
475     else
476         return (d != other.d);
477 }
478 
479 /*!
480     Returns \c true if this time zone is valid.
481 */
482 
isValid() const483 bool QTimeZone::isValid() const
484 {
485     if (d)
486         return d->isValid();
487     else
488         return false;
489 }
490 
491 /*!
492     Returns the IANA ID for the time zone.
493 
494     IANA IDs are used on all platforms.  On Windows these are translated
495     from the Windows ID into the closest IANA ID for the time zone and country.
496 */
497 
id() const498 QByteArray QTimeZone::id() const
499 {
500     if (d)
501         return d->id();
502     else
503         return QByteArray();
504 }
505 
506 /*!
507     Returns the country for the time zone.
508 */
509 
country() const510 QLocale::Country QTimeZone::country() const
511 {
512     if (isValid())
513         return d->country();
514     else
515         return QLocale::AnyCountry;
516 }
517 
518 /*!
519     Returns any comment for the time zone.
520 
521     A comment may be provided by the host platform to assist users in
522     choosing the correct time zone.  Depending on the platform this may not
523     be localized.
524 */
525 
comment() const526 QString QTimeZone::comment() const
527 {
528     if (isValid())
529         return d->comment();
530     else
531         return QString();
532 }
533 
534 /*!
535     Returns the localized time zone display name at the given \a atDateTime
536     for the given \a nameType in the given \a locale.  The \a nameType and
537     \a locale requested may not be supported on all platforms, in which case
538     the best available option will be returned.
539 
540     If the \a locale is not provided then the application default locale will
541     be used.
542 
543     The display name may change depending on DST or historical events.
544 
545     \sa abbreviation()
546 */
547 
displayName(const QDateTime & atDateTime,NameType nameType,const QLocale & locale) const548 QString QTimeZone::displayName(const QDateTime &atDateTime, NameType nameType,
549                                const QLocale &locale) const
550 {
551     if (isValid())
552         return d->displayName(atDateTime.toMSecsSinceEpoch(), nameType, locale);
553     else
554         return QString();
555 }
556 
557 /*!
558     Returns the localized time zone display name for the given \a timeType
559     and \a nameType in the given \a locale. The \a nameType and \a locale
560     requested may not be supported on all platforms, in which case the best
561     available option will be returned.
562 
563     If the \a locale is not provided then the application default locale will
564     be used.
565 
566     Where the time zone display names have changed over time then the most
567     recent names will be used.
568 
569     \sa abbreviation()
570 */
571 
displayName(TimeType timeType,NameType nameType,const QLocale & locale) const572 QString QTimeZone::displayName(TimeType timeType, NameType nameType,
573                                const QLocale &locale) const
574 {
575     if (isValid())
576         return d->displayName(timeType, nameType, locale);
577     else
578         return QString();
579 }
580 
581 /*!
582     Returns the time zone abbreviation at the given \a atDateTime.  The
583     abbreviation may change depending on DST or even historical events.
584 
585     Note that the abbreviation is not guaranteed to be unique to this time zone
586     and should not be used in place of the ID or display name.
587 
588     \sa displayName()
589 */
590 
abbreviation(const QDateTime & atDateTime) const591 QString QTimeZone::abbreviation(const QDateTime &atDateTime) const
592 {
593     if (isValid())
594         return d->abbreviation(atDateTime.toMSecsSinceEpoch());
595     else
596         return QString();
597 }
598 
599 /*!
600     Returns the total effective offset at the given \a atDateTime, i.e. the
601     number of seconds to add to UTC to obtain the local time.  This includes
602     any DST offset that may be in effect, i.e. it is the sum of
603     standardTimeOffset() and daylightTimeOffset() for the given datetime.
604 
605     For example, for the time zone "Europe/Berlin" the standard time offset is
606     +3600 seconds and the DST offset is +3600 seconds.  During standard time
607     offsetFromUtc() will return +3600 (UTC+01:00), and during DST it will
608     return +7200 (UTC+02:00).
609 
610     \sa standardTimeOffset(), daylightTimeOffset()
611 */
612 
offsetFromUtc(const QDateTime & atDateTime) const613 int QTimeZone::offsetFromUtc(const QDateTime &atDateTime) const
614 {
615     if (isValid())
616         return d->offsetFromUtc(atDateTime.toMSecsSinceEpoch());
617     else
618         return 0;
619 }
620 
621 /*!
622     Returns the standard time offset at the given \a atDateTime, i.e. the
623     number of seconds to add to UTC to obtain the local Standard Time.  This
624     excludes any DST offset that may be in effect.
625 
626     For example, for the time zone "Europe/Berlin" the standard time offset is
627     +3600 seconds.  During both standard and DST offsetFromUtc() will return
628     +3600 (UTC+01:00).
629 
630     \sa offsetFromUtc(), daylightTimeOffset()
631 */
632 
standardTimeOffset(const QDateTime & atDateTime) const633 int QTimeZone::standardTimeOffset(const QDateTime &atDateTime) const
634 {
635     if (isValid())
636         return d->standardTimeOffset(atDateTime.toMSecsSinceEpoch());
637     else
638         return 0;
639 }
640 
641 /*!
642     Returns the daylight-saving time offset at the given \a atDateTime,
643     i.e. the number of seconds to add to the standard time offset to obtain the
644     local daylight-saving time.
645 
646     For example, for the time zone "Europe/Berlin" the DST offset is +3600
647     seconds.  During standard time daylightTimeOffset() will return 0, and when
648     daylight-saving is in effect it will return +3600.
649 
650     \sa offsetFromUtc(), standardTimeOffset()
651 */
652 
daylightTimeOffset(const QDateTime & atDateTime) const653 int QTimeZone::daylightTimeOffset(const QDateTime &atDateTime) const
654 {
655     if (hasDaylightTime())
656         return d->daylightTimeOffset(atDateTime.toMSecsSinceEpoch());
657     else
658         return 0;
659 }
660 
661 /*!
662     Returns \c true if the time zone has practiced daylight-saving at any time.
663 
664     \sa isDaylightTime(), daylightTimeOffset()
665 */
666 
hasDaylightTime() const667 bool QTimeZone::hasDaylightTime() const
668 {
669     if (isValid())
670         return d->hasDaylightTime();
671     else
672         return false;
673 }
674 
675 /*!
676     Returns \c true if daylight-saving was in effect at the given \a atDateTime.
677 
678     \sa hasDaylightTime(), daylightTimeOffset()
679 */
680 
isDaylightTime(const QDateTime & atDateTime) const681 bool QTimeZone::isDaylightTime(const QDateTime &atDateTime) const
682 {
683     if (hasDaylightTime())
684         return d->isDaylightTime(atDateTime.toMSecsSinceEpoch());
685     else
686         return false;
687 }
688 
689 /*!
690     Returns the effective offset details at the given \a forDateTime. This is
691     the equivalent of calling offsetFromUtc(), abbreviation(), etc individually but is
692     more efficient.
693 
694     \sa offsetFromUtc(), standardTimeOffset(), daylightTimeOffset(), abbreviation()
695 */
696 
offsetData(const QDateTime & forDateTime) const697 QTimeZone::OffsetData QTimeZone::offsetData(const QDateTime &forDateTime) const
698 {
699     if (hasTransitions())
700         return QTimeZonePrivate::toOffsetData(d->data(forDateTime.toMSecsSinceEpoch()));
701     else
702         return QTimeZonePrivate::invalidOffsetData();
703 }
704 
705 /*!
706     Returns \c true if the system backend supports obtaining transitions.
707 
708     Transitions are changes in the time-zone: these happen when DST turns on or
709     off and when authorities alter the offsets for the time-zone.
710 
711     \sa nextTransition(), previousTransition(), transitions()
712 */
713 
hasTransitions() const714 bool QTimeZone::hasTransitions() const
715 {
716     if (isValid())
717         return d->hasTransitions();
718     else
719         return false;
720 }
721 
722 /*!
723     Returns the first time zone Transition after the given \a afterDateTime.
724     This is most useful when you have a Transition time and wish to find the
725     Transition after it.
726 
727     If there is no transition after the given \a afterDateTime then an invalid
728     OffsetData will be returned with an invalid QDateTime.
729 
730     The given \a afterDateTime is exclusive.
731 
732     \sa hasTransitions(), previousTransition(), transitions()
733 */
734 
nextTransition(const QDateTime & afterDateTime) const735 QTimeZone::OffsetData QTimeZone::nextTransition(const QDateTime &afterDateTime) const
736 {
737     if (hasTransitions())
738         return QTimeZonePrivate::toOffsetData(d->nextTransition(afterDateTime.toMSecsSinceEpoch()));
739     else
740         return QTimeZonePrivate::invalidOffsetData();
741 }
742 
743 /*!
744     Returns the first time zone Transition before the given \a beforeDateTime.
745     This is most useful when you have a Transition time and wish to find the
746     Transition before it.
747 
748     If there is no transition before the given \a beforeDateTime then an invalid
749     OffsetData will be returned with an invalid QDateTime.
750 
751     The given \a beforeDateTime is exclusive.
752 
753     \sa hasTransitions(), nextTransition(), transitions()
754 */
755 
previousTransition(const QDateTime & beforeDateTime) const756 QTimeZone::OffsetData QTimeZone::previousTransition(const QDateTime &beforeDateTime) const
757 {
758     if (hasTransitions())
759         return QTimeZonePrivate::toOffsetData(d->previousTransition(beforeDateTime.toMSecsSinceEpoch()));
760     else
761         return QTimeZonePrivate::invalidOffsetData();
762 }
763 
764 /*!
765     Returns a list of all time zone transitions between the given datetimes.
766 
767     The given \a fromDateTime and \a toDateTime are inclusive.
768 
769     \sa hasTransitions(), nextTransition(), previousTransition()
770 */
771 
transitions(const QDateTime & fromDateTime,const QDateTime & toDateTime) const772 QTimeZone::OffsetDataList QTimeZone::transitions(const QDateTime &fromDateTime,
773                                                  const QDateTime &toDateTime) const
774 {
775     OffsetDataList list;
776     if (hasTransitions()) {
777         const QTimeZonePrivate::DataList plist = d->transitions(fromDateTime.toMSecsSinceEpoch(),
778                                                                 toDateTime.toMSecsSinceEpoch());
779         list.reserve(plist.count());
780         for (const QTimeZonePrivate::Data &pdata : plist)
781             list.append(QTimeZonePrivate::toOffsetData(pdata));
782     }
783     return list;
784 }
785 
786 // Static methods
787 
788 /*!
789     Returns the current system time zone IANA ID.
790 
791     On Windows this ID is translated from the Windows ID using an internal
792     translation table and the user's selected country.  As a consequence there
793     is a small chance any Windows install may have IDs not known by Qt, in
794     which case "UTC" will be returned.
795 */
796 
systemTimeZoneId()797 QByteArray QTimeZone::systemTimeZoneId()
798 {
799     const QByteArray sys = global_tz->backend->systemTimeZoneId();
800     if (!sys.isEmpty())
801         return sys;
802     // The system zone, despite the empty ID, may know its real ID anyway:
803     auto zone = systemTimeZone();
804     if (zone.isValid() && !zone.id().isEmpty())
805         return zone.id();
806     // If all else fails, guess UTC.
807     return QTimeZonePrivate::utcQByteArray();
808 }
809 
810 /*!
811     \since 5.5
812     Returns a QTimeZone object that refers to the local system time, as
813     specified by systemTimeZoneId().
814 
815     \sa utc()
816 */
systemTimeZone()817 QTimeZone QTimeZone::systemTimeZone()
818 {
819     return QTimeZone(global_tz->backend->systemTimeZoneId());
820 }
821 
822 /*!
823     \since 5.5
824     Returns a QTimeZone object that refers to UTC (Universal Time Coordinated).
825 
826     \sa systemTimeZone()
827 */
utc()828 QTimeZone QTimeZone::utc()
829 {
830     return QTimeZone(QTimeZonePrivate::utcQByteArray());
831 }
832 
833 /*!
834     Returns \c true if a given time zone \a ianaId is available on this system.
835 
836     \sa availableTimeZoneIds()
837 */
838 
isTimeZoneIdAvailable(const QByteArray & ianaId)839 bool QTimeZone::isTimeZoneIdAvailable(const QByteArray &ianaId)
840 {
841     // isValidId is not strictly required, but faster to weed out invalid
842     // IDs as availableTimeZoneIds() may be slow
843     if (!QTimeZonePrivate::isValidId(ianaId))
844         return false;
845     return QUtcTimeZonePrivate().isTimeZoneIdAvailable(ianaId) ||
846            global_tz->backend->isTimeZoneIdAvailable(ianaId);
847 }
848 
set_union(const QList<QByteArray> & l1,const QList<QByteArray> & l2)849 static QList<QByteArray> set_union(const QList<QByteArray> &l1, const QList<QByteArray> &l2)
850 {
851     QList<QByteArray> result;
852     result.reserve(l1.size() + l2.size());
853     std::set_union(l1.begin(), l1.end(),
854                    l2.begin(), l2.end(),
855                    std::back_inserter(result));
856     return result;
857 }
858 
859 /*!
860     Returns a list of all available IANA time zone IDs on this system.
861 
862     \sa isTimeZoneIdAvailable()
863 */
864 
availableTimeZoneIds()865 QList<QByteArray> QTimeZone::availableTimeZoneIds()
866 {
867     return set_union(QUtcTimeZonePrivate().availableTimeZoneIds(),
868                      global_tz->backend->availableTimeZoneIds());
869 }
870 
871 /*!
872     Returns a list of all available IANA time zone IDs for a given \a country.
873 
874     As a special case, a \a country of Qt::AnyCountry returns those time zones
875     that do not have any country related to them, such as UTC.  If you require
876     a list of all time zone IDs for all countries then use the standard
877     availableTimeZoneIds() method.
878 
879     \sa isTimeZoneIdAvailable()
880 */
881 
availableTimeZoneIds(QLocale::Country country)882 QList<QByteArray> QTimeZone::availableTimeZoneIds(QLocale::Country country)
883 {
884     return set_union(QUtcTimeZonePrivate().availableTimeZoneIds(country),
885                      global_tz->backend->availableTimeZoneIds(country));
886 }
887 
888 /*!
889     Returns a list of all available IANA time zone IDs with a given standard
890     time offset of \a offsetSeconds.
891 
892     \sa isTimeZoneIdAvailable()
893 */
894 
availableTimeZoneIds(int offsetSeconds)895 QList<QByteArray> QTimeZone::availableTimeZoneIds(int offsetSeconds)
896 {
897     return set_union(QUtcTimeZonePrivate().availableTimeZoneIds(offsetSeconds),
898                      global_tz->backend->availableTimeZoneIds(offsetSeconds));
899 }
900 
901 /*!
902     Returns the Windows ID equivalent to the given \a ianaId.
903 
904     \sa windowsIdToDefaultIanaId(), windowsIdToIanaIds()
905 */
906 
ianaIdToWindowsId(const QByteArray & ianaId)907 QByteArray QTimeZone::ianaIdToWindowsId(const QByteArray &ianaId)
908 {
909     return QTimeZonePrivate::ianaIdToWindowsId(ianaId);
910 }
911 
912 /*!
913     Returns the default IANA ID for a given \a windowsId.
914 
915     Because a Windows ID can cover several IANA IDs in several different
916     countries, this function returns the most frequently used IANA ID with no
917     regard for the country and should thus be used with care.  It is usually
918     best to request the default for a specific country.
919 
920     \sa ianaIdToWindowsId(), windowsIdToIanaIds()
921 */
922 
windowsIdToDefaultIanaId(const QByteArray & windowsId)923 QByteArray QTimeZone::windowsIdToDefaultIanaId(const QByteArray &windowsId)
924 {
925     return QTimeZonePrivate::windowsIdToDefaultIanaId(windowsId);
926 }
927 
928 /*!
929     Returns the default IANA ID for a given \a windowsId and \a country.
930 
931     Because a Windows ID can cover several IANA IDs within a given country,
932     the most frequently used IANA ID in that country is returned.
933 
934     As a special case, QLocale::AnyCountry returns the default of those IANA IDs
935     that do not have any specific country.
936 
937     \sa ianaIdToWindowsId(), windowsIdToIanaIds()
938 */
939 
windowsIdToDefaultIanaId(const QByteArray & windowsId,QLocale::Country country)940 QByteArray QTimeZone::windowsIdToDefaultIanaId(const QByteArray &windowsId,
941                                                 QLocale::Country country)
942 {
943     return QTimeZonePrivate::windowsIdToDefaultIanaId(windowsId, country);
944 }
945 
946 /*!
947     Returns all the IANA IDs for a given \a windowsId.
948 
949     The returned list is sorted alphabetically.
950 
951     \sa ianaIdToWindowsId(), windowsIdToDefaultIanaId()
952 */
953 
windowsIdToIanaIds(const QByteArray & windowsId)954 QList<QByteArray> QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId)
955 {
956     return QTimeZonePrivate::windowsIdToIanaIds(windowsId);
957 }
958 
959 /*!
960     Returns all the IANA IDs for a given \a windowsId and \a country.
961 
962     As a special case QLocale::AnyCountry returns those IANA IDs that do
963     not have any specific country.
964 
965     The returned list is in order of frequency of usage, i.e. larger zones
966     within a country are listed first.
967 
968     \sa ianaIdToWindowsId(), windowsIdToDefaultIanaId()
969 */
970 
windowsIdToIanaIds(const QByteArray & windowsId,QLocale::Country country)971 QList<QByteArray> QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId,
972                                                     QLocale::Country country)
973 {
974     return QTimeZonePrivate::windowsIdToIanaIds(windowsId, country);
975 }
976 
977 #ifndef QT_NO_DATASTREAM
978 // Invalid, as an IANA ID: too long, starts with - and has other invalid characters in it
invalidId()979 static inline QString invalidId() { return QStringLiteral("-No Time Zone Specified!"); }
980 
operator <<(QDataStream & ds,const QTimeZone & tz)981 QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz)
982 {
983     if (tz.isValid())
984         tz.d->serialize(ds);
985     else
986         ds << invalidId();
987     return ds;
988 }
989 
operator >>(QDataStream & ds,QTimeZone & tz)990 QDataStream &operator>>(QDataStream &ds, QTimeZone &tz)
991 {
992     QString ianaId;
993     ds >> ianaId;
994     if (ianaId == invalidId()) {
995         tz = QTimeZone();
996     } else if (ianaId == QLatin1String("OffsetFromUtc")) {
997         int utcOffset;
998         QString name;
999         QString abbreviation;
1000         int country;
1001         QString comment;
1002         ds >> ianaId >> utcOffset >> name >> abbreviation >> country >> comment;
1003         // Try creating as a system timezone, which succeeds (producing a valid
1004         // zone) iff ianaId is valid; we can then ignore the other data.
1005         tz = QTimeZone(ianaId.toUtf8());
1006         // If not, then construct a custom timezone using all the saved values:
1007         if (!tz.isValid())
1008             tz = QTimeZone(ianaId.toUtf8(), utcOffset, name, abbreviation,
1009                            QLocale::Country(country), comment);
1010     } else {
1011         tz = QTimeZone(ianaId.toUtf8());
1012     }
1013     return ds;
1014 }
1015 #endif // QT_NO_DATASTREAM
1016 
1017 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug dbg,const QTimeZone & tz)1018 QDebug operator<<(QDebug dbg, const QTimeZone &tz)
1019 {
1020     QDebugStateSaver saver(dbg);
1021     //TODO Include backend and data version details?
1022     dbg.nospace() << "QTimeZone(" << QString::fromUtf8(tz.id()) << ')';
1023     return dbg;
1024 }
1025 #endif
1026 
1027 QT_END_NAMESPACE
1028