Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qtimezoneprivate_android.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2014 Drew Parsons <dparsons@emerall.com>
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 "qtimezone.h"
7
8#include <QtCore/QJniEnvironment>
9#include <QtCore/QSet>
10
12
13Q_DECLARE_JNI_CLASS(TimeZone, "java/util/TimeZone");
14Q_DECLARE_JNI_CLASS(Locale, "java/util/Locale");
15Q_DECLARE_JNI_CLASS(Date, "java/util/Date");
16Q_DECLARE_JNI_TYPE(StringArray, "[Ljava/lang/String;")
17
18/*
19 Private
20
21 Android implementation
22
23 Note that a QJniObject manages a global reference, so it serves as an
24 owning smart-pointer, ensuring an object doesn't get garbage-collected
25 before we're done with it.
26*/
27
28// Create the system default time zone
29QAndroidTimeZonePrivate::QAndroidTimeZonePrivate()
31{
32 // Keep in sync with systemTimeZoneId():
33 androidTimeZone = QJniObject::callStaticMethod<QtJniTypes::TimeZone>(
34 QtJniTypes::className<QtJniTypes::TimeZone>(), "getDefault");
35 const QJniObject id = androidTimeZone.callMethod<jstring>("getID");
36 m_id = id.toString().toUtf8();
37}
38
39// Create a named time zone
40QAndroidTimeZonePrivate::QAndroidTimeZonePrivate(const QByteArray &ianaId)
42{
43 init(ianaId);
44}
45
46QAndroidTimeZonePrivate::QAndroidTimeZonePrivate(const QAndroidTimeZonePrivate &other)
48{
49 androidTimeZone = other.androidTimeZone;
50 m_id = other.id();
51}
52
53QAndroidTimeZonePrivate::~QAndroidTimeZonePrivate()
54{
55}
56
57static QString getDisplayName(QJniObject zone, jint style, jboolean dst,
58 const QLocale &locale)
59{
60 QJniObject jbcpTag = QJniObject::fromString(locale.bcp47Name());
61 QJniObject jlocale = QJniObject::callStaticMethod<QtJniTypes::Locale>(
62 QtJniTypes::className<QtJniTypes::Locale>(), "forLanguageTag",
63 jbcpTag.object<jstring>());
64
65 return zone.callMethod<jstring>("getDisplayName", dst, style,
66 jlocale.object<QtJniTypes::Locale>()).toString();
67}
68
69void QAndroidTimeZonePrivate::init(const QByteArray &ianaId)
70{
71 const QString iana = QString::fromUtf8(ianaId);
72 androidTimeZone = QJniObject::callStaticMethod<QtJniTypes::TimeZone>(
73 QtJniTypes::className<QtJniTypes::TimeZone>(), "getTimeZone",
74 QJniObject::fromString(iana).object<jstring>());
75
76 // The ID or display name of the zone we've got, if it looks like what we asked for:
77 const auto match = [iana](const QString &name) -> QByteArray {
78 if (iana.compare(name, Qt::CaseInsensitive) == 0)
79 return name.toUtf8();
80
81 return QByteArray();
82 };
83
84 // Painfully, JNI gives us back a default zone object if it doesn't
85 // recognize the name; so check for whether ianaId is a recognized name of
86 // the zone object we got and ignore the zone if not.
87 // Try checking ianaId against getID(), getDisplayName():
88 m_id = match(androidTimeZone.callMethod<jstring>("getID").toString());
89 for (int style = 1; m_id.isEmpty() && style >= 0; --style) {
90 for (int dst = 1; m_id.isEmpty() && dst >= 0; --dst) {
91 for (int pick = 2; m_id.isEmpty() && pick >= 0; --pick) {
93 : pick == 1 ? QLocale() : QLocale::c());
94 m_id = match(getDisplayName(androidTimeZone, style, jboolean(dst), locale));
95 }
96 }
97 }
98}
99
100QAndroidTimeZonePrivate *QAndroidTimeZonePrivate::clone() const
101{
102 return new QAndroidTimeZonePrivate(*this);
103}
104
105
106QString QAndroidTimeZonePrivate::displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType,
107 const QLocale &locale) const
108{
110
111 if (androidTimeZone.isValid()) {
112 jboolean daylightTime = (timeType == QTimeZone::DaylightTime); // treat QTimeZone::GenericTime as QTimeZone::StandardTime
113
114 // treat all NameTypes as java TimeZone style LONG (value 1), except of course QTimeZone::ShortName which is style SHORT (value 0);
115 jint style = (nameType == QTimeZone::ShortName ? 0 : 1);
116
117 name = getDisplayName(androidTimeZone, style, daylightTime, locale);
118 }
119
120 return name;
121}
122
123QString QAndroidTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
124{
125 if ( isDaylightTime( atMSecsSinceEpoch ) )
126 return displayName(QTimeZone::DaylightTime, QTimeZone::ShortName, QLocale() );
127 else
128 return displayName(QTimeZone::StandardTime, QTimeZone::ShortName, QLocale() );
129}
130
131int QAndroidTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const
132{
133 // offsetFromUtc( ) is invoked when androidTimeZone may not yet be set at startup,
134 // so a validity test is needed here
135 if ( androidTimeZone.isValid() ) {
136 // the java method getOffset() returns milliseconds, but QTimeZone returns seconds
137 return androidTimeZone.callMethod<jint>(
138 "getOffset", static_cast<jlong>(atMSecsSinceEpoch) ) / 1000;
139 } else {
140 return 0;
141 }
142}
143
144int QAndroidTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const
145{
146 // the java method does not use a reference time
147 Q_UNUSED( atMSecsSinceEpoch );
148 if ( androidTimeZone.isValid() )
149 // the java method getRawOffset() returns milliseconds, but QTimeZone returns seconds
150 return androidTimeZone.callMethod<jint>( "getRawOffset" ) / 1000;
151 else
152 return 0;
153}
154
155int QAndroidTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const
156{
157 return ( offsetFromUtc(atMSecsSinceEpoch) - standardTimeOffset(atMSecsSinceEpoch) );
158}
159
160bool QAndroidTimeZonePrivate::hasDaylightTime() const
161{
162 if ( androidTimeZone.isValid() )
163 /* note: the Java function only tests for future DST transitions, not past */
164 return androidTimeZone.callMethod<jboolean>("useDaylightTime" );
165 else
166 return false;
167}
168
169bool QAndroidTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
170{
171 if ( androidTimeZone.isValid() ) {
172 QJniObject jDate = QJniObject::construct<QtJniTypes::Date>(
173 static_cast<jlong>(atMSecsSinceEpoch));
174 return androidTimeZone.callMethod<jboolean>("inDaylightTime",
175 jDate.object<QtJniTypes::Date>());
176 }
177 else
178 return false;
179}
180
181QTimeZonePrivate::Data QAndroidTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
182{
183 if (androidTimeZone.isValid()) {
184 Data data;
185 data.atMSecsSinceEpoch = forMSecsSinceEpoch;
186 data.standardTimeOffset = standardTimeOffset(forMSecsSinceEpoch);
187 data.offsetFromUtc = offsetFromUtc(forMSecsSinceEpoch);
188 data.daylightTimeOffset = data.offsetFromUtc - data.standardTimeOffset;
189 data.abbreviation = abbreviation(forMSecsSinceEpoch);
190 return data;
191 } else {
192 return invalidData();
193 }
194}
195
196// java.util.TimeZone does not directly provide transitions,
197// so don't over-ride QTZP's base implementation of transition methods.
198
199QByteArray QAndroidTimeZonePrivate::systemTimeZoneId() const
200{
201 // Keep in sync with default constructor:
202 QJniObject androidSystemTimeZone = QJniObject::callStaticMethod<QtJniTypes::TimeZone>(
203 QtJniTypes::className<QtJniTypes::TimeZone>(), "getDefault");
204 const QJniObject id = androidSystemTimeZone.callMethod<jstring>("getID");
205 return id.toString().toUtf8();
206}
207
208QList<QByteArray> QAndroidTimeZonePrivate::availableTimeZoneIds() const
209{
210 QList<QByteArray> availableTimeZoneIdList;
211 QJniObject androidAvailableIdList = QJniObject::callStaticMethod<QtJniTypes::StringArray>(
212 QtJniTypes::className<QtJniTypes::TimeZone>(), "getAvailableIDs");
213
214 QJniEnvironment jniEnv;
215 int androidTZcount = jniEnv->GetArrayLength(androidAvailableIdList.object<jarray>());
216
217 // need separate jobject and QJniObject here so that we can delete (DeleteLocalRef) the reference to the jobject
218 // (or else the JNI reference table fills after 512 entries from GetObjectArrayElement)
219 jobject androidTZobject;
220 QJniObject androidTZ;
221 for (int i = 0; i < androidTZcount; i++) {
222 androidTZobject = jniEnv->GetObjectArrayElement(androidAvailableIdList.object<jobjectArray>(), i);
223 androidTZ = androidTZobject;
224 availableTimeZoneIdList.append(androidTZ.toString().toUtf8());
225 jniEnv->DeleteLocalRef(androidTZobject);
226 }
227
228 return availableTimeZoneIdList;
229}
230
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
\inmodule QtCore
Definition qlist.h:74
void append(parameter_type t)
Definition qlist.h:441
static QLocale system()
Returns a QLocale object initialized to the system locale.
Definition qlocale.cpp:2742
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6498
Q_QML_PRIVATE_EXPORT QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName)
Provides locale specific properties and formatted data.
Combined button and popup list for selecting options.
@ CaseInsensitive
static QString displayName(CGDirectDisplayID displayID)
static int pick(bool vertical, const QSize &size)
static const qint64 invalidData
GLuint object
[3]
GLenum GLenum dst
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
const GLubyte * c
Q_DECLARE_JNI_TYPE(File, "Ljava/io/File;")
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
static QString getDisplayName(QJniObject zone, jint style, jboolean dst, const QLocale &locale)
QT_BEGIN_NAMESPACE Q_DECLARE_JNI_CLASS(TimeZone, "java/util/TimeZone")
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
long long qint64
Definition qtypes.h:55
QSharedPointer< T > other(t)
[5]