/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2019 Intel Corporation.
** 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 "qglobal.h"

#if !defined(QWS) && defined(Q_OS_MAC)
#   include "private/qcore_mac_p.h"
#   include <CoreFoundation/CoreFoundation.h>
#endif

#include "qplatformdefs.h"

#include "qdatastream.h"
#include "qdebug.h"
#include "qhashfunctions.h"
#include "qstring.h"
#include "qlocale.h"
#include "qlocale_p.h"
#include "qlocale_tools_p.h"
#if QT_CONFIG(datetimeparser)
#include "private/qdatetimeparser_p.h"
#endif
#include "qnamespace.h"
#include "qdatetime.h"
#include "qstringlist.h"
#include "qvariant.h"
#include "qstringbuilder.h"
#include "private/qnumeric_p.h"
#include <cmath>
#ifndef QT_NO_SYSTEMLOCALE
#   include "qmutex.h"
#endif
#ifdef Q_OS_WIN
#   include <qt_windows.h>
#   include <time.h>
#endif

#include "private/qcalendarbackend_p.h"
#include "private/qgregoriancalendar_p.h"
#include "qcalendar.h"

QT_BEGIN_NAMESPACE

#ifndef QT_NO_SYSTEMLOCALE
static QSystemLocale *_systemLocale = nullptr;
class QSystemLocaleSingleton: public QSystemLocale
{
public:
    QSystemLocaleSingleton() : QSystemLocale(true) {}
};

Q_GLOBAL_STATIC(QSystemLocaleSingleton, QSystemLocale_globalSystemLocale)
static QLocaleData globalLocaleData;
#endif

/******************************************************************************
** Helpers for accessing Qt locale database
*/

QT_BEGIN_INCLUDE_NAMESPACE
#include "qlocale_data_p.h"
QT_END_INCLUDE_NAMESPACE

QLocale::Language QLocalePrivate::codeToLanguage(QStringView code) noexcept
{
    const auto len = code.size();
    if (len != 2 && len != 3)
        return QLocale::C;
    ushort uc1 = code[0].toLower().unicode();
    ushort uc2 = code[1].toLower().unicode();
    ushort uc3 = len > 2 ? code[2].toLower().unicode() : 0;

    const unsigned char *c = language_code_list;
    for (; *c != 0; c += 3) {
        if (uc1 == c[0] && uc2 == c[1] && uc3 == c[2])
            return QLocale::Language((c - language_code_list)/3);
    }

    if (uc3 == 0) {
        // legacy codes
        if (uc1 == 'n' && uc2 == 'o') // no -> nb
            return QLocale::NorwegianBokmal;
        if (uc1 == 't' && uc2 == 'l') // tl -> fil
            return QLocale::Filipino;
        if (uc1 == 's' && uc2 == 'h') // sh -> sr[_Latn]
            return QLocale::Serbian;
        if (uc1 == 'm' && uc2 == 'o') // mo -> ro
            return QLocale::Romanian;
        // Android uses the following deprecated codes
        if (uc1 == 'i' && uc2 == 'w') // iw -> he
            return QLocale::Hebrew;
        if (uc1 == 'i' && uc2 == 'n') // in -> id
            return QLocale::Indonesian;
        if (uc1 == 'j' && uc2 == 'i') // ji -> yi
            return QLocale::Yiddish;
    }
    return QLocale::C;
}

QLocale::Script QLocalePrivate::codeToScript(QStringView code) noexcept
{
    const auto len = code.size();
    if (len != 4)
        return QLocale::AnyScript;

    // script is titlecased in our data
    unsigned char c0 = code[0].toUpper().toLatin1();
    unsigned char c1 = code[1].toLower().toLatin1();
    unsigned char c2 = code[2].toLower().toLatin1();
    unsigned char c3 = code[3].toLower().toLatin1();

    const unsigned char *c = script_code_list;
    for (int i = 0; i < QLocale::LastScript; ++i, c += 4) {
        if (c0 == c[0] && c1 == c[1] && c2 == c[2] && c3 == c[3])
            return QLocale::Script(i);
    }
    return QLocale::AnyScript;
}

QLocale::Country QLocalePrivate::codeToCountry(QStringView code) noexcept
{
    const auto len = code.size();
    if (len != 2 && len != 3)
        return QLocale::AnyCountry;

    ushort uc1 = code[0].toUpper().unicode();
    ushort uc2 = code[1].toUpper().unicode();
    ushort uc3 = len > 2 ? code[2].toUpper().unicode() : 0;

    const unsigned char *c = country_code_list;
    for (; *c != 0; c += 3) {
        if (uc1 == c[0] && uc2 == c[1] && uc3 == c[2])
            return QLocale::Country((c - country_code_list)/3);
    }

    return QLocale::AnyCountry;
}

QLatin1String QLocalePrivate::languageToCode(QLocale::Language language)
{
    if (language == QLocale::AnyLanguage)
        return QLatin1String();
    if (language == QLocale::C)
        return QLatin1String("C");

    const unsigned char *c = language_code_list + 3*(uint(language));

    return QLatin1String(reinterpret_cast<const char*>(c), c[2] == 0 ? 2 : 3);

}

QLatin1String QLocalePrivate::scriptToCode(QLocale::Script script)
{
    if (script == QLocale::AnyScript || script > QLocale::LastScript)
        return QLatin1String();
    const unsigned char *c = script_code_list + 4*(uint(script));
    return QLatin1String(reinterpret_cast<const char *>(c), 4);
}

QLatin1String QLocalePrivate::countryToCode(QLocale::Country country)
{
    if (country == QLocale::AnyCountry)
        return QLatin1String();

    const unsigned char *c = country_code_list + 3*(uint(country));

    return QLatin1String(reinterpret_cast<const char*>(c), c[2] == 0 ? 2 : 3);
}

// http://www.unicode.org/reports/tr35/#Likely_Subtags
static bool addLikelySubtags(QLocaleId &localeId)
{
    // ### optimize with bsearch
    const int likely_subtags_count = sizeof(likely_subtags) / sizeof(likely_subtags[0]);
    const QLocaleId *p = likely_subtags;
    const QLocaleId *const e = p + likely_subtags_count;
    for ( ; p < e; p += 2) {
        if (localeId == p[0]) {
            localeId = p[1];
            return true;
        }
    }
    return false;
}

QLocaleId QLocaleId::withLikelySubtagsAdded() const
{
    // language_script_region
    if (language_id || script_id || country_id) {
        QLocaleId id = QLocaleId::fromIds(language_id, script_id, country_id);
        if (addLikelySubtags(id))
            return id;
    }
    // language_region
    if (script_id) {
        QLocaleId id = QLocaleId::fromIds(language_id, 0, country_id);
        if (addLikelySubtags(id)) {
            id.script_id = script_id;
            return id;
        }
    }
    // language_script
    if (country_id) {
        QLocaleId id = QLocaleId::fromIds(language_id, script_id, 0);
        if (addLikelySubtags(id)) {
            id.country_id = country_id;
            return id;
        }
    }
    // language
    if (script_id && country_id) {
        QLocaleId id = QLocaleId::fromIds(language_id, 0, 0);
        if (addLikelySubtags(id)) {
            id.script_id = script_id;
            id.country_id = country_id;
            return id;
        }
    }
    // und_script
    if (language_id) {
        QLocaleId id = QLocaleId::fromIds(0, script_id, 0);
        if (addLikelySubtags(id)) {
            id.language_id = language_id;
            return id;
        }
    }
    return *this;
}

QLocaleId QLocaleId::withLikelySubtagsRemoved() const
{
    QLocaleId max = withLikelySubtagsAdded();
    // language
    {
        QLocaleId id = QLocaleId::fromIds(language_id, 0, 0);
        if (id.withLikelySubtagsAdded() == max)
            return id;
    }
    // language_region
    if (country_id) {
        QLocaleId id = QLocaleId::fromIds(language_id, 0, country_id);
        if (id.withLikelySubtagsAdded() == max)
            return id;
    }
    // language_script
    if (script_id) {
        QLocaleId id = QLocaleId::fromIds(language_id, script_id, 0);
        if (id.withLikelySubtagsAdded() == max)
            return id;
    }
    return max;
}

QByteArray QLocaleId::name(char separator) const
{
    if (language_id == QLocale::AnyLanguage)
        return QByteArray();
    if (language_id == QLocale::C)
        return QByteArrayLiteral("C");

    const unsigned char *lang = language_code_list + 3 * language_id;
    const unsigned char *script =
            (script_id != QLocale::AnyScript ? script_code_list + 4 * script_id : nullptr);
    const unsigned char *country =
            (country_id != QLocale::AnyCountry ? country_code_list + 3 * country_id : nullptr);
    char len = (lang[2] != 0 ? 3 : 2) + (script ? 4 + 1 : 0)
        + (country ? (country[2] != 0 ? 3 : 2) + 1 : 0);
    QByteArray name(len, Qt::Uninitialized);
    char *uc = name.data();
    *uc++ = lang[0];
    *uc++ = lang[1];
    if (lang[2] != 0)
        *uc++ = lang[2];
    if (script) {
        *uc++ = separator;
        *uc++ = script[0];
        *uc++ = script[1];
        *uc++ = script[2];
        *uc++ = script[3];
    }
    if (country) {
        *uc++ = separator;
        *uc++ = country[0];
        *uc++ = country[1];
        if (country[2] != 0)
            *uc++ = country[2];
    }
    return name;
}

QByteArray QLocalePrivate::bcp47Name(char separator) const
{
    if (m_data->m_language_id == QLocale::AnyLanguage)
        return QByteArray();
    if (m_data->m_language_id == QLocale::C)
        return QByteArrayLiteral("en");

    QLocaleId localeId = QLocaleId::fromIds(m_data->m_language_id, m_data->m_script_id,
                                            m_data->m_country_id);
    return localeId.withLikelySubtagsRemoved().name(separator);
}

static const QLocaleData *findLocaleDataById(const QLocaleId &localeId)
{
    const uint idx = locale_index[localeId.language_id];

    const QLocaleData *data = locale_data + idx;

    // If there are no locales for specified language (so we we've got the
    // default language, which has no associated script or country), give up:
    if (localeId.language_id && idx == 0)
        return data;

    Q_ASSERT(localeId.language_id
             ? data->m_language_id == localeId.language_id
             : data->m_language_id);

    if (localeId.script_id == QLocale::AnyScript && localeId.country_id == QLocale::AnyCountry)
        return data;

    if (localeId.script_id == QLocale::AnyScript) {
        do {
            if (data->m_country_id == localeId.country_id)
                return data;
            ++data;
        } while (localeId.language_id
                 ? data->m_language_id == localeId.language_id
                 : data->m_language_id);
    } else if (localeId.country_id == QLocale::AnyCountry) {
        do {
            if (data->m_script_id == localeId.script_id)
                return data;
            ++data;
        } while (localeId.language_id
                 ? data->m_language_id == localeId.language_id
                 : data->m_language_id);;
    } else {
        do {
            if (data->m_script_id == localeId.script_id
                && data->m_country_id == localeId.country_id) {
                return data;
            }
            ++data;
        } while (localeId.language_id
                 ? data->m_language_id == localeId.language_id
                 : data->m_language_id);;
    }

    return nullptr;
}

const QLocaleData *QLocaleData::findLocaleData(QLocale::Language language, QLocale::Script script,
                                               QLocale::Country country)
{
    QLocaleId localeId = QLocaleId::fromIds(language, script, country);
    QLocaleId likelyId = localeId.withLikelySubtagsAdded();

    const uint idx = locale_index[likelyId.language_id];

    // Try a straight match with the likely data:
    if (const QLocaleData *const data = findLocaleDataById(likelyId))
        return data;
    QList<QLocaleId> tried;
    tried.push_back(likelyId);

    // No match; try again with raw data:
    if (!tried.contains(localeId)) {
        if (const QLocaleData *const data = findLocaleDataById(localeId))
            return data;
        tried.push_back(localeId);
    }

    // No match; try again with likely country
    if (country != QLocale::AnyCountry
        && (language != QLocale::AnyLanguage || script != QLocale::AnyScript)) {
        localeId = QLocaleId::fromIds(language, script, QLocale::AnyCountry);
        likelyId = localeId.withLikelySubtagsAdded();
        if (!tried.contains(likelyId)) {
            if (const QLocaleData *const data = findLocaleDataById(likelyId))
                return data;
            tried.push_back(likelyId);
        }

        // No match; try again with any country
        if (!tried.contains(localeId)) {
            if (const QLocaleData *const data = findLocaleDataById(localeId))
                return data;
            tried.push_back(localeId);
        }
    }

    // No match; try again with likely script
    if (script != QLocale::AnyScript
        && (language != QLocale::AnyLanguage || country != QLocale::AnyCountry)) {
        localeId = QLocaleId::fromIds(language, QLocale::AnyScript, country);
        likelyId = localeId.withLikelySubtagsAdded();
        if (!tried.contains(likelyId)) {
            if (const QLocaleData *const data = findLocaleDataById(likelyId))
                return data;
            tried.push_back(likelyId);
        }

        // No match; try again with any script
        if (!tried.contains(localeId)) {
            if (const QLocaleData *const data = findLocaleDataById(localeId))
                return data;
            tried.push_back(localeId);
        }
    }

    // No match; return data at original index
    return locale_data + idx;
}

uint QLocaleData::findLocaleOffset(QLocale::Language language, QLocale::Script script,
                                   QLocale::Country country)
{
    return findLocaleData(language, script, country) - locale_data;
}

static bool parse_locale_tag(const QString &input, int &i, QString *result,
                             const QString &separators)
{
    *result = QString(8, Qt::Uninitialized); // worst case according to BCP47
    QChar *pch = result->data();
    const QChar *uc = input.data() + i;
    const int l = input.length();
    int size = 0;
    for (; i < l && size < 8; ++i, ++size) {
        if (separators.contains(*uc))
            break;
        if (! ((uc->unicode() >= 'a' && uc->unicode() <= 'z') ||
               (uc->unicode() >= 'A' && uc->unicode() <= 'Z') ||
               (uc->unicode() >= '0' && uc->unicode() <= '9')) ) // latin only
            return false;
        *pch++ = *uc++;
    }
    result->truncate(size);
    return true;
}

bool qt_splitLocaleName(const QString &name, QString &lang, QString &script, QString &cntry)
{
    const int length = name.length();

    lang = script = cntry = QString();

    const QString separators = QStringLiteral("_-.@");
    enum ParserState { NoState, LangState, ScriptState, CountryState };
    ParserState state = LangState;
    for (int i = 0; i < length && state != NoState; ) {
        QString value;
        if (!parse_locale_tag(name, i, &value, separators) ||value.isEmpty())
            break;
        QChar sep = i < length ? name.at(i) : QChar();
        switch (state) {
        case LangState:
            if (!sep.isNull() && !separators.contains(sep)) {
                state = NoState;
                break;
            }
            lang = value;
            if (i == length) {
                // just language was specified
                state = NoState;
                break;
            }
            state = ScriptState;
            break;
        case ScriptState: {
            QString scripts = QString::fromLatin1((const char *)script_code_list,
                                                  sizeof(script_code_list) - 1);
            if (value.length() == 4 && scripts.indexOf(value) % 4 == 0) {
                // script name is always 4 characters
                script = value;
                state = CountryState;
            } else {
                // it wasn't a script, maybe it is a country then?
                cntry = value;
                state = NoState;
            }
            break;
        }
        case CountryState:
            cntry = value;
            state = NoState;
            break;
        case NoState:
            // shouldn't happen
            qWarning("QLocale: This should never happen");
            break;
        }
        ++i;
    }
    return lang.length() == 2 || lang.length() == 3;
}

