1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2021 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qglobal.h"
6
7#if (defined(QT_STATIC) || defined(QT_BOOTSTRAPPED)) && defined(Q_CC_GNU_ONLY) && Q_CC_GNU >= 1000
8QT_WARNING_DISABLE_GCC("-Wfree-nonheap-object") // false positive tracking
9#endif
10
11#if defined(Q_OS_MACOS)
12# include "private/qcore_mac_p.h"
13# include <CoreFoundation/CoreFoundation.h>
14#endif
15
16#include "qplatformdefs.h"
17
18#include "qdatastream.h"
19#include "qdebug.h"
20#include "qhashfunctions.h"
21#include "qstring.h"
22#include "qlocale.h"
23#include "qlocale_p.h"
24#include "qlocale_tools_p.h"
25#include <private/qtools_p.h>
26#if QT_CONFIG(datetimeparser)
27#include "private/qdatetimeparser_p.h"
28#endif
29#include "qnamespace.h"
30#include "qdatetime.h"
31#include "qstringlist.h"
32#include "qvariant.h"
33#include "qvarlengtharray.h"
34#include "qstringbuilder.h"
35#if QT_CONFIG(timezone)
36# include "qtimezone.h"
37#endif
38#include "private/qnumeric_p.h"
39#include "private/qtools_p.h"
40#include <cmath>
41#ifndef QT_NO_SYSTEMLOCALE
42# include "qmutex.h"
43#endif
44#ifdef Q_OS_WIN
45# include <qt_windows.h>
46# include <time.h>
47#endif
48
49#include "private/qcalendarbackend_p.h"
50#include "private/qgregoriancalendar_p.h"
51#include "qcalendar.h"
52
53#include <q20iterator.h>
54
55QT_BEGIN_NAMESPACE
56
57QT_IMPL_METATYPE_EXTERN_TAGGED(QList<Qt::DayOfWeek>, QList_Qt__DayOfWeek)
58#ifndef QT_NO_SYSTEMLOCALE
59QT_IMPL_METATYPE_EXTERN_TAGGED(QSystemLocale::CurrencyToStringArgument,
60 QSystemLocale__CurrencyToStringArgument)
61#endif
62
63using namespace Qt::StringLiterals;
64using namespace QtMiscUtils;
65
66#ifndef QT_NO_SYSTEMLOCALE
67Q_CONSTINIT static QSystemLocale *_systemLocale = nullptr;
68Q_CONSTINIT static QLocaleData systemLocaleData = {};
69#endif
70
71static_assert(ascii_isspace(c: ' '));
72static_assert(ascii_isspace(c: '\t'));
73static_assert(ascii_isspace(c: '\n'));
74static_assert(ascii_isspace(c: '\v'));
75static_assert(ascii_isspace(c: '\f'));
76static_assert(ascii_isspace(c: '\r'));
77static_assert(!ascii_isspace(c: '\0'));
78static_assert(!ascii_isspace(c: '\a'));
79static_assert(!ascii_isspace(c: 'a'));
80static_assert(!ascii_isspace(c: '\177'));
81static_assert(!ascii_isspace(c: uchar('\200')));
82static_assert(!ascii_isspace(c: uchar('\xA0'))); // NBSP (is a space but Latin 1, not ASCII)
83static_assert(!ascii_isspace(c: uchar('\377')));
84
85/******************************************************************************
86** Helpers for accessing Qt locale database
87*/
88
89QT_BEGIN_INCLUDE_NAMESPACE
90#include "qlocale_data_p.h"
91QT_END_INCLUDE_NAMESPACE
92
93QLocale::Language QLocalePrivate::codeToLanguage(QStringView code,
94 QLocale::LanguageCodeTypes codeTypes) noexcept
95{
96 const auto len = code.size();
97 if (len != 2 && len != 3)
98 return QLocale::AnyLanguage;
99
100 const char16_t uc1 = code[0].toLower().unicode();
101 const char16_t uc2 = code[1].toLower().unicode();
102 const char16_t uc3 = len > 2 ? code[2].toLower().unicode() : 0;
103
104 // All language codes are ASCII.
105 if (uc1 > 0x7F || uc2 > 0x7F || uc3 > 0x7F)
106 return QLocale::AnyLanguage;
107
108 const AlphaCode codeBuf = { char(uc1), char(uc2), char(uc3) };
109
110 auto searchCode = [codeBuf](auto f) {
111 return std::find_if(languageCodeList.begin(), languageCodeList.end(),
112 [=](const LanguageCodeEntry &i) { return f(i) == codeBuf; });
113 };
114
115 if (codeTypes.testFlag(flag: QLocale::ISO639Part1) && uc3 == 0) {
116 auto i = searchCode([](const LanguageCodeEntry &i) { return i.part1; });
117 if (i != languageCodeList.end())
118 return QLocale::Language(std::distance(first: languageCodeList.begin(), last: i));
119 }
120
121 if (uc3 != 0) {
122 if (codeTypes.testFlag(flag: QLocale::ISO639Part2B)) {
123 auto i = searchCode([](const LanguageCodeEntry &i) { return i.part2B; });
124 if (i != languageCodeList.end())
125 return QLocale::Language(std::distance(first: languageCodeList.begin(), last: i));
126 }
127
128 // Optimization: Part 2T code if present is always the same as Part 3 code.
129 // This is asserted in iso639_3.LanguageCodeData.
130 if (codeTypes.testFlag(flag: QLocale::ISO639Part2T)
131 && !codeTypes.testFlag(flag: QLocale::ISO639Part3)) {
132 auto i = searchCode([](const LanguageCodeEntry &i) { return i.part2T; });
133 if (i != languageCodeList.end())
134 return QLocale::Language(std::distance(first: languageCodeList.begin(), last: i));
135 }
136
137 if (codeTypes.testFlag(flag: QLocale::ISO639Part3)) {
138 auto i = searchCode([](const LanguageCodeEntry &i) { return i.part3; });
139 if (i != languageCodeList.end())
140 return QLocale::Language(std::distance(first: languageCodeList.begin(), last: i));
141 }
142 }
143
144 if (codeTypes.testFlag(flag: QLocale::LegacyLanguageCode) && uc3 == 0) {
145 // legacy codes
146 if (uc1 == 'n' && uc2 == 'o') // no -> nb
147 return QLocale::NorwegianBokmal;
148 if (uc1 == 't' && uc2 == 'l') // tl -> fil
149 return QLocale::Filipino;
150 if (uc1 == 's' && uc2 == 'h') // sh -> sr[_Latn]
151 return QLocale::Serbian;
152 if (uc1 == 'm' && uc2 == 'o') // mo -> ro
153 return QLocale::Romanian;
154 // Android uses the following deprecated codes
155 if (uc1 == 'i' && uc2 == 'w') // iw -> he
156 return QLocale::Hebrew;
157 if (uc1 == 'i' && uc2 == 'n') // in -> id
158 return QLocale::Indonesian;
159 if (uc1 == 'j' && uc2 == 'i') // ji -> yi
160 return QLocale::Yiddish;
161 }
162 return QLocale::AnyLanguage;
163}
164
165QLocale::Script QLocalePrivate::codeToScript(QStringView code) noexcept
166{
167 const auto len = code.size();
168 if (len != 4)
169 return QLocale::AnyScript;
170
171 // script is titlecased in our data
172 unsigned char c0 = code[0].toUpper().toLatin1();
173 unsigned char c1 = code[1].toLower().toLatin1();
174 unsigned char c2 = code[2].toLower().toLatin1();
175 unsigned char c3 = code[3].toLower().toLatin1();
176
177 const unsigned char *c = script_code_list;
178 for (qsizetype i = 0; i < QLocale::LastScript; ++i, c += 4) {
179 if (c0 == c[0] && c1 == c[1] && c2 == c[2] && c3 == c[3])
180 return QLocale::Script(i);
181 }
182 return QLocale::AnyScript;
183}
184
185QLocale::Territory QLocalePrivate::codeToTerritory(QStringView code) noexcept
186{
187 const auto len = code.size();
188 if (len != 2 && len != 3)
189 return QLocale::AnyTerritory;
190
191 char16_t uc1 = code[0].toUpper().unicode();
192 char16_t uc2 = code[1].toUpper().unicode();
193 char16_t uc3 = len > 2 ? code[2].toUpper().unicode() : 0;
194
195 const unsigned char *c = territory_code_list;
196 for (; *c != 0; c += 3) {
197 if (uc1 == c[0] && uc2 == c[1] && uc3 == c[2])
198 return QLocale::Territory((c - territory_code_list)/3);
199 }
200
201 return QLocale::AnyTerritory;
202}
203
204std::array<char, 4> QLocalePrivate::languageToCode(QLocale::Language language,
205 QLocale::LanguageCodeTypes codeTypes)
206{
207 if (language == QLocale::AnyLanguage || language > QLocale::LastLanguage)
208 return {};
209 if (language == QLocale::C)
210 return {'C'};
211
212 const LanguageCodeEntry &i = languageCodeList[language];
213
214 if (codeTypes.testFlag(flag: QLocale::ISO639Part1) && i.part1.isValid())
215 return i.part1.decode();
216
217 if (codeTypes.testFlag(flag: QLocale::ISO639Part2B) && i.part2B.isValid())
218 return i.part2B.decode();
219
220 if (codeTypes.testFlag(flag: QLocale::ISO639Part2T) && i.part2T.isValid())
221 return i.part2T.decode();
222
223 if (codeTypes.testFlag(flag: QLocale::ISO639Part3))
224 return i.part3.decode();
225
226 return {};
227}
228
229QLatin1StringView QLocalePrivate::scriptToCode(QLocale::Script script)
230{
231 if (script == QLocale::AnyScript || script > QLocale::LastScript)
232 return {};
233 const unsigned char *c = script_code_list + 4 * script;
234 return {reinterpret_cast<const char *>(c), 4};
235}
236
237QLatin1StringView QLocalePrivate::territoryToCode(QLocale::Territory territory)
238{
239 if (territory == QLocale::AnyTerritory || territory > QLocale::LastTerritory)
240 return {};
241
242 const unsigned char *c = territory_code_list + 3 * territory;
243 return {reinterpret_cast<const char*>(c), c[2] == 0 ? 2 : 3};
244}
245
246namespace {
247struct LikelyPair
248{
249 QLocaleId key; // Search key.
250 QLocaleId value = QLocaleId { .language_id: 0, .script_id: 0, .territory_id: 0 };
251};
252
253bool operator<(const LikelyPair &lhs, const LikelyPair &rhs)
254{
255 // Must match the comparison LocaleDataWriter.likelySubtags() uses when
256 // sorting, see qtbase/util/locale_database.qlocalexml2cpp.py
257 const auto compare = [](int lhs, int rhs) {
258 // 0 sorts after all other values; lhs and rhs are passed ushort values.
259 const int huge = 0x10000;
260 return (lhs ? lhs : huge) - (rhs ? rhs : huge);
261 };
262 const auto &left = lhs.key;
263 const auto &right = rhs.key;
264 // Comparison order: language, region, script:
265 if (int cmp = compare(left.language_id, right.language_id))
266 return cmp < 0;
267 if (int cmp = compare(left.territory_id, right.territory_id))
268 return cmp < 0;
269 return compare(left.script_id, right.script_id) < 0;
270}
271} // anonymous namespace
272
273/*!
274 Fill in blank fields of a locale ID.
275
276 An ID in which some fields are zero stands for any locale that agrees with
277 it in its non-zero fields. CLDR's likely-subtag data is meant to help us
278 chose which candidate to prefer. (Note, however, that CLDR does have some
279 cases where it maps an ID to a "best match" for which CLDR does not provide
280 data, even though there are locales for which CLDR does provide data that do
281 match the given ID. It's telling us, unhelpfully but truthfully, what
282 locale would (most likely) be meant by (someone using) the combination
283 requested, even when that locale isn't yet supported.) It may also map an
284 obsolete or generic tag to a modern or more specific replacement, possibly
285 filling in some of the other fields in the process (presently only for
286 countries). Note that some fields of the result may remain blank, but there
287 is no more specific recommendation available.
288
289 For the formal specification, see
290 https://www.unicode.org/reports/tr35/#Likely_Subtags
291
292 \note We also search und_script_region and und_region; they're not mentioned
293 in the spec, but the examples clearly presume them and CLDR does provide
294 such likely matches.
295*/
296QLocaleId QLocaleId::withLikelySubtagsAdded() const
297{
298 /* Each pattern that appears in a comments below, language_script_region and
299 similar, indicates which of this's fields (even if blank) are being
300 attended to in a given search; for fields left out of the pattern, the
301 search uses 0 regardless of whether this has specified the field.
302
303 If a key matches what we're searching for (possibly with a wildcard in
304 the key matching a non-wildcard in our search), the tags from this that
305 are specified in the key are replaced by the match (even if different);
306 but the other tags of this replace what's in the match (even when the
307 match does specify a value).
308 */
309 static_assert(std::size(likely_subtags) % 2 == 0);
310 auto *pairs = reinterpret_cast<const LikelyPair *>(likely_subtags);
311 auto *const afterPairs = pairs + std::size(likely_subtags) / 2;
312 LikelyPair sought { .key: *this };
313 // Our array is sorted in the order that puts all candidate matches in the
314 // order we would want them; ones we should prefer appear before the others.
315 if (language_id) {
316 // language_script_region, language_region, language_script, language:
317 pairs = std::lower_bound(first: pairs, last: afterPairs, val: sought);
318 // Single language's block isn't long enough to warrant more binary
319 // chopping within it - just traverse it all:
320 for (; pairs < afterPairs && pairs->key.language_id == language_id; ++pairs) {
321 const QLocaleId &key = pairs->key;
322 if (key.territory_id && key.territory_id != territory_id)
323 continue;
324 if (key.script_id && key.script_id != script_id)
325 continue;
326 QLocaleId value = pairs->value;
327 if (territory_id && !key.territory_id)
328 value.territory_id = territory_id;
329 if (script_id && !key.script_id)
330 value.script_id = script_id;
331 return value;
332 }
333 }
334 // und_script_region or und_region (in that order):
335 if (territory_id) {
336 sought.key = QLocaleId { .language_id: 0, .script_id: script_id, .territory_id: territory_id };
337 pairs = std::lower_bound(first: pairs, last: afterPairs, val: sought);
338 // Again, individual und_?_region block isn't long enough to make binary
339 // chop a win:
340 for (; pairs < afterPairs && pairs->key.territory_id == territory_id; ++pairs) {
341 const QLocaleId &key = pairs->key;
342 Q_ASSERT(!key.language_id);
343 if (key.script_id && key.script_id != script_id)
344 continue;
345 QLocaleId value = pairs->value;
346 if (language_id)
347 value.language_id = language_id;
348 if (script_id && !key.script_id)
349 value.script_id = script_id;
350 return value;
351 }
352 }
353 // und_script:
354 if (script_id) {
355 sought.key = QLocaleId { .language_id: 0, .script_id: script_id, .territory_id: 0 };
356 pairs = std::lower_bound(first: pairs, last: afterPairs, val: sought);
357 if (pairs < afterPairs && pairs->key.script_id == script_id) {
358 Q_ASSERT(!pairs->key.language_id && !pairs->key.territory_id);
359 QLocaleId value = pairs->value;
360 if (language_id)
361 value.language_id = language_id;
362 if (territory_id)
363 value.territory_id = territory_id;
364 return value;
365 }
366 }
367 if (matchesAll()) { // Skipped all of the above.
368 // CLDR has no match-all at v37, but might get one some day ...
369 pairs = std::lower_bound(first: pairs, last: afterPairs, val: sought);
370 if (pairs < afterPairs) {
371 // All other keys are < match-all.
372 Q_ASSERT(pairs + 1 == afterPairs);
373 Q_ASSERT(pairs->key.matchesAll());
374 return pairs->value;
375 }
376 }
377 return *this;
378}
379
380QLocaleId QLocaleId::withLikelySubtagsRemoved() const
381{
382 QLocaleId max = withLikelySubtagsAdded();
383 // language
384 {
385 QLocaleId id { .language_id: language_id, .script_id: 0, .territory_id: 0 };
386 if (id.withLikelySubtagsAdded() == max)
387 return id;
388 }
389 // language_region
390 if (territory_id) {
391 QLocaleId id { .language_id: language_id, .script_id: 0, .territory_id: territory_id };
392 if (id.withLikelySubtagsAdded() == max)
393 return id;
394 }
395 // language_script
396 if (script_id) {
397 QLocaleId id { .language_id: language_id, .script_id: script_id, .territory_id: 0 };
398 if (id.withLikelySubtagsAdded() == max)
399 return id;
400 }
401 return max;
402}
403
404QByteArray QLocaleId::name(char separator) const
405{
406 if (language_id == QLocale::AnyLanguage)
407 return QByteArray();
408 if (language_id == QLocale::C)
409 return QByteArrayLiteral("C");
410
411 const LanguageCodeEntry &language = languageCodeList[language_id];
412 AlphaCode lang;
413 qsizetype langLen;
414
415 if (language.part1.isValid()) {
416 lang = language.part1;
417 langLen = 2;
418 } else {
419 lang = language.part2B.isValid() ? language.part2B : language.part3;
420 langLen = 3;
421 }
422
423 const unsigned char *script =
424 (script_id != QLocale::AnyScript ? script_code_list + 4 * script_id : nullptr);
425 const unsigned char *country =
426 (territory_id != QLocale::AnyTerritory
427 ? territory_code_list + 3 * territory_id : nullptr);
428 qsizetype len = langLen + (script ? 4 + 1 : 0) + (country ? (country[2] != 0 ? 3 : 2) + 1 : 0);
429 QByteArray name(len, Qt::Uninitialized);
430 char *uc = name.data();
431
432 auto langArray = lang.decode();
433
434 *uc++ = langArray[0];
435 *uc++ = langArray[1];
436 if (langLen > 2)
437 *uc++ = langArray[2];
438
439 if (script) {
440 *uc++ = separator;
441 *uc++ = script[0];
442 *uc++ = script[1];
443 *uc++ = script[2];
444 *uc++ = script[3];
445 }
446 if (country) {
447 *uc++ = separator;
448 *uc++ = country[0];
449 *uc++ = country[1];
450 if (country[2] != 0)
451 *uc++ = country[2];
452 }
453 return name;
454}
455
456QByteArray QLocalePrivate::bcp47Name(char separator) const
457{
458 if (m_data->m_language_id == QLocale::AnyLanguage)
459 return QByteArray();
460 if (m_data->m_language_id == QLocale::C)
461 return QByteArrayLiteral("en");
462
463 return m_data->id().withLikelySubtagsRemoved().name(separator);
464}
465
466static qsizetype findLocaleIndexById(const QLocaleId &localeId)
467{
468 qsizetype idx = locale_index[localeId.language_id];
469 // If there are no locales for specified language (so we we've got the
470 // default language, which has no associated script or country), give up:
471 if (localeId.language_id && idx == 0)
472 return idx;
473
474 Q_ASSERT(localeId.acceptLanguage(locale_data[idx].m_language_id));
475
476 do {
477 if (localeId.acceptScriptTerritory(other: locale_data[idx].id()))
478 return idx;
479 ++idx;
480 } while (localeId.acceptLanguage(lang: locale_data[idx].m_language_id));
481
482 return -1;
483}
484
485qsizetype QLocaleData::findLocaleIndex(QLocaleId lid)
486{
487 QLocaleId localeId = lid;
488 QLocaleId likelyId = localeId.withLikelySubtagsAdded();
489 const ushort fallback = likelyId.language_id;
490
491 // Try a straight match with the likely data:
492 qsizetype index = findLocaleIndexById(localeId: likelyId);
493 if (index >= 0)
494 return index;
495 QVarLengthArray<QLocaleId, 6> tried;
496 tried.push_back(t: likelyId);
497
498#define CheckCandidate(id) do { \
499 if (!tried.contains(id)) { \
500 index = findLocaleIndexById(id); \
501 if (index >= 0) \
502 return index; \
503 tried.push_back(id); \
504 } \
505 } while (false) // end CheckCandidate
506
507 // No match; try again with raw data:
508 CheckCandidate(localeId);
509
510 // No match; try again with likely country for language_script
511 if (lid.territory_id && (lid.language_id || lid.script_id)) {
512 localeId.territory_id = 0;
513 likelyId = localeId.withLikelySubtagsAdded();
514 CheckCandidate(likelyId);
515
516 // No match; try again with any country
517 CheckCandidate(localeId);
518 }
519
520 // No match; try again with likely script for language_region
521 if (lid.script_id && (lid.language_id || lid.territory_id)) {
522 localeId = QLocaleId { .language_id: lid.language_id, .script_id: 0, .territory_id: lid.territory_id };
523 likelyId = localeId.withLikelySubtagsAdded();
524 CheckCandidate(likelyId);
525
526 // No match; try again with any script
527 CheckCandidate(localeId);
528 }
529#undef CheckCandidate
530
531 // No match; return base index for initial likely language:
532 return locale_index[fallback];
533}
534
535static QStringView findTag(QStringView name) noexcept
536{
537 const std::u16string_view v(name.utf16(), size_t(name.size()));
538 const auto i = v.find_first_of(str: u"_-.@");
539 if (i == std::string_view::npos)
540 return name;
541 return name.first(n: qsizetype(i));
542}
543
544static bool validTag(QStringView tag)
545{
546 // Is tag is a non-empty sequence of ASCII letters and/or digits ?
547 for (QChar uc : tag) {
548 const char16_t ch = uc.unicode();
549 if (!isAsciiLetterOrNumber(c: ch))
550 return false;
551 }
552 return tag.size() > 0;
553}
554
555static bool isScript(QStringView tag)
556{
557 // Every script name is 4 characters, a capital followed by three lower-case;
558 // so a search for tag in allScripts *can* only match if it's aligned.
559 static const QString allScripts =
560 QString::fromLatin1(str: reinterpret_cast<const char *>(script_code_list),
561 size: sizeof(script_code_list) - 1);
562 return tag.size() == 4 && allScripts.indexOf(s: tag) % 4 == 0;
563}
564
565bool qt_splitLocaleName(QStringView name, QStringView *lang, QStringView *script, QStringView *land)
566{
567 // Assume each of lang, script and land is nullptr or points to an empty QStringView.
568 enum ParserState { NoState, LangState, ScriptState, CountryState };
569 ParserState state = LangState;
570 while (name.size() && state != NoState) {
571 const QStringView tag = findTag(name);
572 if (!validTag(tag))
573 break;
574 name = name.sliced(pos: tag.size());
575 const bool sep = name.size() > 0;
576 if (sep) // tag wasn't all that remained; there was a separator
577 name = name.sliced(pos: 1);
578
579 switch (state) {
580 case LangState:
581 if (tag.size() != 2 && tag.size() != 3)
582 return false;
583 if (lang)
584 *lang = tag;
585 state = sep ? ScriptState : NoState;
586 break;
587 case ScriptState:
588 if (isScript(tag)) {
589 if (script)
590 *script = tag;
591 state = sep ? CountryState : NoState;
592 break;
593 }
594 // It wasn't a script, assume it's a country.
595 Q_FALLTHROUGH();
596 case CountryState:
597 if (land)
598 *land = tag;
599 state = NoState;
600 break;
601 case NoState: // Precluded by loop condition !
602 Q_UNREACHABLE();
603 break;
604 }
605 }
606 return state != LangState;
607}
608
609QLocaleId QLocaleId::fromName(QStringView name)
610{
611 QStringView lang;
612 QStringView script;
613 QStringView land;
614 if (!qt_splitLocaleName(name, lang: &lang, script: &script, land: &land))
615 return { .language_id: QLocale::C, .script_id: 0, .territory_id: 0 };
616
617 QLocale::Language langId = QLocalePrivate::codeToLanguage(code: lang);
618 if (langId == QLocale::AnyLanguage)
619 return { .language_id: QLocale::C, .script_id: 0, .territory_id: 0 };
620 return { .language_id: langId, .script_id: QLocalePrivate::codeToScript(code: script), .territory_id: QLocalePrivate::codeToTerritory(code: land) };
621}
622
623QString qt_readEscapedFormatString(QStringView format, qsizetype *idx)
624{
625 qsizetype &i = *idx;
626
627 Q_ASSERT(format.at(i) == u'\'');
628 ++i;
629 if (i == format.size())
630 return QString();
631 if (format.at(n: i).unicode() == '\'') { // "''" outside of a quoted string
632 ++i;
633 return "'"_L1;
634 }
635
636 QString result;
637
638 while (i < format.size()) {
639 if (format.at(n: i).unicode() == '\'') {
640 if (format.mid(pos: i + 1).startsWith(c: u'\'')) {
641 // "''" inside a quoted string
642 result.append(c: u'\'');
643 i += 2;
644 } else {
645 break;
646 }
647 } else {
648 result.append(c: format.at(n: i++));
649 }
650 }
651 if (i < format.size())
652 ++i;
653
654 return result;
655}
656
657/*!
658 \internal
659
660 Counts the number of identical leading characters in \a s.
661
662 If \a s is empty, returns 0.
663
664 Otherwise, returns the number of consecutive \c{s.front()}
665 characters at the start of \a s.
666
667 \code
668 qt_repeatCount(u"a"); // == 1
669 qt_repeatCount(u"ab"); // == 1
670 qt_repeatCount(u"aab"); // == 2
671 \endcode
672*/
673qsizetype qt_repeatCount(QStringView s)
674{
675 if (s.isEmpty())
676 return 0;
677 const QChar c = s.front();
678 qsizetype j = 1;
679 while (j < s.size() && s.at(n: j) == c)
680 ++j;
681 return j;
682}
683
684Q_CONSTINIT static const QLocaleData *default_data = nullptr;
685Q_CONSTINIT QBasicAtomicInt QLocalePrivate::s_generation = Q_BASIC_ATOMIC_INITIALIZER(0);
686
687static QLocalePrivate *c_private()
688{
689 static QLocalePrivate c_locale(locale_data, 0, QLocale::OmitGroupSeparator, 1);
690 return &c_locale;
691}
692
693#ifndef QT_NO_SYSTEMLOCALE
694/******************************************************************************
695** Default system locale behavior
696*/
697
698/*!
699 \internal
700 Constructs a QSystemLocale object.
701
702 The constructor will automatically install this object as the system locale.
703 It and the destructor maintain a stack of system locales, with the
704 most-recently-created instance (that hasn't yet been deleted) used as the
705 system locale. This is only intended as a way to let a platform plugin
706 install its own system locale, overriding what might otherwise be provided
707 for its class of platform (as Android does, differing from Linux), and to
708 let tests transiently over-ride the system or plugin-supplied one. As such,
709 there should not be diverse threads creating and destroying QSystemLocale
710 instances concurrently, so no attempt is made at thread-safety in managing
711 the stack.
712
713 This constructor also resets the flag that'll prompt QLocale::system() to
714 re-initialize its data, so that instantiating a QSystemLocale (even
715 transiently) triggers a refresh of the system locale's data. This is
716 exploited by some test code.
717*/
718QSystemLocale::QSystemLocale() : next(_systemLocale)
719{
720 _systemLocale = this;
721
722 systemLocaleData.m_language_id = 0;
723}
724
725/*!
726 \internal
727 Deletes the object.
728*/
729QSystemLocale::~QSystemLocale()
730{
731 if (_systemLocale == this) {
732 _systemLocale = next;
733
734 // Change to system locale => force refresh.
735 systemLocaleData.m_language_id = 0;
736 } else {
737 for (QSystemLocale *p = _systemLocale; p; p = p->next) {
738 if (p->next == this)
739 p->next = next;
740 }
741 }
742}
743
744static const QSystemLocale *systemLocale()
745{
746 if (_systemLocale)
747 return _systemLocale;
748
749 // As this is only ever instantiated with _systemLocale null, it is
750 // necessarily the ->next-most in any chain that may subsequently develop;
751 // and it won't be destructed until exit()-time.
752 static QSystemLocale globalInstance;
753 return &globalInstance;
754}
755
756static void updateSystemPrivate()
757{
758 // This function is NOT thread-safe!
759 // It *should not* be called by anything but systemData()
760 // It *is* called before {system,default}LocalePrivate exist.
761 const QSystemLocale *sys_locale = systemLocale();
762
763 // tell the object that the system locale has changed.
764 sys_locale->query(type: QSystemLocale::LocaleChanged);
765
766 // Populate system locale with fallback as basis
767 systemLocaleData = locale_data[sys_locale->fallbackLocaleIndex()];
768
769 QVariant res = sys_locale->query(type: QSystemLocale::LanguageId);
770 if (!res.isNull()) {
771 systemLocaleData.m_language_id = res.toInt();
772 systemLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
773 }
774 res = sys_locale->query(type: QSystemLocale::TerritoryId);
775 if (!res.isNull()) {
776 systemLocaleData.m_territory_id = res.toInt();
777 systemLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
778 }
779 res = sys_locale->query(type: QSystemLocale::ScriptId);
780 if (!res.isNull())
781 systemLocaleData.m_script_id = res.toInt();
782
783 // Should we replace Any values based on likely sub-tags ?
784
785 // If system locale is default locale, update the default collator's generation:
786 if (default_data == &systemLocaleData)
787 QLocalePrivate::s_generation.fetchAndAddRelaxed(valueToAdd: 1);
788}
789#endif // !QT_NO_SYSTEMLOCALE
790
791static const QLocaleData *systemData()
792{
793#ifndef QT_NO_SYSTEMLOCALE
794 /*
795 Copy over the information from the fallback locale and modify.
796
797 This modifies (cross-thread) global state, so take care to only call it in
798 one thread.
799 */
800 {
801 Q_CONSTINIT static QBasicMutex systemDataMutex;
802 systemDataMutex.lock();
803 if (systemLocaleData.m_language_id == 0)
804 updateSystemPrivate();
805 systemDataMutex.unlock();
806 }
807
808 return &systemLocaleData;
809#else
810 return locale_data;
811#endif
812}
813
814static const QLocaleData *defaultData()
815{
816 if (!default_data)
817 default_data = systemData();
818 return default_data;
819}
820
821static qsizetype defaultIndex()
822{
823 const QLocaleData *const data = defaultData();
824#ifndef QT_NO_SYSTEMLOCALE
825 if (data == &systemLocaleData) {
826 // Work out a suitable index matching the system data, for use when
827 // accessing calendar data, when not fetched from system.
828 return QLocaleData::findLocaleIndex(lid: data->id());
829 }
830#endif
831
832 using QtPrivate::q_points_into_range;
833 Q_ASSERT(q_points_into_range(data, locale_data));
834 return data - locale_data;
835}
836
837const QLocaleData *QLocaleData::c()
838{
839 Q_ASSERT(locale_index[QLocale::C] == 0);
840 return locale_data;
841}
842
843#ifndef QT_NO_DATASTREAM
844QDataStream &operator<<(QDataStream &ds, const QLocale &l)
845{
846 ds << l.name();
847 return ds;
848}
849
850QDataStream &operator>>(QDataStream &ds, QLocale &l)
851{
852 QString s;
853 ds >> s;
854 l = QLocale(s);
855 return ds;
856}
857#endif // QT_NO_DATASTREAM
858
859static constexpr qsizetype locale_data_size = q20::ssize(locale_data) - 1; // trailing guard
860
861Q_GLOBAL_STATIC(QSharedDataPointer<QLocalePrivate>, defaultLocalePrivate,
862 new QLocalePrivate(defaultData(), defaultIndex()))
863
864static QLocalePrivate *localePrivateByName(QStringView name)
865{
866 if (name == u"C")
867 return c_private();
868 const qsizetype index = QLocaleData::findLocaleIndex(lid: QLocaleId::fromName(name));
869 Q_ASSERT(index >= 0 && index < locale_data_size);
870 return new QLocalePrivate(locale_data + index, index,
871 locale_data[index].m_language_id == QLocale::C
872 ? QLocale::OmitGroupSeparator : QLocale::DefaultNumberOptions);
873}
874
875static QLocalePrivate *findLocalePrivate(QLocale::Language language, QLocale::Script script,
876 QLocale::Territory territory)
877{
878 if (language == QLocale::C)
879 return c_private();
880
881 qsizetype index = QLocaleData::findLocaleIndex(lid: QLocaleId { .language_id: language, .script_id: script, .territory_id: territory });
882 Q_ASSERT(index >= 0 && index < locale_data_size);
883 const QLocaleData *data = locale_data + index;
884
885 QLocale::NumberOptions numberOptions = QLocale::DefaultNumberOptions;
886
887 // If not found, should use default locale:
888 if (data->m_language_id == QLocale::C) {
889 if (defaultLocalePrivate.exists())
890 numberOptions = defaultLocalePrivate->data()->m_numberOptions;
891 data = defaultData();
892 index = defaultIndex();
893 }
894 return new QLocalePrivate(data, index, numberOptions);
895}
896
897static std::optional<QString>
898systemLocaleString(const QLocaleData *that, QSystemLocale::QueryType type)
899{
900#ifndef QT_NO_SYSTEMLOCALE
901 if (that != &systemLocaleData)
902 return std::nullopt;
903
904 QVariant v = systemLocale()->query(type);
905 if (v.metaType() != QMetaType::fromType<QString>())
906 return std::nullopt;
907
908 return v.toString();
909#else
910 Q_UNUSED(that)
911 Q_UNUSED(type)
912 return std::nullopt;
913#endif
914}
915
916static QString localeString(const QLocaleData *that, QSystemLocale::QueryType type,
917 QLocaleData::DataRange range)
918{
919 if (auto opt = systemLocaleString(that, type))
920 return *opt;
921 return range.getData(table: single_character_data);
922}
923
924QString QLocaleData::decimalPoint() const
925{
926 return localeString(that: this, type: QSystemLocale::DecimalPoint, range: decimalSeparator());
927}
928
929QString QLocaleData::groupSeparator() const
930{
931 return localeString(that: this, type: QSystemLocale::GroupSeparator, range: groupDelim());
932}
933
934QString QLocaleData::percentSign() const
935{
936 return percent().getData(table: single_character_data);
937}
938
939QString QLocaleData::listSeparator() const
940{
941 return listDelimit().getData(table: single_character_data);
942}
943
944QString QLocaleData::zeroDigit() const
945{
946 return localeString(that: this, type: QSystemLocale::ZeroDigit, range: zero());
947}
948
949char32_t QLocaleData::zeroUcs() const
950{
951#ifndef QT_NO_SYSTEMLOCALE
952 if (this == &systemLocaleData) {
953 const auto text = systemLocale()->query(type: QSystemLocale::ZeroDigit).toString();
954 if (!text.isEmpty()) {
955 if (text.size() == 1 && !text.at(i: 0).isSurrogate())
956 return text.at(i: 0).unicode();
957 if (text.size() == 2 && text.at(i: 0).isHighSurrogate())
958 return QChar::surrogateToUcs4(high: text.at(i: 0), low: text.at(i: 1));
959 }
960 }
961#endif
962 return zero().ucsFirst(table: single_character_data);
963}
964
965QString QLocaleData::negativeSign() const
966{
967 return localeString(that: this, type: QSystemLocale::NegativeSign, range: minus());
968}
969
970QString QLocaleData::positiveSign() const
971{
972 return localeString(that: this, type: QSystemLocale::PositiveSign, range: plus());
973}
974
975QString QLocaleData::exponentSeparator() const
976{
977 return exponential().getData(table: single_character_data);
978}
979
980/*!
981 \internal
982*/
983QLocale::QLocale(QLocalePrivate &dd)
984 : d(&dd)
985{}
986
987
988/*!
989 \since 6.3
990
991 Constructs a QLocale object with the specified \a name.
992
993 The name has the format "language[_script][_territory][.codeset][@modifier]"
994 or "C", where:
995
996 \list
997 \li language is a lowercase, two-letter, ISO 639 language code (some
998 three-letter codes are also recognized),
999 \li script is a capitalized, four-letter, ISO 15924 script code,
1000 \li territory is an uppercase, two-letter, ISO 3166 territory code
1001 (some numeric codes are also recognized), and
1002 \li codeset and modifier are ignored.
1003 \endlist
1004
1005 The separator can be either underscore \c{'_'} (U+005F, "low line") or a
1006 dash \c{'-'} (U+002D, "hyphen-minus"). If QLocale has no data for the
1007 specified combination of language, script, and territory, then it uses the
1008 most suitable match it can find instead. If the string violates the locale
1009 format, or no suitable data can be found for the specified keys, the "C"
1010 locale is used instead.
1011
1012 This constructor is much slower than QLocale(Language, Script, Territory) or
1013 QLocale(Language, Territory).
1014
1015 \sa bcp47Name(), {Matching combinations of language, script and territory}
1016*/
1017QLocale::QLocale(QStringView name)
1018 : d(localePrivateByName(name))
1019{
1020}
1021
1022/*!
1023 \fn QLocale::QLocale(const QString &name)
1024 \overload
1025*/
1026
1027/*!
1028 Constructs a QLocale object initialized with the default locale.
1029
1030 If no default locale was set using setDefault(), this locale will be the
1031 same as the one returned by system().
1032
1033 \sa setDefault(), system()
1034*/
1035
1036QLocale::QLocale()
1037 : d(*defaultLocalePrivate)
1038{
1039 // Make sure system data is up to date:
1040 systemData();
1041}
1042
1043/*!
1044 Constructs a QLocale object for the specified \a language and \a territory.
1045
1046 If there is more than one script in use for this combination, a likely
1047 script will be selected. If QLocale has no data for the specified \a
1048 language, the default locale is used. If QLocale has no data for the
1049 specified combination of \a language and \a territory, an alternative
1050 territory may be used instead.
1051
1052 \sa setDefault(), {Matching combinations of language, script and territory}
1053*/
1054
1055QLocale::QLocale(Language language, Territory territory)
1056 : d(findLocalePrivate(language, script: QLocale::AnyScript, territory))
1057{
1058}
1059
1060/*!
1061 \since 4.8
1062
1063 Constructs a QLocale object for the specified \a language, \a script and \a
1064 territory.
1065
1066 If QLocale does not have data for the given combination, it will find data
1067 for as good a match as it can. It falls back on the default locale if
1068
1069 \list
1070 \li \a language is \c AnyLanguage and no language can be inferred from \a
1071 script and \a territory
1072 \li QLocale has no data for the language, either given as \a language or
1073 inferred as above.
1074 \endlist
1075
1076 \sa setDefault(), {Matching combinations of language, script and territory}
1077*/
1078
1079QLocale::QLocale(Language language, Script script, Territory territory)
1080 : d(findLocalePrivate(language, script, territory))
1081{
1082}
1083
1084/*!
1085 Constructs a QLocale object as a copy of \a other.
1086*/
1087
1088QLocale::QLocale(const QLocale &other) noexcept = default;
1089
1090/*!
1091 Destructor
1092*/
1093
1094QLocale::~QLocale()
1095{
1096}
1097
1098/*!
1099 Assigns \a other to this QLocale object and returns a reference
1100 to this QLocale object.
1101*/
1102
1103QLocale &QLocale::operator=(const QLocale &other) noexcept = default;
1104
1105/*!
1106 \internal
1107 Equality comparison.
1108*/
1109
1110bool QLocale::equals(const QLocale &other) const
1111{
1112 return d->m_data == other.d->m_data && d->m_numberOptions == other.d->m_numberOptions;
1113}
1114
1115/*!
1116 \fn void QLocale::swap(QLocale &other)
1117 \since 5.6
1118
1119 Swaps locale \a other with this locale. This operation is very fast and
1120 never fails.
1121*/
1122
1123/*!
1124 \since 5.6
1125 \relates QLocale
1126
1127 Returns the hash value for \a key, using
1128 \a seed to seed the calculation.
1129*/
1130size_t qHash(const QLocale &key, size_t seed) noexcept
1131{
1132 return qHashMulti(seed, args: key.d->m_data, args: key.d->m_numberOptions);
1133}
1134
1135/*!
1136 \since 4.2
1137
1138 Sets the \a options related to number conversions for this
1139 QLocale instance.
1140
1141 \sa numberOptions(), FloatingPointPrecisionOption
1142*/
1143void QLocale::setNumberOptions(NumberOptions options)
1144{
1145 d->m_numberOptions = options;
1146}
1147
1148/*!
1149 \since 4.2
1150
1151 Returns the options related to number conversions for this
1152 QLocale instance.
1153
1154 By default, no options are set for the standard locales, except
1155 for the "C" locale, which has OmitGroupSeparator set by default.
1156
1157 \sa setNumberOptions(), toString(), groupSeparator(), FloatingPointPrecisionOption
1158*/
1159QLocale::NumberOptions QLocale::numberOptions() const
1160{
1161 return static_cast<NumberOptions>(d->m_numberOptions);
1162}
1163
1164/*!
1165 \fn QString QLocale::quoteString(const QString &str, QuotationStyle style) const
1166
1167 \since 4.8
1168
1169 Returns \a str quoted according to the current locale using the given
1170 quotation \a style.
1171*/
1172
1173/*!
1174 \since 6.0
1175
1176 \overload
1177*/
1178QString QLocale::quoteString(QStringView str, QuotationStyle style) const
1179{
1180#ifndef QT_NO_SYSTEMLOCALE
1181 if (d->m_data == &systemLocaleData) {
1182 QVariant res;
1183 if (style == QLocale::AlternateQuotation)
1184 res = systemLocale()->query(type: QSystemLocale::StringToAlternateQuotation,
1185 in: QVariant::fromValue(value: str));
1186 if (res.isNull() || style == QLocale::StandardQuotation)
1187 res = systemLocale()->query(type: QSystemLocale::StringToStandardQuotation,
1188 in: QVariant::fromValue(value: str));
1189 if (!res.isNull())
1190 return res.toString();
1191 }
1192#endif
1193
1194 QLocaleData::DataRange start, end;
1195 if (style == QLocale::StandardQuotation) {
1196 start = d->m_data->quoteStart();
1197 end = d->m_data->quoteEnd();
1198 } else {
1199 start = d->m_data->quoteStartAlternate();
1200 end = d->m_data->quoteEndAlternate();
1201 }
1202
1203 return start.viewData(table: single_character_data) % str % end.viewData(table: single_character_data);
1204}
1205
1206/*!
1207 \since 4.8
1208
1209 Returns a string that represents a join of a given \a list of strings with
1210 a separator defined by the locale.
1211*/
1212QString QLocale::createSeparatedList(const QStringList &list) const
1213{
1214 // May be empty if list is empty or sole entry is empty.
1215#ifndef QT_NO_SYSTEMLOCALE
1216 if (d->m_data == &systemLocaleData) {
1217 QVariant res =
1218 systemLocale()->query(type: QSystemLocale::ListToSeparatedString, in: QVariant::fromValue(value: list));
1219
1220 if (!res.isNull())
1221 return res.toString();
1222 }
1223#endif
1224
1225 const qsizetype size = list.size();
1226 if (size < 1)
1227 return QString();
1228
1229 if (size == 1)
1230 return list.at(i: 0);
1231
1232 if (size == 2)
1233 return d->m_data->pairListPattern().getData(
1234 table: list_pattern_part_data).arg(args: list.at(i: 0), args: list.at(i: 1));
1235
1236 QStringView formatStart = d->m_data->startListPattern().viewData(table: list_pattern_part_data);
1237 QStringView formatMid = d->m_data->midListPattern().viewData(table: list_pattern_part_data);
1238 QStringView formatEnd = d->m_data->endListPattern().viewData(table: list_pattern_part_data);
1239 QString result = formatStart.arg(args: list.at(i: 0), args: list.at(i: 1));
1240 for (qsizetype i = 2; i < size - 1; ++i)
1241 result = formatMid.arg(args&: result, args: list.at(i));
1242 result = formatEnd.arg(args&: result, args: list.at(i: size - 1));
1243 return result;
1244}
1245
1246/*!
1247 \nonreentrant
1248
1249 Sets the global default locale to \a locale. These
1250 values are used when a QLocale object is constructed with
1251 no arguments. If this function is not called, the system's
1252 locale is used.
1253
1254 \warning In a multithreaded application, the default locale
1255 should be set at application startup, before any non-GUI threads
1256 are created.
1257
1258 \sa system(), c()
1259*/
1260
1261void QLocale::setDefault(const QLocale &locale)
1262{
1263 default_data = locale.d->m_data;
1264
1265 if (defaultLocalePrivate.isDestroyed())
1266 return; // avoid crash on exit
1267 if (!defaultLocalePrivate.exists()) {
1268 // Force it to exist; see QTBUG-83016
1269 QLocale ignoreme;
1270 Q_ASSERT(defaultLocalePrivate.exists());
1271 }
1272
1273 // update the cached private
1274 *defaultLocalePrivate = locale.d;
1275 QLocalePrivate::s_generation.fetchAndAddRelaxed(valueToAdd: 1);
1276}
1277
1278/*!
1279 Returns the language of this locale.
1280
1281 \sa script(), territory(), languageToString(), bcp47Name()
1282*/
1283QLocale::Language QLocale::language() const
1284{
1285 return Language(d->languageId());
1286}
1287
1288/*!
1289 \since 4.8
1290
1291 Returns the script of this locale.
1292
1293 \sa language(), territory(), languageToString(), scriptToString(), bcp47Name()
1294*/
1295QLocale::Script QLocale::script() const
1296{
1297 return Script(d->m_data->m_script_id);
1298}
1299
1300/*!
1301 \since 6.2
1302
1303 Returns the territory of this locale.
1304
1305 \sa language(), script(), territoryToString(), bcp47Name()
1306*/
1307QLocale::Territory QLocale::territory() const
1308{
1309 return Territory(d->territoryId());
1310}
1311
1312#if QT_DEPRECATED_SINCE(6, 6)
1313/*!
1314 \deprecated [6.6] Use \l territory() instead.
1315
1316 Returns the territory of this locale.
1317
1318 \sa language(), script(), territoryToString(), bcp47Name()
1319*/
1320QLocale::Country QLocale::country() const
1321{
1322 return territory();
1323}
1324#endif
1325
1326/*!
1327 \brief The short name of this locale.
1328
1329 Returns the language and territory of this locale as a string of the form
1330 "language_territory", where language is a lowercase, two-letter ISO 639
1331 language code, and territory is an uppercase, two- or three-letter ISO 3166
1332 territory code. If the locale has no specified territory, only the language
1333 name is returned.
1334
1335 Even if the QLocale object was constructed with an explicit script, name()
1336 will not contain it for compatibility reasons. Use \l bcp47Name() instead if
1337 you need a full locale name, or construct the string you want to identify a
1338 locale by from those returned by passing its \l language() to \l
1339 languageToCode() and similar for the script and territory.
1340
1341 \sa QLocale(), language(), script(), territory(), bcp47Name(), uiLanguages()
1342*/
1343
1344QString QLocale::name() const
1345{
1346 const auto code = d->languageCode();
1347 QLatin1StringView view{code.data()};
1348
1349 Language l = language();
1350 if (l == C)
1351 return view;
1352
1353 Territory c = territory();
1354 if (c == AnyTerritory)
1355 return view;
1356
1357 return view + u'_' + d->territoryCode();
1358}
1359
1360template <typename T> static inline
1361T toIntegral_helper(const QLocalePrivate *d, QStringView str, bool *ok)
1362{
1363 constexpr bool isUnsigned = std::is_unsigned_v<T>;
1364 using Int64 = typename std::conditional_t<isUnsigned, quint64, qint64>;
1365
1366 Int64 val = 0;
1367 if constexpr (isUnsigned)
1368 val = d->m_data->stringToUnsLongLong(str, base: 10, ok, options: d->m_numberOptions);
1369 else
1370 val = d->m_data->stringToLongLong(str, base: 10, ok, options: d->m_numberOptions);
1371
1372 if (T(val) != val) {
1373 if (ok != nullptr)
1374 *ok = false;
1375 val = 0;
1376 }
1377 return T(val);
1378}
1379
1380
1381/*!
1382 \since 4.8
1383
1384 \brief Returns the BCP47 field names joined with dashes.
1385
1386 This combines as many of language, script and territory (and possibly other
1387 BCP47 fields) for this locale as are needed to uniquely specify it. Note
1388 that fields may be omitted if the Unicode consortium's \l {Matching
1389 combinations of language, script and territory}{Likely Subtag Rules} imply
1390 the omitted fields when given those retained. See \l name() for how to
1391 construct a string from individual fields, if some other format is needed.
1392
1393 Unlike uiLanguages(), the value returned by bcp47Name() represents the
1394 locale name of the QLocale data; this need not be the language the
1395 user-interface should be in.
1396
1397 This function tries to conform the locale name to BCP47.
1398
1399 \sa name(), language(), territory(), script(), uiLanguages()
1400*/
1401QString QLocale::bcp47Name() const
1402{
1403 return QString::fromLatin1(ba: d->bcp47Name());
1404}
1405
1406/*!
1407 Returns the two- or three-letter language code for \a language, as defined
1408 in the ISO 639 standards.
1409
1410 If specified, \a codeTypes selects which set of codes to consider. The first
1411 code from the set that is defined for \a language is returned. Otherwise,
1412 all ISO-639 codes are considered. The codes are considered in the following
1413 order: \c ISO639Part1, \c ISO639Part2B, \c ISO639Part2T, \c ISO639Part3.
1414 \c LegacyLanguageCode is ignored by this function.
1415
1416 \note For \c{QLocale::C} the function returns \c{"C"}.
1417 For \c QLocale::AnyLanguage an empty string is returned.
1418 If the language has no code in any selected code set, an empty string
1419 is returned.
1420
1421 \since 6.3
1422 \sa codeToLanguage(), language(), name(), bcp47Name(), territoryToCode(), scriptToCode()
1423*/
1424QString QLocale::languageToCode(Language language, LanguageCodeTypes codeTypes)
1425{
1426 const auto code = QLocalePrivate::languageToCode(language, codeTypes);
1427 return QLatin1StringView{code.data()};
1428}
1429
1430/*!
1431 Returns the QLocale::Language enum corresponding to the two- or three-letter
1432 \a languageCode, as defined in the ISO 639 standards.
1433
1434 If specified, \a codeTypes selects which set of codes to consider for
1435 conversion. By default all codes known to Qt are considered. The codes are
1436 matched in the following order: \c ISO639Part1, \c ISO639Part2B,
1437 \c ISO639Part2T, \c ISO639Part3, \c LegacyLanguageCode.
1438
1439 If the code is invalid or not known \c QLocale::AnyLanguage is returned.
1440
1441 \since 6.3
1442 \sa languageToCode(), codeToTerritory(), codeToScript()
1443*/
1444QLocale::Language QLocale::codeToLanguage(QStringView languageCode,
1445 LanguageCodeTypes codeTypes) noexcept
1446{
1447 return QLocalePrivate::codeToLanguage(code: languageCode, codeTypes);
1448}
1449
1450/*!
1451 \since 6.2
1452
1453 Returns the two-letter territory code for \a territory, as defined
1454 in the ISO 3166 standard.
1455
1456 \note For \c{QLocale::AnyTerritory} an empty string is returned.
1457
1458 \sa codeToTerritory(), territory(), name(), bcp47Name(), languageToCode(), scriptToCode()
1459*/
1460QString QLocale::territoryToCode(QLocale::Territory territory)
1461{
1462 return QLocalePrivate::territoryToCode(territory);
1463}
1464
1465/*!
1466 \since 6.2
1467
1468 Returns the QLocale::Territory enum corresponding to the two-letter or
1469 three-digit \a territoryCode, as defined in the ISO 3166 standard.
1470
1471 If the code is invalid or not known QLocale::AnyTerritory is returned.
1472
1473 \sa territoryToCode(), codeToLanguage(), codeToScript()
1474*/
1475QLocale::Territory QLocale::codeToTerritory(QStringView territoryCode) noexcept
1476{
1477 return QLocalePrivate::codeToTerritory(code: territoryCode);
1478}
1479
1480#if QT_DEPRECATED_SINCE(6, 6)
1481/*!
1482 \deprecated [6.6] Use \l territoryToCode() instead.
1483
1484 Returns the two-letter territory code for \a country, as defined
1485 in the ISO 3166 standard.
1486
1487 \note For \c{QLocale::AnyTerritory} or \c{QLocale::AnyCountry} an empty string is returned.
1488
1489 \sa codeToTerritory(), territory(), name(), bcp47Name(), languageToCode(), scriptToCode()
1490*/
1491QString QLocale::countryToCode(Country country)
1492{
1493 return territoryToCode(territory: country);
1494}
1495
1496/*!
1497 Returns the QLocale::Territory enum corresponding to the two-letter or
1498 three-digit \a countryCode, as defined in the ISO 3166 standard.
1499
1500 If the code is invalid or not known QLocale::AnyTerritory is returned.
1501
1502 \deprecated [6.6] Use codeToTerritory(QStringView) instead.
1503 \since 6.1
1504 \sa territoryToCode(), codeToLanguage(), codeToScript()
1505*/
1506QLocale::Country QLocale::codeToCountry(QStringView countryCode) noexcept
1507{
1508 return QLocalePrivate::codeToTerritory(code: countryCode);
1509}
1510#endif
1511
1512/*!
1513 Returns the four-letter script code for \a script, as defined in the
1514 ISO 15924 standard.
1515
1516 \note For \c{QLocale::AnyScript} an empty string is returned.
1517
1518 \since 6.1
1519 \sa script(), name(), bcp47Name(), languageToCode(), territoryToCode()
1520*/
1521QString QLocale::scriptToCode(Script script)
1522{
1523 return QLocalePrivate::scriptToCode(script);
1524}
1525
1526/*!
1527 Returns the QLocale::Script enum corresponding to the four-letter script
1528 \a scriptCode, as defined in the ISO 15924 standard.
1529
1530 If the code is invalid or not known QLocale::AnyScript is returned.
1531
1532 \since 6.1
1533 \sa scriptToCode(), codeToLanguage(), codeToTerritory()
1534*/
1535QLocale::Script QLocale::codeToScript(QStringView scriptCode) noexcept
1536{
1537 return QLocalePrivate::codeToScript(code: scriptCode);
1538}
1539
1540/*!
1541 Returns a QString containing the name of \a language.
1542
1543 \sa territoryToString(), scriptToString(), bcp47Name()
1544*/
1545
1546QString QLocale::languageToString(Language language)
1547{
1548 if (language > QLocale::LastLanguage)
1549 return "Unknown"_L1;
1550 return QLatin1StringView(language_name_list + language_name_index[language]);
1551}
1552
1553/*!
1554 \since 6.2
1555
1556 Returns a QString containing the name of \a territory.
1557
1558 \sa languageToString(), scriptToString(), territory(), bcp47Name()
1559*/
1560QString QLocale::territoryToString(QLocale::Territory territory)
1561{
1562 if (territory > QLocale::LastTerritory)
1563 return "Unknown"_L1;
1564 return QLatin1StringView(territory_name_list + territory_name_index[territory]);
1565}
1566
1567#if QT_DEPRECATED_SINCE(6, 6)
1568/*!
1569 \deprecated [6.6] Use \l territoryToString() instead.
1570
1571 Returns a QString containing the name of \a country.
1572
1573 \sa languageToString(), scriptToString(), territory(), bcp47Name()
1574*/
1575QString QLocale::countryToString(Country country)
1576{
1577 return territoryToString(territory: country);
1578}
1579#endif
1580
1581/*!
1582 \since 4.8
1583
1584 Returns a QString containing the name of \a script.
1585
1586 \sa languageToString(), territoryToString(), script(), bcp47Name()
1587*/
1588QString QLocale::scriptToString(QLocale::Script script)
1589{
1590 if (script > QLocale::LastScript)
1591 return "Unknown"_L1;
1592 return QLatin1StringView(script_name_list + script_name_index[script]);
1593}
1594
1595/*!
1596 \fn short QLocale::toShort(const QString &s, bool *ok) const
1597
1598 Returns the short int represented by the localized string \a s.
1599
1600 If the conversion fails the function returns 0.
1601
1602 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1603 to \c false, and success by setting *\a{ok} to \c true.
1604
1605 This function ignores leading and trailing whitespace.
1606
1607 \sa toUShort(), toString()
1608*/
1609
1610/*!
1611 \fn ushort QLocale::toUShort(const QString &s, bool *ok) const
1612
1613 Returns the unsigned short int represented by the localized string \a s.
1614
1615 If the conversion fails the function returns 0.
1616
1617 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1618 to \c false, and success by setting *\a{ok} to \c true.
1619
1620 This function ignores leading and trailing whitespace.
1621
1622 \sa toShort(), toString()
1623*/
1624
1625/*!
1626 \fn int QLocale::toInt(const QString &s, bool *ok) const
1627 Returns the int represented by the localized string \a s.
1628
1629 If the conversion fails the function returns 0.
1630
1631 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1632 to \c false, and success by setting *\a{ok} to \c true.
1633
1634 This function ignores leading and trailing whitespace.
1635
1636 \sa toUInt(), toString()
1637*/
1638
1639/*!
1640 \fn uint QLocale::toUInt(const QString &s, bool *ok) const
1641 Returns the unsigned int represented by the localized string \a s.
1642
1643 If the conversion fails the function returns 0.
1644
1645 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1646 to \c false, and success by setting *\a{ok} to \c true.
1647
1648 This function ignores leading and trailing whitespace.
1649
1650 \sa toInt(), toString()
1651*/
1652
1653/*!
1654 \since 5.13
1655 \fn long QLocale::toLong(const QString &s, bool *ok) const
1656
1657 Returns the long int represented by the localized string \a s.
1658
1659 If the conversion fails the function returns 0.
1660
1661 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1662 to \c false, and success by setting *\a{ok} to \c true.
1663
1664 This function ignores leading and trailing whitespace.
1665
1666 \sa toInt(), toULong(), toDouble(), toString()
1667*/
1668
1669/*!
1670 \since 5.13
1671 \fn ulong QLocale::toULong(const QString &s, bool *ok) const
1672
1673 Returns the unsigned long int represented by the localized
1674 string \a s.
1675
1676 If the conversion fails the function returns 0.
1677
1678 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1679 to \c false, and success by setting *\a{ok} to \c true.
1680
1681 This function ignores leading and trailing whitespace.
1682
1683 \sa toLong(), toInt(), toDouble(), toString()
1684*/
1685
1686/*!
1687 \fn qlonglong QLocale::toLongLong(const QString &s, bool *ok) const
1688 Returns the long long int represented by the localized string \a s.
1689
1690 If the conversion fails the function returns 0.
1691
1692 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1693 to \c false, and success by setting *\a{ok} to \c true.
1694
1695 This function ignores leading and trailing whitespace.
1696
1697 \sa toInt(), toULongLong(), toDouble(), toString()
1698*/
1699
1700/*!
1701 \fn qulonglong QLocale::toULongLong(const QString &s, bool *ok) const
1702
1703 Returns the unsigned long long int represented by the localized
1704 string \a s.
1705
1706 If the conversion fails the function returns 0.
1707
1708 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1709 to \c false, and success by setting *\a{ok} to \c true.
1710
1711 This function ignores leading and trailing whitespace.
1712
1713 \sa toLongLong(), toInt(), toDouble(), toString()
1714*/
1715
1716/*!
1717 \fn float QLocale::toFloat(const QString &s, bool *ok) const
1718
1719 Returns the float represented by the localized string \a s.
1720
1721 Returns an infinity if the conversion overflows or 0.0 if the
1722 conversion fails for any other reason (e.g. underflow).
1723
1724 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1725 to \c false, and success by setting *\a{ok} to \c true.
1726
1727 This function ignores leading and trailing whitespace.
1728
1729 \sa toDouble(), toInt(), toString()
1730*/
1731
1732/*!
1733 \fn double QLocale::toDouble(const QString &s, bool *ok) const
1734 Returns the double represented by the localized string \a s.
1735
1736 Returns an infinity if the conversion overflows or 0.0 if the
1737 conversion fails for any other reason (e.g. underflow).
1738
1739 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1740 to \c false, and success by setting *\a{ok} to \c true.
1741
1742 \snippet code/src_corelib_text_qlocale.cpp 3
1743
1744 Notice that the last conversion returns 1234.0, because '.' is the
1745 thousands group separator in the German locale.
1746
1747 This function ignores leading and trailing whitespace.
1748
1749 \sa toFloat(), toInt(), toString()
1750*/
1751
1752/*!
1753 Returns the short int represented by the localized string \a s.
1754
1755 If the conversion fails, the function returns 0.
1756
1757 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1758 to \c false, and success by setting *\a{ok} to \c true.
1759
1760 This function ignores leading and trailing whitespace.
1761
1762 \sa toUShort(), toString()
1763
1764 \since 5.10
1765*/
1766
1767short QLocale::toShort(QStringView s, bool *ok) const
1768{
1769 return toIntegral_helper<short>(d, str: s, ok);
1770}
1771
1772/*!
1773 Returns the unsigned short int represented by the localized string \a s.
1774
1775 If the conversion fails, the function returns 0.
1776
1777 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1778 to \c false, and success by setting *\a{ok} to \c true.
1779
1780 This function ignores leading and trailing whitespace.
1781
1782 \sa toShort(), toString()
1783
1784 \since 5.10
1785*/
1786
1787ushort QLocale::toUShort(QStringView s, bool *ok) const
1788{
1789 return toIntegral_helper<ushort>(d, str: s, ok);
1790}
1791
1792/*!
1793 Returns the int represented by the localized string \a s.
1794
1795 If the conversion fails, the function returns 0.
1796
1797 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1798 to \c false, and success by setting *\a{ok} to \c true.
1799
1800 This function ignores leading and trailing whitespace.
1801
1802 \sa toUInt(), toString()
1803
1804 \since 5.10
1805*/
1806
1807int QLocale::toInt(QStringView s, bool *ok) const
1808{
1809 return toIntegral_helper<int>(d, str: s, ok);
1810}
1811
1812/*!
1813 Returns the unsigned int represented by the localized string \a s.
1814
1815 If the conversion fails, the function returns 0.
1816
1817 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1818 to \c false, and success by setting *\a{ok} to \c true.
1819
1820 This function ignores leading and trailing whitespace.
1821
1822 \sa toInt(), toString()
1823
1824 \since 5.10
1825*/
1826
1827uint QLocale::toUInt(QStringView s, bool *ok) const
1828{
1829 return toIntegral_helper<uint>(d, str: s, ok);
1830}
1831
1832/*!
1833 \since 5.13
1834 Returns the long int represented by the localized string \a s.
1835
1836 If the conversion fails the function returns 0.
1837
1838 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1839 to \c false, and success by setting *\a{ok} to \c true.
1840
1841 This function ignores leading and trailing whitespace.
1842
1843 \sa toInt(), toULong(), toDouble(), toString()
1844*/
1845
1846long QLocale::toLong(QStringView s, bool *ok) const
1847{
1848 return toIntegral_helper<long>(d, str: s, ok);
1849}
1850
1851/*!
1852 \since 5.13
1853 Returns the unsigned long int represented by the localized
1854 string \a s.
1855
1856 If the conversion fails the function returns 0.
1857
1858 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1859 to \c false, and success by setting *\a{ok} to \c true.
1860
1861 This function ignores leading and trailing whitespace.
1862
1863 \sa toLong(), toInt(), toDouble(), toString()
1864*/
1865
1866ulong QLocale::toULong(QStringView s, bool *ok) const
1867{
1868 return toIntegral_helper<ulong>(d, str: s, ok);
1869}
1870
1871/*!
1872 Returns the long long int represented by the localized string \a s.
1873
1874 If the conversion fails, the function returns 0.
1875
1876 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1877 to \c false, and success by setting *\a{ok} to \c true.
1878
1879 This function ignores leading and trailing whitespace.
1880
1881 \sa toInt(), toULongLong(), toDouble(), toString()
1882
1883 \since 5.10
1884*/
1885
1886
1887qlonglong QLocale::toLongLong(QStringView s, bool *ok) const
1888{
1889 return toIntegral_helper<qlonglong>(d, str: s, ok);
1890}
1891
1892/*!
1893 Returns the unsigned long long int represented by the localized
1894 string \a s.
1895
1896 If the conversion fails, the function returns 0.
1897
1898 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1899 to \c false, and success by setting *\a{ok} to \c true.
1900
1901 This function ignores leading and trailing whitespace.
1902
1903 \sa toLongLong(), toInt(), toDouble(), toString()
1904
1905 \since 5.10
1906*/
1907
1908qulonglong QLocale::toULongLong(QStringView s, bool *ok) const
1909{
1910 return toIntegral_helper<qulonglong>(d, str: s, ok);
1911}
1912
1913/*!
1914 Returns the float represented by the localized string \a s.
1915
1916 Returns an infinity if the conversion overflows or 0.0 if the
1917 conversion fails for any other reason (e.g. underflow).
1918
1919 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1920 to \c false, and success by setting *\a{ok} to \c true.
1921
1922 This function ignores leading and trailing whitespace.
1923
1924 \sa toDouble(), toInt(), toString()
1925
1926 \since 5.10
1927*/
1928
1929float QLocale::toFloat(QStringView s, bool *ok) const
1930{
1931 return QLocaleData::convertDoubleToFloat(d: toDouble(s, ok), ok);
1932}
1933
1934/*!
1935 Returns the double represented by the localized string \a s.
1936
1937 Returns an infinity if the conversion overflows or 0.0 if the
1938 conversion fails for any other reason (e.g. underflow).
1939
1940 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1941 to \c false, and success by setting *\a{ok} to \c true.
1942
1943 \snippet code/src_corelib_text_qlocale.cpp 3-qstringview
1944
1945 Notice that the last conversion returns 1234.0, because '.' is the
1946 thousands group separator in the German locale.
1947
1948 This function ignores leading and trailing whitespace.
1949
1950 \sa toFloat(), toInt(), toString()
1951
1952 \since 5.10
1953*/
1954
1955double QLocale::toDouble(QStringView s, bool *ok) const
1956{
1957 return d->m_data->stringToDouble(str: s, ok, options: d->m_numberOptions);
1958}
1959
1960/*!
1961 Returns a localized string representation of \a i.
1962
1963 \sa toLongLong(), numberOptions(), zeroDigit(), positiveSign()
1964*/
1965
1966QString QLocale::toString(qlonglong i) const
1967{
1968 int flags = (d->m_numberOptions & OmitGroupSeparator
1969 ? 0 : QLocaleData::GroupDigits);
1970
1971 return d->m_data->longLongToString(l: i, precision: -1, base: 10, width: -1, flags);
1972}
1973
1974/*!
1975 \overload
1976
1977 \sa toULongLong(), numberOptions(), zeroDigit(), positiveSign()
1978*/
1979
1980QString QLocale::toString(qulonglong i) const
1981{
1982 int flags = (d->m_numberOptions & OmitGroupSeparator
1983 ? 0 : QLocaleData::GroupDigits);
1984
1985 return d->m_data->unsLongLongToString(l: i, precision: -1, base: 10, width: -1, flags);
1986}
1987
1988/*!
1989 Returns a localized string representation of the given \a date in the
1990 specified \a format.
1991 If \a format is an empty string, an empty string is returned.
1992
1993 \sa QDate::toString()
1994*/
1995
1996QString QLocale::toString(QDate date, const QString &format) const
1997{
1998 return toString(date, format: qToStringViewIgnoringNull(s: format));
1999}
2000
2001/*!
2002 Returns a localized string representation of the given \a time according
2003 to the specified \a format.
2004 If \a format is an empty string, an empty string is returned.
2005
2006 \sa QTime::toString()
2007*/
2008
2009QString QLocale::toString(QTime time, const QString &format) const
2010{
2011 return toString(time, format: qToStringViewIgnoringNull(s: format));
2012}
2013
2014/*!
2015 \since 4.4
2016 \fn QString QLocale::toString(const QDateTime &dateTime, const QString &format) const
2017
2018 Returns a localized string representation of the given \a dateTime according
2019 to the specified \a format.
2020 If \a format is an empty string, an empty string is returned.
2021
2022 \sa QDateTime::toString(), QDate::toString(), QTime::toString()
2023*/
2024
2025/*!
2026 \since 5.14
2027
2028 Returns a localized string representation of the given \a date in the
2029 specified \a format, optionally for a specified calendar \a cal.
2030 If \a format is an empty string, an empty string is returned.
2031
2032 \sa QDate::toString()
2033*/
2034QString QLocale::toString(QDate date, QStringView format, QCalendar cal) const
2035{
2036 return cal.dateTimeToString(format, datetime: QDateTime(), dateOnly: date, timeOnly: QTime(), locale: *this);
2037}
2038
2039/*!
2040 \since 5.10
2041 \overload
2042*/
2043QString QLocale::toString(QDate date, QStringView format) const
2044{
2045 return QCalendar().dateTimeToString(format, datetime: QDateTime(), dateOnly: date, timeOnly: QTime(), locale: *this);
2046}
2047
2048/*!
2049 \since 5.14
2050
2051 Returns a localized string representation of the given \a date according
2052 to the specified \a format (see dateFormat()), optionally for a specified
2053 calendar \a cal.
2054
2055 \note Some locales may use formats that limit the range of years they can
2056 represent.
2057*/
2058QString QLocale::toString(QDate date, FormatType format, QCalendar cal) const
2059{
2060 if (!date.isValid())
2061 return QString();
2062
2063#ifndef QT_NO_SYSTEMLOCALE
2064 if (cal.isGregorian() && d->m_data == &systemLocaleData) {
2065 QVariant res = systemLocale()->query(type: format == LongFormat
2066 ? QSystemLocale::DateToStringLong
2067 : QSystemLocale::DateToStringShort,
2068 in: date);
2069 if (!res.isNull())
2070 return res.toString();
2071 }
2072#endif
2073
2074 QString format_str = dateFormat(format);
2075 return toString(date, format: format_str, cal);
2076}
2077
2078/*!
2079 \since 4.5
2080 \overload
2081*/
2082QString QLocale::toString(QDate date, FormatType format) const
2083{
2084 if (!date.isValid())
2085 return QString();
2086
2087#ifndef QT_NO_SYSTEMLOCALE
2088 if (d->m_data == &systemLocaleData) {
2089 QVariant res = systemLocale()->query(type: format == LongFormat
2090 ? QSystemLocale::DateToStringLong
2091 : QSystemLocale::DateToStringShort,
2092 in: date);
2093 if (!res.isNull())
2094 return res.toString();
2095 }
2096#endif
2097
2098 QString format_str = dateFormat(format);
2099 return toString(date, format: format_str);
2100}
2101
2102static bool timeFormatContainsAP(QStringView format)
2103{
2104 qsizetype i = 0;
2105 while (i < format.size()) {
2106 if (format.at(n: i).unicode() == '\'') {
2107 qt_readEscapedFormatString(format, idx: &i);
2108 continue;
2109 }
2110
2111 if (format.at(n: i).toLower().unicode() == 'a')
2112 return true;
2113
2114 ++i;
2115 }
2116 return false;
2117}
2118
2119/*!
2120 \since 4.5
2121
2122 Returns a localized string representation of the given \a time according
2123 to the specified \a format.
2124 If \a format is an empty string, an empty string is returned.
2125
2126 \sa QTime::toString()
2127*/
2128QString QLocale::toString(QTime time, QStringView format) const
2129{
2130 return QCalendar().dateTimeToString(format, datetime: QDateTime(), dateOnly: QDate(), timeOnly: time, locale: *this);
2131}
2132
2133/*!
2134 \since 5.14
2135
2136 Returns a localized string representation of the given \a dateTime according
2137 to the specified \a format, optionally for a specified calendar \a cal.
2138 If \a format is an empty string, an empty string is returned.
2139
2140 \sa QDateTime::toString(), QDate::toString(), QTime::toString()
2141*/
2142QString QLocale::toString(const QDateTime &dateTime, QStringView format, QCalendar cal) const
2143{
2144 return cal.dateTimeToString(format, datetime: dateTime, dateOnly: QDate(), timeOnly: QTime(), locale: *this);
2145}
2146
2147/*!
2148 \since 5.10
2149 \overload
2150*/
2151QString QLocale::toString(const QDateTime &dateTime, QStringView format) const
2152{
2153 return QCalendar().dateTimeToString(format, datetime: dateTime, dateOnly: QDate(), timeOnly: QTime(), locale: *this);
2154}
2155
2156/*!
2157 \since 5.14
2158
2159 Returns a localized string representation of the given \a dateTime according
2160 to the specified \a format (see dateTimeFormat()), optionally for a
2161 specified calendar \a cal.
2162
2163 \note Some locales may use formats that limit the range of years they can
2164 represent.
2165*/
2166QString QLocale::toString(const QDateTime &dateTime, FormatType format, QCalendar cal) const
2167{
2168 if (!dateTime.isValid())
2169 return QString();
2170
2171#ifndef QT_NO_SYSTEMLOCALE
2172 if (cal.isGregorian() && d->m_data == &systemLocaleData) {
2173 QVariant res = systemLocale()->query(type: format == LongFormat
2174 ? QSystemLocale::DateTimeToStringLong
2175 : QSystemLocale::DateTimeToStringShort,
2176 in: dateTime);
2177 if (!res.isNull())
2178 return res.toString();
2179 }
2180#endif
2181
2182 const QString format_str = dateTimeFormat(format);
2183 return toString(dateTime, format: format_str, cal);
2184}
2185
2186/*!
2187 \since 4.4
2188 \overload
2189*/
2190QString QLocale::toString(const QDateTime &dateTime, FormatType format) const
2191{
2192 if (!dateTime.isValid())
2193 return QString();
2194
2195#ifndef QT_NO_SYSTEMLOCALE
2196 if (d->m_data == &systemLocaleData) {
2197 QVariant res = systemLocale()->query(type: format == LongFormat
2198 ? QSystemLocale::DateTimeToStringLong
2199 : QSystemLocale::DateTimeToStringShort,
2200 in: dateTime);
2201 if (!res.isNull())
2202 return res.toString();
2203 }
2204#endif
2205
2206 const QString format_str = dateTimeFormat(format);
2207 return toString(dateTime, format: format_str);
2208}
2209
2210
2211/*!
2212 Returns a localized string representation of the given \a time in the
2213 specified \a format (see timeFormat()).
2214*/
2215
2216QString QLocale::toString(QTime time, FormatType format) const
2217{
2218 if (!time.isValid())
2219 return QString();
2220
2221#ifndef QT_NO_SYSTEMLOCALE
2222 if (d->m_data == &systemLocaleData) {
2223 QVariant res = systemLocale()->query(type: format == LongFormat
2224 ? QSystemLocale::TimeToStringLong
2225 : QSystemLocale::TimeToStringShort,
2226 in: time);
2227 if (!res.isNull())
2228 return res.toString();
2229 }
2230#endif
2231
2232 QString format_str = timeFormat(format);
2233 return toString(time, format: format_str);
2234}
2235
2236/*!
2237 \since 4.1
2238
2239 Returns the date format used for the current locale.
2240
2241 If \a format is LongFormat, the format will be elaborate, otherwise it will be short.
2242 For example, LongFormat for the \c{en_US} locale is \c{dddd, MMMM d, yyyy},
2243 ShortFormat is \c{M/d/yy}.
2244
2245 \sa QDate::toString(), QDate::fromString()
2246*/
2247
2248QString QLocale::dateFormat(FormatType format) const
2249{
2250#ifndef QT_NO_SYSTEMLOCALE
2251 if (d->m_data == &systemLocaleData) {
2252 QVariant res = systemLocale()->query(type: format == LongFormat
2253 ? QSystemLocale::DateFormatLong
2254 : QSystemLocale::DateFormatShort,
2255 in: QVariant());
2256 if (!res.isNull())
2257 return res.toString();
2258 }
2259#endif
2260
2261 return (format == LongFormat
2262 ? d->m_data->longDateFormat()
2263 : d->m_data->shortDateFormat()
2264 ).getData(table: date_format_data);
2265}
2266
2267/*!
2268 \since 4.1
2269
2270 Returns the time format used for the current locale.
2271
2272 If \a format is LongFormat, the format will be elaborate, otherwise it will be short.
2273 For example, LongFormat for the \c{en_US} locale is \c{h:mm:ss AP t},
2274 ShortFormat is \c{h:mm AP}.
2275
2276 \sa QTime::toString(), QTime::fromString()
2277*/
2278
2279QString QLocale::timeFormat(FormatType format) const
2280{
2281#ifndef QT_NO_SYSTEMLOCALE
2282 if (d->m_data == &systemLocaleData) {
2283 QVariant res = systemLocale()->query(type: format == LongFormat
2284 ? QSystemLocale::TimeFormatLong
2285 : QSystemLocale::TimeFormatShort,
2286 in: QVariant());
2287 if (!res.isNull())
2288 return res.toString();
2289 }
2290#endif
2291
2292 return (format == LongFormat
2293 ? d->m_data->longTimeFormat()
2294 : d->m_data->shortTimeFormat()
2295 ).getData(table: time_format_data);
2296}
2297
2298/*!
2299 \since 4.4
2300
2301 Returns the date time format used for the current locale.
2302
2303 If \a format is LongFormat, the format will be elaborate, otherwise it will be short.
2304 For example, LongFormat for the \c{en_US} locale is \c{dddd, MMMM d, yyyy h:mm:ss AP t},
2305 ShortFormat is \c{M/d/yy h:mm AP}.
2306
2307 \sa QDateTime::toString(), QDateTime::fromString()
2308*/
2309
2310QString QLocale::dateTimeFormat(FormatType format) const
2311{
2312#ifndef QT_NO_SYSTEMLOCALE
2313 if (d->m_data == &systemLocaleData) {
2314 QVariant res = systemLocale()->query(type: format == LongFormat
2315 ? QSystemLocale::DateTimeFormatLong
2316 : QSystemLocale::DateTimeFormatShort,
2317 in: QVariant());
2318 if (!res.isNull()) {
2319 return res.toString();
2320 }
2321 }
2322#endif
2323 return dateFormat(format) + u' ' + timeFormat(format);
2324}
2325
2326#if QT_CONFIG(datestring)
2327/*!
2328 \since 4.4
2329
2330 Reads \a string as a time in a locale-specific \a format.
2331
2332 Parses \a string and returns the time it represents. The format of the time
2333 string is chosen according to the \a format parameter (see timeFormat()).
2334
2335 \note Any am/pm indicators used must match \l amText() or \l pmText(),
2336 ignoring case.
2337
2338 If the time could not be parsed, returns an invalid time.
2339
2340 \sa timeFormat(), toDate(), toDateTime(), QTime::fromString()
2341*/
2342QTime QLocale::toTime(const QString &string, FormatType format) const
2343{
2344 return toTime(string, format: timeFormat(format));
2345}
2346
2347/*!
2348 \since 4.4
2349
2350 Reads \a string as a date in a locale-specific \a format.
2351
2352 Parses \a string and returns the date it represents. The format of the date
2353 string is chosen according to the \a format parameter (see dateFormat()).
2354
2355 \note Month and day names, where used, must be given in the locale's
2356 language.
2357
2358 If the date could not be parsed, returns an invalid date.
2359
2360 \sa dateFormat(), toTime(), toDateTime(), QDate::fromString()
2361*/
2362QDate QLocale::toDate(const QString &string, FormatType format) const
2363{
2364 return toDate(string, format: dateFormat(format));
2365}
2366
2367/*!
2368 \since 5.14
2369 \overload
2370*/
2371QDate QLocale::toDate(const QString &string, FormatType format, QCalendar cal) const
2372{
2373 return toDate(string, format: dateFormat(format), cal);
2374}
2375
2376/*!
2377 \since 4.4
2378
2379 Reads \a string as a date-time in a locale-specific \a format.
2380
2381 Parses \a string and returns the date-time it represents. The format of the
2382 date string is chosen according to the \a format parameter (see
2383 dateFormat()).
2384
2385 \note Month and day names, where used, must be given in the locale's
2386 language. Any am/pm indicators used must match \l amText() or \l pmText(),
2387 ignoring case.
2388
2389 If the string could not be parsed, returns an invalid QDateTime.
2390
2391 \sa dateTimeFormat(), toTime(), toDate(), QDateTime::fromString()
2392*/
2393QDateTime QLocale::toDateTime(const QString &string, FormatType format) const
2394{
2395 return toDateTime(string, format: dateTimeFormat(format));
2396}
2397
2398/*!
2399 \since 5.14
2400 \overload
2401*/
2402QDateTime QLocale::toDateTime(const QString &string, FormatType format, QCalendar cal) const
2403{
2404 return toDateTime(string, format: dateTimeFormat(format), cal);
2405}
2406
2407/*!
2408 \since 4.4
2409
2410 Reads \a string as a time in the given \a format.
2411
2412 Parses \a string and returns the time it represents. See QTime::fromString()
2413 for the interpretation of \a format.
2414
2415 \note Any am/pm indicators used must match \l amText() or \l pmText(),
2416 ignoring case.
2417
2418 If the time could not be parsed, returns an invalid time.
2419
2420 \sa timeFormat(), toDate(), toDateTime(), QTime::fromString()
2421*/
2422QTime QLocale::toTime(const QString &string, const QString &format) const
2423{
2424 QTime time;
2425#if QT_CONFIG(datetimeparser)
2426 QDateTimeParser dt(QMetaType::QTime, QDateTimeParser::FromString, QCalendar());
2427 dt.setDefaultLocale(*this);
2428 if (dt.parseFormat(format))
2429 dt.fromString(text: string, date: nullptr, time: &time);
2430#else
2431 Q_UNUSED(string);
2432 Q_UNUSED(format);
2433#endif
2434 return time;
2435}
2436
2437/*!
2438 \since 4.4
2439
2440 Reads \a string as a date in the given \a format.
2441
2442 Parses \a string and returns the date it represents. See QDate::fromString()
2443 for the interpretation of \a format.
2444
2445 \note Month and day names, where used, must be given in the locale's
2446 language.
2447
2448 If the date could not be parsed, returns an invalid date.
2449
2450 \sa dateFormat(), toTime(), toDateTime(), QDate::fromString()
2451*/
2452QDate QLocale::toDate(const QString &string, const QString &format) const
2453{
2454 return toDate(string, format, cal: QCalendar());
2455}
2456
2457/*!
2458 \since 5.14
2459 \overload
2460*/
2461QDate QLocale::toDate(const QString &string, const QString &format, QCalendar cal) const
2462{
2463 QDate date;
2464#if QT_CONFIG(datetimeparser)
2465 QDateTimeParser dt(QMetaType::QDate, QDateTimeParser::FromString, cal);
2466 dt.setDefaultLocale(*this);
2467 if (dt.parseFormat(format))
2468 dt.fromString(text: string, date: &date, time: nullptr);
2469#else
2470 Q_UNUSED(string);
2471 Q_UNUSED(format);
2472 Q_UNUSED(cal);
2473#endif
2474 return date;
2475}
2476
2477/*!
2478 \since 4.4
2479
2480 Reads \a string as a date-time in the given \a format.
2481
2482 Parses \a string and returns the date-time it represents. See
2483 QDateTime::fromString() for the interpretation of \a format.
2484
2485 \note Month and day names, where used, must be given in the locale's
2486 language. Any am/pm indicators used must match \l amText() or \l pmText(),
2487 ignoring case.
2488
2489 If the string could not be parsed, returns an invalid QDateTime. If the
2490 string can be parsed and represents an invalid date-time (e.g. in a gap
2491 skipped by a time-zone transition), an invalid QDateTime is returned, whose
2492 toMSecsSinceEpoch() represents a near-by date-time that is valid. Passing
2493 that to fromMSecsSinceEpoch() will produce a valid date-time that isn't
2494 faithfully represented by the string parsed.
2495
2496 \sa dateTimeFormat(), toTime(), toDate(), QDateTime::fromString()
2497*/
2498QDateTime QLocale::toDateTime(const QString &string, const QString &format) const
2499{
2500 return toDateTime(string, format, cal: QCalendar());
2501}
2502
2503/*!
2504 \since 5.14
2505 \overload
2506*/
2507QDateTime QLocale::toDateTime(const QString &string, const QString &format, QCalendar cal) const
2508{
2509#if QT_CONFIG(datetimeparser)
2510 QDateTime datetime;
2511
2512 QDateTimeParser dt(QMetaType::QDateTime, QDateTimeParser::FromString, cal);
2513 dt.setDefaultLocale(*this);
2514 if (dt.parseFormat(format) && (dt.fromString(text: string, datetime: &datetime) || !datetime.isValid()))
2515 return datetime;
2516#else
2517 Q_UNUSED(string);
2518 Q_UNUSED(format);
2519 Q_UNUSED(cal);
2520#endif
2521 return QDateTime();
2522}
2523#endif // datestring
2524
2525/*!
2526 \since 4.1
2527
2528 Returns the fractional part separator for this locale.
2529
2530 This is the token that separates the whole number part from the fracional
2531 part in the representation of a number which has a fractional part. This is
2532 commonly called the "decimal point character" - even though, in many
2533 locales, it is not a "point" (or similar dot). It is (since Qt 6.0) returned
2534 as a string in case some locale needs more than one UTF-16 code-point to
2535 represent its separator.
2536
2537 \sa groupSeparator(), toString()
2538*/
2539QString QLocale::decimalPoint() const
2540{
2541 return d->m_data->decimalPoint();
2542}
2543
2544/*!
2545 \since 4.1
2546
2547 Returns the digit-grouping separator for this locale.
2548
2549 This is a token used to break up long sequences of digits, in the
2550 representation of a number, to make it easier to read. In some locales it
2551 may be empty, indicating that digits should not be broken up into groups in
2552 this way. In others it may be a spacing character. It is (since Qt 6.0)
2553 returned as a string in case some locale needs more than one UTF-16
2554 code-point to represent its separator.
2555
2556 \sa decimalPoint(), toString()
2557*/
2558QString QLocale::groupSeparator() const
2559{
2560 return d->m_data->groupSeparator();
2561}
2562
2563/*!
2564 \since 4.1
2565
2566 Returns the percent marker of this locale.
2567
2568 This is a token presumed to be appended to a number to indicate a
2569 percentage. It is (since Qt 6.0) returned as a string because, in some
2570 locales, it is not a single character - for example, because it includes a
2571 text-direction-control character.
2572
2573 \sa toString()
2574*/
2575QString QLocale::percent() const
2576{
2577 return d->m_data->percentSign();
2578}
2579
2580/*!
2581 \since 4.1
2582
2583 Returns the zero digit character of this locale.
2584
2585 This is a single Unicode character but may be encoded as a surrogate pair,
2586 so is (since Qt 6.0) returned as a string. In most locales, other digits
2587 follow it in Unicode ordering - however, some number systems, notably those
2588 using U+3007 as zero, do not have contiguous digits. Use toString() to
2589 obtain suitable representations of numbers, rather than trying to construct
2590 them from this zero digit.
2591
2592 \sa toString()
2593*/
2594QString QLocale::zeroDigit() const
2595{
2596 return d->m_data->zeroDigit();
2597}
2598
2599/*!
2600 \since 4.1
2601
2602 Returns the negative sign indicator of this locale.
2603
2604 This is a token presumed to be used as a prefix to a number to indicate that
2605 it is negative. It is (since Qt 6.0) returned as a string because, in some
2606 locales, it is not a single character - for example, because it includes a
2607 text-direction-control character.
2608
2609 \sa positiveSign(), toString()
2610*/
2611QString QLocale::negativeSign() const
2612{
2613 return d->m_data->negativeSign();
2614}
2615
2616/*!
2617 \since 4.5
2618
2619 Returns the positive sign indicator of this locale.
2620
2621 This is a token presumed to be used as a prefix to a number to indicate that
2622 it is positive. It is (since Qt 6.0) returned as a string because, in some
2623 locales, it is not a single character - for example, because it includes a
2624 text-direction-control character.
2625
2626 \sa negativeSign(), toString()
2627*/
2628QString QLocale::positiveSign() const
2629{
2630 return d->m_data->positiveSign();
2631}
2632
2633/*!
2634 \since 4.1
2635
2636 Returns the exponent separator for this locale.
2637
2638 This is a token used to separate mantissa from exponent in some
2639 floating-point numeric representations. It is (since Qt 6.0) returned as a
2640 string because, in some locales, it is not a single character - for example,
2641 it may consist of a multiplication sign and a representation of the "ten to
2642 the power" operator.
2643
2644 \sa toString(double, char, int)
2645*/
2646QString QLocale::exponential() const
2647{
2648 return d->m_data->exponentSeparator();
2649}
2650
2651/*!
2652 \overload
2653 Returns a string representing the floating-point number \a f.
2654
2655 The form of the representation is controlled by the \a format and \a
2656 precision parameters.
2657
2658 The \a format defaults to \c{'g'}. It can be any of the following:
2659
2660 \table
2661 \header \li Format \li Meaning \li Meaning of \a precision
2662 \row \li \c 'e' \li format as [-]9.9e[+|-]999 \li number of digits \e after the decimal point
2663 \row \li \c 'E' \li format as [-]9.9E[+|-]999 \li "
2664 \row \li \c 'f' \li format as [-]9.9 \li "
2665 \row \li \c 'F' \li same as \c 'f' except for INF and NAN (see below) \li "
2666 \row \li \c 'g' \li use \c 'e' or \c 'f' format, whichever is more concise \li maximum number of significant digits (trailing zeroes are omitted)
2667 \row \li \c 'G' \li use \c 'E' or \c 'F' format, whichever is more concise \li "
2668 \endtable
2669
2670 The special \a precision value QLocale::FloatingPointShortest selects the
2671 shortest representation that, when read as a number, gets back the original floating-point
2672 value. Aside from that, any negative \a precision is ignored in favor of the
2673 default, 6.
2674
2675 For the \c 'e', \c 'f' and \c 'g' formats, positive infinity is represented
2676 as "inf", negative infinity as "-inf" and floating-point NaN (not-a-number)
2677 values are represented as "nan". For the \c 'E', \c 'F' and \c 'G' formats,
2678 "INF" and "NAN" are used instead. This does not vary with locale.
2679
2680 \sa toDouble(), numberOptions(), exponential(), decimalPoint(), zeroDigit(),
2681 positiveSign(), percent(), toCurrencyString(), formattedDataSize(),
2682 QLocale::FloatingPointPrecisionOption
2683*/
2684
2685QString QLocale::toString(double f, char format, int precision) const
2686{
2687 QLocaleData::DoubleForm form = QLocaleData::DFDecimal;
2688 uint flags = isAsciiUpper(c: format) ? QLocaleData::CapitalEorX : 0;
2689
2690 switch (QtMiscUtils::toAsciiLower(ch: format)) {
2691 case 'f':
2692 form = QLocaleData::DFDecimal;
2693 break;
2694 case 'e':
2695 form = QLocaleData::DFExponent;
2696 break;
2697 case 'g':
2698 form = QLocaleData::DFSignificantDigits;
2699 break;
2700 default:
2701 break;
2702 }
2703
2704 if (!(d->m_numberOptions & OmitGroupSeparator))
2705 flags |= QLocaleData::GroupDigits;
2706 if (!(d->m_numberOptions & OmitLeadingZeroInExponent))
2707 flags |= QLocaleData::ZeroPadExponent;
2708 if (d->m_numberOptions & IncludeTrailingZeroesAfterDot)
2709 flags |= QLocaleData::AddTrailingZeroes;
2710 return d->m_data->doubleToString(d: f, precision, form, width: -1, flags);
2711}
2712
2713/*!
2714 \fn QLocale QLocale::c()
2715
2716 Returns a QLocale object initialized to the "C" locale.
2717
2718 This locale is based on en_US but with various quirks of its own, such as
2719 simplified number formatting and its own date formatting. It implements the
2720 POSIX standards that describe the behavior of standard library functions of
2721 the "C" programming language.
2722
2723 Among other things, this means its collation order is based on the ASCII
2724 values of letters, so that (for case-sensitive sorting) all upper-case
2725 letters sort before any lower-case one (rather than each letter's upper- and
2726 lower-case forms sorting adjacent to one another, before the next letter's
2727 two forms).
2728
2729 \sa system()
2730*/
2731
2732/*!
2733 Returns a QLocale object initialized to the system locale.
2734
2735 The system locale may use system-specific sources for locale data, where
2736 available, otherwise falling back on QLocale's built-in database entry for
2737 the language, script and territory the system reports.
2738
2739 For example, on Windows and Mac, this locale will use the decimal/grouping
2740 characters and date/time formats specified in the system configuration
2741 panel.
2742
2743 \sa c()
2744*/
2745
2746QLocale QLocale::system()
2747{
2748 QT_PREPEND_NAMESPACE(systemData)(); // Ensure system data is up to date.
2749 static QLocalePrivate locale(systemData(), defaultIndex(), DefaultNumberOptions, 1);
2750
2751 return QLocale(locale);
2752}
2753
2754/*!
2755 Returns a list of valid locale objects that match the given \a language, \a
2756 script and \a territory.
2757
2758 Getting a list of all locales:
2759 QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
2760 QLocale::AnyTerritory);
2761
2762 Getting a list of locales suitable for Russia:
2763 QList<QLocale> locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
2764 QLocale::Russia);
2765*/
2766QList<QLocale> QLocale::matchingLocales(QLocale::Language language, QLocale::Script script,
2767 QLocale::Territory territory)
2768{
2769 const QLocaleId filter { .language_id: language, .script_id: script, .territory_id: territory };
2770 if (!filter.isValid())
2771 return QList<QLocale>();
2772
2773 if (language == QLocale::C)
2774 return QList<QLocale>() << QLocale(QLocale::C);
2775
2776 QList<QLocale> result;
2777 if (filter.matchesAll())
2778 result.reserve(asize: locale_data_size);
2779
2780 quint16 index = locale_index[language];
2781 // There may be no matches, for some languages (e.g. Abkhazian at CLDR v39).
2782 while (filter.acceptLanguage(lang: locale_data[index].m_language_id)) {
2783 const QLocaleId id = locale_data[index].id();
2784 if (filter.acceptScriptTerritory(other: id)) {
2785 result.append(t: QLocale(*(id.language_id == C ? c_private()
2786 : new QLocalePrivate(locale_data + index, index))));
2787 }
2788 ++index;
2789 }
2790
2791 // Add current system locale, if it matches
2792 const auto syslocaledata = systemData();
2793
2794 if (filter.acceptLanguage(lang: syslocaledata->m_language_id)) {
2795 const QLocaleId id = syslocaledata->id();
2796 if (filter.acceptScriptTerritory(other: id))
2797 result.append(t: QLocale::system());
2798 }
2799
2800 return result;
2801}
2802
2803#if QT_DEPRECATED_SINCE(6, 6)
2804/*!
2805 \deprecated [6.6] Use \l matchingLocales() instead and consult the \l territory() of each.
2806 \since 4.3
2807
2808 Returns the list of countries that have entries for \a language in Qt's locale
2809 database. If the result is an empty list, then \a language is not represented in
2810 Qt's locale database.
2811
2812 \sa matchingLocales()
2813*/
2814QList<QLocale::Country> QLocale::countriesForLanguage(Language language)
2815{
2816 const auto locales = matchingLocales(language, script: AnyScript, territory: AnyCountry);
2817 QList<QLocale::Country> result;
2818 result.reserve(asize: locales.size());
2819 for (const auto &locale : locales)
2820 result.append(t: locale.territory());
2821 return result;
2822}
2823#endif
2824
2825/*!
2826 \since 4.2
2827
2828 Returns the localized name of \a month, in the format specified
2829 by \a type.
2830
2831 For example, if the locale is \c en_US and \a month is 1,
2832 \l LongFormat will return \c January. \l ShortFormat \c Jan,
2833 and \l NarrowFormat \c J.
2834
2835 \sa dayName(), standaloneMonthName()
2836*/
2837QString QLocale::monthName(int month, FormatType type) const
2838{
2839 return QCalendar().monthName(locale: *this, month, year: QCalendar::Unspecified, format: type);
2840}
2841
2842/*!
2843 \since 4.5
2844
2845 Returns the localized name of \a month that is used as a
2846 standalone text, in the format specified by \a type.
2847
2848 If the locale information doesn't specify the standalone month
2849 name then return value is the same as in monthName().
2850
2851 \sa monthName(), standaloneDayName()
2852*/
2853QString QLocale::standaloneMonthName(int month, FormatType type) const
2854{
2855 return QCalendar().standaloneMonthName(locale: *this, month, year: QCalendar::Unspecified, format: type);
2856}
2857
2858/*!
2859 \since 4.2
2860
2861 Returns the localized name of the \a day (where 1 represents
2862 Monday, 2 represents Tuesday and so on), in the format specified
2863 by \a type.
2864
2865 For example, if the locale is \c en_US and \a day is 1,
2866 \l LongFormat will return \c Monday, \l ShortFormat \c Mon,
2867 and \l NarrowFormat \c M.
2868
2869 \sa monthName(), standaloneDayName()
2870*/
2871QString QLocale::dayName(int day, FormatType type) const
2872{
2873 return QCalendar().weekDayName(locale: *this, day, format: type);
2874}
2875
2876/*!
2877 \since 4.5
2878
2879 Returns the localized name of the \a day (where 1 represents
2880 Monday, 2 represents Tuesday and so on) that is used as a
2881 standalone text, in the format specified by \a type.
2882
2883 If the locale information does not specify the standalone day
2884 name then return value is the same as in dayName().
2885
2886 \sa dayName(), standaloneMonthName()
2887*/
2888QString QLocale::standaloneDayName(int day, FormatType type) const
2889{
2890 return QCalendar().standaloneWeekDayName(locale: *this, day, format: type);
2891}
2892
2893// Calendar look-up of month and day names:
2894
2895/*!
2896 \internal
2897 */
2898
2899static QString rawMonthName(const QCalendarLocale &localeData,
2900 const char16_t *monthsData, int month,
2901 QLocale::FormatType type)
2902{
2903 QLocaleData::DataRange range;
2904 switch (type) {
2905 case QLocale::LongFormat:
2906 range = localeData.longMonth();
2907 break;
2908 case QLocale::ShortFormat:
2909 range = localeData.shortMonth();
2910 break;
2911 case QLocale::NarrowFormat:
2912 range = localeData.narrowMonth();
2913 break;
2914 default:
2915 return QString();
2916 }
2917 return range.getListEntry(table: monthsData, index: month - 1);
2918}
2919
2920/*!
2921 \internal
2922 */
2923
2924static QString rawStandaloneMonthName(const QCalendarLocale &localeData,
2925 const char16_t *monthsData, int month,
2926 QLocale::FormatType type)
2927{
2928 QLocaleData::DataRange range;
2929 switch (type) {
2930 case QLocale::LongFormat:
2931 range = localeData.longMonthStandalone();
2932 break;
2933 case QLocale::ShortFormat:
2934 range = localeData.shortMonthStandalone();
2935 break;
2936 case QLocale::NarrowFormat:
2937 range = localeData.narrowMonthStandalone();
2938 break;
2939 default:
2940 return QString();
2941 }
2942 QString name = range.getListEntry(table: monthsData, index: month - 1);
2943 return name.isEmpty() ? rawMonthName(localeData, monthsData, month, type) : name;
2944}
2945
2946/*!
2947 \internal
2948 */
2949
2950static QString rawWeekDayName(const QLocaleData *data, const int day,
2951 QLocale::FormatType type)
2952{
2953 QLocaleData::DataRange range;
2954 switch (type) {
2955 case QLocale::LongFormat:
2956 range = data->longDayNames();
2957 break;
2958 case QLocale::ShortFormat:
2959 range = data->shortDayNames();
2960 break;
2961 case QLocale::NarrowFormat:
2962 range = data->narrowDayNames();
2963 break;
2964 default:
2965 return QString();
2966 }
2967 return range.getListEntry(table: days_data, index: day == 7 ? 0 : day);
2968}
2969
2970/*!
2971 \internal
2972 */
2973
2974static QString rawStandaloneWeekDayName(const QLocaleData *data, const int day,
2975 QLocale::FormatType type)
2976{
2977 QLocaleData::DataRange range;
2978 switch (type) {
2979 case QLocale::LongFormat:
2980 range =data->longDayNamesStandalone();
2981 break;
2982 case QLocale::ShortFormat:
2983 range = data->shortDayNamesStandalone();
2984 break;
2985 case QLocale::NarrowFormat:
2986 range = data->narrowDayNamesStandalone();
2987 break;
2988 default:
2989 return QString();
2990 }
2991 QString name = range.getListEntry(table: days_data, index: day == 7 ? 0 : day);
2992 if (name.isEmpty())
2993 return rawWeekDayName(data, day, type);
2994 return name;
2995}
2996
2997// Refugees from qcalendar.cpp that need functions above:
2998
2999QString QCalendarBackend::monthName(const QLocale &locale, int month, int,
3000 QLocale::FormatType format) const
3001{
3002 Q_ASSERT(month >= 1 && month <= maximumMonthsInYear());
3003 return rawMonthName(localeData: localeMonthIndexData()[locale.d->m_index],
3004 monthsData: localeMonthData(), month, type: format);
3005}
3006
3007QString QGregorianCalendar::monthName(const QLocale &locale, int month, int year,
3008 QLocale::FormatType format) const
3009{
3010#ifndef QT_NO_SYSTEMLOCALE
3011 if (locale.d->m_data == &systemLocaleData) {
3012 Q_ASSERT(month >= 1 && month <= 12);
3013 QSystemLocale::QueryType queryType = QSystemLocale::MonthNameLong;
3014 switch (format) {
3015 case QLocale::LongFormat:
3016 queryType = QSystemLocale::MonthNameLong;
3017 break;
3018 case QLocale::ShortFormat:
3019 queryType = QSystemLocale::MonthNameShort;
3020 break;
3021 case QLocale::NarrowFormat:
3022 queryType = QSystemLocale::MonthNameNarrow;
3023 break;
3024 }
3025 QVariant res = systemLocale()->query(type: queryType, in: month);
3026 if (!res.isNull())
3027 return res.toString();
3028 }
3029#endif
3030
3031 return QCalendarBackend::monthName(locale, month, year, format);
3032}
3033
3034QString QCalendarBackend::standaloneMonthName(const QLocale &locale, int month, int,
3035 QLocale::FormatType format) const
3036{
3037 Q_ASSERT(month >= 1 && month <= maximumMonthsInYear());
3038 return rawStandaloneMonthName(localeData: localeMonthIndexData()[locale.d->m_index],
3039 monthsData: localeMonthData(), month, type: format);
3040}
3041
3042QString QGregorianCalendar::standaloneMonthName(const QLocale &locale, int month, int year,
3043 QLocale::FormatType format) const
3044{
3045#ifndef QT_NO_SYSTEMLOCALE
3046 if (locale.d->m_data == &systemLocaleData) {
3047 Q_ASSERT(month >= 1 && month <= 12);
3048 QSystemLocale::QueryType queryType = QSystemLocale::StandaloneMonthNameLong;
3049 switch (format) {
3050 case QLocale::LongFormat:
3051 queryType = QSystemLocale::StandaloneMonthNameLong;
3052 break;
3053 case QLocale::ShortFormat:
3054 queryType = QSystemLocale::StandaloneMonthNameShort;
3055 break;
3056 case QLocale::NarrowFormat:
3057 queryType = QSystemLocale::StandaloneMonthNameNarrow;
3058 break;
3059 }
3060 QVariant res = systemLocale()->query(type: queryType, in: month);
3061 if (!res.isNull())
3062 return res.toString();
3063 }
3064#endif
3065
3066 return QCalendarBackend::standaloneMonthName(locale, month, year, format);
3067}
3068
3069// Most calendars share the common week-day naming, modulo locale.
3070// Calendars that don't must override these methods.
3071QString QCalendarBackend::weekDayName(const QLocale &locale, int day,
3072 QLocale::FormatType format) const
3073{
3074 if (day < 1 || day > 7)
3075 return QString();
3076
3077#ifndef QT_NO_SYSTEMLOCALE
3078 if (locale.d->m_data == &systemLocaleData) {
3079 QSystemLocale::QueryType queryType = QSystemLocale::DayNameLong;
3080 switch (format) {
3081 case QLocale::LongFormat:
3082 queryType = QSystemLocale::DayNameLong;
3083 break;
3084 case QLocale::ShortFormat:
3085 queryType = QSystemLocale::DayNameShort;
3086 break;
3087 case QLocale::NarrowFormat:
3088 queryType = QSystemLocale::DayNameNarrow;
3089 break;
3090 }
3091 QVariant res = systemLocale()->query(type: queryType, in: day);
3092 if (!res.isNull())
3093 return res.toString();
3094 }
3095#endif
3096
3097 return rawWeekDayName(data: locale.d->m_data, day, type: format);
3098}
3099
3100QString QCalendarBackend::standaloneWeekDayName(const QLocale &locale, int day,
3101 QLocale::FormatType format) const
3102{
3103 if (day < 1 || day > 7)
3104 return QString();
3105
3106#ifndef QT_NO_SYSTEMLOCALE
3107 if (locale.d->m_data == &systemLocaleData) {
3108 QSystemLocale::QueryType queryType = QSystemLocale::StandaloneDayNameLong;
3109 switch (format) {
3110 case QLocale::LongFormat:
3111 queryType = QSystemLocale::StandaloneDayNameLong;
3112 break;
3113 case QLocale::ShortFormat:
3114 queryType = QSystemLocale::StandaloneDayNameShort;
3115 break;
3116 case QLocale::NarrowFormat:
3117 queryType = QSystemLocale::StandaloneDayNameNarrow;
3118 break;
3119 }
3120 QVariant res = systemLocale()->query(type: queryType, in: day);
3121 if (!res.isNull())
3122 return res.toString();
3123 }
3124#endif
3125
3126 return rawStandaloneWeekDayName(data: locale.d->m_data, day, type: format);
3127}
3128
3129// End of this block of qcalendar.cpp refugees. (One more follows.)
3130
3131/*!
3132 \since 4.8
3133
3134 Returns the first day of the week according to the current locale.
3135*/
3136Qt::DayOfWeek QLocale::firstDayOfWeek() const
3137{
3138#ifndef QT_NO_SYSTEMLOCALE
3139 if (d->m_data == &systemLocaleData) {
3140 const auto res = systemLocale()->query(type: QSystemLocale::FirstDayOfWeek);
3141 if (!res.isNull())
3142 return static_cast<Qt::DayOfWeek>(res.toUInt());
3143 }
3144#endif
3145 return static_cast<Qt::DayOfWeek>(d->m_data->m_first_day_of_week);
3146}
3147
3148QLocale::MeasurementSystem QLocalePrivate::measurementSystem() const
3149{
3150 for (const auto &system : ImperialMeasurementSystems) {
3151 if (system.languageId == m_data->m_language_id
3152 && system.territoryId == m_data->m_territory_id) {
3153 return system.system;
3154 }
3155 }
3156 return QLocale::MetricSystem;
3157}
3158
3159/*!
3160 \since 4.8
3161
3162 Returns a list of days that are considered weekdays according to the current locale.
3163*/
3164QList<Qt::DayOfWeek> QLocale::weekdays() const
3165{
3166#ifndef QT_NO_SYSTEMLOCALE
3167 if (d->m_data == &systemLocaleData) {
3168 auto res
3169 = qvariant_cast<QList<Qt::DayOfWeek> >(v: systemLocale()->query(type: QSystemLocale::Weekdays));
3170 if (!res.isEmpty())
3171 return res;
3172 }
3173#endif
3174 QList<Qt::DayOfWeek> weekdays;
3175 quint16 weekendStart = d->m_data->m_weekend_start;
3176 quint16 weekendEnd = d->m_data->m_weekend_end;
3177 for (int day = Qt::Monday; day <= Qt::Sunday; day++) {
3178 if ((weekendEnd >= weekendStart && (day < weekendStart || day > weekendEnd)) ||
3179 (weekendEnd < weekendStart && (day > weekendEnd && day < weekendStart)))
3180 weekdays << static_cast<Qt::DayOfWeek>(day);
3181 }
3182 return weekdays;
3183}
3184
3185/*!
3186 \since 4.4
3187
3188 Returns the measurement system for the locale.
3189*/
3190QLocale::MeasurementSystem QLocale::measurementSystem() const
3191{
3192#ifndef QT_NO_SYSTEMLOCALE
3193 if (d->m_data == &systemLocaleData) {
3194 const auto res = systemLocale()->query(type: QSystemLocale::MeasurementSystem);
3195 if (!res.isNull())
3196 return MeasurementSystem(res.toInt());
3197 }
3198#endif
3199
3200 return d->measurementSystem();
3201}
3202
3203/*!
3204 \since 4.7
3205
3206 Returns the text direction of the language.
3207*/
3208Qt::LayoutDirection QLocale::textDirection() const
3209{
3210 switch (script()) {
3211 case QLocale::AdlamScript:
3212 case QLocale::ArabicScript:
3213 case QLocale::AvestanScript:
3214 case QLocale::CypriotScript:
3215 case QLocale::HatranScript:
3216 case QLocale::HebrewScript:
3217 case QLocale::ImperialAramaicScript:
3218 case QLocale::InscriptionalPahlaviScript:
3219 case QLocale::InscriptionalParthianScript:
3220 case QLocale::KharoshthiScript:
3221 case QLocale::LydianScript:
3222 case QLocale::MandaeanScript:
3223 case QLocale::ManichaeanScript:
3224 case QLocale::MendeKikakuiScript:
3225 case QLocale::MeroiticCursiveScript:
3226 case QLocale::MeroiticScript:
3227 case QLocale::NabataeanScript:
3228 case QLocale::NkoScript:
3229 case QLocale::OldHungarianScript:
3230 case QLocale::OldNorthArabianScript:
3231 case QLocale::OldSouthArabianScript:
3232 case QLocale::OrkhonScript:
3233 case QLocale::PalmyreneScript:
3234 case QLocale::PhoenicianScript:
3235 case QLocale::PsalterPahlaviScript:
3236 case QLocale::SamaritanScript:
3237 case QLocale::SyriacScript:
3238 case QLocale::ThaanaScript:
3239 return Qt::RightToLeft;
3240 default:
3241 break;
3242 }
3243 return Qt::LeftToRight;
3244}
3245
3246/*!
3247 \since 4.8
3248
3249 Returns an uppercase copy of \a str.
3250
3251 If Qt Core is using the ICU libraries, they will be used to perform
3252 the transformation according to the rules of the current locale.
3253 Otherwise the conversion may be done in a platform-dependent manner,
3254 with QString::toUpper() as a generic fallback.
3255
3256 \sa QString::toUpper()
3257*/
3258QString QLocale::toUpper(const QString &str) const
3259{
3260#if QT_CONFIG(icu)
3261 bool ok = true;
3262 QString result = QIcu::toUpper(localeId: d->bcp47Name(separator: '_'), str, ok: &ok);
3263 if (ok)
3264 return result;
3265 // else fall through and use Qt's toUpper
3266#endif
3267 return str.toUpper();
3268}
3269
3270/*!
3271 \since 4.8
3272
3273 Returns a lowercase copy of \a str.
3274
3275 If Qt Core is using the ICU libraries, they will be used to perform
3276 the transformation according to the rules of the current locale.
3277 Otherwise the conversion may be done in a platform-dependent manner,
3278 with QString::toLower() as a generic fallback.
3279
3280 \sa QString::toLower()
3281*/
3282QString QLocale::toLower(const QString &str) const
3283{
3284#if QT_CONFIG(icu)
3285 bool ok = true;
3286 const QString result = QIcu::toLower(localeId: d->bcp47Name(separator: '_'), str, ok: &ok);
3287 if (ok)
3288 return result;
3289 // else fall through and use Qt's toUpper
3290#endif
3291 return str.toLower();
3292}
3293
3294
3295/*!
3296 \since 4.5
3297
3298 Returns the localized name of the "AM" suffix for times specified using
3299 the conventions of the 12-hour clock.
3300
3301 \sa pmText()
3302*/
3303QString QLocale::amText() const
3304{
3305#ifndef QT_NO_SYSTEMLOCALE
3306 if (d->m_data == &systemLocaleData) {
3307 auto res = systemLocale()->query(type: QSystemLocale::AMText).toString();
3308 if (!res.isEmpty())
3309 return res;
3310 }
3311#endif
3312 return d->m_data->anteMeridiem().getData(table: am_data);
3313}
3314
3315/*!
3316 \since 4.5
3317
3318 Returns the localized name of the "PM" suffix for times specified using
3319 the conventions of the 12-hour clock.
3320
3321 \sa amText()
3322*/
3323QString QLocale::pmText() const
3324{
3325#ifndef QT_NO_SYSTEMLOCALE
3326 if (d->m_data == &systemLocaleData) {
3327 auto res = systemLocale()->query(type: QSystemLocale::PMText).toString();
3328 if (!res.isEmpty())
3329 return res;
3330 }
3331#endif
3332 return d->m_data->postMeridiem().getData(table: pm_data);
3333}
3334
3335// Another intrusion from QCalendar, using some of the tools above:
3336
3337QString QCalendarBackend::dateTimeToString(QStringView format, const QDateTime &datetime,
3338 QDate dateOnly, QTime timeOnly,
3339 const QLocale &locale) const
3340{
3341 QDate date;
3342 QTime time;
3343 bool formatDate = false;
3344 bool formatTime = false;
3345 if (datetime.isValid()) {
3346 date = datetime.date();
3347 time = datetime.time();
3348 formatDate = true;
3349 formatTime = true;
3350 } else if (dateOnly.isValid()) {
3351 date = dateOnly;
3352 formatDate = true;
3353 } else if (timeOnly.isValid()) {
3354 time = timeOnly;
3355 formatTime = true;
3356 } else {
3357 return QString();
3358 }
3359
3360 QString result;
3361 int year = 0, month = 0, day = 0;
3362 if (formatDate) {
3363 const auto parts = julianDayToDate(jd: date.toJulianDay());
3364 if (!parts.isValid())
3365 return QString();
3366 year = parts.year;
3367 month = parts.month;
3368 day = parts.day;
3369 }
3370
3371 auto appendToResult = [&](int t, int repeat) {
3372 auto data = locale.d->m_data;
3373 if (repeat > 1)
3374 result.append(s: data->longLongToString(l: t, precision: -1, base: 10, width: repeat, flags: QLocaleData::ZeroPadded));
3375 else
3376 result.append(s: data->longLongToString(l: t));
3377 };
3378
3379 auto formatType = [](int repeat) {
3380 return repeat == 3 ? QLocale::ShortFormat : QLocale::LongFormat;
3381 };
3382
3383 qsizetype i = 0;
3384 while (i < format.size()) {
3385 if (format.at(n: i).unicode() == '\'') {
3386 result.append(s: qt_readEscapedFormatString(format, idx: &i));
3387 continue;
3388 }
3389
3390 const QChar c = format.at(n: i);
3391 qsizetype rep = qt_repeatCount(s: format.mid(pos: i));
3392 Q_ASSERT(rep < std::numeric_limits<int>::max());
3393 int repeat = int(rep);
3394 bool used = false;
3395 if (formatDate) {
3396 switch (c.unicode()) {
3397 case 'y':
3398 used = true;
3399 if (repeat >= 4)
3400 repeat = 4;
3401 else if (repeat >= 2)
3402 repeat = 2;
3403
3404 switch (repeat) {
3405 case 4:
3406 appendToResult(year, (year < 0) ? 5 : 4);
3407 break;
3408 case 2:
3409 appendToResult(year % 100, 2);
3410 break;
3411 default:
3412 repeat = 1;
3413 result.append(c);
3414 break;
3415 }
3416 break;
3417
3418 case 'M':
3419 used = true;
3420 repeat = qMin(a: repeat, b: 4);
3421 if (repeat <= 2)
3422 appendToResult(month, repeat);
3423 else
3424 result.append(s: monthName(locale, month, year, format: formatType(repeat)));
3425 break;
3426
3427 case 'd':
3428 used = true;
3429 repeat = qMin(a: repeat, b: 4);
3430 if (repeat <= 2)
3431 appendToResult(day, repeat);
3432 else
3433 result.append(
3434 s: locale.dayName(day: dayOfWeek(jd: date.toJulianDay()), type: formatType(repeat)));
3435 break;
3436
3437 default:
3438 break;
3439 }
3440 }
3441 if (!used && formatTime) {
3442 switch (c.unicode()) {
3443 case 'h': {
3444 used = true;
3445 repeat = qMin(a: repeat, b: 2);
3446 int hour = time.hour();
3447 if (timeFormatContainsAP(format)) {
3448 if (hour > 12)
3449 hour -= 12;
3450 else if (hour == 0)
3451 hour = 12;
3452 }
3453 appendToResult(hour, repeat);
3454 break;
3455 }
3456 case 'H':
3457 used = true;
3458 repeat = qMin(a: repeat, b: 2);
3459 appendToResult(time.hour(), repeat);
3460 break;
3461
3462 case 'm':
3463 used = true;
3464 repeat = qMin(a: repeat, b: 2);
3465 appendToResult(time.minute(), repeat);
3466 break;
3467
3468 case 's':
3469 used = true;
3470 repeat = qMin(a: repeat, b: 2);
3471 appendToResult(time.second(), repeat);
3472 break;
3473
3474 case 'A':
3475 case 'a': {
3476 QString text = time.hour() < 12 ? locale.amText() : locale.pmText();
3477 used = true;
3478 repeat = 1;
3479 if (format.mid(pos: i + 1).startsWith(c: u'p', cs: Qt::CaseInsensitive))
3480 ++repeat;
3481 if (c.unicode() == 'A' && (repeat == 1 || format.at(n: i + 1).unicode() == 'P'))
3482 text = std::move(text).toUpper();
3483 else if (c.unicode() == 'a' && (repeat == 1 || format.at(n: i + 1).unicode() == 'p'))
3484 text = std::move(text).toLower();
3485 // else 'Ap' or 'aP' => use CLDR text verbatim, preserving case
3486 result.append(s: text);
3487 break;
3488 }
3489
3490 case 'z':
3491 used = true;
3492 repeat = qMin(a: repeat, b: 3);
3493
3494 // note: the millisecond component is treated like the decimal part of the seconds
3495 // so ms == 2 is always printed as "002", but ms == 200 can be either "2" or "200"
3496 appendToResult(time.msec(), 3);
3497 if (repeat != 3) {
3498 if (result.endsWith(s: locale.zeroDigit()))
3499 result.chop(n: 1);
3500 if (result.endsWith(s: locale.zeroDigit()))
3501 result.chop(n: 1);
3502 }
3503 break;
3504
3505 case 't': {
3506 used = true;
3507 repeat = qMin(a: repeat, b: 4);
3508 // If we don't have a date-time, use the current system time:
3509 const QDateTime when = formatDate ? datetime : QDateTime::currentDateTime();
3510 QString text;
3511 switch (repeat) {
3512#if QT_CONFIG(timezone)
3513 case 4:
3514 text = when.timeZone().displayName(atDateTime: when, nameType: QTimeZone::LongName);
3515 break;
3516#endif // timezone
3517 case 3:
3518 case 2:
3519 text = when.toOffsetFromUtc(offsetSeconds: when.offsetFromUtc()).timeZoneAbbreviation();
3520 // If the offset is UTC that'll be a Qt::UTC, otherwise Qt::OffsetFromUTC.
3521 Q_ASSERT(text.startsWith("UTC"_L1));
3522 // The Qt::UTC case omits the zero offset, which we want:
3523 text = text.size() == 3 ? u"+00:00"_s : text.sliced(pos: 3);
3524 if (repeat == 2) // +hhmm format, rather than +hh:mm format
3525 text = text.remove(c: u':');
3526 break;
3527 default:
3528 text = when.timeZoneAbbreviation();
3529 break;
3530 }
3531 if (!text.isEmpty())
3532 result.append(s: text);
3533 break;
3534 }
3535
3536 default:
3537 break;
3538 }
3539 }
3540 if (!used)
3541 result.resize(size: result.size() + repeat, fillChar: c);
3542 i += repeat;
3543 }
3544
3545 return result;
3546}
3547
3548// End of QCalendar intrustions
3549
3550QString QLocaleData::doubleToString(double d, int precision, DoubleForm form,
3551 int width, unsigned flags) const
3552{
3553 // Although the special handling of F.P.Shortest below is limited to
3554 // DFSignificantDigits, the double-conversion library does treat it
3555 // specially for the other forms, shedding trailing zeros for DFDecimal and
3556 // using the shortest mantissa that faithfully represents the value for
3557 // DFExponent.
3558 if (precision != QLocale::FloatingPointShortest && precision < 0)
3559 precision = 6;
3560 if (width < 0)
3561 width = 0;
3562
3563 int decpt;
3564 qsizetype bufSize = 1;
3565 if (precision == QLocale::FloatingPointShortest)
3566 bufSize += std::numeric_limits<double>::max_digits10;
3567 else if (form == DFDecimal && qIsFinite(d))
3568 bufSize += wholePartSpace(d: qAbs(t: d)) + precision;
3569 else // Add extra digit due to different interpretations of precision.
3570 bufSize += qMax(a: 2, b: precision) + 1; // Must also be big enough for "nan" or "inf"
3571
3572 QVarLengthArray<char> buf(bufSize);
3573 int length;
3574 bool negative = false;
3575 qt_doubleToAscii(d, form, precision, buf: buf.data(), bufSize, sign&: negative, length, decpt);
3576
3577 const QString prefix = signPrefix(negative: negative && !isZero(d), flags);
3578 QString numStr;
3579
3580 if (length == 3
3581 && (qstrncmp(str1: buf.data(), str2: "inf", len: 3) == 0 || qstrncmp(str1: buf.data(), str2: "nan", len: 3) == 0)) {
3582 numStr = QString::fromLatin1(str: buf.data(), size: length);
3583 } else { // Handle finite values
3584 const QString zero = zeroDigit();
3585 QString digits = QString::fromLatin1(str: buf.data(), size: length);
3586
3587 if (zero == u"0") {
3588 // No need to convert digits.
3589 Q_ASSERT(std::all_of(buf.cbegin(), buf.cbegin() + length, isAsciiDigit));
3590 // That check is taken care of in unicodeForDigits, below.
3591 } else if (zero.size() == 2 && zero.at(i: 0).isHighSurrogate()) {
3592 const char32_t zeroUcs4 = QChar::surrogateToUcs4(high: zero.at(i: 0), low: zero.at(i: 1));
3593 QString converted;
3594 converted.reserve(asize: 2 * digits.size());
3595 for (QChar ch : std::as_const(t&: digits)) {
3596 const char32_t digit = unicodeForDigit(digit: ch.unicode() - '0', zero: zeroUcs4);
3597 Q_ASSERT(QChar::requiresSurrogates(digit));
3598 converted.append(c: QChar::highSurrogate(ucs4: digit));
3599 converted.append(c: QChar::lowSurrogate(ucs4: digit));
3600 }
3601 digits = converted;
3602 } else {
3603 Q_ASSERT(zero.size() == 1);
3604 Q_ASSERT(!zero.at(0).isSurrogate());
3605 char16_t z = zero.at(i: 0).unicode();
3606 char16_t *const value = reinterpret_cast<char16_t *>(digits.data());
3607 for (qsizetype i = 0; i < digits.size(); ++i)
3608 value[i] = unicodeForDigit(digit: value[i] - '0', zero: z);
3609 }
3610
3611 const bool mustMarkDecimal = flags & ForcePoint;
3612 const bool groupDigits = flags & GroupDigits;
3613 const int minExponentDigits = flags & ZeroPadExponent ? 2 : 1;
3614 switch (form) {
3615 case DFExponent:
3616 numStr = exponentForm(digits: std::move(digits), decpt, precision, pm: PMDecimalDigits,
3617 mustMarkDecimal, minExponentDigits);
3618 break;
3619 case DFDecimal:
3620 numStr = decimalForm(digits: std::move(digits), decpt, precision, pm: PMDecimalDigits,
3621 mustMarkDecimal, groupDigits);
3622 break;
3623 case DFSignificantDigits: {
3624 PrecisionMode mode
3625 = (flags & AddTrailingZeroes) ? PMSignificantDigits : PMChopTrailingZeros;
3626
3627 /* POSIX specifies sprintf() to follow fprintf(), whose 'g/G' format
3628 says; with P = 6 if precision unspecified else 1 if precision is
3629 0 else precision; when 'e/E' would have exponent X, use:
3630 * 'f/F' if P > X >= -4, with precision P-1-X
3631 * 'e/E' otherwise, with precision P-1
3632 Helpfully, we already have mapped precision < 0 to 6 - except for
3633 F.P.Shortest mode, which is its own story - and those of our
3634 callers with unspecified precision either used 6 or -1 for it.
3635 */
3636 bool useDecimal;
3637 if (precision == QLocale::FloatingPointShortest) {
3638 // Find out which representation is shorter.
3639 // Set bias to everything added to exponent form but not
3640 // decimal, minus the converse.
3641
3642 // Exponent adds separator, sign and digits:
3643 int bias = 2 + minExponentDigits;
3644 // Decimal form may get grouping separators inserted:
3645 if (groupDigits && decpt >= m_grouping_top + m_grouping_least)
3646 bias -= (decpt - m_grouping_least) / m_grouping_higher + 1;
3647 // X = decpt - 1 needs two digits if decpt > 10:
3648 if (decpt > 10 && minExponentDigits == 1)
3649 ++bias;
3650 // Assume digitCount < 95, so we can ignore the 3-digit
3651 // exponent case (we'll set useDecimal false anyway).
3652
3653 const qsizetype digitCount = digits.size() / zero.size();
3654 if (!mustMarkDecimal) {
3655 // Decimal separator is skipped if at end; adjust if
3656 // that happens for only one form:
3657 if (digitCount <= decpt && digitCount > 1)
3658 ++bias; // decimal but not exponent
3659 else if (digitCount == 1 && decpt <= 0)
3660 --bias; // exponent but not decimal
3661 }
3662 // When 0 < decpt <= digitCount, the forms have equal digit
3663 // counts, plus things bias has taken into account; otherwise
3664 // decimal form's digit count is right-padded with zeros to
3665 // decpt, when decpt is positive, otherwise it's left-padded
3666 // with 1 - decpt zeros.
3667 useDecimal = (decpt <= 0 ? 1 - decpt <= bias
3668 : decpt <= digitCount ? 0 <= bias : decpt <= digitCount + bias);
3669 } else {
3670 // X == decpt - 1, POSIX's P; -4 <= X < P iff -4 < decpt <= P
3671 Q_ASSERT(precision >= 0);
3672 useDecimal = decpt > -4 && decpt <= (precision ? precision : 1);
3673 }
3674
3675 numStr = useDecimal
3676 ? decimalForm(digits: std::move(digits), decpt, precision, pm: mode,
3677 mustMarkDecimal, groupDigits)
3678 : exponentForm(digits: std::move(digits), decpt, precision, pm: mode,
3679 mustMarkDecimal, minExponentDigits);
3680 break;
3681 }
3682 }
3683
3684 // Pad with zeros. LeftAdjusted overrides ZeroPadded.
3685 if (flags & ZeroPadded && !(flags & LeftAdjusted)) {
3686 for (qsizetype i = numStr.size() / zero.size() + prefix.size(); i < width; ++i)
3687 numStr.prepend(s: zero);
3688 }
3689 }
3690
3691 return prefix + (flags & CapitalEorX ? std::move(numStr).toUpper() : numStr);
3692}
3693
3694QString QLocaleData::decimalForm(QString &&digits, int decpt, int precision,
3695 PrecisionMode pm, bool mustMarkDecimal,
3696 bool groupDigits) const
3697{
3698 const QString zero = zeroDigit();
3699 const auto digitWidth = zero.size();
3700 Q_ASSERT(digitWidth == 1 || digitWidth == 2);
3701 Q_ASSERT(digits.size() % digitWidth == 0);
3702
3703 // Separator needs to go at index decpt: so add zeros before or after the
3704 // given digits, if they don't reach that position already:
3705 if (decpt < 0) {
3706 for (; decpt < 0; ++decpt)
3707 digits.prepend(s: zero);
3708 } else {
3709 for (qsizetype i = digits.size() / digitWidth; i < decpt; ++i)
3710 digits.append(s: zero);
3711 }
3712
3713 switch (pm) {
3714 case PMDecimalDigits:
3715 for (qsizetype i = digits.size() / digitWidth - decpt; i < precision; ++i)
3716 digits.append(s: zero);
3717 break;
3718 case PMSignificantDigits:
3719 for (qsizetype i = digits.size() / digitWidth; i < precision; ++i)
3720 digits.append(s: zero);
3721 break;
3722 case PMChopTrailingZeros:
3723 Q_ASSERT(digits.size() / digitWidth <= qMax(decpt, 1) || !digits.endsWith(zero));
3724 break;
3725 }
3726
3727 if (mustMarkDecimal || decpt < digits.size() / digitWidth)
3728 digits.insert(i: decpt * digitWidth, s: decimalPoint());
3729
3730 if (groupDigits) {
3731 const QString group = groupSeparator();
3732 qsizetype i = decpt - m_grouping_least;
3733 if (i >= m_grouping_top) {
3734 digits.insert(i: i * digitWidth, s: group);
3735 while ((i -= m_grouping_higher) > 0)
3736 digits.insert(i: i * digitWidth, s: group);
3737 }
3738 }
3739
3740 if (decpt == 0)
3741 digits.prepend(s: zero);
3742
3743 return std::move(digits);
3744}
3745
3746QString QLocaleData::exponentForm(QString &&digits, int decpt, int precision,
3747 PrecisionMode pm, bool mustMarkDecimal,
3748 int minExponentDigits) const
3749{
3750 const QString zero = zeroDigit();
3751 const auto digitWidth = zero.size();
3752 Q_ASSERT(digitWidth == 1 || digitWidth == 2);
3753 Q_ASSERT(digits.size() % digitWidth == 0);
3754
3755 switch (pm) {
3756 case PMDecimalDigits:
3757 for (qsizetype i = digits.size() / digitWidth; i < precision + 1; ++i)
3758 digits.append(s: zero);
3759 break;
3760 case PMSignificantDigits:
3761 for (qsizetype i = digits.size() / digitWidth; i < precision; ++i)
3762 digits.append(s: zero);
3763 break;
3764 case PMChopTrailingZeros:
3765 Q_ASSERT(digits.size() / digitWidth <= 1 || !digits.endsWith(zero));
3766 break;
3767 }
3768
3769 if (mustMarkDecimal || digits.size() > digitWidth)
3770 digits.insert(i: digitWidth, s: decimalPoint());
3771
3772 digits.append(s: exponentSeparator());
3773 digits.append(s: longLongToString(l: decpt - 1, precision: minExponentDigits, base: 10, width: -1, flags: AlwaysShowSign));
3774
3775 return std::move(digits);
3776}
3777
3778QString QLocaleData::signPrefix(bool negative, unsigned flags) const
3779{
3780 if (negative)
3781 return negativeSign();
3782 if (flags & AlwaysShowSign)
3783 return positiveSign();
3784 if (flags & BlankBeforePositive)
3785 return QStringView(u" ").toString();
3786 return {};
3787}
3788
3789QString QLocaleData::longLongToString(qlonglong n, int precision,
3790 int base, int width, unsigned flags) const
3791{
3792 bool negative = n < 0;
3793
3794 /*
3795 Negating std::numeric_limits<qlonglong>::min() hits undefined behavior, so
3796 taking an absolute value has to take a slight detour.
3797 */
3798 QString numStr = qulltoa(l: negative ? 1u + qulonglong(-(n + 1)) : qulonglong(n),
3799 base, zero: zeroDigit());
3800
3801 return applyIntegerFormatting(numStr: std::move(numStr), negative, precision, base, width, flags);
3802}
3803
3804QString QLocaleData::unsLongLongToString(qulonglong l, int precision,
3805 int base, int width, unsigned flags) const
3806{
3807 const QString zero = zeroDigit();
3808 QString resultZero = base == 10 ? zero : QStringLiteral("0");
3809 return applyIntegerFormatting(numStr: l ? qulltoa(l, base, zero) : resultZero,
3810 negative: false, precision, base, width, flags);
3811}
3812
3813QString QLocaleData::applyIntegerFormatting(QString &&numStr, bool negative, int precision,
3814 int base, int width, unsigned flags) const
3815{
3816 const QString zero = base == 10 ? zeroDigit() : QStringLiteral("0");
3817 const auto digitWidth = zero.size();
3818 const auto digitCount = numStr.size() / digitWidth;
3819
3820 const auto basePrefix = [&] () -> QStringView {
3821 if (flags & ShowBase) {
3822 const bool upper = flags & UppercaseBase;
3823 if (base == 16)
3824 return upper ? u"0X" : u"0x";
3825 if (base == 2)
3826 return upper ? u"0B" : u"0b";
3827 if (base == 8 && !numStr.startsWith(s: zero))
3828 return zero;
3829 }
3830 return {};
3831 }();
3832
3833 const QString prefix = signPrefix(negative, flags) + basePrefix;
3834 // Count how much of width we've used up. Each digit counts as one
3835 qsizetype usedWidth = digitCount + prefix.size();
3836
3837 if (base == 10 && flags & GroupDigits) {
3838 const QString group = groupSeparator();
3839 qsizetype i = digitCount - m_grouping_least;
3840 if (i >= m_grouping_top) {
3841 numStr.insert(i: i * digitWidth, s: group);
3842 ++usedWidth;
3843 while ((i -= m_grouping_higher) > 0) {
3844 numStr.insert(i: i * digitWidth, s: group);
3845 ++usedWidth;
3846 }
3847 }
3848 // TODO: should we group any zero-padding we add later ?
3849 }
3850
3851 const bool noPrecision = precision == -1;
3852 if (noPrecision)
3853 precision = 1;
3854
3855 for (qsizetype i = numStr.size(); i < precision; ++i) {
3856 numStr.prepend(s: zero);
3857 usedWidth++;
3858 }
3859
3860 // LeftAdjusted overrides ZeroPadded; and sprintf() only pads when
3861 // precision is not specified in the format string.
3862 if (noPrecision && flags & ZeroPadded && !(flags & LeftAdjusted)) {
3863 for (qsizetype i = usedWidth; i < width; ++i)
3864 numStr.prepend(s: zero);
3865 }
3866
3867 QString result(flags & CapitalEorX ? std::move(numStr).toUpper() : std::move(numStr));
3868 if (prefix.size())
3869 result.prepend(s: prefix);
3870 return result;
3871}
3872
3873inline QLocaleData::NumericData QLocaleData::numericData(QLocaleData::NumberMode mode) const
3874{
3875 NumericData result;
3876 if (this == c()) {
3877 result.isC = true;
3878 return result;
3879 }
3880 result.setZero(zero().viewData(table: single_character_data));
3881 result.group = groupDelim().viewData(table: single_character_data);
3882 // Note: minus, plus and exponent might not actually be single characters.
3883 result.minus = minus().viewData(table: single_character_data);
3884 result.plus = plus().viewData(table: single_character_data);
3885 if (mode != IntegerMode)
3886 result.decimal = decimalSeparator().viewData(table: single_character_data);
3887 if (mode == DoubleScientificMode) {
3888 result.exponent = exponential().viewData(table: single_character_data);
3889 // exponentCyrillic means "apply the Cyrrilic-specific exponent hack"
3890 result.exponentCyrillic = m_script_id == QLocale::CyrillicScript;
3891 }
3892#ifndef QT_NO_SYSTEMLOCALE
3893 if (this == &systemLocaleData) {
3894 const auto getString = [sys = systemLocale()](QSystemLocale::QueryType query) {
3895 return sys->query(type: query).toString();
3896 };
3897 if (mode != IntegerMode) {
3898 result.sysDecimal = getString(QSystemLocale::DecimalPoint);
3899 if (result.sysDecimal.size())
3900 result.decimal = QStringView{result.sysDecimal};
3901 }
3902 result.sysGroup = getString(QSystemLocale::GroupSeparator);
3903 if (result.sysGroup.size())
3904 result.group = QStringView{result.sysGroup};
3905 result.sysMinus = getString(QSystemLocale::NegativeSign);
3906 if (result.sysMinus.size())
3907 result.minus = QStringView{result.sysMinus};
3908 result.sysPlus = getString(QSystemLocale::PositiveSign);
3909 if (result.sysPlus.size())
3910 result.plus = QStringView{result.sysPlus};
3911 result.setZero(getString(QSystemLocale::ZeroDigit));
3912 }
3913#endif
3914
3915 return result;
3916}
3917
3918namespace {
3919// A bit like QStringIterator but rather specialized ... and some of the tokens
3920// it recognizes aren't single Unicode code-points (but it does map each to a
3921// single character).
3922class NumericTokenizer
3923{
3924 // TODO: use deterministic finite-state-automata.
3925 // TODO QTBUG-95460: CLDR has Inf/NaN representations per locale.
3926 static constexpr char lettersInfNaN[] = "afin"; // Letters of Inf, NaN
3927 static constexpr auto matchInfNaN = QtPrivate::makeCharacterSetMatch<lettersInfNaN>();
3928 const QStringView m_text;
3929 const QLocaleData::NumericData m_guide;
3930 qsizetype m_index = 0;
3931 const QLocaleData::NumberMode m_mode;
3932 static_assert('+' + 1 == ',' && ',' + 1 == '-' && '-' + 1 == '.');
3933 char lastMark; // C locale accepts '+' through lastMark.
3934public:
3935 NumericTokenizer(QStringView text, QLocaleData::NumericData &&guide,
3936 QLocaleData::NumberMode mode)
3937 : m_text(text), m_guide(guide), m_mode(mode),
3938 lastMark(mode == QLocaleData::IntegerMode ? '-' : '.')
3939 {
3940 Q_ASSERT(m_guide.isValid(mode));
3941 }
3942 bool done() const { return !(m_index < m_text.size()); }
3943 qsizetype index() const { return m_index; }
3944 inline int asBmpDigit(char16_t digit) const;
3945 char nextToken();
3946};
3947
3948int NumericTokenizer::asBmpDigit(char16_t digit) const
3949{
3950 // If digit *is* a digit, result will be in range 0 through 9; otherwise not.
3951 // Must match qlocale_tools.h's unicodeForDigit()
3952 if (m_guide.zeroUcs != u'\u3007' || digit == m_guide.zeroUcs)
3953 return digit - m_guide.zeroUcs;
3954
3955 // QTBUG-85409: Suzhou's digits aren't contiguous !
3956 if (digit == u'\u3020') // U+3020 POSTAL MARK FACE is not a digit.
3957 return -1;
3958 // ... but is followed by digits 1 through 9.
3959 return digit - u'\u3020';
3960}
3961
3962char NumericTokenizer::nextToken()
3963{
3964 // As long as caller stops iterating on a zero return, those don't need to
3965 // keep m_index correctly updated.
3966 Q_ASSERT(!done());
3967 // Mauls non-letters above 'Z' but we don't care:
3968 const auto asciiLower = [](unsigned char c) { return c >= 'A' ? c | 0x20 : c; };
3969 const QStringView tail = m_text.sliced(pos: m_index);
3970 const QChar ch = tail.front();
3971 if (ch == u'\u2212') {
3972 // Special case: match the "proper" minus sign, for all locales.
3973 ++m_index;
3974 return '-';
3975 }
3976 if (m_guide.isC) {
3977 // "Conversion" to C locale is just a filter:
3978 ++m_index;
3979 if (Q_LIKELY(ch.unicode() < 256)) {
3980 unsigned char ascii = asciiLower(ch.toLatin1());
3981 if (Q_LIKELY(isAsciiDigit(ascii) || ('+' <= ascii && ascii <= lastMark)
3982 // No caller presently (6.5) passes DoubleStandardMode,
3983 // so !IntegerMode implies scientific, for now.
3984 || (m_mode != QLocaleData::IntegerMode
3985 && matchInfNaN.matches(ascii))
3986 || (m_mode == QLocaleData::DoubleScientificMode
3987 && ascii == 'e'))) {
3988 return ascii;
3989 }
3990 }
3991 return 0;
3992 }
3993 if (ch.unicode() < 256) {
3994 // Accept the C locale's digits and signs in all locales:
3995 char ascii = asciiLower(ch.toLatin1());
3996 if (isAsciiDigit(c: ascii) || ascii == '-' || ascii == '+'
3997 // Also its Inf and NaN letters:
3998 || (m_mode != QLocaleData::IntegerMode && matchInfNaN.matches(c: ascii))) {
3999 ++m_index;
4000 return ascii;
4001 }
4002 }
4003
4004 // Other locales may be trickier:
4005 if (tail.startsWith(s: m_guide.minus)) {
4006 m_index += m_guide.minus.size();
4007 return '-';
4008 }
4009 if (tail.startsWith(s: m_guide.plus)) {
4010 m_index += m_guide.plus.size();
4011 return '+';
4012 }
4013 if (!m_guide.group.isEmpty() && tail.startsWith(s: m_guide.group)) {
4014 m_index += m_guide.group.size();
4015 return ',';
4016 }
4017 if (m_mode != QLocaleData::IntegerMode && tail.startsWith(s: m_guide.decimal)) {
4018 m_index += m_guide.decimal.size();
4019 return '.';
4020 }
4021 if (m_mode == QLocaleData::DoubleScientificMode
4022 && tail.startsWith(s: m_guide.exponent, cs: Qt::CaseInsensitive)) {
4023 m_index += m_guide.exponent.size();
4024 return 'e';
4025 }
4026
4027 // Must match qlocale_tools.h's unicodeForDigit()
4028 if (m_guide.zeroLen == 1) {
4029 if (!ch.isSurrogate()) {
4030 const uint gap = asBmpDigit(digit: ch.unicode());
4031 if (gap < 10u) {
4032 ++m_index;
4033 return '0' + gap;
4034 }
4035 } else if (ch.isHighSurrogate() && tail.size() > 1 && tail.at(n: 1).isLowSurrogate()) {
4036 return 0;
4037 }
4038 } else if (ch.isHighSurrogate()) {
4039 // None of the corner cases below matches a surrogate, so (update
4040 // already and) return early if we don't have a digit.
4041 if (tail.size() > 1) {
4042 QChar low = tail.at(n: 1);
4043 if (low.isLowSurrogate()) {
4044 m_index += 2;
4045 const uint gap = QChar::surrogateToUcs4(high: ch, low) - m_guide.zeroUcs;
4046 return gap < 10u ? '0' + gap : 0;
4047 }
4048 }
4049 return 0;
4050 }
4051
4052 // All cases where tail starts with properly-matched surrogate pair
4053 // have been handled by this point.
4054 Q_ASSERT(!(ch.isHighSurrogate() && tail.size() > 1 && tail.at(1).isLowSurrogate()));
4055
4056 // Weird corner cases follow (code above assumes these match no surrogates).
4057
4058 // Some locales use a non-breaking space (U+00A0) or its thin version
4059 // (U+202f) for grouping. These look like spaces, so people (and thus some
4060 // of our tests) use a regular space instead and complain if it doesn't
4061 // work.
4062 // Should this be extended generally to any case where group is a space ?
4063 if ((m_guide.group == u"\u00a0" || m_guide.group == u"\u202f") && tail.startsWith(c: u' ')) {
4064 ++m_index;
4065 return ',';
4066 }
4067
4068 // Cyrillic has its own E, used by Ukrainian as exponent; but others
4069 // writing Cyrillic may well use that; and Ukrainians might well use E.
4070 // All other Cyrillic locales (officially) use plain ASCII E.
4071 if (m_guide.exponentCyrillic // Only true in scientific float mode.
4072 && (tail.startsWith(s: u"\u0415", cs: Qt::CaseInsensitive)
4073 || tail.startsWith(s: u"E", cs: Qt::CaseInsensitive))) {
4074 ++m_index;
4075 return 'e';
4076 }
4077
4078 return 0;
4079}
4080} // namespace with no name
4081
4082/*
4083 Converts a number in locale representation to the C locale equivalent.
4084
4085 Only has to guarantee that a string that is a correct representation of a
4086 number will be converted. Checks signs, separators and digits appear in all
4087 the places they should, and nowhere else.
4088
4089 Returns true precisely if the number appears to be well-formed, modulo
4090 things a parser for C Locale strings (without digit-grouping separators;
4091 they're stripped) will catch. When it returns true, it records (and
4092 '\0'-terminates) the C locale representation in *result.
4093
4094 Note: only QString integer-parsing methods have a base parameter (hence need
4095 to cope with letters as possible digits); but these are now all routed via
4096 byteArrayToU?LongLong(), so no longer come via here. The QLocale
4097 number-parsers only work in decimal, so don't have to cope with any digits
4098 other than 0 through 9.
4099*/
4100bool QLocaleData::numberToCLocale(QStringView s, QLocale::NumberOptions number_options,
4101 NumberMode mode, CharBuff *result) const
4102{
4103 s = s.trimmed();
4104 if (s.size() < 1)
4105 return false;
4106 NumericTokenizer tokens(s, numericData(mode), mode);
4107
4108 // Digit-grouping details (all modes):
4109 qsizetype digitsInGroup = 0;
4110 qsizetype last_separator_idx = -1;
4111 qsizetype start_of_digits_idx = -1;
4112
4113 // Floating-point details (non-integer modes):
4114 qsizetype decpt_idx = -1;
4115 qsizetype exponent_idx = -1;
4116
4117 char last = '\0';
4118 while (!tokens.done()) {
4119 qsizetype idx = tokens.index(); // before nextToken() advances
4120 char out = tokens.nextToken();
4121 if (out == 0)
4122 return false;
4123 Q_ASSERT(tokens.index() > idx); // it always *should* advance (except on zero return)
4124
4125 if (out == '.') {
4126 // Fail if more than one decimal point or point after e
4127 if (decpt_idx != -1 || exponent_idx != -1)
4128 return false;
4129 decpt_idx = idx;
4130 } else if (out == 'e') {
4131 exponent_idx = idx;
4132 }
4133
4134 if (number_options.testFlag(flag: QLocale::RejectLeadingZeroInExponent)
4135 && exponent_idx != -1 && out == '0') {
4136 // After the exponent there can only be '+', '-' or digits.
4137 // If we find a '0' directly after some non-digit, then that is a
4138 // leading zero, acceptable only if it is the whole exponent.
4139 if (!tokens.done() && !isAsciiDigit(c: last))
4140 return false;
4141 }
4142
4143 if (number_options.testFlag(flag: QLocale::RejectTrailingZeroesAfterDot) && decpt_idx >= 0) {
4144 // In a fractional part, a 0 just before the exponent is trailing:
4145 if (idx == exponent_idx && last == '0')
4146 return false;
4147 }
4148
4149 if (!number_options.testFlag(flag: QLocale::RejectGroupSeparator)) {
4150 if (isAsciiDigit(c: out)) {
4151 if (start_of_digits_idx == -1)
4152 start_of_digits_idx = idx;
4153 ++digitsInGroup;
4154 } else if (out == ',') {
4155 // Don't allow group chars after the decimal point or exponent
4156 if (decpt_idx != -1 || exponent_idx != -1)
4157 return false;
4158
4159 if (last_separator_idx == -1) {
4160 // Check distance from the beginning of the digits:
4161 if (start_of_digits_idx == -1 || m_grouping_top > digitsInGroup
4162 || digitsInGroup >= m_grouping_least + m_grouping_top) {
4163 return false;
4164 }
4165 } else {
4166 // Check distance from the last separator:
4167 if (digitsInGroup != m_grouping_higher)
4168 return false;
4169 }
4170
4171 last_separator_idx = idx;
4172 digitsInGroup = 0;
4173 } else if (mode != IntegerMode && (out == '.' || idx == exponent_idx)
4174 && last_separator_idx != -1) {
4175 // Were there enough digits since the last group separator?
4176 if (digitsInGroup != m_grouping_least)
4177 return false;
4178
4179 // stop processing separators
4180 last_separator_idx = -1;
4181 }
4182 } else if (out == ',') {
4183 return false;
4184 }
4185
4186 last = out;
4187 if (out != ',') // Leave group separators out of the result.
4188 result->append(t: out);
4189 }
4190
4191 if (!number_options.testFlag(flag: QLocale::RejectGroupSeparator) && last_separator_idx != -1) {
4192 // Were there enough digits since the last group separator?
4193 if (digitsInGroup != m_grouping_least)
4194 return false;
4195 }
4196
4197 if (number_options.testFlag(flag: QLocale::RejectTrailingZeroesAfterDot)
4198 && decpt_idx != -1 && exponent_idx == -1) {
4199 // In the fractional part, a final zero is trailing:
4200 if (last == '0')
4201 return false;
4202 }
4203
4204 result->append(t: '\0');
4205 return true;
4206}
4207
4208bool QLocaleData::validateChars(QStringView str, NumberMode numMode, QByteArray *buff,
4209 int decDigits, QLocale::NumberOptions number_options) const
4210{
4211 buff->clear();
4212 buff->reserve(asize: str.size());
4213
4214 enum { Whole, Fractional, Exponent } state = Whole;
4215 const bool scientific = numMode == DoubleScientificMode;
4216 NumericTokenizer tokens(str, numericData(mode: numMode), numMode);
4217 char last = '\0';
4218
4219 while (!tokens.done()) {
4220 char c = tokens.nextToken();
4221
4222 if (isAsciiDigit(c)) {
4223 switch (state) {
4224 case Whole:
4225 // Nothing special to do (unless we want to check grouping sizes).
4226 break;
4227 case Fractional:
4228 // If a double has too many digits in its fractional part it is Invalid.
4229 if (decDigits-- == 0)
4230 return false;
4231 break;
4232 case Exponent:
4233 if (!isAsciiDigit(c: last)) {
4234 // This is the first digit in the exponent (there may have beena '+'
4235 // or '-' in before). If it's a zero, the exponent is zero-padded.
4236 if (c == '0' && (number_options & QLocale::RejectLeadingZeroInExponent))
4237 return false;
4238 }
4239 break;
4240 }
4241
4242 } else {
4243 switch (c) {
4244 case '.':
4245 // If an integer has a decimal point, it is Invalid.
4246 // A double can only have one, at the end of its whole-number part.
4247 if (numMode == IntegerMode || state != Whole)
4248 return false;
4249 // Even when decDigits is 0, we do allow the decimal point to be
4250 // present - just as long as no digits follow it.
4251
4252 state = Fractional;
4253 break;
4254
4255 case '+':
4256 case '-':
4257 // A sign can only appear at the start or after the e of scientific:
4258 if (last != '\0' && !(scientific && last == 'e'))
4259 return false;
4260 break;
4261
4262 case ',':
4263 // Grouping is only allowed after a digit in the whole-number portion:
4264 if ((number_options & QLocale::RejectGroupSeparator) || state != Whole
4265 || !isAsciiDigit(c: last)) {
4266 return false;
4267 }
4268 // We could check grouping sizes are correct, but fixup()s are
4269 // probably better off correcting any misplacement instead.
4270 break;
4271
4272 case 'e':
4273 // Only one e is allowed and only in scientific:
4274 if (!scientific || state == Exponent)
4275 return false;
4276 state = Exponent;
4277 break;
4278
4279 default:
4280 // Nothing else can validly appear in a number.
4281 // NumericTokenizer allows letters of "inf" and "nan", but
4282 // validators don't accept those values.
4283 // For anything else, tokens.nextToken() must have returned 0.
4284 Q_ASSERT(!c || c == 'a' || c == 'f' || c == 'i' || c == 'n');
4285 return false;
4286 }
4287 }
4288
4289 last = c;
4290 if (c != ',') // Skip grouping
4291 buff->append(c);
4292 }
4293
4294 return true;
4295}
4296
4297double QLocaleData::stringToDouble(QStringView str, bool *ok,
4298 QLocale::NumberOptions number_options) const
4299{
4300 CharBuff buff;
4301 if (!numberToCLocale(s: str, number_options, mode: DoubleScientificMode, result: &buff)) {
4302 if (ok != nullptr)
4303 *ok = false;
4304 return 0.0;
4305 }
4306 auto r = qt_asciiToDouble(num: buff.constData(), numLen: buff.size() - 1);
4307 if (ok != nullptr)
4308 *ok = r.ok();
4309 return r.result;
4310}
4311
4312qlonglong QLocaleData::stringToLongLong(QStringView str, int base, bool *ok,
4313 QLocale::NumberOptions number_options) const
4314{
4315 CharBuff buff;
4316 if (!numberToCLocale(s: str, number_options, mode: IntegerMode, result: &buff)) {
4317 if (ok != nullptr)
4318 *ok = false;
4319 return 0;
4320 }
4321
4322 return bytearrayToLongLong(num: QByteArrayView(buff.constData(), buff.size()), base, ok);
4323}
4324
4325qulonglong QLocaleData::stringToUnsLongLong(QStringView str, int base, bool *ok,
4326 QLocale::NumberOptions number_options) const
4327{
4328 CharBuff buff;
4329 if (!numberToCLocale(s: str, number_options, mode: IntegerMode, result: &buff)) {
4330 if (ok != nullptr)
4331 *ok = false;
4332 return 0;
4333 }
4334
4335 return bytearrayToUnsLongLong(num: QByteArrayView(buff.constData(), buff.size()), base, ok);
4336}
4337
4338static bool checkParsed(QByteArrayView num, qsizetype used)
4339{
4340 if (used <= 0)
4341 return false;
4342
4343 const qsizetype len = num.size();
4344 if (used < len && num[used] != '\0') {
4345 while (used < len && ascii_isspace(c: num[used]))
4346 ++used;
4347 }
4348
4349 if (used < len && num[used] != '\0')
4350 // we stopped at a non-digit character after converting some digits
4351 return false;
4352
4353 return true;
4354}
4355
4356qlonglong QLocaleData::bytearrayToLongLong(QByteArrayView num, int base, bool *ok)
4357{
4358 auto r = qstrntoll(nptr: num.data(), size: num.size(), base);
4359 const bool parsed = checkParsed(num, used: r.used);
4360 if (ok)
4361 *ok = parsed;
4362 return parsed ? r.result : 0;
4363}
4364
4365qulonglong QLocaleData::bytearrayToUnsLongLong(QByteArrayView num, int base, bool *ok)
4366{
4367 auto r = qstrntoull(nptr: num.data(), size: num.size(), base);
4368 const bool parsed = checkParsed(num, used: r.used);
4369 if (ok)
4370 *ok = parsed;
4371 return parsed ? r.result : 0;
4372}
4373
4374/*!
4375 \since 4.8
4376
4377 \enum QLocale::CurrencySymbolFormat
4378
4379 Specifies the format of the currency symbol.
4380
4381 \value CurrencyIsoCode a ISO-4217 code of the currency.
4382 \value CurrencySymbol a currency symbol.
4383 \value CurrencyDisplayName a user readable name of the currency.
4384*/
4385
4386/*!
4387 \since 4.8
4388 Returns a currency symbol according to the \a format.
4389*/
4390QString QLocale::currencySymbol(QLocale::CurrencySymbolFormat format) const
4391{
4392#ifndef QT_NO_SYSTEMLOCALE
4393 if (d->m_data == &systemLocaleData) {
4394 auto res = systemLocale()->query(type: QSystemLocale::CurrencySymbol, in: format).toString();
4395 if (!res.isEmpty())
4396 return res;
4397 }
4398#endif
4399 switch (format) {
4400 case CurrencySymbol:
4401 return d->m_data->currencySymbol().getData(table: currency_symbol_data);
4402 case CurrencyDisplayName:
4403 return d->m_data->currencyDisplayName().getData(table: currency_display_name_data);
4404 case CurrencyIsoCode: {
4405 const char *code = d->m_data->m_currency_iso_code;
4406 if (auto len = qstrnlen(str: code, maxlen: 3))
4407 return QString::fromLatin1(str: code, size: qsizetype(len));
4408 break;
4409 }
4410 }
4411 return QString();
4412}
4413
4414/*!
4415 \since 4.8
4416
4417 Returns a localized string representation of \a value as a currency.
4418 If the \a symbol is provided it is used instead of the default currency symbol.
4419
4420 \sa currencySymbol()
4421*/
4422QString QLocale::toCurrencyString(qlonglong value, const QString &symbol) const
4423{
4424#ifndef QT_NO_SYSTEMLOCALE
4425 if (d->m_data == &systemLocaleData) {
4426 QSystemLocale::CurrencyToStringArgument arg(value, symbol);
4427 auto res = systemLocale()->query(type: QSystemLocale::CurrencyToString,
4428 in: QVariant::fromValue(value: arg)).toString();
4429 if (!res.isEmpty())
4430 return res;
4431 }
4432#endif
4433 QLocaleData::DataRange range = d->m_data->currencyFormatNegative();
4434 if (!range.size || value >= 0)
4435 range = d->m_data->currencyFormat();
4436 else
4437 value = -value;
4438 QString str = toString(i: value);
4439 QString sym = symbol.isNull() ? currencySymbol() : symbol;
4440 if (sym.isEmpty())
4441 sym = currencySymbol(format: QLocale::CurrencyIsoCode);
4442 return range.viewData(table: currency_format_data).arg(args&: str, args&: sym);
4443}
4444
4445/*!
4446 \since 4.8
4447 \overload
4448*/
4449QString QLocale::toCurrencyString(qulonglong value, const QString &symbol) const
4450{
4451#ifndef QT_NO_SYSTEMLOCALE
4452 if (d->m_data == &systemLocaleData) {
4453 QSystemLocale::CurrencyToStringArgument arg(value, symbol);
4454 auto res = systemLocale()->query(type: QSystemLocale::CurrencyToString,
4455 in: QVariant::fromValue(value: arg)).toString();
4456 if (!res.isEmpty())
4457 return res;
4458 }
4459#endif
4460 QString str = toString(i: value);
4461 QString sym = symbol.isNull() ? currencySymbol() : symbol;
4462 if (sym.isEmpty())
4463 sym = currencySymbol(format: QLocale::CurrencyIsoCode);
4464 return d->m_data->currencyFormat().getData(table: currency_format_data).arg(args&: str, args&: sym);
4465}
4466
4467/*!
4468 \since 5.7
4469 \overload toCurrencyString()
4470
4471 Returns a localized string representation of \a value as a currency.
4472 If the \a symbol is provided it is used instead of the default currency symbol.
4473 If the \a precision is provided it is used to set the precision of the currency value.
4474
4475 \sa currencySymbol()
4476 */
4477QString QLocale::toCurrencyString(double value, const QString &symbol, int precision) const
4478{
4479#ifndef QT_NO_SYSTEMLOCALE
4480 if (d->m_data == &systemLocaleData) {
4481 QSystemLocale::CurrencyToStringArgument arg(value, symbol);
4482 auto res = systemLocale()->query(type: QSystemLocale::CurrencyToString,
4483 in: QVariant::fromValue(value: arg)).toString();
4484 if (!res.isEmpty())
4485 return res;
4486 }
4487#endif
4488 QLocaleData::DataRange range = d->m_data->currencyFormatNegative();
4489 if (!range.size || value >= 0)
4490 range = d->m_data->currencyFormat();
4491 else
4492 value = -value;
4493 QString str = toString(f: value, format: 'f', precision: precision == -1 ? d->m_data->m_currency_digits : precision);
4494 QString sym = symbol.isNull() ? currencySymbol() : symbol;
4495 if (sym.isEmpty())
4496 sym = currencySymbol(format: QLocale::CurrencyIsoCode);
4497 return range.viewData(table: currency_format_data).arg(args&: str, args&: sym);
4498}
4499
4500/*!
4501 \fn QString QLocale::toCurrencyString(float i, const QString &symbol, int precision) const
4502 \overload toCurrencyString()
4503*/
4504
4505/*!
4506 \since 5.10
4507
4508 \enum QLocale::DataSizeFormat
4509
4510 Specifies the format for representation of data quantities.
4511
4512 \omitvalue DataSizeBase1000
4513 \omitvalue DataSizeSIQuantifiers
4514 \value DataSizeIecFormat format using base 1024 and IEC prefixes: KiB, MiB, GiB, ...
4515 \value DataSizeTraditionalFormat format using base 1024 and SI prefixes: kB, MB, GB, ...
4516 \value DataSizeSIFormat format using base 1000 and SI prefixes: kB, MB, GB, ...
4517
4518 \sa formattedDataSize()
4519*/
4520
4521/*!
4522 \since 5.10
4523
4524 Converts a size in bytes to a human-readable localized string, comprising a
4525 number and a quantified unit. The quantifier is chosen such that the number
4526 is at least one, and as small as possible. For example if \a bytes is
4527 16384, \a precision is 2, and \a format is \l DataSizeIecFormat (the
4528 default), this function returns "16.00 KiB"; for 1330409069609 bytes it
4529 returns "1.21 GiB"; and so on. If \a format is \l DataSizeIecFormat or
4530 \l DataSizeTraditionalFormat, the given number of bytes is divided by a
4531 power of 1024, with result less than 1024; for \l DataSizeSIFormat, it is
4532 divided by a power of 1000, with result less than 1000.
4533 \c DataSizeIecFormat uses the new IEC standard quantifiers Ki, Mi and so on,
4534 whereas \c DataSizeSIFormat uses the older SI quantifiers k, M, etc., and
4535 \c DataSizeTraditionalFormat abuses them.
4536*/
4537QString QLocale::formattedDataSize(qint64 bytes, int precision, DataSizeFormats format) const
4538{
4539 int power, base = 1000;
4540 if (!bytes) {
4541 power = 0;
4542 } else if (format & DataSizeBase1000) {
4543 power = int(std::log10(x: qAbs(t: bytes)) / 3);
4544 } else { // Compute log2(bytes) / 10:
4545 power = int((63 - qCountLeadingZeroBits(v: quint64(qAbs(t: bytes)))) / 10);
4546 base = 1024;
4547 }
4548 // Only go to doubles if we'll be using a quantifier:
4549 const QString number = power
4550 ? toString(f: bytes / std::pow(x: double(base), y: power), format: 'f', precision: qMin(a: precision, b: 3 * power))
4551 : toString(i: bytes);
4552
4553 // We don't support sizes in units larger than exbibytes because
4554 // the number of bytes would not fit into qint64.
4555 Q_ASSERT(power <= 6 && power >= 0);
4556 QStringView unit;
4557 if (power > 0) {
4558 QLocaleData::DataRange range = (format & DataSizeSIQuantifiers)
4559 ? d->m_data->byteAmountSI() : d->m_data->byteAmountIEC();
4560 unit = range.viewListEntry(table: byte_unit_data, index: power - 1);
4561 } else {
4562 unit = d->m_data->byteCount().viewData(table: byte_unit_data);
4563 }
4564
4565 return number + u' ' + unit;
4566}
4567
4568/*!
4569 \since 4.8
4570 \brief List of locale names for use in selecting translations
4571
4572 Each entry in the returned list is the dash-joined name of a locale,
4573 suitable to the user's preferences for what to translate the UI into. For
4574 example, if the user has configured their system to use English as used in
4575 the USA, the list would be "en-Latn-US", "en-US", "en". The order of entries
4576 is the order in which to check for translations; earlier items in the list
4577 are to be preferred over later ones.
4578
4579 Most likely you do not need to use this function directly, but just pass the
4580 QLocale object to the QTranslator::load() function.
4581
4582 \sa QTranslator, bcp47Name()
4583*/
4584QStringList QLocale::uiLanguages() const
4585{
4586 QStringList uiLanguages;
4587 QList<QLocaleId> localeIds;
4588#ifdef QT_NO_SYSTEMLOCALE
4589 constexpr bool isSystem = false;
4590#else
4591 const bool isSystem = d->m_data == &systemLocaleData;
4592 if (isSystem) {
4593 uiLanguages = systemLocale()->query(type: QSystemLocale::UILanguages).toStringList();
4594 // ... but we need to include likely-adjusted forms of each of those, too.
4595 // For now, collect up locale Ids representing the entries, for later processing:
4596 for (const auto &entry : std::as_const(t&: uiLanguages))
4597 localeIds.append(t: QLocaleId::fromName(name: entry));
4598 if (localeIds.isEmpty())
4599 localeIds.append(t: systemLocale()->fallbackLocale().d->m_data->id());
4600 // If the system locale (isn't C and) didn't include itself in the list,
4601 // or as fallback, presume to know better than it and put its name
4602 // first. (Known issue, QTBUG-104930, on some macOS versions when in
4603 // locale en_DE.) Our translation system might have a translation for a
4604 // locale the platform doesn't believe in.
4605 const QString name = bcp47Name();
4606 if (!name.isEmpty() && language() != C && !uiLanguages.contains(str: name)) {
4607 // That uses contains(name) as a cheap pre-test, but there may be an
4608 // entry that matches this on purging likely subtags.
4609 const QLocaleId mine = d->m_data->id().withLikelySubtagsRemoved();
4610 const auto isMine = [mine](const QString &entry) {
4611 return QLocaleId::fromName(name: entry).withLikelySubtagsRemoved() == mine;
4612 };
4613 if (std::none_of(first: uiLanguages.constBegin(), last: uiLanguages.constEnd(), pred: isMine)) {
4614 localeIds.prepend(t: d->m_data->id());
4615 uiLanguages.prepend(t: name);
4616 }
4617 }
4618 } else
4619#endif
4620 {
4621 localeIds.append(t: d->m_data->id());
4622 }
4623 for (qsizetype i = localeIds.size(); i-- > 0; ) {
4624 QLocaleId id = localeIds.at(i);
4625 qsizetype j;
4626 QByteArray prior;
4627 if (isSystem && i < uiLanguages.size()) {
4628 // Adding likely-adjusted forms to system locale's list.
4629 // Name the locale is derived from:
4630 prior = uiLanguages.at(i).toLatin1();
4631 // Insert just after the entry we're supplementing:
4632 j = i + 1;
4633 } else if (id.language_id == C) {
4634 // Attempt no likely sub-tag amendments to C:
4635 uiLanguages.append(t: QString::fromLatin1(ba: id.name()));
4636 continue;
4637 } else {
4638 // Plain locale or empty system uiLanguages; just append.
4639 prior = id.name();
4640 uiLanguages.append(t: QString::fromLatin1(ba: prior));
4641 j = uiLanguages.size();
4642 }
4643
4644 const QLocaleId max = id.withLikelySubtagsAdded();
4645 const QLocaleId min = max.withLikelySubtagsRemoved();
4646
4647 // Include minimal version (last) unless it's what our locale is derived from:
4648 if (auto name = min.name(); name != prior)
4649 uiLanguages.insert(i: j, t: QString::fromLatin1(ba: name));
4650 else if (!isSystem)
4651 --j; // bcp47Name() matches min(): put more specific forms *before* it.
4652
4653 if (id.script_id) {
4654 // Include scriptless version if likely-equivalent and distinct:
4655 id.script_id = 0;
4656 if (id != min && id.withLikelySubtagsAdded() == max) {
4657 if (auto name = id.name(); name != prior)
4658 uiLanguages.insert(i: j, t: QString::fromLatin1(ba: name));
4659 }
4660 }
4661
4662 if (!id.territory_id) {
4663 Q_ASSERT(!min.territory_id);
4664 Q_ASSERT(!id.script_id); // because we just cleared it.
4665 // Include version with territory if it likely-equivalent and distinct:
4666 id.territory_id = max.territory_id;
4667 if (id != max && id.withLikelySubtagsAdded() == max) {
4668 if (auto name = id.name(); name != prior)
4669 uiLanguages.insert(i: j, t: QString::fromLatin1(ba: name));
4670 }
4671 }
4672
4673 // Include version with all likely sub-tags (first) if distinct from the rest:
4674 if (max != min && max != id) {
4675 if (auto name = max.name(); name != prior)
4676 uiLanguages.insert(i: j, t: QString::fromLatin1(ba: name));
4677 }
4678 }
4679 return uiLanguages;
4680}
4681
4682/*!
4683 \since 5.13
4684
4685 Returns the locale to use for collation.
4686
4687 The result is usually this locale; however, the system locale (which is
4688 commonly the default locale) will return the system collation locale.
4689 The result is suitable for passing to QCollator's constructor.
4690
4691 \sa QCollator
4692*/
4693QLocale QLocale::collation() const
4694{
4695#ifndef QT_NO_SYSTEMLOCALE
4696 if (d->m_data == &systemLocaleData) {
4697 const auto res = systemLocale()->query(type: QSystemLocale::Collation).toString();
4698 if (!res.isEmpty())
4699 return QLocale(res);
4700 }
4701#endif
4702 return *this;
4703}
4704
4705/*!
4706 \since 4.8
4707
4708 Returns a native name of the language for the locale. For example
4709 "Schweizer Hochdeutsch" for the Swiss-German locale.
4710
4711 \sa nativeTerritoryName(), languageToString()
4712*/
4713QString QLocale::nativeLanguageName() const
4714{
4715#ifndef QT_NO_SYSTEMLOCALE
4716 if (d->m_data == &systemLocaleData) {
4717 auto res = systemLocale()->query(type: QSystemLocale::NativeLanguageName).toString();
4718 if (!res.isEmpty())
4719 return res;
4720 }
4721#endif
4722 return d->m_data->endonymLanguage().getData(table: endonyms_data);
4723}
4724
4725/*!
4726 \since 6.2
4727
4728 Returns a native name of the territory for the locale. For example
4729 "España" for Spanish/Spain locale.
4730
4731 \sa nativeLanguageName(), territoryToString()
4732*/
4733QString QLocale::nativeTerritoryName() const
4734{
4735#ifndef QT_NO_SYSTEMLOCALE
4736 if (d->m_data == &systemLocaleData) {
4737 auto res = systemLocale()->query(type: QSystemLocale::NativeTerritoryName).toString();
4738 if (!res.isEmpty())
4739 return res;
4740 }
4741#endif
4742 return d->m_data->endonymTerritory().getData(table: endonyms_data);
4743}
4744
4745#if QT_DEPRECATED_SINCE(6, 6)
4746/*!
4747 \deprecated [6.6] Use \l nativeTerritoryName() instead.
4748 \since 4.8
4749
4750 Returns a native name of the territory for the locale. For example
4751 "España" for Spanish/Spain locale.
4752
4753 \sa nativeLanguageName(), territoryToString()
4754*/
4755QString QLocale::nativeCountryName() const
4756{
4757 return nativeTerritoryName();
4758}
4759#endif
4760
4761#ifndef QT_NO_DEBUG_STREAM
4762QDebug operator<<(QDebug dbg, const QLocale &l)
4763{
4764 QDebugStateSaver saver(dbg);
4765 dbg.nospace().noquote()
4766 << "QLocale(" << QLocale::languageToString(language: l.language())
4767 << ", " << QLocale::scriptToString(script: l.script())
4768 << ", " << QLocale::territoryToString(territory: l.territory()) << ')';
4769 return dbg;
4770}
4771#endif
4772QT_END_NAMESPACE
4773
4774#ifndef QT_NO_QOBJECT
4775#include "moc_qlocale.cpp"
4776#endif
4777

source code of qtbase/src/corelib/text/qlocale.cpp