1 | // Copyright (C) 2024 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include <private/qtimezonelocale_p.h> |
5 | #include <private/qtimezoneprivate_p.h> |
6 | |
7 | #if !QT_CONFIG(icu) |
8 | # include <private/qdatetime_p.h> |
9 | // Use data generated from CLDR: |
10 | # include <private/qtimezonelocale_data_p.h> |
11 | # include <private/qtimezoneprivate_data_p.h> |
12 | #endif |
13 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | #if QT_CONFIG(icu) // Get data from ICU: |
17 | namespace { |
18 | |
19 | // Convert TimeType and NameType into ICU UCalendarDisplayNameType |
20 | constexpr UCalendarDisplayNameType ucalDisplayNameType(QTimeZone::TimeType timeType, |
21 | QTimeZone::NameType nameType) |
22 | { |
23 | // TODO ICU C UCalendarDisplayNameType does not support full set of C++ TimeZone::EDisplayType |
24 | // For now, treat Generic as Standard |
25 | switch (nameType) { |
26 | case QTimeZone::OffsetName: |
27 | Q_UNREACHABLE(); // Callers of ucalTimeZoneDisplayName() should take care of OffsetName. |
28 | case QTimeZone::ShortName: |
29 | return timeType == QTimeZone::DaylightTime ? UCAL_SHORT_DST : UCAL_SHORT_STANDARD; |
30 | case QTimeZone::DefaultName: |
31 | case QTimeZone::LongName: |
32 | return timeType == QTimeZone::DaylightTime ? UCAL_DST : UCAL_STANDARD; |
33 | } |
34 | Q_UNREACHABLE_RETURN(UCAL_STANDARD); |
35 | } |
36 | |
37 | } // nameless namespace |
38 | |
39 | namespace QtTimeZoneLocale { |
40 | |
41 | // Qt wrapper around ucal_getTimeZoneDisplayName() |
42 | // Used directly by ICU backend; indirectly by TZ (see below). |
43 | QString ucalTimeZoneDisplayName(UCalendar *ucal, |
44 | QTimeZone::TimeType timeType, |
45 | QTimeZone::NameType nameType, |
46 | const QByteArray &localeCode) |
47 | { |
48 | constexpr int32_t BigNameLength = 50; |
49 | int32_t size = BigNameLength; |
50 | QString result(size, Qt::Uninitialized); |
51 | auto dst = [&result]() { return reinterpret_cast<UChar *>(result.data()); }; |
52 | UErrorCode status = U_ZERO_ERROR; |
53 | const UCalendarDisplayNameType utype = ucalDisplayNameType(timeType, nameType); |
54 | |
55 | // size = ucal_getTimeZoneDisplayName(cal, type, locale, result, resultLength, status) |
56 | size = ucal_getTimeZoneDisplayName(cal: ucal, type: utype, locale: localeCode.constData(), |
57 | result: dst(), resultLength: size, status: &status); |
58 | |
59 | // If overflow, then resize and retry |
60 | if (size > BigNameLength || status == U_BUFFER_OVERFLOW_ERROR) { |
61 | result.resize(size); |
62 | status = U_ZERO_ERROR; |
63 | size = ucal_getTimeZoneDisplayName(cal: ucal, type: utype, locale: localeCode.constData(), |
64 | result: dst(), resultLength: size, status: &status); |
65 | } |
66 | |
67 | if (!U_SUCCESS(code: status)) |
68 | return QString(); |
69 | |
70 | // Resize and return: |
71 | result.resize(size); |
72 | return result; |
73 | } |
74 | |
75 | } // QtTimeZoneLocale |
76 | |
77 | // Used by TZ backends when ICU is available: |
78 | QString QTimeZonePrivate::localeName(qint64 atMSecsSinceEpoch, int offsetFromUtc, |
79 | QTimeZone::TimeType timeType, |
80 | QTimeZone::NameType nameType, |
81 | const QLocale &locale) const |
82 | { |
83 | Q_UNUSED(atMSecsSinceEpoch); |
84 | // TODO: use CLDR data for the offset name. |
85 | // No ICU API for offset formats, so fall back to our ISO one, even if |
86 | // locale isn't C: |
87 | if (nameType == QTimeZone::OffsetName) |
88 | return isoOffsetFormat(offsetFromUtc); |
89 | |
90 | const QString id = QString::fromUtf8(ba: m_id); |
91 | const QByteArray loc = locale.name().toUtf8(); |
92 | UErrorCode status = U_ZERO_ERROR; |
93 | UCalendar *ucal = ucal_open(zoneID: reinterpret_cast<const UChar *>(id.data()), len: id.size(), |
94 | locale: loc.constData(), type: UCAL_DEFAULT, status: &status); |
95 | if (ucal && U_SUCCESS(code: status)) { |
96 | auto tidier = qScopeGuard(f: [ucal]() { ucal_close(cal: ucal); }); |
97 | return QtTimeZoneLocale::ucalTimeZoneDisplayName(ucal, timeType, nameType, localeCode: loc); |
98 | } |
99 | return QString(); |
100 | } |
101 | #else // No ICU, use QTZ[LP}_data_p.h data for feature timezone_locale. |
102 | namespace { |
103 | using namespace QtTimeZoneLocale; // QTZL_data_p.h |
104 | using namespace QtTimeZoneCldr; // QTZP_data_p.h |
105 | // Accessors for the QTZL_data_p.h |
106 | |
107 | // Accessors for the QTZP_data_p.h |
108 | |
109 | } // nameless namespace |
110 | |
111 | QString QTimeZonePrivate::localeName(qint64 atMSecsSinceEpoch, int offsetFromUtc, |
112 | QTimeZone::TimeType timeType, |
113 | QTimeZone::NameType nameType, |
114 | const QLocale &locale) const |
115 | { |
116 | Q_ASSERT(nameType != QTimeZone::OffsetName || locale.language() != QLocale::C); |
117 | // Get data from QTZ[LP]_data_p.h |
118 | |
119 | Q_UNUSED(atMSecsSinceEpoch); |
120 | Q_UNUSED(offsetFromUtc); |
121 | Q_UNUSED(timeType); |
122 | return QString(); |
123 | } |
124 | #endif // ICU |
125 | |
126 | QT_END_NAMESPACE |
127 | |