void QLocalePrivate::getLangAndCountry(const QString &name, QLocale::Language &lang,
                                       QLocale::Script &script, QLocale::Country &cntry)
{
    lang = QLocale::C;
    script = QLocale::AnyScript;
    cntry = QLocale::AnyCountry;

    QString lang_code;
    QString script_code;
    QString cntry_code;
    if (!qt_splitLocaleName(name, lang_code, script_code, cntry_code))
        return;

    lang = QLocalePrivate::codeToLanguage(lang_code);
    if (lang == QLocale::C)
        return;
    script = QLocalePrivate::codeToScript(script_code);
    cntry = QLocalePrivate::codeToCountry(cntry_code);
}

static const QLocaleData *findLocaleData(const QString &name)
{
    QLocale::Language lang;
    QLocale::Script script;
    QLocale::Country cntry;
    QLocalePrivate::getLangAndCountry(name, lang, script, cntry);

    return QLocaleData::findLocaleData(lang, script, cntry);
}

static uint findLocaleOffset(const QString &name)
{
    QLocale::Language lang;
    QLocale::Script script;
    QLocale::Country cntry;
    QLocalePrivate::getLangAndCountry(name, lang, script, cntry);

    return QLocaleData::findLocaleOffset(lang, script, cntry);
}

QString qt_readEscapedFormatString(QStringView format, int *idx)
{
    int &i = *idx;

    Q_ASSERT(format.at(i) == QLatin1Char('\''));
    ++i;
    if (i == format.size())
        return QString();
    if (format.at(i).unicode() == '\'') { // "''" outside of a quoted stirng
        ++i;
        return QLatin1String("'");
    }

    QString result;

    while (i < format.size()) {
        if (format.at(i).unicode() == '\'') {
            if (format.mid(i + 1).startsWith(QLatin1Char('\''))) {
                // "''" inside a quoted string
                result.append(QLatin1Char('\''));
                i += 2;
            } else {
                break;
            }
        } else {
            result.append(format.at(i++));
        }
    }
    if (i < format.size())
        ++i;

    return result;
}

/*!
    \internal

    Counts the number of identical leading characters in \a s.

    If \a s is empty, returns 0.

    Otherwise, returns the number of consecutive \c{s.front()}
    characters at the start of \a s.

    \code
    qt_repeatCount(u"a");   // == 1
    qt_repeatCount(u"ab");  // == 1
    qt_repeatCount(u"aab"); // == 2
    \endcode
*/
int qt_repeatCount(QStringView s)
{
    if (s.isEmpty())
        return 0;
    const QChar c = s.front();
    qsizetype j = 1;
    while (j < s.size() && s.at(j) == c)
        ++j;
    return int(j);
}

static const QLocaleData *default_data = nullptr;

static const QLocaleData *const c_data = locale_data;
static QLocalePrivate *c_private()
{
    static QLocalePrivate c_locale{
            c_data, Q_BASIC_ATOMIC_INITIALIZER(1), 0, QLocale::OmitGroupSeparator };
    return &c_locale;
}

static const QLocaleData *systemData();
static QLocale::NumberOptions system_number_options = QLocale::DefaultNumberOptions;
Q_GLOBAL_STATIC_WITH_ARGS(QExplicitlySharedDataPointer<QLocalePrivate>, systemLocalePrivate,
                          (QLocalePrivate::create(systemData(), system_number_options)))

#ifndef QT_NO_SYSTEMLOCALE
/******************************************************************************
** Default system locale behavior
*/

/*!
  Constructs a QSystemLocale object.

  The constructor will automatically install this object as the system locale,
  if there's not one active.  It also resets the flag that'll prompt
  QLocale::system() to re-initialize its data, so that instantiating a
  QSystemLocale transiently (doesn't install the transient as system locale if
  there was one already and) triggers an update to the system locale's data.
*/
QSystemLocale::QSystemLocale()
{
    if (!_systemLocale)
        _systemLocale = this;

    globalLocaleData.m_language_id = 0;
}

/*!
    \internal
*/
QSystemLocale::QSystemLocale(bool)
{ }

/*!
  Deletes the object.
*/
QSystemLocale::~QSystemLocale()
{
    if (_systemLocale == this) {
        _systemLocale = nullptr;

        globalLocaleData.m_language_id = 0;
    }
}

static const QSystemLocale *systemLocale()
{
    if (_systemLocale)
        return _systemLocale;
    return QSystemLocale_globalSystemLocale();
}

static void updateSystemPrivate()
{
    // This function is NOT thread-safe!
    // It *should not* be called by anything but systemData()
    // It *is* called before {system,default}LocalePrivate exist.
    const QSystemLocale *sys_locale = systemLocale();

    // tell the object that the system locale has changed.
    sys_locale->query(QSystemLocale::LocaleChanged, QVariant());

    // Populate global with fallback as basis:
    globalLocaleData = *sys_locale->fallbackUiLocaleData();
    system_number_options = QLocale::DefaultNumberOptions;

    QVariant res = sys_locale->query(QSystemLocale::LanguageId, QVariant());
    if (!res.isNull()) {
        globalLocaleData.m_language_id = res.toInt();
        globalLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
        if (globalLocaleData.m_language_id == QLocale::C)
            system_number_options = QLocale::OmitGroupSeparator;
    }
    res = sys_locale->query(QSystemLocale::CountryId, QVariant());
    if (!res.isNull()) {
        globalLocaleData.m_country_id = res.toInt();
        globalLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
    }
    res = sys_locale->query(QSystemLocale::ScriptId, QVariant());
    if (!res.isNull())
        globalLocaleData.m_script_id = res.toInt();

    res = sys_locale->query(QSystemLocale::DecimalPoint, QVariant());
    if (!res.isNull() && !res.toString().isEmpty())
        globalLocaleData.m_decimal = res.toString().at(0).unicode();

    // System may supply empty group separator to say we should omit grouping;
    // and it makes no sense to use the same separator for decimal and grouping
    // (which might happen by system supplying, as decimal, what CLDR has given
    // us for grouping; or the other way round). Assume, at least, that each of
    // system and CLDR has decimal != group, all the same.
    res = sys_locale->query(QSystemLocale::GroupSeparator, QVariant());
    if (res.isNull()) {
        // The case where system over-rides decimal but not group, and its
        // decimal clashes with CLDR's group.
        if (globalLocaleData.m_group == globalLocaleData.m_decimal)
            system_number_options |= QLocale::OmitGroupSeparator;
    } else if (res.toString().isEmpty()) {
        system_number_options |= QLocale::OmitGroupSeparator;
    } else {
        const ushort group = res.toString().at(0).unicode();
        if (group != globalLocaleData.m_decimal)
            globalLocaleData.m_group = group;
    }

    res = sys_locale->query(QSystemLocale::ZeroDigit, QVariant());
    if (!res.isNull() && !res.toString().isEmpty())
        globalLocaleData.m_zero = res.toString().at(0).unicode();

    res = sys_locale->query(QSystemLocale::NegativeSign, QVariant());
    if (!res.isNull() && !res.toString().isEmpty())
        globalLocaleData.m_minus = res.toString().at(0).unicode();

    res = sys_locale->query(QSystemLocale::PositiveSign, QVariant());
    if (!res.isNull() && !res.toString().isEmpty())
        globalLocaleData.m_plus = res.toString().at(0).unicode();

    if (systemLocalePrivate.exists())
        systemLocalePrivate->data()->m_numberOptions = system_number_options;
    // else: system_number_options will be passed to create() when constructing.
}
#endif // !QT_NO_SYSTEMLOCALE

static const QLocaleData *systemData()
{
#ifndef QT_NO_SYSTEMLOCALE
    /*
      Copy over the information from the fallback locale and modify.

      This modifies (cross-thread) global state, so take care to only call it in
      one thread.
    */
    {
        static QBasicMutex systemDataMutex;
        systemDataMutex.lock();
        if (globalLocaleData.m_language_id == 0)
            updateSystemPrivate();
        systemDataMutex.unlock();
    }

    return &globalLocaleData;
#else
    return locale_data;
#endif
}

static const QLocaleData *defaultData()
{
    if (!default_data)
        default_data = systemData();
    return default_data;
}

const QLocaleData *QLocaleData::c()
{
    Q_ASSERT(locale_index[QLocale::C] == 0);
    return c_data;
}

static inline QString getLocaleData(const ushort *data, int size)
{
    return size > 0 ? QString::fromRawData(reinterpret_cast<const QChar *>(data), size) : QString();
}

static QString getLocaleListData(const ushort *data, int size, int index)
{
    static const ushort separator = ';';
    while (index && size > 0) {
        while (*data != separator)
            ++data, --size;
        --index;
        ++data;
        --size;
    }
    const ushort *end = data;
    while (size > 0 && *end != separator)
        ++end, --size;
    return getLocaleData(data, end - data);
}


#ifndef QT_NO_DATASTREAM
QDataStream &operator<<(QDataStream &ds, const QLocale &l)
{
    ds << l.name();
    return ds;
}

QDataStream &operator>>(QDataStream &ds, QLocale &l)
{
    QString s;
    ds >> s;
    l = QLocale(s);
    return ds;
}
#endif // QT_NO_DATASTREAM


static const int locale_data_size = sizeof(locale_data)/sizeof(QLocaleData) - 1;

Q_GLOBAL_STATIC_WITH_ARGS(QSharedDataPointer<QLocalePrivate>, defaultLocalePrivate,
                          (QLocalePrivate::create(defaultData())))

static QLocalePrivate *localePrivateByName(const QString &name)
{
    if (name == QLatin1String("C"))
        return c_private();
    // TODO: Remove this version, and use offset everywhere
    const QLocaleData *data = findLocaleData(name);
    return QLocalePrivate::create(data, findLocaleOffset(name),
                                  data->m_language_id == QLocale::C
                                  ? QLocale::OmitGroupSeparator : QLocale::DefaultNumberOptions);
}

static QLocalePrivate *findLocalePrivate(QLocale::Language language, QLocale::Script script,
                                         QLocale::Country country)
{
    if (language == QLocale::C)
        return c_private();

    // TODO: Remove pointer, use index instead
    const QLocaleData *data = QLocaleData::findLocaleData(language, script, country);
    const uint offset = QLocaleData::findLocaleOffset(language, script, country);

    QLocale::NumberOptions numberOptions = QLocale::DefaultNumberOptions;

    // If not found, should default to system
    if (data->m_language_id == QLocale::C) {
        if (defaultLocalePrivate.exists())
            numberOptions = defaultLocalePrivate->data()->m_numberOptions;
        data = defaultData();
    }
    return QLocalePrivate::create(data, offset, numberOptions);
}


/*!
 \internal
*/
QLocale::QLocale(QLocalePrivate &dd)
    : d(&dd)
{}


/*!
    Constructs a QLocale object with the specified \a name,
    which has the format
    "language[_script][_country][.codeset][@modifier]" or "C", where:

    \list
    \li language is a lowercase, two-letter, ISO 639 language code (also some
        three-letter codes),
    \li script is a titlecase, four-letter, ISO 15924 script code,
    \li country is an uppercase, two-letter, ISO 3166 country code
        (also "419" as defined by United Nations),
    \li and codeset and modifier are ignored.
    \endlist

    The separator can be either underscore or a minus sign.

    If the string violates the locale format, or language is not
    a valid ISO 639 code, the "C" locale is used instead. If country
    is not present, or is not a valid ISO 3166 code, the most
    appropriate country is chosen for the specified language.

    The language, script and country codes are converted to their respective
    \c Language, \c Script and \c Country enums. After this conversion is
    performed, the constructor behaves exactly like QLocale(Country, Script,
    Language).

    This constructor is much slower than QLocale(Country, Script, Language).

    \sa bcp47Name()
*/

QLocale::QLocale(const QString &name)
    : d(localePrivateByName(name))
{
}

/*!
    Constructs a QLocale object initialized with the default locale. If
    no default locale was set using setDefault(), this locale will
    be the same as the one returned by system().

    \sa setDefault()
*/

QLocale::QLocale()
    : d(*defaultLocalePrivate)
{
    // Make sure system data is up to date
    systemData();
}

/*!
    Constructs a QLocale object with the specified \a language and \a
    country.

    \list
    \li If the language/country pair is found in the database, it is used.
    \li If the language is found but the country is not, or if the country
       is \c AnyCountry, the language is used with the most
       appropriate available country (for example, Germany for German),
    \li If neither the language nor the country are found, QLocale
       defaults to the default locale (see setDefault()).
    \endlist

    The language and country that are actually used can be queried
    using language() and country().

    \sa setDefault(), language(), country()
*/

QLocale::QLocale(Language language, Country country)
    : d(findLocalePrivate(language, QLocale::AnyScript, country))
{
}

/*!
    \since 4.8

    Constructs a QLocale object with the specified \a language, \a script and
    \a country.

    \list
    \li If the language/script/country is found in the database, it is used.
    \li If both \a script is AnyScript and \a country is AnyCountry, the
       language is used with the most appropriate available script and country
       (for example, Germany for German),
    \li If either \a script is AnyScript or \a country is AnyCountry, the
       language is used with the first locale that matches the given \a script
       and \a country.
    \li If neither the language nor the country are found, QLocale
       defaults to the default locale (see setDefault()).
    \endlist

    The language, script and country that are actually used can be queried
    using language(), script() and country().

    \sa setDefault(), language(), script(), country()
*/

QLocale::QLocale(Language language, Script script, Country country)
    : d(findLocalePrivate(language, script, country))
{
}

/*!
    Constructs a QLocale object as a copy of \a other.
*/

QLocale::QLocale(const QLocale &other)
{
    d = other.d;
}

/*!
    Destructor
*/

QLocale::~QLocale()
{
}

/*!
    Assigns \a other to this QLocale object and returns a reference
    to this QLocale object.
*/

QLocale &QLocale::operator=(const QLocale &other)
{
    d = other.d;
    return *this;
}

bool QLocale::operator==(const QLocale &other) const
{
    return d->m_data == other.d->m_data && d->m_numberOptions == other.d->m_numberOptions;
}

bool QLocale::operator!=(const QLocale &other) const
{
    return d->m_data != other.d->m_data || d->m_numberOptions != other.d->m_numberOptions;
}

/*!
    \fn void QLocale::swap(QLocale &other)
    \since 5.6

    Swaps locale \a other with this locale. This operation is very fast and
    never fails.
*/

/*!
    \since 5.6
    \relates QLocale

    Returns the hash value for \a key, using
    \a seed to seed the calculation.
*/
uint qHash(const QLocale &key, uint seed) noexcept
{
    QtPrivate::QHashCombine hash;
    seed = hash(seed, key.d->m_data);
    seed = hash(seed, key.d->m_numberOptions);
    return seed;
}

/*!
    \since 4.2

    Sets the \a options related to number conversions for this
    QLocale instance.

    \sa numberOptions()
*/
void QLocale::setNumberOptions(NumberOptions options)
{
    d->m_numberOptions = options;
}

/*!
    \since 4.2

    Returns the options related to number conversions for this
    QLocale instance.

    By default, no options are set for the standard locales, except
    for the "C" locale, which has OmitGroupSeparator set by default.

    \sa setNumberOptions(), toString(), groupSeparator()
*/
QLocale::NumberOptions QLocale::numberOptions() const
{
    return static_cast<NumberOptions>(d->m_numberOptions);
}

/*!
    \since 4.8

    Returns \a str quoted according to the current locale using the given
    quotation \a style.
*/
QString QLocale::quoteString(const QString &str, QuotationStyle style) const
{
    return quoteString(QStringRef(&str), style);
}

/*!
    \since 4.8

    \overload
*/
QString QLocale::quoteString(const QStringRef &str, QuotationStyle style) const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res;
        if (style == QLocale::AlternateQuotation)
            res = systemLocale()->query(QSystemLocale::StringToAlternateQuotation,
                                        QVariant::fromValue(str));
        if (res.isNull() || style == QLocale::StandardQuotation)
            res = systemLocale()->query(QSystemLocale::StringToStandardQuotation,
                                        QVariant::fromValue(str));
        if (!res.isNull())
            return res.toString();
    }
