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
51QT_BEGIN_NAMESPACE
52
53// Create default time zone using appropriate backend
54static 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
80static 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
106class QTimeZoneSingleton
107{
108public:
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 QExplicitlySharedDataPointer<QTimeZonePrivate> backend;
116};
117
118Q_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 QList<OffsetData>.
315*/
316
317/*!
318 Create a null/invalid time zone instance.
319*/
320
321QTimeZone::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
335QTimeZone::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.constData()->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.constData()->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
366QTimeZone::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
388QTimeZone::QTimeZone(const QByteArray &ianaId, int offsetSeconds, const QString &name,
389 const QString &abbreviation, QLocale::Country country, const QString &comment)
390{
391 if (!isTimeZoneIdAvailable(ianaId))
392 d = new QUtcTimeZonePrivate(ianaId, offsetSeconds, name, abbreviation, country, comment);
393}
394
395/*!
396 \internal
397
398 Private. Create time zone with given private backend
399*/
400
401QTimeZone::QTimeZone(QTimeZonePrivate &dd)
402 : d(&dd)
403{
404}
405
406/*!
407 Copy constructor, copy \a other to this.
408*/
409
410QTimeZone::QTimeZone(const QTimeZone &other)
411 : d(other.d)
412{
413}
414
415/*!
416 Destroys the time zone.
417*/
418
419QTimeZone::~QTimeZone()
420{
421}
422
423/*!
424 \fn QTimeZone::swap(QTimeZone &other)
425
426 Swaps this time zone instance with \a other. This function is very
427 fast and never fails.
428*/
429
430/*!
431 Assignment operator, assign \a other to this.
432*/
433
434QTimeZone &QTimeZone::operator=(const QTimeZone &other)
435{
436 d = other.d;
437 return *this;
438}
439
440/*
441 \fn void QTimeZone::swap(QTimeZone &other)
442
443 Swaps this timezone with \a other. This function is very fast and
444 never fails.
445*/
446
447/*!
448 \fn QTimeZone &QTimeZone::operator=(QTimeZone &&other)
449
450 Move-assigns \a other to this QTimeZone instance, transferring the
451 ownership of the managed pointer to this instance.
452*/
453
454/*!
455 Returns \c true if this time zone is equal to the \a other time zone.
456*/
457
458bool QTimeZone::operator==(const QTimeZone &other) const
459{
460 return d == other.d || (d && other.d && *d == *other.d);
461}
462
463/*!
464 Returns \c true if this time zone is not equal to the \a other time zone.
465*/
466
467bool QTimeZone::operator!=(const QTimeZone &other) const
468{
469 return d != other.d && (!d || !other.d || *d != *other.d);
470}
471
472/*!
473 Returns \c true if this time zone is valid.
474*/
475
476bool QTimeZone::isValid() const
477{
478 return d && d->isValid();
479}
480
481/*!
482 Returns the IANA ID for the time zone.
483
484 IANA IDs are used on all platforms. On Windows these are translated
485 from the Windows ID into the closest IANA ID for the time zone and country.
486*/
487
488QByteArray QTimeZone::id() const
489{
490 return d ? d->id() : QByteArray();
491}
492
493/*!
494 Returns the country for the time zone.
495*/
496
497QLocale::Country QTimeZone::country() const
498{
499 return isValid() ? d->country() : QLocale::AnyCountry;
500}
501
502/*!
503 Returns any comment for the time zone.
504
505 A comment may be provided by the host platform to assist users in
506 choosing the correct time zone. Depending on the platform this may not
507 be localized.
508*/
509
510QString QTimeZone::comment() const
511{
512 return isValid() ? d->comment() : QString();
513}
514
515/*!
516 Returns the localized time zone display name at the given \a atDateTime
517 for the given \a nameType in the given \a locale. The \a nameType and
518 \a locale requested may not be supported on all platforms, in which case
519 the best available option will be returned.
520
521 If the \a locale is not provided then the application default locale will
522 be used.
523
524 The display name may change depending on DST or historical events.
525
526 \sa abbreviation()
527*/
528
529QString QTimeZone::displayName(const QDateTime &atDateTime, NameType nameType,
530 const QLocale &locale) const
531{
532 if (isValid())
533 return d->displayName(atDateTime.toMSecsSinceEpoch(), nameType, locale);
534
535 return QString();
536}
537
538/*!
539 Returns the localized time zone display name for the given \a timeType
540 and \a nameType in the given \a locale. The \a nameType and \a locale
541 requested may not be supported on all platforms, in which case the best
542 available option will be returned.
543
544 If the \a locale is not provided then the application default locale will
545 be used.
546
547 Where the time zone display names have changed over time then the most
548 recent names will be used.
549
550 \sa abbreviation()
551*/
552
553QString QTimeZone::displayName(TimeType timeType, NameType nameType,
554 const QLocale &locale) const
555{
556 if (isValid())
557 return d->displayName(timeType, nameType, locale);
558
559 return QString();
560}
561
562/*!
563 Returns the time zone abbreviation at the given \a atDateTime. The
564 abbreviation may change depending on DST or even historical events.
565
566 Note that the abbreviation is not guaranteed to be unique to this time zone
567 and should not be used in place of the ID or display name.
568
569 \sa displayName()
570*/
571
572QString QTimeZone::abbreviation(const QDateTime &atDateTime) const
573{
574 if (isValid())
575 return d->abbreviation(atDateTime.toMSecsSinceEpoch());
576
577 return QString();
578}
579
580/*!
581 Returns the total effective offset at the given \a atDateTime, i.e. the
582 number of seconds to add to UTC to obtain the local time. This includes
583 any DST offset that may be in effect, i.e. it is the sum of
584 standardTimeOffset() and daylightTimeOffset() for the given datetime.
585
586 For example, for the time zone "Europe/Berlin" the standard time offset is
587 +3600 seconds and the DST offset is +3600 seconds. During standard time
588 offsetFromUtc() will return +3600 (UTC+01:00), and during DST it will
589 return +7200 (UTC+02:00).
590
591 \sa standardTimeOffset(), daylightTimeOffset()
592*/
593
594int QTimeZone::offsetFromUtc(const QDateTime &atDateTime) const
595{
596 if (isValid())
597 return d->offsetFromUtc(atDateTime.toMSecsSinceEpoch());
598
599 return 0;
600}
601
602/*!
603 Returns the standard time offset at the given \a atDateTime, i.e. the
604 number of seconds to add to UTC to obtain the local Standard Time. This
605 excludes any DST offset that may be in effect.
606
607 For example, for the time zone "Europe/Berlin" the standard time offset is
608 +3600 seconds. During both standard and DST offsetFromUtc() will return
609 +3600 (UTC+01:00).
610
611 \sa offsetFromUtc(), daylightTimeOffset()
612*/
613
614int QTimeZone::standardTimeOffset(const QDateTime &atDateTime) const
615{
616 if (isValid())
617 return d->standardTimeOffset(atDateTime.toMSecsSinceEpoch());
618
619 return 0;
620}
621
622/*!
623 Returns the daylight-saving time offset at the given \a atDateTime,
624 i.e. the number of seconds to add to the standard time offset to obtain the
625 local daylight-saving time.
626
627 For example, for the time zone "Europe/Berlin" the DST offset is +3600
628 seconds. During standard time daylightTimeOffset() will return 0, and when
629 daylight-saving is in effect it will return +3600.
630
631 \sa offsetFromUtc(), standardTimeOffset()
632*/
633
634int QTimeZone::daylightTimeOffset(const QDateTime &atDateTime) const
635{
636 if (hasDaylightTime())
637 return d->daylightTimeOffset(atDateTime.toMSecsSinceEpoch());
638
639 return 0;
640}
641
642/*!
643 Returns \c true if the time zone has practiced daylight-saving at any time.
644
645 \sa isDaylightTime(), daylightTimeOffset()
646*/
647
648bool QTimeZone::hasDaylightTime() const
649{
650 return isValid() && d->hasDaylightTime();
651}
652
653/*!
654 Returns \c true if daylight-saving was in effect at the given \a atDateTime.
655
656 \sa hasDaylightTime(), daylightTimeOffset()
657*/
658
659bool QTimeZone::isDaylightTime(const QDateTime &atDateTime) const
660{
661 return hasDaylightTime() && d->isDaylightTime(atDateTime.toMSecsSinceEpoch());
662}
663
664/*!
665 Returns the effective offset details at the given \a forDateTime. This is
666 the equivalent of calling offsetFromUtc(), abbreviation(), etc individually but is
667 more efficient.
668
669 \sa offsetFromUtc(), standardTimeOffset(), daylightTimeOffset(), abbreviation()
670*/
671
672QTimeZone::OffsetData QTimeZone::offsetData(const QDateTime &forDateTime) const
673{
674 if (hasTransitions())
675 return QTimeZonePrivate::toOffsetData(d->data(forDateTime.toMSecsSinceEpoch()));
676
677 return QTimeZonePrivate::invalidOffsetData();
678}
679
680/*!
681 Returns \c true if the system backend supports obtaining transitions.
682
683 Transitions are changes in the time-zone: these happen when DST turns on or
684 off and when authorities alter the offsets for the time-zone.
685
686 \sa nextTransition(), previousTransition(), transitions()
687*/
688
689bool QTimeZone::hasTransitions() const
690{
691 return isValid() && d->hasTransitions();
692}
693
694/*!
695 Returns the first time zone Transition after the given \a afterDateTime.
696 This is most useful when you have a Transition time and wish to find the
697 Transition after it.
698
699 If there is no transition after the given \a afterDateTime then an invalid
700 OffsetData will be returned with an invalid QDateTime.
701
702 The given \a afterDateTime is exclusive.
703
704 \sa hasTransitions(), previousTransition(), transitions()
705*/
706
707QTimeZone::OffsetData QTimeZone::nextTransition(const QDateTime &afterDateTime) const
708{
709 if (hasTransitions())
710 return QTimeZonePrivate::toOffsetData(d->nextTransition(afterDateTime.toMSecsSinceEpoch()));
711
712 return QTimeZonePrivate::invalidOffsetData();
713}
714
715/*!
716 Returns the first time zone Transition before the given \a beforeDateTime.
717 This is most useful when you have a Transition time and wish to find the
718 Transition before it.
719
720 If there is no transition before the given \a beforeDateTime then an invalid
721 OffsetData will be returned with an invalid QDateTime.
722
723 The given \a beforeDateTime is exclusive.
724
725 \sa hasTransitions(), nextTransition(), transitions()
726*/
727
728QTimeZone::OffsetData QTimeZone::previousTransition(const QDateTime &beforeDateTime) const
729{
730 if (hasTransitions())
731 return QTimeZonePrivate::toOffsetData(d->previousTransition(beforeDateTime.toMSecsSinceEpoch()));
732
733 return QTimeZonePrivate::invalidOffsetData();
734}
735
736/*!
737 Returns a list of all time zone transitions between the given datetimes.
738
739 The given \a fromDateTime and \a toDateTime are inclusive.
740
741 \sa hasTransitions(), nextTransition(), previousTransition()
742*/
743
744QTimeZone::OffsetDataList QTimeZone::transitions(const QDateTime &fromDateTime,
745 const QDateTime &toDateTime) const
746{
747 OffsetDataList list;
748 if (hasTransitions()) {
749 const QTimeZonePrivate::DataList plist = d->transitions(fromDateTime.toMSecsSinceEpoch(),
750 toDateTime.toMSecsSinceEpoch());
751 list.reserve(plist.count());
752 for (const QTimeZonePrivate::Data &pdata : plist)
753 list.append(QTimeZonePrivate::toOffsetData(pdata));
754 }
755 return list;
756}
757
758// Static methods
759
760/*!
761 Returns the current system time zone IANA ID.
762
763 On Windows this ID is translated from the Windows ID using an internal
764 translation table and the user's selected country. As a consequence there
765 is a small chance any Windows install may have IDs not known by Qt, in
766 which case "UTC" will be returned.
767*/
768
769QByteArray QTimeZone::systemTimeZoneId()
770{
771 const QByteArray sys = global_tz->backend->systemTimeZoneId();
772 if (!sys.isEmpty())
773 return sys;
774 // The system zone, despite the empty ID, may know its real ID anyway:
775 auto zone = systemTimeZone();
776 if (zone.isValid() && !zone.id().isEmpty())
777 return zone.id();
778 // If all else fails, guess UTC.
779 return QTimeZonePrivate::utcQByteArray();
780}
781
782/*!
783 \since 5.5
784 Returns a QTimeZone object that refers to the local system time, as
785 specified by systemTimeZoneId().
786
787 \sa utc()
788*/
789QTimeZone QTimeZone::systemTimeZone()
790{
791 return QTimeZone(global_tz->backend->systemTimeZoneId());
792}
793
794/*!
795 \since 5.5
796 Returns a QTimeZone object that refers to UTC (Universal Time Coordinated).
797
798 \sa systemTimeZone()
799*/
800QTimeZone QTimeZone::utc()
801{
802 return QTimeZone(QTimeZonePrivate::utcQByteArray());
803}
804
805/*!
806 Returns \c true if a given time zone \a ianaId is available on this system.
807
808 \sa availableTimeZoneIds()
809*/
810
811bool QTimeZone::isTimeZoneIdAvailable(const QByteArray &ianaId)
812{
813 // isValidId is not strictly required, but faster to weed out invalid
814 // IDs as availableTimeZoneIds() may be slow
815 if (!QTimeZonePrivate::isValidId(ianaId))
816 return false;
817 return QUtcTimeZonePrivate().isTimeZoneIdAvailable(ianaId) ||
818 global_tz->backend->isTimeZoneIdAvailable(ianaId);
819}
820
821static QList<QByteArray> set_union(const QList<QByteArray> &l1, const QList<QByteArray> &l2)
822{
823 QList<QByteArray> result;
824 result.reserve(l1.size() + l2.size());
825 std::set_union(l1.begin(), l1.end(),
826 l2.begin(), l2.end(),
827 std::back_inserter(result));
828 return result;
829}
830
831/*!
832 Returns a list of all available IANA time zone IDs on this system.
833
834 \sa isTimeZoneIdAvailable()
835*/
836
837QList<QByteArray> QTimeZone::availableTimeZoneIds()
838{
839 return set_union(QUtcTimeZonePrivate().availableTimeZoneIds(),
840 global_tz->backend->availableTimeZoneIds());
841}
842
843/*!
844 Returns a list of all available IANA time zone IDs for a given \a country.
845
846 As a special case, a \a country of Qt::AnyCountry returns those time zones
847 that do not have any country related to them, such as UTC. If you require
848 a list of all time zone IDs for all countries then use the standard
849 availableTimeZoneIds() method.
850
851 \sa isTimeZoneIdAvailable()
852*/
853
854QList<QByteArray> QTimeZone::availableTimeZoneIds(QLocale::Country country)
855{
856 return set_union(QUtcTimeZonePrivate().availableTimeZoneIds(country),
857 global_tz->backend->availableTimeZoneIds(country));
858}
859
860/*!
861 Returns a list of all available IANA time zone IDs with a given standard
862 time offset of \a offsetSeconds.
863
864 \sa isTimeZoneIdAvailable()
865*/
866
867QList<QByteArray> QTimeZone::availableTimeZoneIds(int offsetSeconds)
868{
869 return set_union(QUtcTimeZonePrivate().availableTimeZoneIds(offsetSeconds),
870 global_tz->backend->availableTimeZoneIds(offsetSeconds));
871}
872
873/*!
874 Returns the Windows ID equivalent to the given \a ianaId.
875
876 \sa windowsIdToDefaultIanaId(), windowsIdToIanaIds()
877*/
878
879QByteArray QTimeZone::ianaIdToWindowsId(const QByteArray &ianaId)
880{
881 return QTimeZonePrivate::ianaIdToWindowsId(ianaId);
882}
883
884/*!
885 Returns the default IANA ID for a given \a windowsId.
886
887 Because a Windows ID can cover several IANA IDs in several different
888 countries, this function returns the most frequently used IANA ID with no
889 regard for the country and should thus be used with care. It is usually
890 best to request the default for a specific country.
891
892 \sa ianaIdToWindowsId(), windowsIdToIanaIds()
893*/
894
895QByteArray QTimeZone::windowsIdToDefaultIanaId(const QByteArray &windowsId)
896{
897 return QTimeZonePrivate::windowsIdToDefaultIanaId(windowsId);
898}
899
900/*!
901 Returns the default IANA ID for a given \a windowsId and \a country.
902
903 Because a Windows ID can cover several IANA IDs within a given country,
904 the most frequently used IANA ID in that country is returned.
905
906 As a special case, QLocale::AnyCountry returns the default of those IANA IDs
907 that do not have any specific country.
908
909 \sa ianaIdToWindowsId(), windowsIdToIanaIds()
910*/
911
912QByteArray QTimeZone::windowsIdToDefaultIanaId(const QByteArray &windowsId,
913 QLocale::Country country)
914{
915 return QTimeZonePrivate::windowsIdToDefaultIanaId(windowsId, country);
916}
917
918/*!
919 Returns all the IANA IDs for a given \a windowsId.
920
921 The returned list is sorted alphabetically.
922
923 \sa ianaIdToWindowsId(), windowsIdToDefaultIanaId()
924*/
925
926QList<QByteArray> QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId)
927{
928 return QTimeZonePrivate::windowsIdToIanaIds(windowsId);
929}
930
931/*!
932 Returns all the IANA IDs for a given \a windowsId and \a country.
933
934 As a special case QLocale::AnyCountry returns those IANA IDs that do
935 not have any specific country.
936
937 The returned list is in order of frequency of usage, i.e. larger zones
938 within a country are listed first.
939
940 \sa ianaIdToWindowsId(), windowsIdToDefaultIanaId()
941*/
942
943QList<QByteArray> QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId,
944 QLocale::Country country)
945{
946 return QTimeZonePrivate::windowsIdToIanaIds(windowsId, country);
947}
948
949#ifndef QT_NO_DATASTREAM
950// Invalid, as an IANA ID: too long, starts with - and has other invalid characters in it
951static inline QString invalidId() { return QStringLiteral("-No Time Zone Specified!"); }
952
953QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz)
954{
955 if (tz.isValid())
956 tz.d->serialize(ds);
957 else
958 ds << invalidId();
959 return ds;
960}
961
962QDataStream &operator>>(QDataStream &ds, QTimeZone &tz)
963{
964 QString ianaId;
965 ds >> ianaId;
966 if (ianaId == invalidId()) {
967 tz = QTimeZone();
968 } else if (ianaId == QLatin1String("OffsetFromUtc")) {
969 int utcOffset;
970 QString name;
971 QString abbreviation;
972 int country;
973 QString comment;
974 ds >> ianaId >> utcOffset >> name >> abbreviation >> country >> comment;
975 // Try creating as a system timezone, which succeeds (producing a valid
976 // zone) iff ianaId is valid; we can then ignore the other data.
977 tz = QTimeZone(ianaId.toUtf8());
978 // If not, then construct a custom timezone using all the saved values:
979 if (!tz.isValid())
980 tz = QTimeZone(ianaId.toUtf8(), utcOffset, name, abbreviation,
981 QLocale::Country(country), comment);
982 } else {
983 tz = QTimeZone(ianaId.toUtf8());
984 }
985 return ds;
986}
987#endif // QT_NO_DATASTREAM
988
989#ifndef QT_NO_DEBUG_STREAM
990QDebug operator<<(QDebug dbg, const QTimeZone &tz)
991{
992 QDebugStateSaver saver(dbg);
993 //TODO Include backend and data version details?
994 dbg.nospace() << "QTimeZone(" << QString::fromUtf8(tz.id()) << ')';
995 return dbg;
996}
997#endif
998
999QT_END_NAMESPACE
1000