summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qtimezoneprivate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools/qtimezoneprivate.cpp')
-rw-r--r--src/corelib/tools/qtimezoneprivate.cpp926
1 files changed, 0 insertions, 926 deletions
diff --git a/src/corelib/tools/qtimezoneprivate.cpp b/src/corelib/tools/qtimezoneprivate.cpp
deleted file mode 100644
index 569b343187..0000000000
--- a/src/corelib/tools/qtimezoneprivate.cpp
+++ /dev/null
@@ -1,926 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 John Layt <jlayt@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-#include "qtimezone.h"
-#include "qtimezoneprivate_p.h"
-#include "qtimezoneprivate_data_p.h"
-
-#include <qdatastream.h>
-#include <qdebug.h>
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-/*
- Static utilities for looking up Windows ID tables
-*/
-
-static const int windowsDataTableSize = sizeof(windowsDataTable) / sizeof(QWindowsData) - 1;
-static const int zoneDataTableSize = sizeof(zoneDataTable) / sizeof(QZoneData) - 1;
-static const int utcDataTableSize = sizeof(utcDataTable) / sizeof(QUtcData) - 1;
-
-
-static const QZoneData *zoneData(quint16 index)
-{
- Q_ASSERT(index < zoneDataTableSize);
- return &zoneDataTable[index];
-}
-
-static const QWindowsData *windowsData(quint16 index)
-{
- Q_ASSERT(index < windowsDataTableSize);
- return &windowsDataTable[index];
-}
-
-static const QUtcData *utcData(quint16 index)
-{
- Q_ASSERT(index < utcDataTableSize);
- return &utcDataTable[index];
-}
-
-// Return the Windows ID literal for a given QWindowsData
-static QByteArray windowsId(const QWindowsData *windowsData)
-{
- return (windowsIdData + windowsData->windowsIdIndex);
-}
-
-// Return the IANA ID literal for a given QWindowsData
-static QByteArray ianaId(const QWindowsData *windowsData)
-{
- return (ianaIdData + windowsData->ianaIdIndex);
-}
-
-// Return the IANA ID literal for a given QZoneData
-static QByteArray ianaId(const QZoneData *zoneData)
-{
- return (ianaIdData + zoneData->ianaIdIndex);
-}
-
-static QByteArray utcId(const QUtcData *utcData)
-{
- return (ianaIdData + utcData->ianaIdIndex);
-}
-
-static quint16 toWindowsIdKey(const QByteArray &winId)
-{
- for (quint16 i = 0; i < windowsDataTableSize; ++i) {
- const QWindowsData *data = windowsData(i);
- if (windowsId(data) == winId)
- return data->windowsIdKey;
- }
- return 0;
-}
-
-static QByteArray toWindowsIdLiteral(quint16 windowsIdKey)
-{
- for (quint16 i = 0; i < windowsDataTableSize; ++i) {
- const QWindowsData *data = windowsData(i);
- if (data->windowsIdKey == windowsIdKey)
- return windowsId(data);
- }
- return QByteArray();
-}
-
-/*
- Base class implementing common utility routines, only intantiate for a null tz.
-*/
-
-QTimeZonePrivate::QTimeZonePrivate()
-{
-}
-
-QTimeZonePrivate::QTimeZonePrivate(const QTimeZonePrivate &other)
- : QSharedData(other), m_id(other.m_id)
-{
-}
-
-QTimeZonePrivate::~QTimeZonePrivate()
-{
-}
-
-QTimeZonePrivate *QTimeZonePrivate::clone() const
-{
- return new QTimeZonePrivate(*this);
-}
-
-bool QTimeZonePrivate::operator==(const QTimeZonePrivate &other) const
-{
- // TODO Too simple, but need to solve problem of comparing different derived classes
- // Should work for all System and ICU classes as names guaranteed unique, but not for Simple.
- // Perhaps once all classes have working transitions can compare full list?
- return (m_id == other.m_id);
-}
-
-bool QTimeZonePrivate::operator!=(const QTimeZonePrivate &other) const
-{
- return !(*this == other);
-}
-
-bool QTimeZonePrivate::isValid() const
-{
- return !m_id.isEmpty();
-}
-
-QByteArray QTimeZonePrivate::id() const
-{
- return m_id;
-}
-
-QLocale::Country QTimeZonePrivate::country() const
-{
- // Default fall-back mode, use the zoneTable to find Region of known Zones
- for (int i = 0; i < zoneDataTableSize; ++i) {
- const QZoneData *data = zoneData(i);
- if (ianaId(data).split(' ').contains(m_id))
- return (QLocale::Country)data->country;
- }
- return QLocale::AnyCountry;
-}
-
-QString QTimeZonePrivate::comment() const
-{
- return QString();
-}
-
-QString QTimeZonePrivate::displayName(qint64 atMSecsSinceEpoch,
- QTimeZone::NameType nameType,
- const QLocale &locale) const
-{
- if (nameType == QTimeZone::OffsetName)
- return isoOffsetFormat(offsetFromUtc(atMSecsSinceEpoch));
-
- if (isDaylightTime(atMSecsSinceEpoch))
- return displayName(QTimeZone::DaylightTime, nameType, locale);
- else
- return displayName(QTimeZone::StandardTime, nameType, locale);
-}
-
-QString QTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
- QTimeZone::NameType nameType,
- const QLocale &locale) const
-{
- Q_UNUSED(timeType)
- Q_UNUSED(nameType)
- Q_UNUSED(locale)
- return QString();
-}
-
-QString QTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
-{
- Q_UNUSED(atMSecsSinceEpoch)
- return QString();
-}
-
-int QTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const
-{
- return standardTimeOffset(atMSecsSinceEpoch) + daylightTimeOffset(atMSecsSinceEpoch);
-}
-
-int QTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const
-{
- Q_UNUSED(atMSecsSinceEpoch)
- return invalidSeconds();
-}
-
-int QTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const
-{
- Q_UNUSED(atMSecsSinceEpoch)
- return invalidSeconds();
-}
-
-bool QTimeZonePrivate::hasDaylightTime() const
-{
- return false;
-}
-
-bool QTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
-{
- Q_UNUSED(atMSecsSinceEpoch)
- return false;
-}
-
-QTimeZonePrivate::Data QTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
-{
- Q_UNUSED(forMSecsSinceEpoch)
- return invalidData();
-}
-
-// Private only method for use by QDateTime to convert local msecs to epoch msecs
-QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs, int hint) const
-{
- if (!hasDaylightTime()) // No DST means same offset for all local msecs
- return data(forLocalMSecs - standardTimeOffset(forLocalMSecs) * 1000);
-
- /*
- We need a UTC time at which to ask for the offset, in order to be able to
- add that offset to forLocalMSecs, to get the UTC time we
- need. Fortunately, no time-zone offset is more than 14 hours; and DST
- transitions happen (much) more than thirty-two hours apart. So sampling
- offset sixteen hours each side gives us information we can be sure
- brackets the correct time and at most one DST transition.
- */
- const qint64 sixteenHoursInMSecs(16 * 3600 * 1000);
- Q_STATIC_ASSERT(-sixteenHoursInMSecs / 1000 < QTimeZone::MinUtcOffsetSecs
- && sixteenHoursInMSecs / 1000 > QTimeZone::MaxUtcOffsetSecs);
- const qint64 recent = forLocalMSecs - sixteenHoursInMSecs;
- const qint64 imminent = forLocalMSecs + sixteenHoursInMSecs;
- /*
- Offsets are Local - UTC, positive to the east of Greenwich, negative to
- the west; DST offset always exceeds standard offset, when DST applies.
- When we have offsets on either side of a transition, the lower one is
- standard, the higher is DST.
-
- Non-DST transitions (jurisdictions changing time-zone and time-zones
- changing their standard offset, typically) are described below as if they
- were DST transitions (since these are more usual and familiar); the code
- mostly concerns itself with offsets from UTC, described in terms of the
- common case for changes in that. If there is no actual change in offset
- (e.g. a DST transition cancelled by a standard offset change), this code
- should handle it gracefully; without transitions, it'll see early == late
- and take the easy path; with transitions, tran and nextTran get the
- correct UTC time as atMSecsSinceEpoch so comparing to nextStart selects
- the right one. In all other cases, the transition changes offset and the
- reasoning that applies to DST applies just the same. Aside from hinting,
- the only thing that looks at DST-ness at all, other than inferred from
- offset changes, is the case without transition data handling an invalid
- time in the gap that a transition passed over.
-
- The handling of hint (see below) is apt to go wrong in non-DST
- transitions. There isn't really a great deal we can hope to do about that
- without adding yet more unreliable complexity to the heuristics in use for
- already obscure corner-cases.
- */
-
- /*
- The hint (really a QDateTimePrivate::DaylightStatus) is > 0 if caller
- thinks we're in DST, 0 if in standard. A value of -2 means never-DST, so
- should have been handled above; if it slips through, it's wrong but we
- should probably treat it as standard anyway (never-DST means
- always-standard, after all). If the hint turns out to be wrong, fall back
- on trying the other possibility: which makes it harmless to treat -1
- (meaning unknown) as standard (i.e. try standard first, then try DST). In
- practice, away from a transition, the only difference hint makes is to
- which candidate we try first: if the hint is wrong (or unknown and
- standard fails), we'll try the other candidate and it'll work.
-
- For the obscure (and invalid) case where forLocalMSecs falls in a
- spring-forward's missing hour, a common case is that we started with a
- date/time for which the hint was valid and adjusted it naively; for that
- case, we should correct the adjustment by shunting across the transition
- into where hint is wrong. So half-way through the gap, arrived at from
- the DST side, should be read as an hour earlier, in standard time; but, if
- arrived at from the standard side, should be read as an hour later, in
- DST. (This shall be wrong in some cases; for example, when a country
- changes its transition dates and changing a date/time by more than six
- months lands it on a transition. However, these cases are even more
- obscure than those where the heuristic is good.)
- */
-
- if (hasTransitions()) {
- /*
- We have transitions.
-
- Each transition gives the offsets to use until the next; so we need the
- most recent transition before the time forLocalMSecs describes. If it
- describes a time *in* a transition, we'll need both that transition and
- the one before it. So find one transition that's probably after (and not
- much before, otherwise) and another that's definitely before, then work
- out which one to use. When both or neither work on forLocalMSecs, use
- hint to disambiguate.
- */
-
- // Get a transition definitely before the local MSecs; usually all we need.
- // Only around the transition times might we need another.
- Data tran = previousTransition(recent);
- Q_ASSERT(forLocalMSecs < 0 || // Pre-epoch TZ info may be unavailable
- forLocalMSecs - tran.offsetFromUtc * 1000 >= tran.atMSecsSinceEpoch);
- Data nextTran = nextTransition(tran.atMSecsSinceEpoch);
- /*
- Now walk those forward until they bracket forLocalMSecs with transitions.
-
- One of the transitions should then be telling us the right offset to use.
- In a transition, we need the transition before it (to describe the run-up
- to the transition) and the transition itself; so we need to stop when
- nextTran is that transition.
- */
- while (nextTran.atMSecsSinceEpoch != invalidMSecs()
- && forLocalMSecs > nextTran.atMSecsSinceEpoch + nextTran.offsetFromUtc * 1000) {
- Data newTran = nextTransition(nextTran.atMSecsSinceEpoch);
- if (newTran.atMSecsSinceEpoch == invalidMSecs()
- || newTran.atMSecsSinceEpoch + newTran.offsetFromUtc * 1000 > imminent) {
- // Definitely not a relevant tansition: too far in the future.
- break;
- }
- tran = nextTran;
- nextTran = newTran;
- }
-
- // Check we do *really* have transitions for this zone:
- if (tran.atMSecsSinceEpoch != invalidMSecs()) {
-
- /*
- So now tran is definitely before and nextTran is either after or only
- slightly before. One is standard time; we interpret the other as DST
- (although the transition might in fact by a change in standard offset). Our
- hint tells us which of those to use (defaulting to standard if no hint): try
- it first; if that fails, try the other; if both fail, life's tricky.
- */
- Q_ASSERT(forLocalMSecs < 0
- || forLocalMSecs - tran.offsetFromUtc * 1000 > tran.atMSecsSinceEpoch);
- const qint64 nextStart = nextTran.atMSecsSinceEpoch;
- // Work out the UTC values it might make sense to return:
- nextTran.atMSecsSinceEpoch = forLocalMSecs - nextTran.offsetFromUtc * 1000;
- tran.atMSecsSinceEpoch = forLocalMSecs - tran.offsetFromUtc * 1000;
-
- // If both or neither have zero DST, treat the one with lower offset as standard:
- const bool nextIsDst = !nextTran.daylightTimeOffset == !tran.daylightTimeOffset
- ? tran.offsetFromUtc < nextTran.offsetFromUtc : nextTran.daylightTimeOffset;
- // If that agrees with hint > 0, our first guess is to use nextTran; else tran.
- const bool nextFirst = nextIsDst == (hint > 0) && nextStart != invalidMSecs();
- for (int i = 0; i < 2; i++) {
- /*
- On the first pass, the case we consider is what hint told us to expect
- (except when hint was -1 and didn't actually tell us what to expect),
- so it's likely right. We only get a second pass if the first failed,
- by which time the second case, that we're trying, is likely right. If
- an overwhelming majority of calls have hint == -1, the Q_LIKELY here
- shall be wrong half the time; otherwise, its errors shall be rarer
- than that.
- */
- if (nextFirst ? i == 0 : i) {
- Q_ASSERT(nextStart != invalidMSecs());
- if (Q_LIKELY(nextStart <= nextTran.atMSecsSinceEpoch))
- return nextTran;
- } else {
- // If next is invalid, nextFirst is false, to route us here first:
- if (nextStart == invalidMSecs() || Q_LIKELY(nextStart > tran.atMSecsSinceEpoch))
- return tran;
- }
- }
-
- /*
- Neither is valid (e.g. in a spring-forward's gap) and
- nextTran.atMSecsSinceEpoch < nextStart <= tran.atMSecsSinceEpoch, so
- 0 < tran.atMSecsSinceEpoch - nextTran.atMSecsSinceEpoch
- = (nextTran.offsetFromUtc - tran.offsetFromUtc) * 1000
- */
- int dstStep = (nextTran.offsetFromUtc - tran.offsetFromUtc) * 1000;
- Q_ASSERT(dstStep > 0); // How else could we get here ?
- if (nextFirst) { // hint thought we needed nextTran, so use tran
- tran.atMSecsSinceEpoch -= dstStep;
- return tran;
- }
- nextTran.atMSecsSinceEpoch += dstStep;
- return nextTran;
- }
- // System has transitions but not for this zone.
- // Try falling back to offsetFromUtc
- }
-
- /* Bracket and refine to discover offset. */
- qint64 utcEpochMSecs;
-
- int early = offsetFromUtc(recent);
- int late = offsetFromUtc(imminent);
- if (Q_LIKELY(early == late)) { // > 99% of the time
- utcEpochMSecs = forLocalMSecs - early * 1000;
- } else {
- // Close to a DST transition: early > late is near a fall-back,
- // early < late is near a spring-forward.
- const int offsetInDst = qMax(early, late);
- const int offsetInStd = qMin(early, late);
- // Candidate values for utcEpochMSecs (if forLocalMSecs is valid):
- const qint64 forDst = forLocalMSecs - offsetInDst * 1000;
- const qint64 forStd = forLocalMSecs - offsetInStd * 1000;
- // Best guess at the answer:
- const qint64 hinted = hint > 0 ? forDst : forStd;
- if (Q_LIKELY(offsetFromUtc(hinted) == (hint > 0 ? offsetInDst : offsetInStd))) {
- utcEpochMSecs = hinted;
- } else if (hint <= 0 && offsetFromUtc(forDst) == offsetInDst) {
- utcEpochMSecs = forDst;
- } else if (hint > 0 && offsetFromUtc(forStd) == offsetInStd) {
- utcEpochMSecs = forStd;
- } else {
- // Invalid forLocalMSecs: in spring-forward gap.
- const int dstStep = daylightTimeOffset(early < late ? imminent : recent) * 1000;
- Q_ASSERT(dstStep); // There can't be a transition without it !
- utcEpochMSecs = (hint > 0) ? forStd - dstStep : forDst + dstStep;
- }
- }
-
- return data(utcEpochMSecs);
-}
-
-bool QTimeZonePrivate::hasTransitions() const
-{
- return false;
-}
-
-QTimeZonePrivate::Data QTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const
-{
- Q_UNUSED(afterMSecsSinceEpoch)
- return invalidData();
-}
-
-QTimeZonePrivate::Data QTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
-{
- Q_UNUSED(beforeMSecsSinceEpoch)
- return invalidData();
-}
-
-QTimeZonePrivate::DataList QTimeZonePrivate::transitions(qint64 fromMSecsSinceEpoch,
- qint64 toMSecsSinceEpoch) const
-{
- DataList list;
- if (toMSecsSinceEpoch >= fromMSecsSinceEpoch) {
- // fromMSecsSinceEpoch is inclusive but nextTransitionTime() is exclusive so go back 1 msec
- Data next = nextTransition(fromMSecsSinceEpoch - 1);
- while (next.atMSecsSinceEpoch != invalidMSecs()
- && next.atMSecsSinceEpoch <= toMSecsSinceEpoch) {
- list.append(next);
- next = nextTransition(next.atMSecsSinceEpoch);
- }
- }
- return list;
-}
-
-QByteArray QTimeZonePrivate::systemTimeZoneId() const
-{
- return QByteArray();
-}
-
-bool QTimeZonePrivate::isTimeZoneIdAvailable(const QByteArray& ianaId) const
-{
- // Fall-back implementation, can be made faster in subclasses
- const QList<QByteArray> tzIds = availableTimeZoneIds();
- return std::binary_search(tzIds.begin(), tzIds.end(), ianaId);
-}
-
-QList<QByteArray> QTimeZonePrivate::availableTimeZoneIds() const
-{
- return QList<QByteArray>();
-}
-
-QList<QByteArray> QTimeZonePrivate::availableTimeZoneIds(QLocale::Country country) const
-{
- // Default fall-back mode, use the zoneTable to find Region of know Zones
- QList<QByteArray> regions;
-
- // First get all Zones in the Zones table belonging to the Region
- for (int i = 0; i < zoneDataTableSize; ++i) {
- if (zoneData(i)->country == country)
- regions += ianaId(zoneData(i)).split(' ');
- }
-
- std::sort(regions.begin(), regions.end());
- regions.erase(std::unique(regions.begin(), regions.end()), regions.end());
-
- // Then select just those that are available
- const QList<QByteArray> all = availableTimeZoneIds();
- QList<QByteArray> result;
- result.reserve(qMin(all.size(), regions.size()));
- std::set_intersection(all.begin(), all.end(), regions.cbegin(), regions.cend(),
- std::back_inserter(result));
- return result;
-}
-
-QList<QByteArray> QTimeZonePrivate::availableTimeZoneIds(int offsetFromUtc) const
-{
- // Default fall-back mode, use the zoneTable to find Offset of know Zones
- QList<QByteArray> offsets;
- // First get all Zones in the table using the Offset
- for (int i = 0; i < windowsDataTableSize; ++i) {
- const QWindowsData *winData = windowsData(i);
- if (winData->offsetFromUtc == offsetFromUtc) {
- for (int j = 0; j < zoneDataTableSize; ++j) {
- const QZoneData *data = zoneData(j);
- if (data->windowsIdKey == winData->windowsIdKey)
- offsets += ianaId(data).split(' ');
- }
- }
- }
-
- std::sort(offsets.begin(), offsets.end());
- offsets.erase(std::unique(offsets.begin(), offsets.end()), offsets.end());
-
- // Then select just those that are available
- const QList<QByteArray> all = availableTimeZoneIds();
- QList<QByteArray> result;
- result.reserve(qMin(all.size(), offsets.size()));
- std::set_intersection(all.begin(), all.end(), offsets.cbegin(), offsets.cend(),
- std::back_inserter(result));
- return result;
-}
-
-#ifndef QT_NO_DATASTREAM
-void QTimeZonePrivate::serialize(QDataStream &ds) const
-{
- ds << QString::fromUtf8(m_id);
-}
-#endif // QT_NO_DATASTREAM
-
-// Static Utility Methods
-
-QTimeZonePrivate::Data QTimeZonePrivate::invalidData()
-{
- Data data;
- data.atMSecsSinceEpoch = invalidMSecs();
- data.offsetFromUtc = invalidSeconds();
- data.standardTimeOffset = invalidSeconds();
- data.daylightTimeOffset = invalidSeconds();
- return data;
-}
-
-QTimeZone::OffsetData QTimeZonePrivate::invalidOffsetData()
-{
- QTimeZone::OffsetData offsetData;
- offsetData.atUtc = QDateTime();
- offsetData.offsetFromUtc = invalidSeconds();
- offsetData.standardTimeOffset = invalidSeconds();
- offsetData.daylightTimeOffset = invalidSeconds();
- return offsetData;
-}
-
-QTimeZone::OffsetData QTimeZonePrivate::toOffsetData(const QTimeZonePrivate::Data &data)
-{
- QTimeZone::OffsetData offsetData = invalidOffsetData();
- if (data.atMSecsSinceEpoch != invalidMSecs()) {
- offsetData.atUtc = QDateTime::fromMSecsSinceEpoch(data.atMSecsSinceEpoch, Qt::UTC);
- offsetData.offsetFromUtc = data.offsetFromUtc;
- offsetData.standardTimeOffset = data.standardTimeOffset;
- offsetData.daylightTimeOffset = data.daylightTimeOffset;
- offsetData.abbreviation = data.abbreviation;
- }
- return offsetData;
-}
-
-// Is the format of the ID valid ?
-bool QTimeZonePrivate::isValidId(const QByteArray &ianaId)
-{
- /*
- Main rules for defining TZ/IANA names as per ftp://ftp.iana.org/tz/code/Theory
- 1. Use only valid POSIX file name components
- 2. Within a file name component, use only ASCII letters, `.', `-' and `_'.
- 3. Do not use digits (except in a [+-]\d+ suffix, when used).
- 4. A file name component must not exceed 14 characters or start with `-'
- However, the rules are really guidelines - a later one says
- - Do not change established names if they only marginally violate the
- above rules.
- We may, therefore, need to be a bit slack in our check here, if we hit
- legitimate exceptions in real time-zone databases.
-
- In particular, aliases such as "Etc/GMT+7" and "SystemV/EST5EDT" are valid
- so we need to accept digits, ':', and '+'; aliases typically have the form
- of POSIX TZ strings, which allow a suffix to a proper IANA name. A POSIX
- suffix starts with an offset (as in GMT+7) and may continue with another
- name (as in EST5EDT, giving the DST name of the zone); a further offset is
- allowed (for DST). The ("hard to describe and [...] error-prone in
- practice") POSIX form even allows a suffix giving the dates (and
- optionally times) of the annual DST transitions. Hopefully, no TZ aliases
- go that far, but we at least need to accept an offset and (single
- fragment) DST-name.
-
- But for the legacy complications, the following would be preferable if
- QRegExp would work on QByteArrays directly:
- const QRegExp rx(QStringLiteral("[a-z+._][a-z+._-]{,13}"
- "(?:/[a-z+._][a-z+._-]{,13})*"
- // Optional suffix:
- "(?:[+-]?\d{1,2}(?::\d{1,2}){,2}" // offset
- // one name fragment (DST):
- "(?:[a-z+._][a-z+._-]{,13})?)"),
- Qt::CaseInsensitive);
- return rx.exactMatch(ianaId);
- */
-
- // Somewhat slack hand-rolled version:
- const int MinSectionLength = 1;
- const int MaxSectionLength = 14;
- int sectionLength = 0;
- for (const char *it = ianaId.begin(), * const end = ianaId.end(); it != end; ++it, ++sectionLength) {
- const char ch = *it;
- if (ch == '/') {
- if (sectionLength < MinSectionLength || sectionLength > MaxSectionLength)
- return false; // violates (4)
- sectionLength = -1;
- } else if (ch == '-') {
- if (sectionLength == 0)
- return false; // violates (4)
- } else if (!(ch >= 'a' && ch <= 'z')
- && !(ch >= 'A' && ch <= 'Z')
- && !(ch == '_')
- && !(ch == '.')
- // Should ideally check these only happen as an offset:
- && !(ch >= '0' && ch <= '9')
- && !(ch == '+')
- && !(ch == ':')) {
- return false; // violates (2)
- }
- }
- if (sectionLength < MinSectionLength || sectionLength > MaxSectionLength)
- return false; // violates (4)
- return true;
-}
-
-QString QTimeZonePrivate::isoOffsetFormat(int offsetFromUtc)
-{
- const int mins = offsetFromUtc / 60;
- return QString::fromUtf8("UTC%1%2:%3").arg(mins >= 0 ? QLatin1Char('+') : QLatin1Char('-'))
- .arg(qAbs(mins) / 60, 2, 10, QLatin1Char('0'))
- .arg(qAbs(mins) % 60, 2, 10, QLatin1Char('0'));
-}
-
-QByteArray QTimeZonePrivate::ianaIdToWindowsId(const QByteArray &id)
-{
- for (int i = 0; i < zoneDataTableSize; ++i) {
- const QZoneData *data = zoneData(i);
- if (ianaId(data).split(' ').contains(id))
- return toWindowsIdLiteral(data->windowsIdKey);
- }
- return QByteArray();
-}
-
-QByteArray QTimeZonePrivate::windowsIdToDefaultIanaId(const QByteArray &windowsId)
-{
- const quint16 windowsIdKey = toWindowsIdKey(windowsId);
- for (int i = 0; i < windowsDataTableSize; ++i) {
- const QWindowsData *data = windowsData(i);
- if (data->windowsIdKey == windowsIdKey)
- return ianaId(data);
- }
- return QByteArray();
-}
-
-QByteArray QTimeZonePrivate::windowsIdToDefaultIanaId(const QByteArray &windowsId,
- QLocale::Country country)
-{
- const QList<QByteArray> list = windowsIdToIanaIds(windowsId, country);
- if (list.count() > 0)
- return list.first();
- else
- return QByteArray();
-}
-
-QList<QByteArray> QTimeZonePrivate::windowsIdToIanaIds(const QByteArray &windowsId)
-{
- const quint16 windowsIdKey = toWindowsIdKey(windowsId);
- QList<QByteArray> list;
-
- for (int i = 0; i < zoneDataTableSize; ++i) {
- const QZoneData *data = zoneData(i);
- if (data->windowsIdKey == windowsIdKey)
- list << ianaId(data).split(' ');
- }
-
- // Return the full list in alpha order
- std::sort(list.begin(), list.end());
- return list;
-}
-
-QList<QByteArray> QTimeZonePrivate::windowsIdToIanaIds(const QByteArray &windowsId,
- QLocale::Country country)
-{
- const quint16 windowsIdKey = toWindowsIdKey(windowsId);
- for (int i = 0; i < zoneDataTableSize; ++i) {
- const QZoneData *data = zoneData(i);
- // Return the region matches in preference order
- if (data->windowsIdKey == windowsIdKey && data->country == (quint16) country)
- return ianaId(data).split(' ');
- }
-
- return QList<QByteArray>();
-}
-
-// Define template for derived classes to reimplement so QSharedDataPointer clone() works correctly
-template<> QTimeZonePrivate *QSharedDataPointer<QTimeZonePrivate>::clone()
-{
- return d->clone();
-}
-
-/*
- UTC Offset implementation, used when QT_NO_SYSTEMLOCALE set and ICU is not being used,
- or for QDateTimes with a Qt:Spec of Qt::OffsetFromUtc.
-*/
-
-// Create default UTC time zone
-QUtcTimeZonePrivate::QUtcTimeZonePrivate()
-{
- const QString name = utcQString();
- init(utcQByteArray(), 0, name, name, QLocale::AnyCountry, name);
-}
-
-// Create a named UTC time zone
-QUtcTimeZonePrivate::QUtcTimeZonePrivate(const QByteArray &id)
-{
- // Look for the name in the UTC list, if found set the values
- for (int i = 0; i < utcDataTableSize; ++i) {
- const QUtcData *data = utcData(i);
- const QByteArray uid = utcId(data);
- if (uid == id) {
- QString name = QString::fromUtf8(id);
- init(id, data->offsetFromUtc, name, name, QLocale::AnyCountry, name);
- break;
- }
- }
-}
-
-// Create offset from UTC
-QUtcTimeZonePrivate::QUtcTimeZonePrivate(qint32 offsetSeconds)
-{
- QString utcId;
-
- if (offsetSeconds == 0)
- utcId = utcQString();
- else
- utcId = isoOffsetFormat(offsetSeconds);
-
- init(utcId.toUtf8(), offsetSeconds, utcId, utcId, QLocale::AnyCountry, utcId);
-}
-
-QUtcTimeZonePrivate::QUtcTimeZonePrivate(const QByteArray &zoneId, int offsetSeconds,
- const QString &name, const QString &abbreviation,
- QLocale::Country country, const QString &comment)
-{
- init(zoneId, offsetSeconds, name, abbreviation, country, comment);
-}
-
-QUtcTimeZonePrivate::QUtcTimeZonePrivate(const QUtcTimeZonePrivate &other)
- : QTimeZonePrivate(other), m_name(other.m_name),
- m_abbreviation(other.m_abbreviation),
- m_comment(other.m_comment),
- m_country(other.m_country),
- m_offsetFromUtc(other.m_offsetFromUtc)
-{
-}
-
-QUtcTimeZonePrivate::~QUtcTimeZonePrivate()
-{
-}
-
-QUtcTimeZonePrivate *QUtcTimeZonePrivate::clone() const
-{
- return new QUtcTimeZonePrivate(*this);
-}
-
-QTimeZonePrivate::Data QUtcTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
-{
- Data d;
- d.abbreviation = m_abbreviation;
- d.atMSecsSinceEpoch = forMSecsSinceEpoch;
- d.standardTimeOffset = d.offsetFromUtc = m_offsetFromUtc;
- d.daylightTimeOffset = 0;
- return d;
-}
-
-void QUtcTimeZonePrivate::init(const QByteArray &zoneId)
-{
- m_id = zoneId;
-}
-
-void QUtcTimeZonePrivate::init(const QByteArray &zoneId, int offsetSeconds, const QString &name,
- const QString &abbreviation, QLocale::Country country,
- const QString &comment)
-{
- m_id = zoneId;
- m_offsetFromUtc = offsetSeconds;
- m_name = name;
- m_abbreviation = abbreviation;
- m_country = country;
- m_comment = comment;
-}
-
-QLocale::Country QUtcTimeZonePrivate::country() const
-{
- return m_country;
-}
-
-QString QUtcTimeZonePrivate::comment() const
-{
- return m_comment;
-}
-
-QString QUtcTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
- QTimeZone::NameType nameType,
- const QLocale &locale) const
-{
- Q_UNUSED(timeType)
- Q_UNUSED(locale)
- if (nameType == QTimeZone::ShortName)
- return m_abbreviation;
- else if (nameType == QTimeZone::OffsetName)
- return isoOffsetFormat(m_offsetFromUtc);
- return m_name;
-}
-
-QString QUtcTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
-{
- Q_UNUSED(atMSecsSinceEpoch)
- return m_abbreviation;
-}
-
-qint32 QUtcTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const
-{
- Q_UNUSED(atMSecsSinceEpoch)
- return m_offsetFromUtc;
-}
-
-qint32 QUtcTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const
-{
- Q_UNUSED(atMSecsSinceEpoch)
- return 0;
-}
-
-QByteArray QUtcTimeZonePrivate::systemTimeZoneId() const
-{
- return utcQByteArray();
-}
-
-bool QUtcTimeZonePrivate::isTimeZoneIdAvailable(const QByteArray &ianaId) const
-{
- for (int i = 0; i < utcDataTableSize; ++i) {
- const QUtcData *data = utcData(i);
- if (utcId(data) == ianaId) {
- return true;
- }
- }
- return false;
-}
-
-QList<QByteArray> QUtcTimeZonePrivate::availableTimeZoneIds() const
-{
- QList<QByteArray> result;
- result.reserve(utcDataTableSize);
- for (int i = 0; i < utcDataTableSize; ++i)
- result << utcId(utcData(i));
- std::sort(result.begin(), result.end()); // ### or already sorted??
- // ### assuming no duplicates
- return result;
-}
-
-QList<QByteArray> QUtcTimeZonePrivate::availableTimeZoneIds(QLocale::Country country) const
-{
- // If AnyCountry then is request for all non-region offset codes
- if (country == QLocale::AnyCountry)
- return availableTimeZoneIds();
- return QList<QByteArray>();
-}
-
-QList<QByteArray> QUtcTimeZonePrivate::availableTimeZoneIds(qint32 offsetSeconds) const
-{
- QList<QByteArray> result;
- for (int i = 0; i < utcDataTableSize; ++i) {
- const QUtcData *data = utcData(i);
- if (data->offsetFromUtc == offsetSeconds)
- result << utcId(data);
- }
- std::sort(result.begin(), result.end()); // ### or already sorted??
- // ### assuming no duplicates
- return result;
-}
-
-#ifndef QT_NO_DATASTREAM
-void QUtcTimeZonePrivate::serialize(QDataStream &ds) const
-{
- ds << QStringLiteral("OffsetFromUtc") << QString::fromUtf8(m_id) << m_offsetFromUtc << m_name
- << m_abbreviation << (qint32) m_country << m_comment;
-}
-#endif // QT_NO_DATASTREAM
-
-QT_END_NAMESPACE