#endif

    if (style == QLocale::StandardQuotation)
        return QChar(d->m_data->m_quotation_start) % str % QChar(d->m_data->m_quotation_end);

    return QChar(d->m_data->m_alternate_quotation_start)
        % str % QChar(d->m_data->m_alternate_quotation_end);
}

/*!
    \since 4.8

    Returns a string that represents a join of a given \a list of strings with
    a separator defined by the locale.
*/
QString QLocale::createSeparatedList(const QStringList &list) const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res =
            systemLocale()->query(QSystemLocale::ListToSeparatedString, QVariant::fromValue(list));

        if (!res.isNull())
            return res.toString();
    }
#endif

    const int size = list.size();
    if (size == 1) {
        return list.at(0);
    } else if (size == 2) {
        QString format = getLocaleData(
            list_pattern_part_data + d->m_data->m_list_pattern_part_two_idx,
            d->m_data->m_list_pattern_part_two_size);
        return format.arg(list.at(0), list.at(1));
    } else if (size > 2) {
        QString formatStart = getLocaleData(
            list_pattern_part_data + d->m_data->m_list_pattern_part_start_idx,
            d->m_data->m_list_pattern_part_start_size);
        QString formatMid = getLocaleData(
            list_pattern_part_data + d->m_data->m_list_pattern_part_mid_idx,
            d->m_data->m_list_pattern_part_mid_size);
        QString formatEnd = getLocaleData(
            list_pattern_part_data + d->m_data->m_list_pattern_part_end_idx,
            d->m_data->m_list_pattern_part_end_size);
        QString result = formatStart.arg(list.at(0), list.at(1));
        for (int i = 2; i < size - 1; ++i)
            result = formatMid.arg(result, list.at(i));
        result = formatEnd.arg(result, list.at(size - 1));
        return result;
    }

    return QString();
}

/*!
    \nonreentrant

    Sets the global default locale to \a locale. These
    values are used when a QLocale object is constructed with
    no arguments. If this function is not called, the system's
    locale is used.

    \warning In a multithreaded application, the default locale
    should be set at application startup, before any non-GUI threads
    are created.

    \sa system(), c()
*/

void QLocale::setDefault(const QLocale &locale)
{
    default_data = locale.d->m_data;

    if (defaultLocalePrivate.isDestroyed())
        return; // avoid crash on exit
    if (!defaultLocalePrivate.exists()) {
        // Force it to exist; see QTBUG-83016
        QLocale ignoreme;
        Q_ASSERT(defaultLocalePrivate.exists());
    }

    // update the cached private
    *defaultLocalePrivate = locale.d;
}

/*!
    Returns the language of this locale.

    \sa script(), country(), languageToString(), bcp47Name()
*/
QLocale::Language QLocale::language() const
{
    return Language(d->languageId());
}

/*!
    \since 4.8

    Returns the script of this locale.

    \sa language(), country(), languageToString(), scriptToString(), bcp47Name()
*/
QLocale::Script QLocale::script() const
{
    return Script(d->m_data->m_script_id);
}

/*!
    Returns the country of this locale.

    \sa language(), script(), countryToString(), bcp47Name()
*/
QLocale::Country QLocale::country() const
{
    return Country(d->countryId());
}

/*!
    Returns the language and country of this locale as a
    string of the form "language_country", where
    language is a lowercase, two-letter ISO 639 language code,
    and country is an uppercase, two- or three-letter ISO 3166 country code.

    Note that even if QLocale object was constructed with an explicit script,
    name() will not contain it for compatibility reasons. Use bcp47Name() instead
    if you need a full locale name.

    \sa QLocale(), language(), script(), country(), bcp47Name()
*/

QString QLocale::name() const
{
    Language l = language();
    if (l == C)
        return d->languageCode();

    Country c = country();
    if (c == AnyCountry)
        return d->languageCode();

    return d->languageCode() + QLatin1Char('_') + d->countryCode();
}

static qlonglong toIntegral_helper(const QLocaleData *d, QStringView str, bool *ok,
                                   QLocale::NumberOptions mode, qlonglong)
{
    return d->stringToLongLong(str, 10, ok, mode);
}

static qulonglong toIntegral_helper(const QLocaleData *d, QStringView str, bool *ok,
                                    QLocale::NumberOptions mode, qulonglong)
{
    return d->stringToUnsLongLong(str, 10, ok, mode);
}

template <typename T> static inline
T toIntegral_helper(const QLocalePrivate *d, QStringView str, bool *ok)
{
    using Int64 =
        typename std::conditional<std::is_unsigned<T>::value, qulonglong, qlonglong>::type;

    // we select the right overload by the last, unused parameter
    Int64 val = toIntegral_helper(d->m_data, str, ok, d->m_numberOptions, Int64());
    if (T(val) != val) {
        if (ok != nullptr)
            *ok = false;
        val = 0;
    }
    return T(val);
}


/*!
    \since 4.8

    Returns the dash-separated language, script and country (and possibly other
    BCP47 fields) of this locale as a string.

    Unlike the uiLanguages() the returned value of the bcp47Name() represents
    the locale name of the QLocale data but not the language the user-interface
    should be in.

    This function tries to conform the locale name to BCP47.

    \sa language(), country(), script(), uiLanguages()
*/
QString QLocale::bcp47Name() const
{
    return QString::fromLatin1(d->bcp47Name());
}

/*!
    Returns a QString containing the name of \a language.

    \sa countryToString(), scriptToString(), bcp47Name()
*/

QString QLocale::languageToString(Language language)
{
    if (uint(language) > uint(QLocale::LastLanguage))
        return QLatin1String("Unknown");
    return QLatin1String(language_name_list + language_name_index[language]);
}

/*!
    Returns a QString containing the name of \a country.

    \sa languageToString(), scriptToString(), country(), bcp47Name()
*/

QString QLocale::countryToString(Country country)
{
    if (uint(country) > uint(QLocale::LastCountry))
        return QLatin1String("Unknown");
    return QLatin1String(country_name_list + country_name_index[country]);
}

/*!
    \since 4.8

    Returns a QString containing the name of \a script.

    \sa languageToString(), countryToString(), script(), bcp47Name()
*/
QString QLocale::scriptToString(QLocale::Script script)
{
    if (uint(script) > uint(QLocale::LastScript))
        return QLatin1String("Unknown");
    return QLatin1String(script_name_list + script_name_index[script]);
}

#if QT_STRINGVIEW_LEVEL < 2
/*!
    Returns the short int represented by the localized string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toUShort(), toString()
*/

short QLocale::toShort(const QString &s, bool *ok) const
{
    return toIntegral_helper<short>(d, s, ok);
}

/*!
    Returns the unsigned short int represented by the localized string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toShort(), toString()
*/

ushort QLocale::toUShort(const QString &s, bool *ok) const
{
    return toIntegral_helper<ushort>(d, s, ok);
}

/*!
    Returns the int represented by the localized string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toUInt(), toString()
*/

int QLocale::toInt(const QString &s, bool *ok) const
{
    return toIntegral_helper<int>(d, s, ok);
}

/*!
    Returns the unsigned int represented by the localized string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toInt(), toString()
*/

uint QLocale::toUInt(const QString &s, bool *ok) const
{
    return toIntegral_helper<uint>(d, s, ok);
}

/*!
 Returns the long int represented by the localized string \a s.

 If the conversion fails the function returns 0.

 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
 to \c false, and success by setting *\a{ok} to \c true.

 This function ignores leading and trailing whitespace.

 \sa toInt(), toULong(), toDouble(), toString()

 \since 5.13
 */


long QLocale::toLong(const QString &s, bool *ok) const
{
    return toIntegral_helper<long>(d, s, ok);
}

/*!
 Returns the unsigned long int represented by the localized
 string \a s.

 If the conversion fails the function returns 0.

 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
 to \c false, and success by setting *\a{ok} to \c true.

 This function ignores leading and trailing whitespace.

 \sa toLong(), toInt(), toDouble(), toString()

 \since 5.13
*/

ulong QLocale::toULong(const QString &s, bool *ok) const
{
    return toIntegral_helper<ulong>(d, s, ok);
}

/*!
    Returns the long long int represented by the localized string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toInt(), toULongLong(), toDouble(), toString()
*/


qlonglong QLocale::toLongLong(const QString &s, bool *ok) const
{
    return toIntegral_helper<qlonglong>(d, s, ok);
}

/*!
    Returns the unsigned long long int represented by the localized
    string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toLongLong(), toInt(), toDouble(), toString()
*/

qulonglong QLocale::toULongLong(const QString &s, bool *ok) const
{
    return toIntegral_helper<qulonglong>(d, s, ok);
}

/*!
    Returns the float represented by the localized string \a s.

    Returns an infinity if the conversion overflows or 0.0 if the
    conversion fails for any other reason (e.g. underflow).

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function does not fall back to the 'C' locale if the string
    cannot be interpreted in this locale.

    This function ignores leading and trailing whitespace.

    \sa toDouble(), toInt(), toString()
*/

float QLocale::toFloat(const QString &s, bool *ok) const
{
    return QLocaleData::convertDoubleToFloat(toDouble(s, ok), ok);
}

/*!
    Returns the double represented by the localized string \a s.

    Returns an infinity if the conversion overflows or 0.0 if the
    conversion fails for any other reason (e.g. underflow).

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function does not fall back to the 'C' locale if the string
    cannot be interpreted in this locale.

    \snippet code/src_corelib_tools_qlocale.cpp 3

    Notice that the last conversion returns 1234.0, because '.' is the
    thousands group separator in the German locale.

    This function ignores leading and trailing whitespace.

    \sa toFloat(), toInt(), toString()
*/

double QLocale::toDouble(const QString &s, bool *ok) const
{
    return d->m_data->stringToDouble(s, ok, d->m_numberOptions);
}

/*!
    Returns the short int represented by the localized string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toUShort(), toString()

    \since 5.1
*/

short QLocale::toShort(const QStringRef &s, bool *ok) const
{
    return toIntegral_helper<short>(d, s, ok);
}

/*!
    Returns the unsigned short int represented by the localized string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toShort(), toString()

    \since 5.1
*/

ushort QLocale::toUShort(const QStringRef &s, bool *ok) const
{
    return toIntegral_helper<ushort>(d, s, ok);
}

/*!
    Returns the int represented by the localized string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toUInt(), toString()

    \since 5.1
*/

int QLocale::toInt(const QStringRef &s, bool *ok) const
{
    return toIntegral_helper<int>(d, s, ok);
}

/*!
    Returns the unsigned int represented by the localized string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toInt(), toString()

    \since 5.1
*/

uint QLocale::toUInt(const QStringRef &s, bool *ok) const
{
    return toIntegral_helper<uint>(d, s, ok);
}

/*!
 Returns the long int represented by the localized string \a s.

 If the conversion fails the function returns 0.

 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
 to \c false, and success by setting *\a{ok} to \c true.

 This function ignores leading and trailing whitespace.

 \sa toInt(), toULong(), toDouble(), toString()

 \since 5.13
 */


long QLocale::toLong(const QStringRef &s, bool *ok) const
{
    return toIntegral_helper<long>(d, s, ok);
}

/*!
 Returns the unsigned long int represented by the localized
 string \a s.

 If the conversion fails the function returns 0.

 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
 to \c false, and success by setting *\a{ok} to \c true.

 This function ignores leading and trailing whitespace.

 \sa toLong(), toInt(), toDouble(), toString()

 \since 5.13
 */

ulong QLocale::toULong(const QStringRef &s, bool *ok) const
{
    return toIntegral_helper<ulong>(d, s, ok);
}

/*!
    Returns the long long int represented by the localized string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toInt(), toULongLong(), toDouble(), toString()

    \since 5.1
*/


qlonglong QLocale::toLongLong(const QStringRef &s, bool *ok) const
{
    return toIntegral_helper<qlonglong>(d, s, ok);
}

/*!
    Returns the unsigned long long int represented by the localized
    string \a s.

    If the conversion fails the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toLongLong(), toInt(), toDouble(), toString()

    \since 5.1
*/

qulonglong QLocale::toULongLong(const QStringRef &s, bool *ok) const
{
    return toIntegral_helper<qulonglong>(d, s, ok);
}

/*!
    Returns the float represented by the localized string \a s.

    Returns an infinity if the conversion overflows or 0.0 if the
    conversion fails for any other reason (e.g. underflow).

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function does not fall back to the 'C' locale if the string
    cannot be interpreted in this locale.

    This function ignores leading and trailing whitespace.

    \sa toDouble(), toInt(), toString()

    \since 5.1
*/

float QLocale::toFloat(const QStringRef &s, bool *ok) const
{
    return QLocaleData::convertDoubleToFloat(toDouble(s, ok), ok);
}

/*!
    Returns the double represented by the localized string \a s.

    Returns an infinity if the conversion overflows or 0.0 if the
    conversion fails for any other reason (e.g. underflow).

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function does not fall back to the 'C' locale if the string
    cannot be interpreted in this locale.

    \snippet code/src_corelib_tools_qlocale.cpp 3

    Notice that the last conversion returns 1234.0, because '.' is the
    thousands group separator in the German locale.

    This function ignores leading and trailing whitespace.

    \sa toFloat(), toInt(), toString()

    \since 5.1
*/

double QLocale::toDouble(const QStringRef &s, bool *ok) const
{
    return d->m_data->stringToDouble(s, ok, d->m_numberOptions);
}
#endif // QT_STRINGVIEW_LEVEL < 2

/*!
    Returns the short int represented by the localized string \a s.

    If the conversion fails, the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toUShort(), toString()

    \since 5.10
*/

short QLocale::toShort(QStringView s, bool *ok) const
{
    return toIntegral_helper<short>(d, s, ok);
}

/*!
    Returns the unsigned short int represented by the localized string \a s.

    If the conversion fails, the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toShort(), toString()

    \since 5.10
*/

ushort QLocale::toUShort(QStringView s, bool *ok) const
{
    return toIntegral_helper<ushort>(d, s, ok);
}

/*!
    Returns the int represented by the localized string \a s.

    If the conversion fails, the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toUInt(), toString()

    \since 5.10
*/

int QLocale::toInt(QStringView s, bool *ok) const
{
    return toIntegral_helper<int>(d, s, ok);
}

/*!
    Returns the unsigned int represented by the localized string \a s.

    If the conversion fails, the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toInt(), toString()

    \since 5.10
*/

uint QLocale::toUInt(QStringView s, bool *ok) const
{
    return toIntegral_helper<uint>(d, s, ok);
}

/*!
 Returns the long int represented by the localized string \a s.

 If the conversion fails the function returns 0.

 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
 to \c false, and success by setting *\a{ok} to \c true.

 This function ignores leading and trailing whitespace.

 \sa toInt(), toULong(), toDouble(), toString()

 \since 5.13
 */


long QLocale::toLong(QStringView s, bool *ok) const
{
    return toIntegral_helper<long>(d, s, ok);
}

/*!
 Returns the unsigned long int represented by the localized
 string \a s.

 If the conversion fails the function returns 0.

 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
 to \c false, and success by setting *\a{ok} to \c true.

 This function ignores leading and trailing whitespace.

 \sa toLong(), toInt(), toDouble(), toString()

 \since 5.13
 */

ulong QLocale::toULong(QStringView s, bool *ok) const
{
    return toIntegral_helper<ulong>(d, s, ok);
}

/*!
    Returns the long long int represented by the localized string \a s.

    If the conversion fails, the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toInt(), toULongLong(), toDouble(), toString()

    \since 5.10
*/


qlonglong QLocale::toLongLong(QStringView s, bool *ok) const
{
    return toIntegral_helper<qlonglong>(d, s, ok);
}

/*!
    Returns the unsigned long long int represented by the localized
    string \a s.

    If the conversion fails, the function returns 0.

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toLongLong(), toInt(), toDouble(), toString()

    \since 5.10
*/

