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