Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qtimezoneprivate.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2013 John Layt <jlayt@kde.org>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5
6#include "qtimezone.h"
8#if QT_CONFIG(timezone_locale)
9# include "qtimezonelocale_p.h"
10#endif
12
13#include <qdatastream.h>
14#include <qdebug.h>
15#include <qstring.h>
16
17#include <private/qcalendarmath_p.h>
18#include <private/qnumeric_p.h>
19#include <private/qtools_p.h>
20
21#include <algorithm>
22
24
25using namespace QtMiscUtils;
26using namespace QtTimeZoneCldr;
27using namespace Qt::StringLiterals;
28
29// For use with std::is_sorted() in assertions:
30[[maybe_unused]]
31constexpr bool earlierZoneData(ZoneData less, ZoneData more) noexcept
32{
33 return less.windowsIdKey < more.windowsIdKey
34 || (less.windowsIdKey == more.windowsIdKey && less.territory < more.territory);
35}
36
37[[maybe_unused]]
38static bool earlierWinData(WindowsData less, WindowsData more) noexcept
39{
40 // Actually only tested in the negative, to check more < less never happens,
41 // so should be true if more < less in either part; hence || not && combines.
42 return less.windowsIdKey < more.windowsIdKey
43 || less.windowsId().compare(more.windowsId(), Qt::CaseInsensitive) < 0;
44}
45
46// For use with std::lower_bound():
47constexpr bool atLowerUtcOffset(UtcData entry, qint32 offsetSeconds) noexcept
48{
49 return entry.offsetFromUtc < offsetSeconds;
50}
51
52constexpr bool atLowerWindowsKey(WindowsData entry, qint16 winIdKey) noexcept
53{
54 return entry.windowsIdKey < winIdKey;
55}
56
57static bool earlierAliasId(AliasData entry, QByteArrayView aliasId) noexcept
58{
59 return entry.aliasId().compare(aliasId, Qt::CaseInsensitive) < 0;
60}
61
63{
64 return entry.windowsId().compare(winId, Qt::CaseInsensitive) < 0;
65}
66
67constexpr bool zoneAtLowerWindowsKey(ZoneData entry, qint16 winIdKey) noexcept
68{
69 return entry.windowsIdKey < winIdKey;
70}
71
72// Static table-lookup helpers
73static quint16 toWindowsIdKey(const QByteArray &winId)
74{
75 // Key and winId are monotonic, table is sorted on them.
76 const auto data = std::lower_bound(std::begin(windowsDataTable), std::end(windowsDataTable),
77 winId, earlierWindowsId);
78 if (data != std::end(windowsDataTable) && data->windowsId() == winId)
79 return data->windowsIdKey;
80 return 0;
81}
82
84{
85 // Caller should be passing a valid (in range) key; and table is sorted in
86 // increasing order, with no gaps in numbering, starting with key = 1 at
87 // index [0]. So this should normally work:
88 if (Q_LIKELY(windowsIdKey > 0 && windowsIdKey <= std::size(windowsDataTable))) {
89 const auto &data = windowsDataTable[windowsIdKey - 1];
90 if (Q_LIKELY(data.windowsIdKey == windowsIdKey))
91 return data.windowsId().toByteArray();
92 }
93 // Fall back on binary chop - key and winId are monotonic, table is sorted on them:
94 const auto data = std::lower_bound(std::begin(windowsDataTable), std::end(windowsDataTable),
95 windowsIdKey, atLowerWindowsKey);
96 if (data != std::end(windowsDataTable) && data->windowsIdKey == windowsIdKey)
97 return data->windowsId().toByteArray();
98
99 return QByteArray();
100}
101
102static auto zoneStartForWindowsId(quint16 windowsIdKey) noexcept
103{
104 // Caller must check the resulting iterator isn't std::end(zoneDataTable)
105 // and does match windowsIdKey, since this is just the lower bound.
106 return std::lower_bound(std::begin(zoneDataTable), std::end(zoneDataTable),
107 windowsIdKey, zoneAtLowerWindowsKey);
108}
109
110/*
111 Base class implementing common utility routines, only instantiate for a null tz.
112*/
113
115{
116 // If std::is_sorted() were constexpr, the first could be a static_assert().
117 // From C++20, we should be able to rework it in terms of std::all_of().
118 Q_ASSERT(std::is_sorted(std::begin(zoneDataTable), std::end(zoneDataTable),
120 Q_ASSERT(std::is_sorted(std::begin(windowsDataTable), std::end(windowsDataTable),
122}
123
128
132
134{
135 return new QTimeZonePrivate(*this);
136}
137
139{
140 // TODO Too simple, but need to solve problem of comparing different derived classes
141 // Should work for all System and ICU classes as names guaranteed unique, but not for Simple.
142 // Perhaps once all classes have working transitions can compare full list?
143 return (m_id == other.m_id);
144}
145
147{
148 return !(*this == other);
149}
150
152{
153 return !m_id.isEmpty();
154}
155
157{
158 return m_id;
159}
160
162{
163 // Default fall-back mode, use the zoneTable to find Region of known Zones
164 const QLatin1StringView sought(m_id.data(), m_id.size());
165 for (const ZoneData &data : zoneDataTable) {
166 for (QLatin1StringView token : data.ids()) {
167 if (token == sought)
168 return QLocale::Territory(data.territory);
169 }
170 }
172}
173
175{
176 return QString();
177}
178
180 QTimeZone::NameType nameType,
181 const QLocale &locale) const
182{
183 const Data tran = data(atMSecsSinceEpoch);
184 if (tran.atMSecsSinceEpoch != invalidMSecs()) {
185 if (nameType == QTimeZone::OffsetName && locale.language() == QLocale::C)
186 return isoOffsetFormat(tran.offsetFromUtc);
187 if (nameType == QTimeZone::ShortName && isDataLocale(locale))
188 return tran.abbreviation;
189
190 QTimeZone::TimeType timeType
191 = tran.daylightTimeOffset != 0 ? QTimeZone::DaylightTime : QTimeZone::StandardTime;
192#if QT_CONFIG(timezone_locale)
193 return localeName(atMSecsSinceEpoch, tran.offsetFromUtc, timeType, nameType, locale);
194#else
195 return displayName(timeType, nameType, locale);
196#endif
197 }
198 return QString();
199}
200
201QString QTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
202 QTimeZone::NameType nameType,
203 const QLocale &locale) const
204{
205 const Data tran = data(timeType);
206 if (tran.atMSecsSinceEpoch != invalidMSecs()) {
207 if (nameType == QTimeZone::OffsetName && isDataLocale(locale))
208 return isoOffsetFormat(tran.offsetFromUtc);
209
210#if QT_CONFIG(timezone_locale)
211 return localeName(tran.atMSecsSinceEpoch, tran.offsetFromUtc, timeType, nameType, locale);
212#endif
213 }
214 return QString();
215}
216
218{
219 return displayName(atMSecsSinceEpoch, QTimeZone::ShortName, QLocale::c());
220}
221
222int QTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const
223{
224 const int std = standardTimeOffset(atMSecsSinceEpoch);
225 const int dst = daylightTimeOffset(atMSecsSinceEpoch);
226 const int bad = invalidSeconds();
227 return std == bad || dst == bad ? bad : std + dst;
228}
229
231{
232 Q_UNUSED(atMSecsSinceEpoch);
233 return invalidSeconds();
234}
235
237{
238 Q_UNUSED(atMSecsSinceEpoch);
239 return invalidSeconds();
240}
241
243{
244 return false;
245}
246
247bool QTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
248{
249 Q_UNUSED(atMSecsSinceEpoch);
250 return false;
251}
252
253QTimeZonePrivate::Data QTimeZonePrivate::data(QTimeZone::TimeType timeType) const
254{
255 // True if tran is valid and has the DST-ness to match timeType:
256 const auto validMatch = [timeType](const QTimeZonePrivate::Data &tran) {
257 return tran.atMSecsSinceEpoch != invalidMSecs()
258 && ((timeType == QTimeZone::DaylightTime) != (tran.daylightTimeOffset == 0));
259 };
260
261 // Get current tran, use if suitable:
262 const qint64 currentMSecs = QDateTime::currentMSecsSinceEpoch();
263 QTimeZonePrivate::Data tran = data(currentMSecs);
264 if (validMatch(tran))
265 return tran;
266
267 if (hasTransitions()) {
268 // Otherwise, next tran probably flips DST-ness:
269 tran = nextTransition(currentMSecs);
270 if (validMatch(tran))
271 return tran;
272
273 // Failing that, prev (or present, if current MSecs is exactly a
274 // transition moment) tran defines what data() got us and the one before
275 // that probably flips DST-ness; failing that, keep marching backwards
276 // in search of a DST interval:
277 tran = previousTransition(currentMSecs + 1);
278 while (tran.atMSecsSinceEpoch != invalidMSecs()) {
279 tran = previousTransition(tran.atMSecsSinceEpoch);
280 if (validMatch(tran))
281 return tran;
282 }
283 }
284 return {};
285}
286
298{
299 // Guess data is for the system locale unless backend overrides that.
300 return locale == QLocale::system();
301}
302
304{
305 Q_UNUSED(forMSecsSinceEpoch);
306 return {};
307}
308
309// Private only method for use by QDateTime to convert local msecs to epoch msecs
311 qint64 forLocalMSecs, QDateTimePrivate::TransitionOptions resolve) const
312{
313 auto dataToState = [](const QTimeZonePrivate::Data &d) {
314 return QDateTimePrivate::ZoneState(d.atMSecsSinceEpoch + d.offsetFromUtc * 1000,
315 d.offsetFromUtc,
316 d.daylightTimeOffset ? QDateTimePrivate::DaylightTime
318 };
319
320 /*
321 We need a UTC time at which to ask for the offset, in order to be able to
322 add that offset to forLocalMSecs, to get the UTC time we need.
323 Fortunately, all time-zone offsets have been less than 17 hours; and DST
324 transitions happen (much) more than thirty-four hours apart. So sampling
325 offset seventeen hours each side gives us information we can be sure
326 brackets the correct time and at most one DST transition.
327 */
328 std::integral_constant<qint64, 17 * 3600 * 1000> seventeenHoursInMSecs;
329 static_assert(-seventeenHoursInMSecs / 1000 < QTimeZone::MinUtcOffsetSecs
330 && seventeenHoursInMSecs / 1000 > QTimeZone::MaxUtcOffsetSecs);
331 qint64 millis;
332 // Clip the bracketing times to the bounds of the supported range.
333 const qint64 recent =
334 qSubOverflow(forLocalMSecs, seventeenHoursInMSecs, &millis) || millis < minMSecs()
335 ? minMSecs() : millis; // Necessarily <= forLocalMSecs + 1.
336 // (Given that minMSecs() is std::numeric_limits<qint64>::min() + 1.)
337 const qint64 imminent =
338 qAddOverflow(forLocalMSecs, seventeenHoursInMSecs, &millis)
339 ? maxMSecs() : millis; // Necessarily >= forLocalMSecs
340 // At most one of those was clipped to its boundary value:
341 Q_ASSERT(recent < imminent && seventeenHoursInMSecs < imminent - recent + 1);
342
343 const Data past = data(recent), future = data(imminent);
344 // > 99% of the time, past and future will agree:
345 if (Q_LIKELY(past.offsetFromUtc == future.offsetFromUtc
346 && past.standardTimeOffset == future.standardTimeOffset
347 // Those two imply same daylightTimeOffset.
348 && past.abbreviation == future.abbreviation)) {
349 Data data = future;
350 data.atMSecsSinceEpoch = forLocalMSecs - future.offsetFromUtc * 1000;
351 return dataToState(data);
352 }
353
354 /*
355 Offsets are Local - UTC, positive to the east of Greenwich, negative to
356 the west; DST offset normally exceeds standard offset, when DST applies.
357 When we have offsets on either side of a transition, the lower one is
358 standard, the higher is DST, unless we have data telling us it's the other
359 way round.
360
361 Non-DST transitions (jurisdictions changing time-zone and time-zones
362 changing their standard offset, typically) are described below as if they
363 were DST transitions (since these are more usual and familiar); the code
364 mostly concerns itself with offsets from UTC, described in terms of the
365 common case for changes in that. If there is no actual change in offset
366 (e.g. a DST transition cancelled by a standard offset change), this code
367 should handle it gracefully; without transitions, it'll see early == late
368 and take the easy path; with transitions, tran and nextTran get the
369 correct UTC time as atMSecsSinceEpoch so comparing to nextStart selects
370 the right one. In all other cases, the transition changes offset and the
371 reasoning that applies to DST applies just the same.
372
373 The resolution of transitions, specified by \a resolve, may be lead astray
374 if (as happens on Windows) the backend has been obliged to guess whether a
375 transition is in fact a DST one or a change to standard offset; or to
376 guess that the higher-offset side is the DST one (the reverse of this is
377 true for Ireland, using negative DST). There's not much we can do about
378 that, though.
379 */
380 if (hasTransitions()) {
381 /*
382 We have transitions.
383
384 Each transition gives the offsets to use until the next; so we need
385 the most recent transition before the time forLocalMSecs describes. If
386 it describes a time *in* a transition, we'll need both that transition
387 and the one before it. So find one transition that's probably after
388 (and not much before, otherwise) and another that's definitely before,
389 then work out which one to use. When both or neither work on
390 forLocalMSecs, use resolve to disambiguate.
391 */
392
393 // Get a transition definitely before the local MSecs; usually all we need.
394 // Only around the transition times might we need another.
395 Data tran = past; // Data after last transition before our window.
396 Q_ASSERT(forLocalMSecs < 0 || // Pre-epoch TZ info may be unavailable
397 forLocalMSecs - tran.offsetFromUtc * 1000 >= tran.atMSecsSinceEpoch);
398 // If offset actually exceeds 17 hours, that assert may trigger.
399 Data nextTran = nextTransition(tran.atMSecsSinceEpoch);
400 /*
401 Now walk those forward until they bracket forLocalMSecs with transitions.
402
403 One of the transitions should then be telling us the right offset to use.
404 In a transition, we need the transition before it (to describe the run-up
405 to the transition) and the transition itself; so we need to stop when
406 nextTran is (invalid or) that transition.
407 */
408 while (nextTran.atMSecsSinceEpoch != invalidMSecs()
409 && forLocalMSecs > nextTran.atMSecsSinceEpoch + nextTran.offsetFromUtc * 1000) {
410 Data newTran = nextTransition(nextTran.atMSecsSinceEpoch);
411 if (newTran.atMSecsSinceEpoch == invalidMSecs()
412 || newTran.atMSecsSinceEpoch + newTran.offsetFromUtc * 1000 > imminent) {
413 // Definitely not a relevant tansition: too far in the future.
414 break;
415 }
416 tran = nextTran;
417 nextTran = newTran;
418 }
419
420 // Check we do *really* have transitions for this zone:
421 if (tran.atMSecsSinceEpoch != invalidMSecs()) {
422 /* So now tran is definitely before ... */
423 Q_ASSERT(forLocalMSecs < 0
424 || forLocalMSecs - tran.offsetFromUtc * 1000 > tran.atMSecsSinceEpoch);
425 // Work out the UTC value it would make sense to return if using tran:
426 tran.atMSecsSinceEpoch = forLocalMSecs - tran.offsetFromUtc * 1000;
427 // If we know of no transition after it, the answer is easy:
428 const qint64 nextStart = nextTran.atMSecsSinceEpoch;
429 if (nextStart == invalidMSecs())
430 return dataToState(tran); // Last valid transition.
431
432 /*
433 ... and nextTran is either after or only slightly before. We're
434 going to interpret one as standard time, the other as DST
435 (although the transition might in fact be a change in standard
436 offset, or a change in DST offset, e.g. to/from double-DST).
437
438 Usually exactly one of those shall be relevant and we'll use it;
439 but if we're close to nextTran we may be in a transition, to be
440 settled according to resolve's rules.
441 */
442 // Work out the UTC value it would make sense to return if using nextTran:
443 nextTran.atMSecsSinceEpoch = forLocalMSecs - nextTran.offsetFromUtc * 1000;
444
445 bool fallBack = false;
446 if (nextStart > nextTran.atMSecsSinceEpoch) {
447 // If both UTC values are before nextTran's offset applies, use tran:
448 if (nextStart > tran.atMSecsSinceEpoch)
449 return dataToState(tran);
450
451 Q_ASSERT(tran.offsetFromUtc < nextTran.offsetFromUtc);
452 // We're in a spring-forward.
453 } else if (nextStart <= tran.atMSecsSinceEpoch) {
454 // Both UTC values say we should be using nextTran:
455 return dataToState(nextTran);
456 } else {
457 Q_ASSERT(nextTran.offsetFromUtc < tran.offsetFromUtc);
458 fallBack = true; // We're in a fall-back.
459 }
460 // (forLocalMSecs - nextStart) / 1000 lies between the two offsets.
461
462 // Apply resolve:
463 // Determine whether FlipForReverseDst affects the outcome:
464 const bool flipped
466 && (fallBack ? !tran.daylightTimeOffset && nextTran.daylightTimeOffset
467 : tran.daylightTimeOffset && !nextTran.daylightTimeOffset);
468
469 if (fallBack) {
470 if (resolve.testFlag(flipped
473 return dataToState(nextTran);
474 }
475 if (resolve.testFlag(flipped
478 return dataToState(tran);
479 }
480 } else {
481 /* Neither is valid (e.g. in a spring-forward's gap) and
482 nextTran.atMSecsSinceEpoch < nextStart <= tran.atMSecsSinceEpoch.
483 So swap their atMSecsSinceEpoch to give each a moment on the
484 side of the transition that it describes, then select the one
485 after or before according to the option set:
486 */
487 std::swap(tran.atMSecsSinceEpoch, nextTran.atMSecsSinceEpoch);
488 if (resolve.testFlag(flipped
491 return dataToState(nextTran);
492 if (resolve.testFlag(flipped
495 return dataToState(tran);
496 }
497 // Reject
498 return {forLocalMSecs};
499 }
500 // Before first transition, or system has transitions but not for this zone.
501 // Try falling back to offsetFromUtc (works for before first transition, at least).
502 }
503
504 /* Bracket and refine to discover offset. */
505 qint64 utcEpochMSecs;
506
507 // We don't have true data on DST-ness, so can't apply FlipForReverseDst.
508 int early = past.offsetFromUtc;
509 int late = future.offsetFromUtc;
510 if (early == late || late == invalidSeconds()) {
511 if (early == invalidSeconds()
512 || qSubOverflow(forLocalMSecs, early * qint64(1000), &utcEpochMSecs)) {
513 return {forLocalMSecs}; // Outside representable range
514 }
515 } else {
516 // Candidate values for utcEpochMSecs (if forLocalMSecs is valid):
517 const qint64 forEarly = forLocalMSecs - early * 1000;
518 const qint64 forLate = forLocalMSecs - late * 1000;
519 // If either of those doesn't have the offset we got it from, it's on
520 // the wrong side of the transition (and both may be, for a gap):
521 const bool earlyOk = offsetFromUtc(forEarly) == early;
522 const bool lateOk = offsetFromUtc(forLate) == late;
523
524 if (earlyOk) {
525 if (lateOk) {
526 Q_ASSERT(early > late);
527 // fall-back's repeated interval
529 utcEpochMSecs = forEarly;
530 else if (resolve.testFlag(QDateTimePrivate::FoldUseAfter))
531 utcEpochMSecs = forLate;
532 else
533 return {forLocalMSecs};
534 } else {
535 // Before and clear of the transition:
536 utcEpochMSecs = forEarly;
537 }
538 } else if (lateOk) {
539 // After and clear of the transition:
540 utcEpochMSecs = forLate;
541 } else {
542 // forLate <= gap < forEarly
543 Q_ASSERT(late > early);
544 const int dstStep = (late - early) * 1000;
546 utcEpochMSecs = forEarly - dstStep;
547 else if (resolve.testFlag(QDateTimePrivate::GapUseAfter))
548 utcEpochMSecs = forLate + dstStep;
549 else
550 return {forLocalMSecs};
551 }
552 }
553
554 return dataToState(data(utcEpochMSecs));
555}
556
558{
559 return false;
560}
561
563{
564 Q_UNUSED(afterMSecsSinceEpoch);
565 return {};
566}
567
569{
570 Q_UNUSED(beforeMSecsSinceEpoch);
571 return {};
572}
573
575 qint64 toMSecsSinceEpoch) const
576{
578 if (toMSecsSinceEpoch >= fromMSecsSinceEpoch) {
579 // fromMSecsSinceEpoch is inclusive but nextTransitionTime() is exclusive so go back 1 msec
580 Data next = nextTransition(fromMSecsSinceEpoch - 1);
581 while (next.atMSecsSinceEpoch != invalidMSecs()
582 && next.atMSecsSinceEpoch <= toMSecsSinceEpoch) {
584 next = nextTransition(next.atMSecsSinceEpoch);
585 }
586 }
587 return list;
588}
589
594
596{
597 // Fall-back implementation, can be made faster in subclasses.
598 // Backends that don't cache the available list SHOULD override this.
599 const QList<QByteArray> tzIds = availableTimeZoneIds();
600 return std::binary_search(tzIds.begin(), tzIds.end(), ianaId);
601}
602
604{
605 return QList<QByteArray>();
606}
607
608static QList<QByteArray> selectAvailable(QList<QByteArrayView> &&desired,
609 const QList<QByteArray> &all)
610{
611 std::sort(desired.begin(), desired.end());
612 const auto newEnd = std::unique(desired.begin(), desired.end());
613 const auto newSize = std::distance(desired.begin(), newEnd);
614 QList<QByteArray> result;
615 result.reserve(qMin(all.size(), newSize));
616 std::set_intersection(all.begin(), all.end(), desired.cbegin(),
617 std::next(desired.cbegin(), newSize), std::back_inserter(result));
618 return result;
619}
620
622{
623 // Default fall-back mode, use the zoneTable to find Region of know Zones
624 QList<QByteArrayView> regions;
625
626 // First get all Zones in the Zones table belonging to the Region
627 for (const ZoneData &data : zoneDataTable) {
628 if (data.territory == territory) {
629 for (auto l1 : data.ids())
630 regions << QByteArrayView(l1.data(), l1.size());
631 }
632 }
633 return selectAvailable(std::move(regions), availableTimeZoneIds());
634}
635
636QList<QByteArray> QTimeZonePrivate::availableTimeZoneIds(int offsetFromUtc) const
637{
638 // Default fall-back mode: use the zoneTable to find offsets of know zones.
639 QList<QByteArrayView> offsets;
640 // First get all Zones in the table using the given offset:
641 for (const WindowsData &winData : windowsDataTable) {
642 if (winData.offsetFromUtc == offsetFromUtc) {
643 for (auto data = zoneStartForWindowsId(winData.windowsIdKey);
644 data != std::end(zoneDataTable) && data->windowsIdKey == winData.windowsIdKey;
645 ++data) {
646 for (auto l1 : data->ids())
647 offsets << QByteArrayView(l1.data(), l1.size());
648 }
649 }
650 }
651 return selectAvailable(std::move(offsets), availableTimeZoneIds());
652}
653
654#ifndef QT_NO_DATASTREAM
656{
657 ds << QString::fromUtf8(m_id);
658}
659#endif // QT_NO_DATASTREAM
660
661// Static Utility Methods
662
664{
665 return { QString(), QDateTime(),
667}
668
670{
671 if (data.atMSecsSinceEpoch == invalidMSecs())
672 return invalidOffsetData();
673
674 return {
675 data.abbreviation,
677 data.offsetFromUtc, data.standardTimeOffset, data.daylightTimeOffset };
678}
679
680// Is the format of the ID valid ?
682{
683 /*
684 Main rules for defining TZ/IANA names, as per
685 https://www.iana.org/time-zones/repository/theory.html, are:
686 1. Use only valid POSIX file name components
687 2. Within a file name component, use only ASCII letters, `.', `-' and `_'.
688 3. Do not use digits (except in a [+-]\d+ suffix, when used).
689 4. A file name component must not exceed 14 characters or start with `-'
690
691 However, the rules are really guidelines - a later one says
692 - Do not change established names if they only marginally violate the
693 above rules.
694 We may, therefore, need to be a bit slack in our check here, if we hit
695 legitimate exceptions in real time-zone databases. In particular, ICU
696 includes some non-standard names with some components > 14 characters
697 long; so does Android, possibly deriving them from ICU.
698
699 In particular, aliases such as "Etc/GMT+7" and "SystemV/EST5EDT" are valid
700 so we need to accept digits, ':', and '+'; aliases typically have the form
701 of POSIX TZ strings, which allow a suffix to a proper IANA name. A POSIX
702 suffix starts with an offset (as in GMT+7) and may continue with another
703 name (as in EST5EDT, giving the DST name of the zone); a further offset is
704 allowed (for DST). The ("hard to describe and [...] error-prone in
705 practice") POSIX form even allows a suffix giving the dates (and
706 optionally times) of the annual DST transitions. Hopefully, no TZ aliases
707 go that far, but we at least need to accept an offset and (single
708 fragment) DST-name.
709
710 But for the legacy complications, the following would be preferable if
711 QRegExp would work on QByteArrays directly:
712 const QRegExp rx(QStringLiteral("[a-z+._][a-z+._-]{,13}"
713 "(?:/[a-z+._][a-z+._-]{,13})*"
714 // Optional suffix:
715 "(?:[+-]?\d{1,2}(?::\d{1,2}){,2}" // offset
716 // one name fragment (DST):
717 "(?:[a-z+._][a-z+._-]{,13})?)"),
718 Qt::CaseInsensitive);
719 return rx.exactMatch(ianaId);
720 */
721
722 // Somewhat slack hand-rolled version:
723 const int MinSectionLength = 1;
724#if defined(Q_OS_ANDROID) || QT_CONFIG(icu)
725 // Android has its own naming of zones. It may well come from ICU.
726 // "Canada/East-Saskatchewan" has a 17-character second component.
727 const int MaxSectionLength = 17;
728#else
729 const int MaxSectionLength = 14;
730#endif
731 int sectionLength = 0;
732 for (const char *it = ianaId.begin(), * const end = ianaId.end(); it != end; ++it, ++sectionLength) {
733 const char ch = *it;
734 if (ch == '/') {
735 if (sectionLength < MinSectionLength || sectionLength > MaxSectionLength)
736 return false; // violates (4)
737 sectionLength = -1;
738 } else if (ch == '-') {
739 if (sectionLength == 0)
740 return false; // violates (4)
741 } else if (!isAsciiLower(ch)
742 && !isAsciiUpper(ch)
743 && !(ch == '_')
744 && !(ch == '.')
745 // Should ideally check these only happen as an offset:
746 && !isAsciiDigit(ch)
747 && !(ch == '+')
748 && !(ch == ':')) {
749 return false; // violates (2)
750 }
751 }
752 if (sectionLength < MinSectionLength || sectionLength > MaxSectionLength)
753 return false; // violates (4)
754 return true;
755}
756
757QString QTimeZonePrivate::isoOffsetFormat(int offsetFromUtc, QTimeZone::NameType mode)
758{
759 if (mode == QTimeZone::ShortName && !offsetFromUtc)
760 return utcQString();
761
762 char sign = '+';
763 if (offsetFromUtc < 0) {
764 sign = '-';
766 }
767 const int secs = offsetFromUtc % 60;
768 const int mins = (offsetFromUtc / 60) % 60;
769 const int hour = offsetFromUtc / 3600;
770 QString result = QString::asprintf("UTC%c%02d", sign, hour);
771 if (mode != QTimeZone::ShortName || secs || mins)
772 result += QString::asprintf(":%02d", mins);
773 if (mode == QTimeZone::LongName || secs)
774 result += QString::asprintf(":%02d", secs);
775 return result;
776}
777
779{
780 const auto data = std::lower_bound(std::begin(aliasMappingTable), std::end(aliasMappingTable),
781 alias, earlierAliasId);
782 if (data != std::end(aliasMappingTable) && data->aliasId() == alias)
783 return data->ianaId().toByteArray();
784 // Note: empty return means not an alias, which is true of an ID that others
785 // are aliases to, as the table omits self-alias entries. Let caller sort
786 // that out, rather than allocating to return alias.toByteArray().
787 return {};
788}
789
791{
792 const auto idUtf8 = QUtf8StringView(id);
793
794 for (const ZoneData &data : zoneDataTable) {
795 for (auto l1 : data.ids()) {
796 if (l1 == idUtf8)
797 return toWindowsIdLiteral(data.windowsIdKey);
798 }
799 }
800 return QByteArray();
801}
802
804{
805 const auto data = std::lower_bound(std::begin(windowsDataTable), std::end(windowsDataTable),
806 windowsId, earlierWindowsId);
807 if (data != std::end(windowsDataTable) && data->windowsId() == windowsId) {
808 QByteArrayView id = data->ianaId();
809 if (qsizetype cut = id.indexOf(' '); cut >= 0)
810 id = id.first(cut);
811 return id.toByteArray();
812 }
813 return QByteArray();
814}
815
817 QLocale::Territory territory)
818{
819 const QList<QByteArray> list = windowsIdToIanaIds(windowsId, territory);
820 return list.size() > 0 ? list.first() : QByteArray();
821}
822
823QList<QByteArray> QTimeZonePrivate::windowsIdToIanaIds(const QByteArray &windowsId)
824{
825 const quint16 windowsIdKey = toWindowsIdKey(windowsId);
826 QList<QByteArray> list;
827
828 for (auto data = zoneStartForWindowsId(windowsIdKey);
829 data != std::end(zoneDataTable) && data->windowsIdKey == windowsIdKey;
830 ++data) {
831 for (auto l1 : data->ids())
832 list << QByteArray(l1.data(), l1.size());
833 }
834
835 // Return the full list in alpha order
836 std::sort(list.begin(), list.end());
837 return list;
838}
839
840QList<QByteArray> QTimeZonePrivate::windowsIdToIanaIds(const QByteArray &windowsId,
841 QLocale::Territory territory)
842{
843 QList<QByteArray> list;
844 const quint16 windowsIdKey = toWindowsIdKey(windowsId);
845 const qint16 land = static_cast<quint16>(territory);
846 for (auto data = zoneStartForWindowsId(windowsIdKey);
847 data != std::end(zoneDataTable) && data->windowsIdKey == windowsIdKey;
848 ++data) {
849 // Return the region matches in preference order
850 if (data->territory == land) {
851 for (auto l1 : data->ids())
852 list << QByteArray(l1.data(), l1.size());
853 break;
854 }
855 }
856
857 return list;
858}
859
860// Define template for derived classes to reimplement so QSharedDataPointer clone() works correctly
862{
863 return d->clone();
864}
865
867{
869 while ((cut = ianaIds.indexOf(' ')) >= 0) {
870 if (id == ianaIds.first(cut))
871 return true;
872 ianaIds = ianaIds.sliced(cut + 1);
873 }
874 return id == ianaIds;
875}
876
877/*
878 UTC Offset backend.
879
880 Always present, based on UTC-offset zones.
881 Complements platform-specific backends.
882 Equivalent to Qt::OffsetFromUtc lightweight time representations.
883*/
884
885// Create default UTC time zone
887{
888 const QString name = utcQString();
889 init(utcQByteArray(), 0, name, name, QLocale::AnyTerritory, name);
890}
891
892// Create a named UTC time zone
894{
895 // Look for the name in the UTC list, if found set the values
896 for (const UtcData &data : utcDataTable) {
897 if (isEntryInIanaList(id, data.id())) {
899 init(id, data.offsetFromUtc, name, name, QLocale::AnyTerritory, name);
900 break;
901 }
902 }
903}
904
906{
907 // Convert reasonable UTC[+-]\d+(:\d+){,2} to offset in seconds.
908 // Assumption: id has already been tried as a CLDR UTC offset ID (notably
909 // including plain "UTC" itself) and a system offset ID; it's neither.
910 if (!id.startsWith("UTC") || id.size() < 5)
911 return invalidSeconds(); // Doesn't match
912 const char signChar = id.at(3);
913 if (signChar != '-' && signChar != '+')
914 return invalidSeconds(); // No sign
915 const int sign = signChar == '-' ? -1 : 1;
916
917 qint32 seconds = 0;
918 int prior = 0; // Number of fields parsed thus far
919 for (auto offset : QLatin1StringView(id.mid(4)).tokenize(':'_L1)) {
920 bool ok = false;
921 unsigned short field = offset.toUShort(&ok);
922 // Bound hour above at 24, minutes and seconds at 60:
923 if (!ok || field >= (prior ? 60 : 24))
924 return invalidSeconds();
925 seconds = seconds * 60 + field;
926 if (++prior > 3)
927 return invalidSeconds(); // Too many numbers
928 }
929
930 if (!prior)
931 return invalidSeconds(); // No numbers
932
933 while (prior++ < 3)
934 seconds *= 60;
935
936 return seconds * sign;
937}
938
939// Create from UTC offset:
941{
944 // If there's an IANA ID for this offset, use it:
945 const auto data = std::lower_bound(std::begin(utcDataTable), std::end(utcDataTable),
946 offsetSeconds, atLowerUtcOffset);
947 if (data != std::end(utcDataTable) && data->offsetFromUtc == offsetSeconds) {
948 QByteArrayView ianaId = data->id();
949 qsizetype cut = ianaId.indexOf(' ');
950 id = (cut < 0 ? ianaId : ianaId.first(cut)).toByteArray();
952 Q_ASSERT(!name.isEmpty());
953 } else { // Fall back to a UTC-offset name:
954 name = isoOffsetFormat(offsetSeconds, QTimeZone::ShortName);
955 id = name.toUtf8();
956 }
957 init(id, offsetSeconds, name, name, QLocale::AnyTerritory, name);
958}
959
961 const QString &name, const QString &abbreviation,
962 QLocale::Territory territory, const QString &comment)
963{
964 init(zoneId, offsetSeconds, name, abbreviation, territory, comment);
965}
966
968 : QTimeZonePrivate(other), m_name(other.m_name),
969 m_abbreviation(other.m_abbreviation),
970 m_comment(other.m_comment),
971 m_territory(other.m_territory),
972 m_offsetFromUtc(other.m_offsetFromUtc)
973{
974}
975
979
984
986{
987 Data d;
988 d.abbreviation = m_abbreviation;
989 d.atMSecsSinceEpoch = forMSecsSinceEpoch;
990 d.standardTimeOffset = d.offsetFromUtc = m_offsetFromUtc;
991 d.daylightTimeOffset = 0;
992 return d;
993}
994
995// Override to shortcut past base's complications:
996QTimeZonePrivate::Data QUtcTimeZonePrivate::data(QTimeZone::TimeType timeType) const
997{
998 Q_UNUSED(timeType);
1000}
1001
1003{
1004 // Officially only supports C locale names; these are surely also viable for English.
1005 return locale.language() == QLocale::C || locale.language() == QLocale::English;
1006}
1007
1008void QUtcTimeZonePrivate::init(const QByteArray &zoneId)
1009{
1010 m_id = zoneId;
1011}
1012
1013void QUtcTimeZonePrivate::init(const QByteArray &zoneId, int offsetSeconds, const QString &name,
1014 const QString &abbreviation, QLocale::Territory territory,
1015 const QString &comment)
1016{
1017 m_id = zoneId;
1018 m_offsetFromUtc = offsetSeconds;
1019 m_name = name;
1020 m_abbreviation = abbreviation;
1021 m_territory = territory;
1022 m_comment = comment;
1023}
1024
1026{
1027 return m_territory;
1028}
1029
1031{
1032 return m_comment;
1033}
1034
1035// Override to bypass complications in base-class:
1037 QTimeZone::NameType nameType,
1038 const QLocale &locale) const
1039{
1040 Q_UNUSED(atMSecsSinceEpoch);
1041 return displayName(QTimeZone::StandardTime, nameType, locale);
1042}
1043
1044QString QUtcTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
1045 QTimeZone::NameType nameType,
1046 const QLocale &locale) const
1047{
1048 Q_UNUSED(timeType);
1049 Q_UNUSED(locale);
1050 if (nameType == QTimeZone::ShortName)
1051 return m_abbreviation;
1052 else if (nameType == QTimeZone::OffsetName)
1053 return isoOffsetFormat(m_offsetFromUtc);
1054 return m_name;
1055}
1056
1058{
1059 Q_UNUSED(atMSecsSinceEpoch);
1060 return m_abbreviation;
1061}
1062
1064{
1065 Q_UNUSED(atMSecsSinceEpoch);
1066 return m_offsetFromUtc;
1067}
1068
1070{
1071 Q_UNUSED(atMSecsSinceEpoch);
1072 return 0;
1073}
1074
1079
1081{
1082 // Only the zone IDs supplied by CLDR and recognized by constructor.
1083 for (const UtcData &data : utcDataTable) {
1084 if (isEntryInIanaList(ianaId, data.id()))
1085 return true;
1086 }
1087 // Callers may want to || offsetFromUtcString(ianaId) != invalidSeconds(),
1088 // but those are technically not IANA IDs and the custom QTimeZone
1089 // constructor needs the return here to reflect that.
1090 return false;
1091}
1092
1094{
1095 // Only the zone IDs supplied by CLDR and recognized by constructor.
1096 QList<QByteArray> result;
1097 result.reserve(std::size(utcDataTable));
1098 for (const UtcData &data : utcDataTable) {
1099 QByteArrayView id = data.id();
1100 qsizetype cut;
1101 while ((cut = id.indexOf(' ')) >= 0) {
1102 result << id.first(cut).toByteArray();
1103 id = id.sliced(cut + 1);
1104 }
1105 result << id.toByteArray();
1106 }
1107 // Not guaranteed to be sorted, so sort:
1108 std::sort(result.begin(), result.end());
1109 // ### assuming no duplicates
1110 return result;
1111}
1112
1114{
1115 // If AnyTerritory then is request for all non-region offset codes
1116 if (country == QLocale::AnyTerritory)
1117 return availableTimeZoneIds();
1118 return QList<QByteArray>();
1119}
1120
1121QList<QByteArray> QUtcTimeZonePrivate::availableTimeZoneIds(qint32 offsetSeconds) const
1122{
1123 // Only if it's present in CLDR. (May get more than one ID: UTC, UTC+00:00
1124 // and UTC-00:00 all have the same offset.)
1125 QList<QByteArray> result;
1126 const auto data = std::lower_bound(std::begin(utcDataTable), std::end(utcDataTable),
1127 offsetSeconds, atLowerUtcOffset);
1128 if (data != std::end(utcDataTable) && data->offsetFromUtc == offsetSeconds) {
1129 QByteArrayView id = data->id();
1130 qsizetype cut;
1131 while ((cut = id.indexOf(' ')) >= 0) {
1132 result << id.first(cut).toByteArray();
1133 id = id.sliced(cut + 1);
1134 }
1135 result << id.toByteArray();
1136 }
1137 // CLDR only has round multiples of a quarter hour, and only some of
1138 // those. For anything else, throw in the ID we would use for this offset
1139 // (if we'd accept that ID).
1140 QByteArray isoName = isoOffsetFormat(offsetSeconds, QTimeZone::ShortName).toUtf8();
1141 if (offsetFromUtcString(isoName) == qint64(offsetSeconds) && !result.contains(isoName))
1142 result << isoName;
1143 // Not guaranteed to be sorted, so sort:
1144 std::sort(result.begin(), result.end());
1145 // ### assuming no duplicates
1146 return result;
1147}
1148
1149#ifndef QT_NO_DATASTREAM
1151{
1152 ds << QStringLiteral("OffsetFromUtc") << QString::fromUtf8(m_id) << m_offsetFromUtc << m_name
1153 << m_abbreviation << static_cast<qint32>(m_territory) << m_comment;
1154}
1155#endif // QT_NO_DATASTREAM
1156
QByteArray toByteArray() const
Definition qbytearray.h:797
constexpr QByteArrayView first(qsizetype n) const
qsizetype indexOf(QByteArrayView a, qsizetype from=0) const noexcept
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:612
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:495
iterator end()
Returns an \l{STL-style iterators}{STL-style iterator} pointing just after the last byte in the byte-...
Definition qbytearray.h:448
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first byte in the byte-array.
Definition qbytearray.h:444
\inmodule QtCore\reentrant
Definition qdatastream.h:47
\inmodule QtCore\reentrant
Definition qdatetime.h:292
static QDateTime fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone)
static qint64 currentMSecsSinceEpoch() noexcept
constexpr auto tokenize(Needle &&needle, Flags...flags) const noexcept(noexcept(qTokenize(std::declval< const QLatin1StringView & >(), std::forward< Needle >(needle), flags...))) -> decltype(qTokenize(*this, std::forward< Needle >(needle), flags...))
qsizetype size() const noexcept
Definition qlist.h:398
T & first()
Definition qlist.h:646
iterator end()
Definition qlist.h:627
iterator begin()
Definition qlist.h:626
void append(parameter_type t)
Definition qlist.h:459
Language language() const
Returns the language of this locale.
Definition qlocale.cpp:1317
Country Territory
Definition qlocale.h:861
@ AnyTerritory
Definition qlocale.h:568
static QLocale c()
Returns a QLocale object initialized to the "C" locale.
Definition qlocale.h:1146
static QLocale system()
Returns a QLocale object initialized to the system locale.
Definition qlocale.cpp:2862
@ English
Definition qlocale.h:119
\inmodule QtCore
Definition qshareddata.h:19
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6028
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7274
virtual bool hasDaylightTime() const
virtual bool isDaylightTime(qint64 atMSecsSinceEpoch) const
static QByteArray utcQByteArray()
static QByteArray aliasToIana(QByteArrayView alias)
virtual QTimeZonePrivate * clone() const
static QByteArray windowsIdToDefaultIanaId(const QByteArray &windowsId)
virtual bool isDataLocale(const QLocale &locale) const
virtual Data nextTransition(qint64 afterMSecsSinceEpoch) const
virtual Data data(qint64 forMSecsSinceEpoch) const
virtual bool hasTransitions() const
virtual Data previousTransition(qint64 beforeMSecsSinceEpoch) const
virtual QString displayName(qint64 atMSecsSinceEpoch, QTimeZone::NameType nameType, const QLocale &locale) const
bool operator==(const QTimeZonePrivate &other) const
QDateTimePrivate::ZoneState stateAtZoneTime(qint64 forLocalMSecs, QDateTimePrivate::TransitionOptions resolve) const
virtual int daylightTimeOffset(qint64 atMSecsSinceEpoch) const
bool operator!=(const QTimeZonePrivate &other) const
static constexpr qint64 invalidMSecs()
virtual bool isTimeZoneIdAvailable(const QByteArray &ianaId) const
virtual QLocale::Territory territory() const
virtual QString comment() const
static constexpr qint64 maxMSecs()
virtual void serialize(QDataStream &ds) const
QByteArray id() const
static QList< QByteArray > windowsIdToIanaIds(const QByteArray &windowsId)
virtual QList< QByteArray > availableTimeZoneIds() const
virtual QString abbreviation(qint64 atMSecsSinceEpoch) const
static QTimeZone::OffsetData toOffsetData(const Data &data)
DataList transitions(qint64 fromMSecsSinceEpoch, qint64 toMSecsSinceEpoch) const
virtual int standardTimeOffset(qint64 atMSecsSinceEpoch) const
static QByteArray ianaIdToWindowsId(const QByteArray &ianaId)
static QString isoOffsetFormat(int offsetFromUtc, QTimeZone::NameType mode=QTimeZone::OffsetName)
static constexpr qint64 minMSecs()
virtual int offsetFromUtc(qint64 atMSecsSinceEpoch) const
static QTimeZone::OffsetData invalidOffsetData()
static constexpr qint64 invalidSeconds()
static QString utcQString()
virtual QByteArray systemTimeZoneId() const
static bool isValidId(const QByteArray &ianaId)
static constexpr int MaxUtcOffsetSecs
Definition qtimezone.h:90
static constexpr int MinUtcOffsetSecs
Definition qtimezone.h:87
QUtcTimeZonePrivate * clone() const override
int standardTimeOffset(qint64 atMSecsSinceEpoch) const override
QList< QByteArray > availableTimeZoneIds() const override
bool isDataLocale(const QLocale &locale) const override
static qint64 offsetFromUtcString(QByteArrayView id)
bool isTimeZoneIdAvailable(const QByteArray &ianaId) const override
QLocale::Territory territory() const override
QString abbreviation(qint64 atMSecsSinceEpoch) const override
Data data(qint64 forMSecsSinceEpoch) const override
void serialize(QDataStream &ds) const override
QString displayName(qint64 atMSecsSinceEpoch, QTimeZone::NameType nameType, const QLocale &locale) const override
QString comment() const override
int daylightTimeOffset(qint64 atMSecsSinceEpoch) const override
QByteArray systemTimeZoneId() const override
QSet< QString >::iterator it
short next
Definition keywords.cpp:445
Token token
Definition keywords.cpp:444
Combined button and popup list for selecting options.
constexpr bool isAsciiDigit(char32_t c) noexcept
Definition qtools_p.h:67
constexpr bool isAsciiLower(char32_t c) noexcept
Definition qtools_p.h:77
constexpr bool isAsciiUpper(char32_t c) noexcept
Definition qtools_p.h:72
static constexpr WindowsData windowsDataTable[]
static constexpr ZoneData zoneDataTable[]
static constexpr UtcData utcDataTable[]
static constexpr AliasData aliasMappingTable[]
@ CaseInsensitive
static jboolean cut(JNIEnv *, jobject)
#define Q_LIKELY(x)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
QT_BEGIN_NAMESPACE constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:19
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
Definition qnumeric.h:113
std::enable_if_t< std::is_unsigned_v< T >, bool > qSubOverflow(T v1, T v2, T *r)
Definition qnumeric.h:153
GLenum mode
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum GLuint id
[7]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLenum dst
GLenum GLuint GLintptr offset
GLuint name
GLint first
GLuint entry
GLuint GLsizei const GLuint const GLintptr * offsets
GLuint64EXT * result
[6]
static const QQmlJSScope * resolve(const QQmlJSScope *current, const QStringList &names)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QBasicUtf8StringView< false > QUtf8StringView
Definition qstringfwd.h:46
#define QStringLiteral(str)
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
constexpr bool atLowerWindowsKey(WindowsData entry, qint16 winIdKey) noexcept
static bool earlierAliasId(AliasData entry, QByteArrayView aliasId) noexcept
static bool isEntryInIanaList(QByteArrayView id, QByteArrayView ianaIds)
static bool earlierWinData(WindowsData less, WindowsData more) noexcept
static auto zoneStartForWindowsId(quint16 windowsIdKey) noexcept
constexpr bool zoneAtLowerWindowsKey(ZoneData entry, qint16 winIdKey) noexcept
static QList< QByteArray > selectAvailable(QList< QByteArrayView > &&desired, const QList< QByteArray > &all)
static quint16 toWindowsIdKey(const QByteArray &winId)
static QByteArray toWindowsIdLiteral(quint16 windowsIdKey)
constexpr bool atLowerUtcOffset(UtcData entry, qint32 offsetSeconds) noexcept
constexpr bool earlierZoneData(ZoneData less, ZoneData more) noexcept
static bool earlierWindowsId(WindowsData entry, QByteArrayView winId) noexcept
#define Q_UNUSED(x)
short qint16
Definition qtypes.h:47
unsigned short quint16
Definition qtypes.h:48
int qint32
Definition qtypes.h:49
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
static int sign(int x)
QList< int > list
[14]
QFuture< void > future
[5]
list indexOf("B")
QSharedPointer< T > other(t)
[5]