qulonglong QLocale::toULongLong(QStringView s, bool *ok) const
{
    return toIntegral_helper<qulonglong>(d, s, ok);
}

/*!
    Returns the float represented by the localized string \a s.

    Returns an infinity if the conversion overflows or 0.0 if the
    conversion fails for any other reason (e.g. underflow).

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    This function ignores leading and trailing whitespace.

    \sa toDouble(), toInt(), toString()

    \since 5.10
*/

float QLocale::toFloat(QStringView s, bool *ok) const
{
    return QLocaleData::convertDoubleToFloat(toDouble(s, ok), ok);
}

/*!
    Returns the double represented by the localized string \a s.

    Returns an infinity if the conversion overflows or 0.0 if the
    conversion fails for any other reason (e.g. underflow).

    If \a ok is not \nullptr, failure is reported by setting *\a{ok}
    to \c false, and success by setting *\a{ok} to \c true.

    Unlike QString::toDouble(), this function does not fall back to
    the "C" locale if the string cannot be interpreted in this
    locale.

    \snippet code/src_corelib_tools_qlocale.cpp 3-qstringview

    Notice that the last conversion returns 1234.0, because '.' is the
    thousands group separator in the German locale.

    This function ignores leading and trailing whitespace.

    \sa toFloat(), toInt(), toString()

    \since 5.10
*/

double QLocale::toDouble(QStringView s, bool *ok) const
{
    return d->m_data->stringToDouble(s, ok, d->m_numberOptions);
}

/*!
    Returns a localized string representation of \a i.

    \sa toLongLong(), numberOptions(), zeroDigit(), positiveSign()
*/

QString QLocale::toString(qlonglong i) const
{
    int flags = d->m_numberOptions & OmitGroupSeparator
                    ? 0
                    : (d->m_data->m_country_id == Country::India)
                      ? QLocaleData::IndianNumberGrouping : QLocaleData::ThousandsGroup;

    return d->m_data->longLongToString(i, -1, 10, -1, flags);
}

/*!
    \overload

    \sa toULongLong(), numberOptions(), zeroDigit(), positiveSign()
*/

QString QLocale::toString(qulonglong i) const
{
    int flags = d->m_numberOptions & OmitGroupSeparator
                    ? 0
                    : (d->m_data->m_country_id == Country::India)
                      ? QLocaleData::IndianNumberGrouping : QLocaleData::ThousandsGroup;

    return d->m_data->unsLongLongToString(i, -1, 10, -1, flags);
}

#if QT_STRINGVIEW_LEVEL < 2
/*!
    Returns a localized string representation of the given \a date in the
    specified \a format.
    If \a format is an empty string, an empty string is returned.

    \sa QDate::toString()
*/

QString QLocale::toString(const QDate &date, const QString &format) const
{
    return QCalendar().dateTimeToString(format, QDateTime(), date, QTime(), *this);
}
#endif

/*!
    \since 5.14

    Returns a localized string representation of the given \a date in the
    specified \a format, optionally for a specified calendar \a cal.
    If \a format is an empty string, an empty string is returned.

    \sa QDate::toString()
*/
QString QLocale::toString(const QDate &date, QStringView format, QCalendar cal) const
{
    return cal.dateTimeToString(format, QDateTime(), date, QTime(), *this);
}

/*!
    \since 5.10
    \overload
*/
QString QLocale::toString(const QDate &date, QStringView format) const
{
    return QCalendar().dateTimeToString(format, QDateTime(), date, QTime(), *this);
}

/*!
    \since 5.14

    Returns a localized string representation of the given \a date according
    to the specified \a format (see dateFormat()), optionally for a specified
    calendar \a cal.

    \note Some locales may use formats that limit the range of years they can
    represent.
*/
QString QLocale::toString(const QDate &date, FormatType format, QCalendar cal) const
{
    if (!date.isValid())
        return QString();

#ifndef QT_NO_SYSTEMLOCALE
    if (cal.isGregorian() && d->m_data == systemData()) {
        QVariant res = systemLocale()->query(format == LongFormat
                                             ? QSystemLocale::DateToStringLong
                                             : QSystemLocale::DateToStringShort,
                                             date);
        if (!res.isNull())
            return res.toString();
    }
#endif

    QString format_str = dateFormat(format);
    return toString(date, format_str, cal);
}

/*!
    \since 4.5
    \overload
*/
QString QLocale::toString(const QDate &date, FormatType format) const
{
    if (!date.isValid())
        return QString();

#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(format == LongFormat
                                             ? QSystemLocale::DateToStringLong
                                             : QSystemLocale::DateToStringShort,
                                             date);
        if (!res.isNull())
            return res.toString();
    }
#endif

    QString format_str = dateFormat(format);
    return toString(date, format_str);
}

static bool timeFormatContainsAP(QStringView format)
{
    int i = 0;
    while (i < format.size()) {
        if (format.at(i).unicode() == '\'') {
            qt_readEscapedFormatString(format, &i);
            continue;
        }

        if (format.at(i).toLower().unicode() == 'a')
            return true;

        ++i;
    }
    return false;
}

#if QT_STRINGVIEW_LEVEL < 2
/*!
    Returns a localized string representation of the given \a time according
    to the specified \a format.
    If \a format is an empty string, an empty string is returned.

    \sa QTime::toString()
*/
QString QLocale::toString(const QTime &time, const QString &format) const
{
    return QCalendar().dateTimeToString(format, QDateTime(), QDate(), time, *this);
}
#endif

/*!
    \since 4.5

    Returns a localized string representation of the given \a time according
    to the specified \a format.
    If \a format is an empty string, an empty string is returned.

    \sa QTime::toString()
*/
QString QLocale::toString(const QTime &time, QStringView format) const
{
    return QCalendar().dateTimeToString(format, QDateTime(), QDate(), time, *this);
}

#if QT_STRINGVIEW_LEVEL < 2
/*!
    \since 4.4

    Returns a localized string representation of the given \a dateTime according
    to the specified \a format.
    If \a format is an empty string, an empty string is returned.

    \sa QDateTime::toString(), QDate::toString(), QTime::toString()
*/

QString QLocale::toString(const QDateTime &dateTime, const QString &format) const
{
    return QCalendar().dateTimeToString(format, dateTime, QDate(), QTime(), *this);
}
#endif

/*!
    \since 5.14

    Returns a localized string representation of the given \a dateTime according
    to the specified \a format, optionally for a specified calendar \a cal.
    If \a format is an empty string, an empty string is returned.

    \sa QDateTime::toString(), QDate::toString(), QTime::toString()
*/
QString QLocale::toString(const QDateTime &dateTime, QStringView format, QCalendar cal) const
{
    return cal.dateTimeToString(format, dateTime, QDate(), QTime(), *this);
}

/*!
    \since 5.10
    \overload
*/
QString QLocale::toString(const QDateTime &dateTime, QStringView format) const
{
    return QCalendar().dateTimeToString(format, dateTime, QDate(), QTime(), *this);
}

/*!
    \since 5.14

    Returns a localized string representation of the given \a dateTime according
    to the specified \a format (see dateTimeFormat()), optionally for a
    specified calendar \a cal.

    \note Some locales may use formats that limit the range of years they can
    represent.
*/
QString QLocale::toString(const QDateTime &dateTime, FormatType format, QCalendar cal) const
{
    if (!dateTime.isValid())
        return QString();

#ifndef QT_NO_SYSTEMLOCALE
    if (cal.isGregorian() && d->m_data == systemData()) {
        QVariant res = systemLocale()->query(format == LongFormat
                                             ? QSystemLocale::DateTimeToStringLong
                                             : QSystemLocale::DateTimeToStringShort,
                                             dateTime);
        if (!res.isNull())
            return res.toString();
    }
#endif

    const QString format_str = dateTimeFormat(format);
    return toString(dateTime, format_str, cal);
}

/*!
    \since 4.4
    \overload
*/
QString QLocale::toString(const QDateTime &dateTime, FormatType format) const
{
    if (!dateTime.isValid())
        return QString();

#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(format == LongFormat
                                             ? QSystemLocale::DateTimeToStringLong
                                             : QSystemLocale::DateTimeToStringShort,
                                             dateTime);
        if (!res.isNull())
            return res.toString();
    }
#endif

    const QString format_str = dateTimeFormat(format);
    return toString(dateTime, format_str);
}


/*!
    Returns a localized string representation of the given \a time in the
    specified \a format (see timeFormat()).
*/

QString QLocale::toString(const QTime &time, FormatType format) const
{
    if (!time.isValid())
        return QString();

#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(format == LongFormat
                                             ? QSystemLocale::TimeToStringLong
                                             : QSystemLocale::TimeToStringShort,
                                             time);
        if (!res.isNull())
            return res.toString();
    }
#endif

    QString format_str = timeFormat(format);
    return toString(time, format_str);
}

/*!
    \since 4.1

    Returns the date format used for the current locale.

    If \a format is LongFormat, the format will be elaborate, otherwise it will be short.
    For example, LongFormat for the \c{en_US} locale is \c{dddd, MMMM d, yyyy},
    ShortFormat is \c{M/d/yy}.

    \sa QDate::toString(), QDate::fromString()
*/

QString QLocale::dateFormat(FormatType format) const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(format == LongFormat
                                             ? QSystemLocale::DateFormatLong
                                             : QSystemLocale::DateFormatShort,
                                             QVariant());
        if (!res.isNull())
            return res.toString();
    }
#endif

    quint32 idx, size;
    switch (format) {
    case LongFormat:
        idx = d->m_data->m_long_date_format_idx;
        size = d->m_data->m_long_date_format_size;
        break;
    default:
        idx = d->m_data->m_short_date_format_idx;
        size = d->m_data->m_short_date_format_size;
        break;
    }
    return getLocaleData(date_format_data + idx, size);
}

/*!
    \since 4.1

    Returns the time format used for the current locale.

    If \a format is LongFormat, the format will be elaborate, otherwise it will be short.
    For example, LongFormat for the \c{en_US} locale is \c{h:mm:ss AP t},
    ShortFormat is \c{h:mm AP}.

    \sa QTime::toString(), QTime::fromString()
*/

QString QLocale::timeFormat(FormatType format) const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(format == LongFormat
                                             ? QSystemLocale::TimeFormatLong
                                             : QSystemLocale::TimeFormatShort,
                                             QVariant());
        if (!res.isNull())
            return res.toString();
    }
#endif

    quint32 idx, size;
    switch (format) {
    case LongFormat:
        idx = d->m_data->m_long_time_format_idx;
        size = d->m_data->m_long_time_format_size;
        break;
    default:
        idx = d->m_data->m_short_time_format_idx;
        size = d->m_data->m_short_time_format_size;
        break;
    }
    return getLocaleData(time_format_data + idx, size);
}

/*!
    \since 4.4

    Returns the date time format used for the current locale.

    If \a format is LongFormat, the format will be elaborate, otherwise it will be short.
    For example, LongFormat for the \c{en_US} locale is \c{dddd, MMMM d, yyyy h:mm:ss AP t},
    ShortFormat is \c{M/d/yy h:mm AP}.

    \sa QDateTime::toString(), QDateTime::fromString()
*/

QString QLocale::dateTimeFormat(FormatType format) const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(format == LongFormat
                                             ? QSystemLocale::DateTimeFormatLong
                                             : QSystemLocale::DateTimeFormatShort,
                                             QVariant());
        if (!res.isNull()) {
            return res.toString();
        }
    }
#endif
    return dateFormat(format) + QLatin1Char(' ') + timeFormat(format);
}

#if QT_CONFIG(datestring)
/*!
    \since 4.4

    Parses the time string given in \a string and returns the
    time. The format of the time string is chosen according to the
    \a format parameter (see timeFormat()).

    If the time could not be parsed, returns an invalid time.

    \sa timeFormat(), toDate(), toDateTime(), QTime::fromString()
*/
QTime QLocale::toTime(const QString &string, FormatType format) const
{
    return toTime(string, timeFormat(format));
}

#if QT_DEPRECATED_SINCE(5, 15)
/*!
    \since 5.14
    \overload
    \deprecated
*/
QTime QLocale::toTime(const QString &string, FormatType format, QCalendar cal) const
{
QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
    return toTime(string, timeFormat(format), cal);
QT_WARNING_POP
}
#endif

/*!
    \since 4.4

    Parses the date string given in \a string and returns the
    date. The format of the date string is chosen according to the
    \a format parameter (see dateFormat()).

    If the date could not be parsed, returns an invalid date.

    \sa dateFormat(), toTime(), toDateTime(), QDate::fromString()
*/
QDate QLocale::toDate(const QString &string, FormatType format) const
{
    return toDate(string, dateFormat(format));
}

/*!
    \since 5.14
    \overload
*/
QDate QLocale::toDate(const QString &string, FormatType format, QCalendar cal) const
{
    return toDate(string, dateFormat(format), cal);
}

/*!
    \since 4.4

    Parses the date/time string given in \a string and returns the
    time. The format of the date/time string is chosen according to the
    \a format parameter (see dateTimeFormat()).

    If the string could not be parsed, returns an invalid QDateTime.

    \sa dateTimeFormat(), toTime(), toDate(), QDateTime::fromString()
*/
QDateTime QLocale::toDateTime(const QString &string, FormatType format) const
{
    return toDateTime(string, dateTimeFormat(format));
}

/*!
    \since 5.14
    \overload
*/
QDateTime QLocale::toDateTime(const QString &string, FormatType format, QCalendar cal) const
{
    return toDateTime(string, dateTimeFormat(format), cal);
}

/*!
    \since 4.4

    Parses the time string given in \a string and returns the
    time. See QTime::fromString() for information on what is a valid
    format string.

    If the time could not be parsed, returns an invalid time.

    \sa timeFormat(), toDate(), toDateTime(), QTime::fromString()
*/
QTime QLocale::toTime(const QString &string, const QString &format) const
{
    QTime time;
#if QT_CONFIG(datetimeparser)
    QDateTimeParser dt(QMetaType::QTime, QDateTimeParser::FromString, QCalendar());
    dt.setDefaultLocale(*this);
    if (dt.parseFormat(format))
        dt.fromString(string, nullptr, &time);
#else
    Q_UNUSED(string);
    Q_UNUSED(format);
#endif
    return time;
}

#if QT_DEPRECATED_SINCE(5, 15)
/*!
    \since 5.14
    \overload
    \deprecated
*/
QTime QLocale::toTime(const QString &string, const QString &format, QCalendar cal) const
{
    QTime time;
#if QT_CONFIG(datetimeparser)
    QDateTimeParser dt(QMetaType::QTime, QDateTimeParser::FromString, cal);
    dt.setDefaultLocale(*this);
    if (dt.parseFormat(format))
        dt.fromString(string, nullptr, &time);
#else
    Q_UNUSED(cal);
    Q_UNUSED(string);
    Q_UNUSED(format);
#endif
    return time;
}
#endif

/*!
    \since 4.4

    Parses the date string given in \a string and returns the
    date. See QDate::fromString() for information on the expressions
    that can be used with this function.

    This function searches month names and the names of the days of
    the week in the current locale.

    If the date could not be parsed, returns an invalid date.

    \sa dateFormat(), toTime(), toDateTime(), QDate::fromString()
*/
QDate QLocale::toDate(const QString &string, const QString &format) const
{
    return toDate(string, format, QCalendar());
}

/*!
    \since 5.14
    \overload
*/
QDate QLocale::toDate(const QString &string, const QString &format, QCalendar cal) const
{
    QDate date;
#if QT_CONFIG(datetimeparser)
    QDateTimeParser dt(QMetaType::QDate, QDateTimeParser::FromString, cal);
    dt.setDefaultLocale(*this);
    if (dt.parseFormat(format))
        dt.fromString(string, &date, nullptr);
#else
    Q_UNUSED(string);
    Q_UNUSED(format);
    Q_UNUSED(cal);
#endif
    return date;
}

