Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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");
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()
30 : QTimeZonePrivate()
31{
32 // Keep in sync with systemTimeZoneId():
33 androidTimeZone = QJniObject::callStaticMethod<QtJniTypes::TimeZone>(
34 QtJniTypes::Traits<QtJniTypes::TimeZone>::className(), "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)
41 : QTimeZonePrivate()
42{
43 init(ianaId);
44}
45
46QAndroidTimeZonePrivate::~QAndroidTimeZonePrivate()
47{
48}
49
50static QString getDisplayName(QJniObject zone, jint style, jboolean dst,
51 const QLocale &locale)
52{
53 QJniObject jbcpTag = QJniObject::fromString(locale.bcp47Name());
54 QJniObject jlocale = QJniObject::callStaticMethod<QtJniTypes::Locale>(
55 QtJniTypes::Traits<QtJniTypes::Locale>::className(), "forLanguageTag",
56 jbcpTag.object<jstring>());
57
58 return zone.callMethod<jstring>("getDisplayName", dst, style,
59 jlocale.object<QtJniTypes::Locale>()).toString();
60}
61
62void QAndroidTimeZonePrivate::init(const QByteArray &ianaId)
63{
64 const QString iana = QString::fromUtf8(ianaId);
65 androidTimeZone = QJniObject::callStaticMethod<QtJniTypes::TimeZone>(
66 QtJniTypes::Traits<QtJniTypes::TimeZone>::className(), "getTimeZone",
67 QJniObject::fromString(iana).object<jstring>());
68
69 // The ID or display name of the zone we've got, if it looks like what we asked for:
70 const auto match = [iana](const QString &name) -> QByteArray {
71 if (iana.compare(name, Qt::CaseInsensitive) == 0)
72 return name.toUtf8();
73
74 return QByteArray();
75 };
76
77 // Painfully, JNI gives us back a default zone object if it doesn't
78 // recognize the name; so check for whether ianaId is a recognized name of
79 // the zone object we got and ignore the zone if not.
80 // Try checking ianaId against getID(), getDisplayName():
81 m_id = match(androidTimeZone.callMethod<jstring>("getID").toString());
82 for (int style = 1; m_id.isEmpty() && style >= 0; --style) {
83 for (int dst = 1; m_id.isEmpty() && dst >= 0; --dst) {
84 for (int pick = 2; m_id.isEmpty() && pick >= 0; --pick) {
85 QLocale locale = (pick == 0 ? QLocale::system()
86 : pick == 1 ? QLocale() : QLocale::c());
87 m_id = match(getDisplayName(androidTimeZone, style, jboolean(dst), locale));
88 }
89 }
90 }
91}
92
93QAndroidTimeZonePrivate *QAndroidTimeZonePrivate::clone() const
94{
95 return new QAndroidTimeZonePrivate(*this);
96}
97
98
99QString QAndroidTimeZonePrivate::displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType,
100 const QLocale &locale) const
101{
102 QString name;
103
104 if (androidTimeZone.isValid()) {
105 jboolean daylightTime = (timeType == QTimeZone::DaylightTime); // treat QTimeZone::GenericTime as QTimeZone::StandardTime
106
107 // treat all NameTypes as java TimeZone style LONG (value 1), except of course QTimeZone::ShortName which is style SHORT (value 0);
108 jint style = (nameType == QTimeZone::ShortName ? 0 : 1);
109
110 name = getDisplayName(androidTimeZone, style, daylightTime, locale);
111 }
112
113 return name;
114}
115
116QString QAndroidTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
117{
118 if ( isDaylightTime( atMSecsSinceEpoch ) )
119 return displayName(QTimeZone::DaylightTime, QTimeZone::ShortName, QLocale() );
120 else
121 return displayName(QTimeZone::StandardTime, QTimeZone::ShortName, QLocale() );
122}
123
124int QAndroidTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const
125{
126 // offsetFromUtc( ) is invoked when androidTimeZone may not yet be set at startup,
127 // so a validity test is needed here
128 if ( androidTimeZone.isValid() ) {
129 // the java method getOffset() returns milliseconds, but QTimeZone returns seconds
130 return androidTimeZone.callMethod<jint>(
131 "getOffset", static_cast<jlong>(atMSecsSinceEpoch) ) / 1000;
132 } else {
133 return 0;
134 }
135}
136
137int QAndroidTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const
138{
139 // the java method does not use a reference time
140 Q_UNUSED( atMSecsSinceEpoch );
141 if ( androidTimeZone.isValid() )
142 // the java method getRawOffset() returns milliseconds, but QTimeZone returns seconds
143 return androidTimeZone.callMethod<jint>( "getRawOffset" ) / 1000;
144 else
145 return 0;
146}
147
148int QAndroidTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const
149{
150 return ( offsetFromUtc(atMSecsSinceEpoch) - standardTimeOffset(atMSecsSinceEpoch) );
151}
152
153bool QAndroidTimeZonePrivate::hasDaylightTime() const
154{
155 if ( androidTimeZone.isValid() )
156 /* note: the Java function only tests for future DST transitions, not past */
157 return androidTimeZone.callMethod<jboolean>("useDaylightTime" );
158 else
159 return false;
160}
161
162bool QAndroidTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
163{
164 if ( androidTimeZone.isValid() ) {
165 QJniObject jDate = QJniObject::construct<QtJniTypes::Date>(
166 static_cast<jlong>(atMSecsSinceEpoch));
167 return androidTimeZone.callMethod<jboolean>("inDaylightTime",
168 jDate.object<QtJniTypes::Date>());
169 }
170 else
171 return false;
172}
173
174QTimeZonePrivate::Data QAndroidTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
175{
176 if (androidTimeZone.isValid()) {
177 return Data(abbreviation(forMSecsSinceEpoch), forMSecsSinceEpoch,
178 offsetFromUtc(forMSecsSinceEpoch),
179 standardTimeOffset(forMSecsSinceEpoch));
180 }
181 return {};
182}
183
184// java.util.TimeZone does not directly provide transitions,
185// so don't over-ride QTZP's base implementation of transition methods.
186
187QByteArray QAndroidTimeZonePrivate::systemTimeZoneId() const
188{
189 // Keep in sync with default constructor:
190 QJniObject androidSystemTimeZone = QJniObject::callStaticMethod<QtJniTypes::TimeZone>(
191 QtJniTypes::Traits<QtJniTypes::TimeZone>::className(), "getDefault");
192 const QJniObject id = androidSystemTimeZone.callMethod<jstring>("getID");
193 return id.toString().toUtf8();
194}
195
196bool QAndroidTimeZonePrivate::isTimeZoneIdAvailable(const QByteArray &ianaId) const
197{
198 QAndroidTimeZonePrivate probe(ianaId);
199 return probe.isValid();
200}
201
202QList<QByteArray> QAndroidTimeZonePrivate::availableTimeZoneIds() const
203{
204 using namespace QtJniTypes;
205
206 const QJniArray androidAvailableIdList
207 = TimeZone::callStaticMethod<String[]>("getAvailableIDs");
208 // Does not document order of entries.
209
210 QList<QByteArray> result;
211 result.reserve(androidAvailableIdList.size());
212 for (const auto &id : androidAvailableIdList)
213 result.append(id.toString().toUtf8());
214
215 // Sort & uniquify (just to be sure; it appears to not need this, but we can't rely on that).
216 std::sort(result.begin(), result.end());
217 result.erase(std::unique(result.begin(), result.end()), result.end());
218 return result;
219}
220
221QT_END_NAMESPACE
Combined button and popup list for selecting options.
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")
static QString getDisplayName(QJniObject zone, jint style, jboolean dst, const QLocale &locale)