1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <private/qtimezonelocale_p.h>
#include <private/qtimezoneprivate_p.h>
#if !QT_CONFIG(icu)
# include <private/qdatetime_p.h>
// Use data generated from CLDR:
# include <private/qtimezonelocale_data_p.h>
# include <private/qtimezoneprivate_data_p.h>
#endif
QT_BEGIN_NAMESPACE
#if QT_CONFIG(icu) // Get data from ICU:
namespace {
// Convert TimeType and NameType into ICU UCalendarDisplayNameType
UCalendarDisplayNameType ucalDisplayNameType(QTimeZone::TimeType timeType,
QTimeZone::NameType nameType)
{
// TODO ICU C UCalendarDisplayNameType does not support full set of C++ TimeZone::EDisplayType
// For now, treat Generic as Standard
switch (nameType) {
case QTimeZone::ShortName:
return timeType == QTimeZone::DaylightTime ? UCAL_SHORT_DST : UCAL_SHORT_STANDARD;
case QTimeZone::DefaultName:
case QTimeZone::LongName:
return timeType == QTimeZone::DaylightTime ? UCAL_DST : UCAL_STANDARD;
case QTimeZone::OffsetName:
Q_UNREACHABLE(); // Callers of ucalTimeZoneDisplayName() should take care of OffsetName.
}
Q_UNREACHABLE_RETURN(UCAL_STANDARD);
}
} // nameless namespace
namespace QtTimeZoneLocale {
// Qt wrapper around ucal_getTimeZoneDisplayName()
// Used directly by ICU backend; indirectly by TZ (see below).
QString ucalTimeZoneDisplayName(UCalendar *ucal,
QTimeZone::TimeType timeType,
QTimeZone::NameType nameType,
const QByteArray &localeCode)
{
constexpr int32_t BigNameLength = 50;
int32_t size = BigNameLength;
QString result(size, Qt::Uninitialized);
auto dst = [&result]() { return reinterpret_cast<UChar *>(result.data()); };
UErrorCode status = U_ZERO_ERROR;
const UCalendarDisplayNameType utype = ucalDisplayNameType(timeType, nameType);
// size = ucal_getTimeZoneDisplayName(cal, type, locale, result, resultLength, status)
size = ucal_getTimeZoneDisplayName(ucal, utype, localeCode.constData(),
dst(), size, &status);
// If overflow, then resize and retry
if (size > BigNameLength || status == U_BUFFER_OVERFLOW_ERROR) {
result.resize(size);
status = U_ZERO_ERROR;
size = ucal_getTimeZoneDisplayName(ucal, utype, localeCode.constData(),
dst(), size, &status);
}
if (!U_SUCCESS(status))
return QString();
// Resize and return:
result.resize(size);
return result;
}
bool ucalKnownTimeZoneId(const QString &ianaStr)
{
const UChar *const name = reinterpret_cast<const UChar *>(ianaStr.constData());
// We are not interested in the value, but we have to pass something.
// No known IANA zone name is (up to 2023) longer than 30 characters.
constexpr size_t size = 64;
UChar buffer[size];
// TODO: convert to ucal_getIanaTimeZoneID(), new draft in ICU 74, once we
// can rely on its availability, assuming it works the same once not draft.
UErrorCode status = U_ZERO_ERROR;
UBool isSys = false;
// Returns the length of the IANA zone name (but we don't care):
ucal_getCanonicalTimeZoneID(name, ianaStr.size(), buffer, size, &isSys, &status);
// We're only interested if the result is a "system" (i.e. IANA) ID:
return isSys;
}
} // QtTimeZoneLocale
// Used by TZ backends when ICU is available:
QString QTimeZonePrivate::localeName(qint64 atMSecsSinceEpoch, int offsetFromUtc,
QTimeZone::TimeType timeType,
QTimeZone::NameType nameType,
const QLocale &locale) const
{
Q_UNUSED(atMSecsSinceEpoch);
// TODO: use CLDR data for the offset name.
// No ICU API for offset formats, so fall back to our ISO one, even if
// locale isn't C:
if (nameType == QTimeZone::OffsetName)
return isoOffsetFormat(offsetFromUtc);
const QString id = QString::fromUtf8(m_id);
// Need to check id is known to ICU, since ucal_open() will return a
// misleading "valid" GMT ucal when it doesn't recognise id.
if (!QtTimeZoneLocale::ucalKnownTimeZoneId(id))
return QString();
const QByteArray loc = locale.name().toUtf8();
UErrorCode status = U_ZERO_ERROR;
UCalendar *ucal = ucal_open(reinterpret_cast<const UChar *>(id.data()), id.size(),
loc.constData(), UCAL_DEFAULT, &status);
if (ucal && U_SUCCESS(status)) {
auto tidier = qScopeGuard([ucal]() { ucal_close(ucal); });
return QtTimeZoneLocale::ucalTimeZoneDisplayName(ucal, timeType, nameType, loc);
}
return QString();
}
#else // No ICU, use QTZ[LP}_data_p.h data for feature timezone_locale.
namespace {
using namespace QtTimeZoneLocale; // QTZL_data_p.h
using namespace QtTimeZoneCldr; // QTZP_data_p.h
// Accessors for the QTZL_data_p.h
// Accessors for the QTZP_data_p.h
} // nameless namespace
QString QTimeZonePrivate::localeName(qint64 atMSecsSinceEpoch, int offsetFromUtc,
QTimeZone::TimeType timeType,
QTimeZone::NameType nameType,
const QLocale &locale) const
{
Q_ASSERT(nameType != QTimeZone::OffsetName || locale.language() != QLocale::C);
// Get data from QTZ[LP]_data_p.h
Q_UNUSED(atMSecsSinceEpoch);
Q_UNUSED(offsetFromUtc);
Q_UNUSED(timeType);
return QString();
}
#endif // ICU
QT_END_NAMESPACE
|