/*!
    \since 4.4

    Parses the date/time string given in \a string and returns the
    time.  See QDateTime::fromString() for information on the expressions
    that can be used with this function.

    \note The month and day names used must be given in the user's local
    language.

    If the string could not be parsed, returns an invalid QDateTime.  If the
    string can be parsed and represents an invalid date-time (e.g. in a gap
    skipped by a time-zone transition), an invalid QDateTime is returned, whose
    toMSecsSinceEpoch() represents a near-by date-time that is valid. Passing
    that to fromMSecsSinceEpoch() will produce a valid date-time that isn't
    faithfully represented by the string parsed.

    \sa dateTimeFormat(), toTime(), toDate(), QDateTime::fromString()
*/
QDateTime QLocale::toDateTime(const QString &string, const QString &format) const
{
    return toDateTime(string, format, QCalendar());
}

/*!
    \since 5.14
    \overload
*/
QDateTime QLocale::toDateTime(const QString &string, const QString &format, QCalendar cal) const
{
#if QT_CONFIG(datetimeparser)
    QDateTime datetime;

    QDateTimeParser dt(QMetaType::QDateTime, QDateTimeParser::FromString, cal);
    dt.setDefaultLocale(*this);
    if (dt.parseFormat(format) && (dt.fromString(string, &datetime) || !datetime.isValid()))
        return datetime;
#else
    Q_UNUSED(string);
    Q_UNUSED(format);
    Q_UNUSED(cal);
#endif
    return QDateTime();
}
#endif // datestring

/*!
    \since 4.1

    Returns the decimal point character of this locale.

    \note This function shall change to return a QString instead of QChar in
    Qt6. Callers are encouraged to exploit the QString(QChar) constructor to
    convert early in preparation for this.

    \sa groupSeparator(), toString()
*/
QChar QLocale::decimalPoint() const
{
    return d->decimal();
}

/*!
    \since 4.1

    Returns the group separator character of this locale.

    \note This function shall change to return a QString instead of QChar in
    Qt6. Callers are encouraged to exploit the QString(QChar) constructor to
    convert early in preparation for this.

    \sa decimalPoint(), toString()
*/
QChar QLocale::groupSeparator() const
{
    return d->group();
}

/*!
    \since 4.1

    Returns the percent character of this locale.

    \note This function shall change to return a QString instead of QChar in
    Qt6. Callers are encouraged to exploit the QString(QChar) constructor to
    convert early in preparation for this.

    \sa toString()
*/
QChar QLocale::percent() const
{
    return d->percent();
}

/*!
    \since 4.1

    Returns the zero digit character of this locale.

    \note This function shall change to return a QString instead of QChar in
    Qt6. Callers are encouraged to exploit the QString(QChar) constructor to
    convert early in preparation for this.

    \sa toString()
*/
QChar QLocale::zeroDigit() const
{
    return d->zero();
}

/*!
    \since 4.1

    Returns the negative sign character of this locale.

    \note This function shall change to return a QString instead of QChar in
    Qt6. Callers are encouraged to exploit the QString(QChar) constructor to
    convert early in preparation for this.

    \sa positiveSign(), toString()
*/
QChar QLocale::negativeSign() const
{
    return d->minus();
}

/*!
    \since 4.5

    Returns the positive sign character of this locale.

    \note This function shall change to return a QString instead of QChar in
    Qt6. Callers are encouraged to exploit the QString(QChar) constructor to
    convert early in preparation for this.

    \sa negativeSign(), toString()
*/
QChar QLocale::positiveSign() const
{
    return d->plus();
}

/*!
    \since 4.1

    Returns the exponential character of this locale, used to separate exponent
    from mantissa in some floating-point numeric representations.

    \note This function shall change to return a QString instead of QChar in
    Qt6. Callers are encouraged to exploit the QString(QChar) constructor to
    convert early in preparation for this.

    \sa toString(double, char, int)
*/
QChar QLocale::exponential() const
{
    return d->exponential();
}

static bool qIsUpper(char c)
{
    return c >= 'A' && c <= 'Z';
}

static char qToLower(char c)
{
    if (c >= 'A' && c <= 'Z')
        return c - 'A' + 'a';
    else
        return c;
}

/*!
    \overload

    \a f and \a prec have the same meaning as in QString::number(double, char, int).

    \sa toDouble(), numberOptions(), exponential(), decimalPoint(), zeroDigit(), positiveSign(), percent()
*/

QString QLocale::toString(double i, char f, int prec) const
{
    QLocaleData::DoubleForm form = QLocaleData::DFDecimal;
    uint flags = 0;

    if (qIsUpper(f))
        flags = QLocaleData::CapitalEorX;
    f = qToLower(f);

    switch (f) {
        case 'f':
            form = QLocaleData::DFDecimal;
            break;
        case 'e':
            form = QLocaleData::DFExponent;
            break;
        case 'g':
            form = QLocaleData::DFSignificantDigits;
            break;
        default:
            break;
    }

    if (!(d->m_numberOptions & OmitGroupSeparator))
        flags |= QLocaleData::ThousandsGroup;
    if (!(d->m_numberOptions & OmitLeadingZeroInExponent))
        flags |= QLocaleData::ZeroPadExponent;
    if (d->m_numberOptions & IncludeTrailingZeroesAfterDot)
        flags |= QLocaleData::AddTrailingZeroes;
    return d->m_data->doubleToString(i, prec, form, -1, flags);
}

/*!
    \fn QLocale QLocale::c()

    Returns a QLocale object initialized to the "C" locale.

    This locale is based on en_US but with various quirks of its own, such as
    simplified number formatting and its own date formatting. It implements the
    POSIX standards that describe the behavior of standard library functions of
    the "C" programming language.

    Among other things, this means its collation order is based on the ASCII
    values of letters, so that (for case-sensitive sorting) all upper-case
    letters sort before any lower-case one (rather than each letter's upper- and
    lower-case forms sorting adjacent to one another, before the next letter's
    two forms).

    \sa system()
*/

/*!
    Returns a QLocale object initialized to the system locale.

    On Windows and Mac, this locale will use the decimal/grouping characters and
    date/time formats specified in the system configuration panel.

    \sa c()
*/

QLocale QLocale::system()
{
    QT_PREPEND_NAMESPACE(systemData)(); // trigger updating of the system data if necessary
    if (systemLocalePrivate.isDestroyed())
        return QLocale(QLocale::C);
    return QLocale(*systemLocalePrivate->data());
}


/*!
    \since 4.8

    Returns a list of valid locale objects that match the given \a language, \a
    script and \a country.

    Getting a list of all locales:
    QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
                                                         QLocale::AnyCountry);

    Getting a list of locales suitable for Russia:
    QList<QLocale> locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
                                                      QLocale::Russia);
*/
QList<QLocale> QLocale::matchingLocales(QLocale::Language language,
                                        QLocale::Script script,
                                        QLocale::Country country)
{
    if (uint(language) > QLocale::LastLanguage || uint(script) > QLocale::LastScript ||
            uint(country) > QLocale::LastCountry)
        return QList<QLocale>();

    if (language == QLocale::C)
        return QList<QLocale>() << QLocale(QLocale::C);

    QList<QLocale> result;
    if (language == QLocale::AnyLanguage && script == QLocale::AnyScript
        && country == QLocale::AnyCountry) {
        result.reserve(locale_data_size);
    }
    const QLocaleData *data = locale_data + locale_index[language];
    while ( (data != locale_data + locale_data_size)
            && (language == QLocale::AnyLanguage || data->m_language_id == uint(language))) {
        if ((script == QLocale::AnyScript || data->m_script_id == uint(script))
            && (country == QLocale::AnyCountry || data->m_country_id == uint(country))) {
            result.append(QLocale(*(data->m_language_id == C ? c_private()
                                    : QLocalePrivate::create(data))));
        }
        ++data;
    }
    return result;
}

/*!
    \obsolete
    \since 4.3

    Returns the list of countries that have entries for \a language in Qt's locale
    database. If the result is an empty list, then \a language is not represented in
    Qt's locale database.

    \sa matchingLocales()
*/
QList<QLocale::Country> QLocale::countriesForLanguage(Language language)
{
    QList<Country> result;
    if (language == C) {
        result << AnyCountry;
        return result;
    }

    unsigned language_id = language;
    const QLocaleData *data = locale_data + locale_index[language_id];
    while (data->m_language_id == language_id) {
        const QLocale::Country country = static_cast<Country>(data->m_country_id);
        if (!result.contains(country))
            result.append(country);
        ++data;
    }

    return result;
}

/*!
    \since 4.2

    Returns the localized name of \a month, in the format specified
    by \a type.

    For example, if the locale is \c en_US and \a month is 1,
    \l LongFormat will return \c January. \l ShortFormat \c Jan,
    and \l NarrowFormat \c J.

    \sa dayName(), standaloneMonthName()
*/
QString QLocale::monthName(int month, FormatType type) const
{
    return QCalendar().monthName(*this, month, QCalendar::Unspecified, type);
}

/*!
    \since 4.5

    Returns the localized name of \a month that is used as a
    standalone text, in the format specified by \a type.

    If the locale information doesn't specify the standalone month
    name then return value is the same as in monthName().

    \sa monthName(), standaloneDayName()
*/
QString QLocale::standaloneMonthName(int month, FormatType type) const
{
    return QCalendar().standaloneMonthName(*this, month, QCalendar::Unspecified, type);
}

/*!
    \since 4.2

    Returns the localized name of the \a day (where 1 represents
    Monday, 2 represents Tuesday and so on), in the format specified
    by \a type.

    For example, if the locale is \c en_US and \a day is 1,
    \l LongFormat will return \c Monday, \l ShortFormat \c Mon,
    and \l NarrowFormat \c M.

    \sa monthName(), standaloneDayName()
*/
QString QLocale::dayName(int day, FormatType type) const
{
    return QCalendar().weekDayName(*this, day, type);
}

/*!
    \since 4.5

    Returns the localized name of the \a day (where 1 represents
    Monday, 2 represents Tuesday and so on) that is used as a
    standalone text, in the format specified by \a type.

    If the locale information does not specify the standalone day
    name then return value is the same as in dayName().

    \sa dayName(), standaloneMonthName()
*/
QString QLocale::standaloneDayName(int day, FormatType type) const
{
    return QCalendar().standaloneWeekDayName(*this, day, type);
}

// Calendar look-up of month and day names:

/*!
  \internal
 */

static QString rawMonthName(const QCalendarLocale &localeData,
                            const ushort *monthsData, int month,
                            QLocale::FormatType type)
{
    quint32 idx, size;
    switch (type) {
    case QLocale::LongFormat:
       idx = localeData.m_long.index;
       size = localeData.m_long.size;
       break;
    case QLocale::ShortFormat:
       idx = localeData.m_short.index;
       size = localeData.m_short.size;
       break;
    case QLocale::NarrowFormat:
       idx = localeData.m_narrow.index;
       size = localeData.m_narrow.size;
       break;
    default:
       return QString();
    }
    return getLocaleListData(monthsData + idx, size, month - 1);
}

/*!
  \internal
 */

static QString rawStandaloneMonthName(const QCalendarLocale &localeData,
                                      const ushort *monthsData, int month,
                                      QLocale::FormatType type)
{
    quint32 idx, size;
    switch (type) {
    case QLocale::LongFormat:
        idx = localeData.m_standalone_long.index;
        size = localeData.m_standalone_long.size;
        break;
    case QLocale::ShortFormat:
        idx = localeData.m_standalone_short.index;
        size = localeData.m_standalone_short.size;
        break;
    case QLocale::NarrowFormat:
        idx = localeData.m_standalone_narrow.index;
        size = localeData.m_standalone_narrow.size;
        break;
    default:
        return QString();
    }
    QString name = getLocaleListData(monthsData + idx, size, month - 1);
    return name.isEmpty() ? rawMonthName(localeData, monthsData, month, type) : name;
}

/*!
  \internal
 */

static QString rawWeekDayName(const QLocaleData *data, const int day,
                              QLocale::FormatType type)
{
    quint32 idx, size;
    switch (type) {
    case QLocale::LongFormat:
        idx = data->m_long_day_names_idx;
        size = data->m_long_day_names_size;
        break;
    case QLocale::ShortFormat:
        idx = data->m_short_day_names_idx;
        size = data->m_short_day_names_size;
        break;
    case QLocale::NarrowFormat:
        idx = data->m_narrow_day_names_idx;
        size = data->m_narrow_day_names_size;
        break;
    default:
        return QString();
    }
    return getLocaleListData(days_data + idx, size, day == 7 ? 0 : day);
}

/*!
  \internal
 */

static QString rawStandaloneWeekDayName(const QLocaleData *data, const int day,
                                        QLocale::FormatType type)
{
    quint32 idx, size;
    switch (type) {
    case QLocale::LongFormat:
        idx = data->m_standalone_long_day_names_idx;
        size = data->m_standalone_long_day_names_size;
        break;
    case QLocale::ShortFormat:
        idx = data->m_standalone_short_day_names_idx;
        size = data->m_standalone_short_day_names_size;
        break;
    case QLocale::NarrowFormat:
        idx = data->m_standalone_narrow_day_names_idx;
        size = data->m_standalone_narrow_day_names_size;
        break;
    default:
        return QString();
    }
    QString name = getLocaleListData(days_data + idx, size, day == 7 ? 0 : day);
    if (name.isEmpty())
        return rawWeekDayName(data, day, type);
    return name;
}

// Refugees from qcalendar.cpp that need functions above:

QString QCalendarBackend::monthName(const QLocale &locale, int month, int,
                                    QLocale::FormatType format) const
{
    Q_ASSERT(month >= 1 && month <= maximumMonthsInYear());
    return rawMonthName(localeMonthIndexData()[locale.d->m_data_offset],
                        localeMonthData(), month, format);
}

QString QGregorianCalendar::monthName(const QLocale &locale, int month, int year,
                                      QLocale::FormatType format) const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (locale.d->m_data == systemData()) {
        Q_ASSERT(month >= 1 && month <= 12);
        QVariant res = systemLocale()->query(format == QLocale::LongFormat
                                             ? QSystemLocale::MonthNameLong
                                             : QSystemLocale::MonthNameShort,
                                             month);
        if (!res.isNull())
            return res.toString();
    }
#endif

    return QCalendarBackend::monthName(locale, month, year, format);
}

QString QCalendarBackend::standaloneMonthName(const QLocale &locale, int month, int,
                                              QLocale::FormatType format) const
{
    Q_ASSERT(month >= 1 && month <= maximumMonthsInYear());
    return rawStandaloneMonthName(localeMonthIndexData()[locale.d->m_data_offset],
                                  localeMonthData(), month, format);
}

QString QGregorianCalendar::standaloneMonthName(const QLocale &locale, int month, int year,
                                                QLocale::FormatType format) const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (locale.d->m_data == systemData()) {
        Q_ASSERT(month >= 1 && month <= 12);
        QVariant res = systemLocale()->query(format == QLocale::LongFormat
                                             ? QSystemLocale::StandaloneMonthNameLong
                                             : QSystemLocale::StandaloneMonthNameShort,
                                             month);
        if (!res.isNull())
            return res.toString();
    }
#endif

    return QCalendarBackend::standaloneMonthName(locale, month, year, format);
}

// Most calendars share the common week-day naming, modulo locale.
// Calendars that don't must override these methods.
QString QCalendarBackend::weekDayName(const QLocale &locale, int day,
                                      QLocale::FormatType format) const
{
    if (day < 1 || day > 7)
        return QString();

#ifndef QT_NO_SYSTEMLOCALE
    if (locale.d->m_data == systemData()) {
        QVariant res = systemLocale()->query(format == QLocale::LongFormat
                                             ? QSystemLocale::DayNameLong
                                             : QSystemLocale::DayNameShort,
                                             day);
        if (!res.isNull())
            return res.toString();
    }
#endif

    return rawWeekDayName(locale.d->m_data, day, format);
}

QString QCalendarBackend::standaloneWeekDayName(const QLocale &locale, int day,
                                                QLocale::FormatType format) const
{
    if (day < 1 || day > 7)
        return QString();

#ifndef QT_NO_SYSTEMLOCALE
    if (locale.d->m_data == systemData()) {
        QVariant res = systemLocale()->query(format == QLocale::LongFormat
                                             ? QSystemLocale::DayNameLong
                                             : QSystemLocale::DayNameShort,
                                             day);
        if (!res.isNull())
            return res.toString();
    }
#endif

    return rawStandaloneWeekDayName(locale.d->m_data, day, format);
}

// End of this block of qcalendar.cpp refugees.  (One more follows.)

/*!
    \since 4.8

    Returns the first day of the week according to the current locale.
*/
Qt::DayOfWeek QLocale::firstDayOfWeek() const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(QSystemLocale::FirstDayOfWeek, QVariant());
        if (!res.isNull())
            return static_cast<Qt::DayOfWeek>(res.toUInt());
    }
#endif
    return static_cast<Qt::DayOfWeek>(d->m_data->m_first_day_of_week);
}

QLocale::MeasurementSystem QLocalePrivate::measurementSystem() const
{
    for (int i = 0; i < ImperialMeasurementSystemsCount; ++i) {
        if (ImperialMeasurementSystems[i].languageId == m_data->m_language_id
            && ImperialMeasurementSystems[i].countryId == m_data->m_country_id) {
            return ImperialMeasurementSystems[i].system;
        }
    }
    return QLocale::MetricSystem;
}

/*!
    \since 4.8

    Returns a list of days that are considered weekdays according to the current locale.
*/
QList<Qt::DayOfWeek> QLocale::weekdays() const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(QSystemLocale::Weekdays, QVariant());
        if (!res.isNull())
            return static_cast<QList<Qt::DayOfWeek> >(qvariant_cast<QList<Qt::DayOfWeek> >(res));
    }
#endif
    QList<Qt::DayOfWeek> weekdays;
    quint16 weekendStart = d->m_data->m_weekend_start;
    quint16 weekendEnd = d->m_data->m_weekend_end;
    for (int day = Qt::Monday; day <= Qt::Sunday; day++) {
        if ((weekendEnd >= weekendStart && (day < weekendStart || day > weekendEnd)) ||
            (weekendEnd < weekendStart && (day > weekendEnd && day < weekendStart)))
                weekdays << static_cast<Qt::DayOfWeek>(day);
    }
    return weekdays;
}

/*!
    \since 4.4

    Returns the measurement system for the locale.
*/
QLocale::MeasurementSystem QLocale::measurementSystem() const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(QSystemLocale::MeasurementSystem, QVariant());
        if (!res.isNull())
            return MeasurementSystem(res.toInt());
    }
#endif

    return d->measurementSystem();
}

/*!
  \since 4.7

  Returns the text direction of the language.
*/
Qt::LayoutDirection QLocale::textDirection() const
{
    switch (script()) {
    case QLocale::AdlamScript:
    case QLocale::ArabicScript:
    case QLocale::AvestanScript:
    case QLocale::CypriotScript:
    case QLocale::HatranScript:
    case QLocale::HebrewScript:
    case QLocale::ImperialAramaicScript:
    case QLocale::InscriptionalPahlaviScript:
    case QLocale::InscriptionalParthianScript:
    case QLocale::KharoshthiScript:
    case QLocale::LydianScript:
    case QLocale::MandaeanScript:
    case QLocale::ManichaeanScript:
    case QLocale::MendeKikakuiScript:
    case QLocale::MeroiticCursiveScript:
    case QLocale::MeroiticScript:
    case QLocale::NabataeanScript:
    case QLocale::NkoScript:
    case QLocale::OldHungarianScript:
    case QLocale::OldNorthArabianScript:
    case QLocale::OldSouthArabianScript:
    case QLocale::OrkhonScript:
    case QLocale::PalmyreneScript:
    case QLocale::PhoenicianScript:
    case QLocale::PsalterPahlaviScript:
    case QLocale::SamaritanScript:
    case QLocale::SyriacScript:
    case QLocale::ThaanaScript:
        return Qt::RightToLeft;
    default:
        break;
    }
    return Qt::LeftToRight;
}

/*!
  \since 4.8

  Returns an uppercase copy of \a str.

  If Qt Core is using the ICU libraries, they will be used to perform
  the transformation according to the rules of the current locale.
  Otherwise the conversion may be done in a platform-dependent manner,
  with QString::toUpper() as a generic fallback.

  \note In some cases the uppercase form of a string may be longer than the
  original.

  \sa QString::toUpper()
*/
QString QLocale::toUpper(const QString &str) const
{
#if QT_CONFIG(icu)
    bool ok = true;
    QString result = QIcu::toUpper(d->bcp47Name('_'), str, &ok);
    if (ok)
        return result;
    // else fall through and use Qt's toUpper
#endif
    return str.toUpper();
}

/*!
  \since 4.8

  Returns a lowercase copy of \a str.

  If Qt Core is using the ICU libraries, they will be used to perform
  the transformation according to the rules of the current locale.
  Otherwise the conversion may be done in a platform-dependent manner,
  with QString::toLower() as a generic fallback.

  \sa QString::toLower()
*/
QString QLocale::toLower(const QString &str) const
{
#if QT_CONFIG(icu)
    bool ok = true;
    const QString result = QIcu::toLower(d->bcp47Name('_'), str, &ok);
    if (ok)
        return result;
    // else fall through and use Qt's toUpper
#endif
    return str.toLower();
}


/*!
    \since 4.5

    Returns the localized name of the "AM" suffix for times specified using
    the conventions of the 12-hour clock.

    \sa pmText()
*/
QString QLocale::amText() const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(QSystemLocale::AMText, QVariant());
        if (!res.isNull())
            return res.toString();
    }
#endif
    return getLocaleData(am_data + d->m_data->m_am_idx, d->m_data->m_am_size);
}

/*!
    \since 4.5

    Returns the localized name of the "PM" suffix for times specified using
    the conventions of the 12-hour clock.

    \sa amText()
*/
QString QLocale::pmText() const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(QSystemLocale::PMText, QVariant());
        if (!res.isNull())
            return res.toString();
    }
#endif
    return getLocaleData(pm_data + d->m_data->m_pm_idx, d->m_data->m_pm_size);
}

// Another intrusion from QCalendar, using some of the tools above:

QString QCalendarBackend::dateTimeToString(QStringView format, const QDateTime &datetime,
                                           const QDate &dateOnly, const QTime &timeOnly,
                                           const QLocale &locale) const
{
    QDate date;
    QTime time;
    bool formatDate = false;
    bool formatTime = false;
    if (datetime.isValid()) {
        date = datetime.date();
        time = datetime.time();
        formatDate = true;
        formatTime = true;
    } else if (dateOnly.isValid()) {
        date = dateOnly;
        formatDate = true;
    } else if (timeOnly.isValid()) {
        time = timeOnly;
        formatTime = true;
    } else {
        return QString();
    }

    QString result;
    int year = 0, month = 0, day = 0;
    if (formatDate) {
        const auto parts = julianDayToDate(date.toJulianDay());
        if (!parts.isValid())
            return QString();
        year = parts.year;
        month = parts.month;
        day = parts.day;
    }

    int i = 0;
    while (i < format.size()) {
        if (format.at(i).unicode() == '\'') {
            result.append(qt_readEscapedFormatString(format, &i));
            continue;
        }

        const QChar c = format.at(i);
        int repeat = qt_repeatCount(format.mid(i));
        bool used = false;
        if (formatDate) {
            switch (c.unicode()) {
            case 'y':
                used = true;
                if (repeat >= 4)
                    repeat = 4;
                else if (repeat >= 2)
                    repeat = 2;

                switch (repeat) {
                case 4: {
                    const int len = (year < 0) ? 5 : 4;
                    result.append(locale.d->m_data->longLongToString(year, -1, 10, len,
                                                                     QLocaleData::ZeroPadded));
                    break;
                }
                case 2:
                    result.append(locale.d->m_data->longLongToString(year % 100, -1, 10, 2,
                                                                     QLocaleData::ZeroPadded));
                    break;
                default:
                    repeat = 1;
                    result.append(c);
                    break;
                }
                break;

            case 'M':
                used = true;
                repeat = qMin(repeat, 4);
                switch (repeat) {
                case 1:
                    result.append(locale.d->m_data->longLongToString(month));
                    break;
                case 2:
                    result.append(locale.d->m_data->longLongToString(month, -1, 10, 2,
                                                                     QLocaleData::ZeroPadded));
                    break;
                case 3:
                    result.append(monthName(locale, month, year, QLocale::ShortFormat));
                    break;
                case 4:
                    result.append(monthName(locale, month, year, QLocale::LongFormat));
                    break;
                }
                break;

            case 'd':
                used = true;
                repeat = qMin(repeat, 4);
                switch (repeat) {
                case 1:
                    result.append(locale.d->m_data->longLongToString(day));
                    break;
                case 2:
                    result.append(locale.d->m_data->longLongToString(day, -1, 10, 2,
                                                                     QLocaleData::ZeroPadded));
                    break;
                case 3:
                    result.append(locale.dayName(
                                      dayOfWeek(date.toJulianDay()), QLocale::ShortFormat));
                    break;
                case 4:
                    result.append(locale.dayName(
                                      dayOfWeek(date.toJulianDay()), QLocale::LongFormat));
                    break;
                }
                break;

            default:
                break;
            }
        }
        if (!used && formatTime) {
            switch (c.unicode()) {
            case 'h': {
                used = true;
                repeat = qMin(repeat, 2);
                int hour = time.hour();
                if (timeFormatContainsAP(format)) {
                    if (hour > 12)
                        hour -= 12;
                    else if (hour == 0)
                        hour = 12;
                }

                switch (repeat) {
                case 1:
                    result.append(locale.d->m_data->longLongToString(hour));
                    break;
                case 2:
                    result.append(locale.d->m_data->longLongToString(hour, -1, 10, 2,
                                                                     QLocaleData::ZeroPadded));
                    break;
                }
                break;
            }
            case 'H':
                used = true;
                repeat = qMin(repeat, 2);
                switch (repeat) {
                case 1:
                    result.append(locale.d->m_data->longLongToString(time.hour()));
                    break;
                case 2:
                    result.append(locale.d->m_data->longLongToString(time.hour(), -1, 10, 2,
                                                                     QLocaleData::ZeroPadded));
                    break;
                }
                break;

            case 'm':
                used = true;
                repeat = qMin(repeat, 2);
                switch (repeat) {
                case 1:
                    result.append(locale.d->m_data->longLongToString(time.minute()));
                    break;
                case 2:
                    result.append(locale.d->m_data->longLongToString(time.minute(), -1, 10, 2,
                                                                     QLocaleData::ZeroPadded));
                    break;
                }
                break;

            case 's':
                used = true;
                repeat = qMin(repeat, 2);
                switch (repeat) {
                case 1:
                    result.append(locale.d->m_data->longLongToString(time.second()));
                    break;
                case 2:
                    result.append(locale.d->m_data->longLongToString(time.second(), -1, 10, 2,
                                                                     QLocaleData::ZeroPadded));
                    break;
                }
                break;

            case 'a':
                used = true;
                repeat = format.mid(i + 1).startsWith(QLatin1Char('p')) ? 2 : 1;
                result.append(time.hour() < 12 ? locale.amText().toLower()
                                               : locale.pmText().toLower());
                break;

            case 'A':
                used = true;
                repeat = format.mid(i + 1).startsWith(QLatin1Char('P')) ? 2 : 1;
                result.append(time.hour() < 12 ? locale.amText().toUpper()
                                                : locale.pmText().toUpper());
                break;

            case 'z':
                used = true;
                repeat = (repeat >= 3) ? 3 : 1;

                // note: the millisecond component is treated like the decimal part of the seconds
                // so ms == 2 is always printed as "002", but ms == 200 can be either "2" or "200"
                result.append(locale.d->m_data->longLongToString(time.msec(), -1, 10, 3,
                                                                 QLocaleData::ZeroPadded));
                if (repeat == 1) {
                    if (result.endsWith(locale.d->zero()))
                        result.chop(1);
                    if (result.endsWith(locale.d->zero()))
                        result.chop(1);
                }
                break;

            case 't':
                used = true;
                repeat = 1;
                // If we have a QDateTime use the time spec otherwise use the current system tzname
                result.append(formatDate ? datetime.timeZoneAbbreviation()
                                         : QDateTime::currentDateTime().timeZoneAbbreviation());
                break;

            default:
                break;
            }
        }
        if (!used)
            result.append(QString(repeat, c));
        i += repeat;
    }

    return result;
}

// End of QCalendar intrustions

QString QLocaleData::doubleToString(double d, int precision, DoubleForm form,
                                    int width, unsigned flags) const
{
    return doubleToString(m_zero, m_plus, m_minus, m_exponential, m_group, m_decimal,
                          d, precision, form, width, flags);
}

QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const QChar minus,
                                    const QChar exponential, const QChar group, const QChar decimal,
                                    double d, int precision, DoubleForm form, int width,
                                    unsigned flags)
{
    if (precision != QLocale::FloatingPointShortest && precision < 0)
        precision = 6;
    if (width < 0)
        width = 0;

    bool negative = false;
    QString num_str;

    int decpt;
    int bufSize = 1;
    if (precision == QLocale::FloatingPointShortest)
        bufSize += DoubleMaxSignificant;
    else if (form == DFDecimal) // optimize for numbers between -512k and 512k
        bufSize += ((d > (1 << 19) || d < -(1 << 19)) ? DoubleMaxDigitsBeforeDecimal : 6) +
                precision;
    else // Add extra digit due to different interpretations of precision. Also, "nan" has to fit.
        bufSize += qMax(2, precision) + 1;

    QVarLengthArray<char> buf(bufSize);
    int length;

    qt_doubleToAscii(d, form, precision, buf.data(), bufSize, negative, length, decpt);

    if (qstrncmp(buf.data(), "inf", 3) == 0 || qstrncmp(buf.data(), "nan", 3) == 0) {
        num_str = QString::fromLatin1(buf.data(), length);
    } else { // Handle normal numbers
        QString digits = QString::fromLatin1(buf.data(), length);

        if (_zero.unicode() != '0') {
            ushort z = _zero.unicode() - '0';
            for (int i = 0; i < digits.length(); ++i)
                reinterpret_cast<ushort *>(digits.data())[i] += z;
        }

        bool always_show_decpt = (flags & ForcePoint);
        switch (form) {
            case DFExponent: {
                num_str = exponentForm(_zero, decimal, exponential, group, plus, minus,
                                       digits, decpt, precision, PMDecimalDigits,
                                       always_show_decpt, flags & ZeroPadExponent);
                break;
            }
            case DFDecimal: {
                num_str = decimalForm(_zero, decimal, group,
                                      digits, decpt, precision, PMDecimalDigits,
                                      always_show_decpt, flags & ThousandsGroup);
                break;
            }
            case DFSignificantDigits: {
                PrecisionMode mode = (flags & AddTrailingZeroes) ?
                            PMSignificantDigits : PMChopTrailingZeros;

                int cutoff = precision < 0 ? 6 : precision;
                // Find out which representation is shorter
                if (precision == QLocale::FloatingPointShortest && decpt > 0) {
                    cutoff = digits.length() + 4; // 'e', '+'/'-', one digit exponent
                    if (decpt <= 10) {
                        ++cutoff;
                    } else {
                        cutoff += decpt > 100 ? 2 : 1;
                    }
                    if (!always_show_decpt && digits.length() > decpt)
                        ++cutoff; // decpt shown in exponent form, but not in decimal form
                }

                if (decpt != digits.length() && (decpt <= -4 || decpt > cutoff))
                    num_str = exponentForm(_zero, decimal, exponential, group, plus, minus,
                                           digits, decpt, precision, mode,
                                           always_show_decpt, flags & ZeroPadExponent);
                else
                    num_str = decimalForm(_zero, decimal, group,
                                          digits, decpt, precision, mode,
                                          always_show_decpt, flags & ThousandsGroup);
                break;
            }
        }

        if (isZero(d))
            negative = false;

        // pad with zeros. LeftAdjusted overrides this flag). Also, we don't
        // pad special numbers
        if (flags & QLocaleData::ZeroPadded && !(flags & QLocaleData::LeftAdjusted)) {
            int num_pad_chars = width - num_str.length();
            // leave space for the sign
            if (negative
                    || flags & QLocaleData::AlwaysShowSign
                    || flags & QLocaleData::BlankBeforePositive)
                --num_pad_chars;

            for (int i = 0; i < num_pad_chars; ++i)
                num_str.prepend(_zero);
        }
    }

    // add sign
    if (negative)
        num_str.prepend(minus);
    else if (flags & QLocaleData::AlwaysShowSign)
        num_str.prepend(plus);
    else if (flags & QLocaleData::BlankBeforePositive)
        num_str.prepend(QLatin1Char(' '));

    if (flags & QLocaleData::CapitalEorX)
        num_str = std::move(num_str).toUpper();

    return num_str;
}

QString QLocaleData::longLongToString(qlonglong l, int precision,
                                            int base, int width,
                                            unsigned flags) const
{
    return longLongToString(m_zero, m_group, m_plus, m_minus,
                                            l, precision, base, width, flags);
}

QString QLocaleData::longLongToString(const QChar zero, const QChar group,
                                         const QChar plus, const QChar minus,
                                         qlonglong l, int precision,
                                         int base, int width,
                                         unsigned flags)
{
    bool precision_not_specified = false;
    if (precision == -1) {
        precision_not_specified = true;
        precision = 1;
    }

    bool negative = l < 0;
    if (base != 10) {
        // these are not supported by sprintf for octal and hex
        flags &= ~AlwaysShowSign;
        flags &= ~BlankBeforePositive;
        negative = false; // neither are negative numbers
    }

QT_WARNING_PUSH
    /* "unary minus operator applied to unsigned type, result still unsigned" */
QT_WARNING_DISABLE_MSVC(4146)
    /*
      Negating std::numeric_limits<qlonglong>::min() hits undefined behavior, so
      taking an absolute value has to cast to unsigned to change sign.
     */
    QString num_str = qulltoa(negative ? -qulonglong(l) : qulonglong(l), base, zero);
QT_WARNING_POP

    uint cnt_thousand_sep = 0;
    if (base == 10){
        if (flags & ThousandsGroup) {
            for (int i = num_str.length() - 3; i > 0; i -= 3) {
                num_str.insert(i, group);
                ++cnt_thousand_sep;
            }
        } else if (flags & IndianNumberGrouping) {
            if (num_str.length() > 3)
                num_str.insert(num_str.length() - 3 , group);
            for (int i = num_str.length() - 6; i > 0; i -= 2) {
                num_str.insert(i, group);
                ++cnt_thousand_sep;
            }
        }
    }

    for (int i = num_str.length()/* - cnt_thousand_sep*/; i < precision; ++i)
        num_str.prepend(base == 10 ? zero : QChar::fromLatin1('0'));

    if ((flags & ShowBase)
            && base == 8
            && (num_str.isEmpty() || num_str[0].unicode() != QLatin1Char('0')))
        num_str.prepend(QLatin1Char('0'));

    // LeftAdjusted overrides this flag ZeroPadded. sprintf only padds
    // when precision is not specified in the format string
    bool zero_padded = flags & ZeroPadded
                        && !(flags & LeftAdjusted)
                        && precision_not_specified;

    if (zero_padded) {
        int num_pad_chars = width - num_str.length();

        // leave space for the sign
        if (negative
                || flags & AlwaysShowSign
                || flags & BlankBeforePositive)
            --num_pad_chars;

        // leave space for optional '0x' in hex form
        if (base == 16 && (flags & ShowBase))
            num_pad_chars -= 2;
        // leave space for optional '0b' in binary form
        else if (base == 2 && (flags & ShowBase))
            num_pad_chars -= 2;

        for (int i = 0; i < num_pad_chars; ++i)
            num_str.prepend(base == 10 ? zero : QChar::fromLatin1('0'));
    }

    if (flags & CapitalEorX)
        num_str = std::move(num_str).toUpper();

    if (base == 16 && (flags & ShowBase))
        num_str.prepend(QLatin1String(flags & UppercaseBase ? "0X" : "0x"));
    if (base == 2 && (flags & ShowBase))
        num_str.prepend(QLatin1String(flags & UppercaseBase ? "0B" : "0b"));

    // add sign
    if (negative)
        num_str.prepend(minus);
    else if (flags & AlwaysShowSign)
        num_str.prepend(plus);
    else if (flags & BlankBeforePositive)
        num_str.prepend(QLatin1Char(' '));

    return num_str;
}

QString QLocaleData::unsLongLongToString(qulonglong l, int precision,
                                            int base, int width,
                                            unsigned flags) const
{
    return unsLongLongToString(m_zero, m_group, m_plus,
                                               l, precision, base, width, flags);
}

QString QLocaleData::unsLongLongToString(const QChar zero, const QChar group,
                                            const QChar plus,
                                            qulonglong l, int precision,
                                            int base, int width,
                                            unsigned flags)
{
    const QChar resultZero = base == 10 ? zero : QChar(QLatin1Char('0'));
    QString num_str = l ? qulltoa(l, base, zero) : QString(resultZero);

    bool precision_not_specified = false;
    if (precision == -1) {
        if (flags == NoFlags)
            return num_str; // fast-path: nothing below applies, so we're done.

        precision_not_specified = true;
        precision = 1;
    }

    uint cnt_thousand_sep = 0;
    if (base == 10) {
        if (flags & ThousandsGroup) {
            for (int i = num_str.length() - 3; i > 0; i -=3) {
                num_str.insert(i, group);
                ++cnt_thousand_sep;
            }
        } else if (flags & IndianNumberGrouping) {
            if (num_str.length() > 3)
                num_str.insert(num_str.length() - 3 , group);
            for (int i = num_str.length() - 6; i > 0; i -= 2) {
                num_str.insert(i, group);
                ++cnt_thousand_sep;
            }
        }
    }

    const int zeroPadding = precision - num_str.length()/* + cnt_thousand_sep*/;
    if (zeroPadding > 0)
        num_str.prepend(QString(zeroPadding, resultZero));

    if ((flags & ShowBase)
            && base == 8
            && (num_str.isEmpty() || num_str.at(0).unicode() != QLatin1Char('0')))
        num_str.prepend(QLatin1Char('0'));

    // LeftAdjusted overrides this flag ZeroPadded. sprintf only padds
    // when precision is not specified in the format string
    bool zero_padded = flags & ZeroPadded
                        && !(flags & LeftAdjusted)
                        && precision_not_specified;

    if (zero_padded) {
        int num_pad_chars = width - num_str.length();

        // leave space for optional '0x' in hex form
        if (base == 16 && flags & ShowBase)
            num_pad_chars -= 2;
        // leave space for optional '0b' in binary form
        else if (base == 2 && flags & ShowBase)
            num_pad_chars -= 2;

        if (num_pad_chars > 0)
            num_str.prepend(QString(num_pad_chars, resultZero));
    }

    if (flags & CapitalEorX)
        num_str = std::move(num_str).toUpper();

    if (base == 16 && flags & ShowBase)
        num_str.prepend(QLatin1String(flags & UppercaseBase ? "0X" : "0x"));
    else if (base == 2 && flags & ShowBase)
        num_str.prepend(QLatin1String(flags & UppercaseBase ? "0B" : "0b"));

    // add sign
    if (flags & AlwaysShowSign)
        num_str.prepend(plus);
    else if (flags & BlankBeforePositive)
        num_str.prepend(QLatin1Char(' '));

    return num_str;
}

/*
    Converts a number in locale to its representation in the C locale.
    Only has to guarantee that a string that is a correct representation of
    a number will be converted. If junk is passed in, junk will be passed
    out and the error will be detected during the actual conversion to a
    number. We can't detect junk here, since we don't even know the base
    of the number.
*/
bool QLocaleData::numberToCLocale(QStringView s, QLocale::NumberOptions number_options,
                                  CharBuff *result) const
{
    const QChar *uc = s.data();
    auto l = s.size();
    decltype(l) idx = 0;

    // Skip whitespace
    while (idx < l && uc[idx].isSpace())
        ++idx;
    if (idx == l)
        return false;

    // Check trailing whitespace
    for (; idx < l; --l) {
        if (!uc[l - 1].isSpace())
            break;
    }

    int group_cnt = 0; // counts number of group chars
    int decpt_idx = -1;
    int last_separator_idx = -1;
    int start_of_digits_idx = -1;
    int exponent_idx = -1;

    while (idx < l) {
        const QChar in = uc[idx];

        char out = digitToCLocale(in);
        if (out == 0) {
            if (in == m_list)
                out = ';';
            else if (in == m_percent)
                out = '%';
            // for handling base-x numbers
            else if (in.unicode() >= 'A' && in.unicode() <= 'Z')
                out = in.toLower().toLatin1();
            else if (in.unicode() >= 'a' && in.unicode() <= 'z')
                out = in.toLatin1();
            else
                break;
        } else if (out == '.') {
            // Fail if more than one decimal point or point after e
            if (decpt_idx != -1 || exponent_idx != -1)
                return false;
            decpt_idx = idx;
        } else if (out == 'e' || out == 'E') {
            exponent_idx = idx;
        }

        if (number_options & QLocale::RejectLeadingZeroInExponent) {
            if (exponent_idx != -1 && out == '0' && idx < l - 1) {
                // After the exponent there can only be '+', '-' or digits.
                // If we find a '0' directly after some non-digit, then that is a leading zero.
                if (result->last() < '0' || result->last() > '9')
                    return false;
            }
        }

        if (number_options & QLocale::RejectTrailingZeroesAfterDot) {
            // If we've seen a decimal point and the last character after the exponent is 0, then
            // that is a trailing zero.
            if (decpt_idx >= 0 && idx == exponent_idx && result->last() == '0')
                    return false;
        }

        if (!(number_options & QLocale::RejectGroupSeparator)) {
            if (start_of_digits_idx == -1 && out >= '0' && out <= '9') {
                start_of_digits_idx = idx;
            } else if (out == ',') {
                // Don't allow group chars after the decimal point or exponent
                if (decpt_idx != -1 || exponent_idx != -1)
                    return false;

                // check distance from the last separator or from the beginning of the digits
                // ### FIXME: Some locales allow other groupings!
                // See https://en.wikipedia.org/wiki/Thousands_separator
                if (m_country_id == QLocale::India) {
                    if (last_separator_idx != -1 && idx - last_separator_idx != 3)
                        return false;
                } else if (last_separator_idx != -1 && idx - last_separator_idx != 4)
                    return false;
                if (last_separator_idx == -1
                    && (start_of_digits_idx == -1 || idx - start_of_digits_idx > 3)) {
                    return false;
                }

                last_separator_idx = idx;
                ++group_cnt;

                // don't add the group separator
                ++idx;
                continue;
            } else if (out == '.' || out == 'e' || out == 'E') {
                // check distance from the last separator
                // ### FIXME: Some locales allow other groupings!
                // See https://en.wikipedia.org/wiki/Thousands_separator
                if (last_separator_idx != -1 && idx - last_separator_idx != 4)
                    return false;

                // stop processing separators
                last_separator_idx = -1;
            }
        }

        result->append(out);

        ++idx;
    }

    if (!(number_options & QLocale::RejectGroupSeparator)) {
        // group separator post-processing
        // did we end in a separator?
        if (last_separator_idx + 1 == idx)
            return false;
        // were there enough digits since the last separator?
        if (last_separator_idx != -1 && idx - last_separator_idx != 4)
            return false;
    }

    if (number_options & QLocale::RejectTrailingZeroesAfterDot) {
        // In decimal form, the last character can be a trailing zero if we've seen a decpt.
        if (decpt_idx != -1 && exponent_idx == -1 && result->last() == '0')
            return false;
    }

    result->append('\0');
    return idx == l;
}

bool QLocaleData::validateChars(QStringView str, NumberMode numMode, QByteArray *buff,
                                int decDigits, QLocale::NumberOptions number_options) const
{
    buff->clear();
    buff->reserve(str.length());

    const bool scientific = numMode == DoubleScientificMode;
    bool lastWasE = false;
    bool lastWasDigit = false;
    int eCnt = 0;
    int decPointCnt = 0;
    bool dec = false;
    int decDigitCnt = 0;

    for (qsizetype i = 0; i < str.size(); ++i) {
        char c = digitToCLocale(str.at(i));

        if (c >= '0' && c <= '9') {
            if (numMode != IntegerMode) {
                // If a double has too many digits after decpt, it shall be Invalid.
                if (dec && decDigits != -1 && decDigits < ++decDigitCnt)
                    return false;
            }

            // The only non-digit character after the 'e' can be '+' or '-'.
            // If a zero is directly after that, then the exponent is zero-padded.
            if ((number_options & QLocale::RejectLeadingZeroInExponent)
                && c == '0' && eCnt > 0 && !lastWasDigit) {
                return false;
            }

            lastWasDigit = true;
        } else {
            switch (c) {
                case '.':
                    if (numMode == IntegerMode) {
                        // If an integer has a decimal point, it shall be Invalid.
                        return false;
                    } else {
                        // If a double has more than one decimal point, it shall be Invalid.
                        if (++decPointCnt > 1)
                            return false;
#if 0
                        // If a double with no decimal digits has a decimal point, it shall be
                        // Invalid.
                        if (decDigits == 0)
                            return false;
#endif                  // On second thoughts, it shall be Valid.

                        dec = true;
                    }
                    break;

                case '+':
                case '-':
                    if (scientific) {
                        // If a scientific has a sign that's not at the beginning or after
                        // an 'e', it shall be Invalid.
                        if (i != 0 && !lastWasE)
                            return false;
                    } else {
                        // If a non-scientific has a sign that's not at the beginning,
                        // it shall be Invalid.
                        if (i != 0)
                            return false;
                    }
                    break;

                case ',':
                    //it can only be placed after a digit which is before the decimal point
                    if ((number_options & QLocale::RejectGroupSeparator) || !lastWasDigit ||
                            decPointCnt > 0)
                        return false;
                    break;

                case 'e':
                    if (scientific) {
                        // If a scientific has more than one 'e', it shall be Invalid.
                        if (++eCnt > 1)
                            return false;
                        dec = false;
                    } else {
                        // If a non-scientific has an 'e', it shall be Invalid.
                        return false;
                    }
                    break;

                default:
                    // If it's not a valid digit, it shall be Invalid.
                    return false;
            }
            lastWasDigit = false;
        }

        lastWasE = c == 'e';
        if (c != ',')
            buff->append(c);
    }

    return true;
}

double QLocaleData::stringToDouble(QStringView str, bool *ok,
                                   QLocale::NumberOptions number_options) const
{
    CharBuff buff;
    if (!numberToCLocale(str, number_options, &buff)) {
        if (ok != nullptr)
            *ok = false;
        return 0.0;
    }
    int processed = 0;
    bool nonNullOk = false;
    double d = qt_asciiToDouble(buff.constData(), buff.length() - 1, nonNullOk, processed);
    if (ok != nullptr)
        *ok = nonNullOk;
    return d;
}

qlonglong QLocaleData::stringToLongLong(QStringView str, int base, bool *ok,
                                        QLocale::NumberOptions number_options) const
{
    CharBuff buff;
    if (!numberToCLocale(str, number_options, &buff)) {
        if (ok != nullptr)
            *ok = false;
        return 0;
    }

    return bytearrayToLongLong(buff.constData(), base, ok);
}

qulonglong QLocaleData::stringToUnsLongLong(QStringView str, int base, bool *ok,
                                            QLocale::NumberOptions number_options) const
{
    CharBuff buff;
    if (!numberToCLocale(str, number_options, &buff)) {
        if (ok != nullptr)
            *ok = false;
        return 0;
    }

    return bytearrayToUnsLongLong(buff.constData(), base, ok);
}

qlonglong QLocaleData::bytearrayToLongLong(const char *num, int base, bool *ok)
{
    bool _ok;
    const char *endptr;

    if (*num == '\0') {
        if (ok != nullptr)
            *ok = false;
        return 0;
    }

    qlonglong l = qstrtoll(num, &endptr, base, &_ok);

    if (!_ok) {
        if (ok != nullptr)
            *ok = false;
        return 0;
    }

    if (*endptr != '\0') {
        while (ascii_isspace(*endptr))
            ++endptr;
    }

    if (*endptr != '\0') {
        // we stopped at a non-digit character after converting some digits
        if (ok != nullptr)
            *ok = false;
        return 0;
    }

    if (ok != nullptr)
        *ok = true;
    return l;
}

qulonglong QLocaleData::bytearrayToUnsLongLong(const char *num, int base, bool *ok)
{
    bool _ok;
    const char *endptr;
    qulonglong l = qstrtoull(num, &endptr, base, &_ok);

    if (!_ok) {
        if (ok != nullptr)
            *ok = false;
        return 0;
    }

    if (*endptr != '\0') {
        while (ascii_isspace(*endptr))
            ++endptr;
    }

    if (*endptr != '\0') {
        if (ok != nullptr)
            *ok = false;
        return 0;
    }

    if (ok != nullptr)
        *ok = true;
    return l;
}

/*!
    \since 4.8

    \enum QLocale::CurrencySymbolFormat

    Specifies the format of the currency symbol.

    \value CurrencyIsoCode a ISO-4217 code of the currency.
    \value CurrencySymbol a currency symbol.
    \value CurrencyDisplayName a user readable name of the currency.
*/

/*!
    \since 4.8
    Returns a currency symbol according to the \a format.
*/
QString QLocale::currencySymbol(QLocale::CurrencySymbolFormat format) const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(QSystemLocale::CurrencySymbol, format);
        if (!res.isNull())
            return res.toString();
    }
#endif
    quint32 idx, size;
    switch (format) {
    case CurrencySymbol:
        idx = d->m_data->m_currency_symbol_idx;
        size = d->m_data->m_currency_symbol_size;
        return getLocaleData(currency_symbol_data + idx, size);
    case CurrencyDisplayName:
        idx = d->m_data->m_currency_display_name_idx;
        size = d->m_data->m_currency_display_name_size;
        return getLocaleListData(currency_display_name_data + idx, size, 0);
    case CurrencyIsoCode: {
        int len = 0;
        const QLocaleData *data = this->d->m_data;
        for (; len < 3; ++len)
            if (!data->m_currency_iso_code[len])
                break;
        return len ? QString::fromLatin1(data->m_currency_iso_code, len) : QString();
    }
    }
    return QString();
}

/*!
    \since 4.8

    Returns a localized string representation of \a value as a currency.
    If the \a symbol is provided it is used instead of the default currency symbol.

    \sa currencySymbol()
*/
QString QLocale::toCurrencyString(qlonglong value, const QString &symbol) const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QSystemLocale::CurrencyToStringArgument arg(value, symbol);
        QVariant res =
            systemLocale()->query(QSystemLocale::CurrencyToString, QVariant::fromValue(arg));
        if (!res.isNull())
            return res.toString();
    }
#endif
    const QLocalePrivate *d = this->d;
    quint8 idx = d->m_data->m_currency_format_idx;
    quint8 size = d->m_data->m_currency_format_size;
    if (d->m_data->m_currency_negative_format_size && value < 0) {
        idx = d->m_data->m_currency_negative_format_idx;
        size = d->m_data->m_currency_negative_format_size;
        value = -value;
    }
    QString str = toString(value);
    QString sym = symbol.isNull() ? currencySymbol() : symbol;
    if (sym.isEmpty())
        sym = currencySymbol(QLocale::CurrencyIsoCode);
    QString format = getLocaleData(currency_format_data + idx, size);
    return format.arg(str, sym);
}

/*!
    \since 4.8
    \overload
*/
QString QLocale::toCurrencyString(qulonglong value, const QString &symbol) const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QSystemLocale::CurrencyToStringArgument arg(value, symbol);
        QVariant res =
            systemLocale()->query(QSystemLocale::CurrencyToString, QVariant::fromValue(arg));
        if (!res.isNull())
            return res.toString();
    }
#endif
    const QLocaleData *data = this->d->m_data;
    quint8 idx = data->m_currency_format_idx;
    quint8 size = data->m_currency_format_size;
    QString str = toString(value);
    QString sym = symbol.isNull() ? currencySymbol() : symbol;
    if (sym.isEmpty())
        sym = currencySymbol(QLocale::CurrencyIsoCode);
    QString format = getLocaleData(currency_format_data + idx, size);
    return format.arg(str, sym);
}

#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
/*!
    \since 4.8
    \overload
*/
QString QLocale::toCurrencyString(double value, const QString &symbol) const
{
    return toCurrencyString(value, symbol, d->m_data->m_currency_digits);
}
#endif

/*!
    \since 5.7
    \overload toCurrencyString()

    Returns a localized string representation of \a value as a currency.
    If the \a symbol is provided it is used instead of the default currency symbol.
    If the \a precision is provided it is used to set the precision of the currency value.

    \sa currencySymbol()
 */
QString QLocale::toCurrencyString(double value, const QString &symbol, int precision) const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QSystemLocale::CurrencyToStringArgument arg(value, symbol);
        QVariant res =
            systemLocale()->query(QSystemLocale::CurrencyToString, QVariant::fromValue(arg));
        if (!res.isNull())
            return res.toString();
    }
#endif
    const QLocaleData *data = this->d->m_data;
    quint8 idx = data->m_currency_format_idx;
    quint8 size = data->m_currency_format_size;
    if (data->m_currency_negative_format_size && value < 0) {
        idx = data->m_currency_negative_format_idx;
        size = data->m_currency_negative_format_size;
        value = -value;
    }
    QString str = toString(value, 'f', precision == -1 ? d->m_data->m_currency_digits : precision);
    QString sym = symbol.isNull() ? currencySymbol() : symbol;
    if (sym.isEmpty())
        sym = currencySymbol(QLocale::CurrencyIsoCode);
    QString format = getLocaleData(currency_format_data + idx, size);
    return format.arg(str, sym);
}

/*!
  \fn QString QLocale::toCurrencyString(float i, const QString &symbol) const
  \fn QString QLocale::toCurrencyString(float i, const QString &symbol, int precision) const
  \overload toCurrencyString()
*/

/*!
    \since 5.10

    \enum QLocale::DataSizeFormat

    Specifies the format for representation of data quantities.

    \omitvalue DataSizeBase1000
    \omitvalue DataSizeSIQuantifiers
    \value DataSizeIecFormat            format using base 1024 and IEC prefixes: KiB, MiB, GiB, ...
    \value DataSizeTraditionalFormat    format using base 1024 and SI prefixes: kB, MB, GB, ...
    \value DataSizeSIFormat             format using base 1000 and SI prefixes: kB, MB, GB, ...

    \sa formattedDataSize()
*/

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
/*!
    \obsolete

    Use the const version instead.
*/
QString QLocale::formattedDataSize(qint64 bytes, int precision, DataSizeFormats format)
{
    const auto *that = this;
    return that->formattedDataSize(bytes, precision, format);
}
#endif

/*!
    \since 5.10

    Converts a size in bytes to a human-readable localized string, comprising a
    number and a quantified unit. The quantifier is chosen such that the number
    is at least one, and as small as possible. For example if \a bytes is
    16384, \a precision is 2, and \a format is \l DataSizeIecFormat (the
    default), this function returns "16.00 KiB"; for 1330409069609 bytes it
    returns "1.21 GiB"; and so on. If \a format is \l DataSizeIecFormat or
    \l DataSizeTraditionalFormat, the given number of bytes is divided by a
    power of 1024, with result less than 1024; for \l DataSizeSIFormat, it is
    divided by a power of 1000, with result less than 1000.
    \c DataSizeIecFormat uses the new IEC standard quantifiers Ki, Mi and so on,
    whereas \c DataSizeSIFormat uses the older SI quantifiers k, M, etc., and
    \c DataSizeTraditionalFormat abuses them.
*/
QString QLocale::formattedDataSize(qint64 bytes, int precision, DataSizeFormats format) const
{
    int power, base = 1000;
    if (!bytes) {
        power = 0;
    } else if (format & DataSizeBase1000) {
        power = int(std::log10(QtPrivate::qUnsignedAbs(bytes)) / 3);
    } else { // Compute log2(bytes) / 10:
        power = int((63 - qCountLeadingZeroBits(QtPrivate::qUnsignedAbs(bytes))) / 10);
        base = 1024;
    }
    // Only go to doubles if we'll be using a quantifier:
    const QString number = power
        ? toString(bytes / std::pow(double(base), power), 'f', qMin(precision, 3 * power))
        : toString(bytes);

    // We don't support sizes in units larger than exbibytes because
    // the number of bytes would not fit into qint64.
    Q_ASSERT(power <= 6 && power >= 0);
    QString unit;
    if (power > 0) {
        quint16 index, size;
        if (format & DataSizeSIQuantifiers) {
            index = d->m_data->m_byte_si_quantified_idx;
            size = d->m_data->m_byte_si_quantified_size;
        } else {
            index = d->m_data->m_byte_iec_quantified_idx;
            size = d->m_data->m_byte_iec_quantified_size;
        }
        unit = getLocaleListData(byte_unit_data + index, size, power - 1);
    } else {
        unit = getLocaleData(byte_unit_data + d->m_data->m_byte_idx, d->m_data->m_byte_size);
    }

    return number + QLatin1Char(' ') + unit;
}

/*!
    \since 4.8

    Returns an ordered list of locale names for translation purposes in
    preference order (like "en-Latn-US", "en-US", "en").

    The return value represents locale names that the user expects to see the
    UI translation in.

    Most like you do not need to use this function directly, but just pass the
    QLocale object to the QTranslator::load() function.

    The first item in the list is the most preferred one.

    \sa QTranslator, bcp47Name()
*/
QStringList QLocale::uiLanguages() const
{
    QStringList uiLanguages;
    QVector<QLocale> locales;
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(QSystemLocale::UILanguages, QVariant());
        if (!res.isNull()) {
            uiLanguages = res.toStringList();
            // ... but we need to include likely-adjusted forms of each of those, too:
            for (const auto &entry : qAsConst(uiLanguages))
                locales.append(QLocale(entry));
        }
        if (locales.isEmpty())
            locales.append(systemLocale()->fallbackUiLocale());
        // If the system locale (isn't C and) didn't include itself in the list,
        // or as fallback, presume to know better than it and put its name
        // first. (Known issue, QTBUG-104930, on some macOS versions when in
        // locale en_DE.) Our translation system might have a translation for a
        // locale the platform doesn't believe in.
        const QString name = bcp47Name();
        if (!name.isEmpty() && language() != C && !uiLanguages.contains(name)) {
            // That uses contains(name) as a cheap pre-test, but there may be an
            // entry that matches this on purging likely subtags.
            QLocaleId mine =
                { d->m_data->m_language_id, d->m_data->m_script_id, d->m_data->m_country_id };
            mine = mine.withLikelySubtagsRemoved();
            const auto isMine = [mine](const QString &entry) {
                QLocale::Language lang;
                QLocale::Script script;
                QLocale::Country cntry;
                QLocalePrivate::getLangAndCountry(entry, lang, script, cntry);
                QLocaleId id = { ushort(lang), ushort(script), ushort(cntry) };
                return id.withLikelySubtagsRemoved() == mine;
            };
            if (std::none_of(uiLanguages.constBegin(), uiLanguages.constEnd(), isMine)) {
                locales.prepend(*this);
                uiLanguages.prepend(name);
            }
        }
    } else
#endif
    {
        locales.append(*this);
    }
    for (int i = locales.size(); i-- > 0; ) {
        const QLocale &locale = locales.at(i);
        int j;
        QByteArray prior;
        if (i < uiLanguages.size()) {
            // Adding likely-adjusted forms to system locale's list.
            // Name the locale is derived from:
            prior = uiLanguages.at(i).toLatin1();
            // Insert just after the entry we're supplementing:
            j = i + 1;
        } else {
            // Plain locale, not system locale; just append.
            j = uiLanguages.size();
        }
        const auto data = locale.d->m_data;

        QLocaleId id
            = QLocaleId::fromIds(data->m_language_id, data->m_script_id, data->m_country_id);
        const QLocaleId max = id.withLikelySubtagsAdded();
        const QLocaleId min = max.withLikelySubtagsRemoved();
        id.script_id = 0; // For re-use as script-less variant.

        // Include version with all likely sub-tags (last) if distinct from the rest:
        if (max != min && max != id && max.name() != prior)
            uiLanguages.insert(j, QString::fromLatin1(max.name()));

        // Include scriptless version if likely-equivalent and distinct:
        if (data->m_script_id && id != min && id.name() != prior
            && id.withLikelySubtagsAdded() == max) {
            uiLanguages.insert(j, QString::fromLatin1(id.name()));
        }

        // Include minimal version (first) unless it's what our locale is derived from:
        if (min.name() != prior)
            uiLanguages.insert(j, QString::fromLatin1(min.name()));
    }
    return uiLanguages;
}

/*!
  \since 5.13

  Returns the locale to use for collation.

  The result is usually this locale; however, the system locale (which is
  commonly the default locale) will return the system collation locale.
  The result is suitable for passing to QCollator's constructor.

  \sa QCollator
*/
QLocale QLocale::collation() const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QString res = systemLocale()->query(QSystemLocale::Collation, QVariant()).toString();
        if (!res.isEmpty())
            return QLocale(res);
    }
#endif
    return *this;
}

/*!
    \since 4.8

    Returns a native name of the language for the locale. For example
    "Schwiizertüütsch" for Swiss-German locale.

    \sa nativeCountryName(), languageToString()
*/
QString QLocale::nativeLanguageName() const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(QSystemLocale::NativeLanguageName, QVariant());
        if (!res.isNull())
            return res.toString();
    }
#endif
    return getLocaleData(endonyms_data + d->m_data->m_language_endonym_idx,
                         d->m_data->m_language_endonym_size);
}

/*!
    \since 4.8

    Returns a native name of the country for the locale. For example
    "España" for Spanish/Spain locale.

    \sa nativeLanguageName(), countryToString()
*/
QString QLocale::nativeCountryName() const
{
#ifndef QT_NO_SYSTEMLOCALE
    if (d->m_data == systemData()) {
        QVariant res = systemLocale()->query(QSystemLocale::NativeCountryName, QVariant());
        if (!res.isNull())
            return res.toString();
    }
#endif
    return getLocaleData(endonyms_data + d->m_data->m_country_endonym_idx,
                         d->m_data->m_country_endonym_size);
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QLocale &l)
{
    QDebugStateSaver saver(dbg);
    dbg.nospace().noquote()
        << "QLocale(" << QLocale::languageToString(l.language())
        << ", " << QLocale::scriptToString(l.script())
        << ", " << QLocale::countryToString(l.country()) << ')';
    return dbg;
}
#endif
QT_END_NAMESPACE

#ifndef QT_NO_QOBJECT
#include "moc_qlocale.cpp"
#endif
