Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/java/time/zone/ZoneRulesProvider.java
41159 views
1
/*
2
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
/*
27
* This file is available under and governed by the GNU General Public
28
* License version 2 only, as published by the Free Software Foundation.
29
* However, the following notice accompanied the original version of this
30
* file:
31
*
32
* Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos
33
*
34
* All rights reserved.
35
*
36
* Redistribution and use in source and binary forms, with or without
37
* modification, are permitted provided that the following conditions are met:
38
*
39
* * Redistributions of source code must retain the above copyright notice,
40
* this list of conditions and the following disclaimer.
41
*
42
* * Redistributions in binary form must reproduce the above copyright notice,
43
* this list of conditions and the following disclaimer in the documentation
44
* and/or other materials provided with the distribution.
45
*
46
* * Neither the name of JSR-310 nor the names of its contributors
47
* may be used to endorse or promote products derived from this software
48
* without specific prior written permission.
49
*
50
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
54
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
55
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
56
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
57
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
58
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
59
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61
*/
62
package java.time.zone;
63
64
import java.security.AccessController;
65
import java.security.PrivilegedAction;
66
import java.time.ZoneId;
67
import java.time.ZonedDateTime;
68
import java.util.ArrayList;
69
import java.util.HashSet;
70
import java.util.Iterator;
71
import java.util.List;
72
import java.util.NavigableMap;
73
import java.util.Objects;
74
import java.util.ServiceConfigurationError;
75
import java.util.ServiceLoader;
76
import java.util.Set;
77
import java.util.concurrent.ConcurrentHashMap;
78
import java.util.concurrent.ConcurrentMap;
79
import java.util.concurrent.CopyOnWriteArrayList;
80
import java.util.Collections;
81
82
/**
83
* Provider of time-zone rules to the system.
84
* <p>
85
* This class manages the configuration of time-zone rules.
86
* The static methods provide the public API that can be used to manage the providers.
87
* The abstract methods provide the SPI that allows rules to be provided.
88
* <p>
89
* ZoneRulesProvider may be installed in an instance of the Java Platform as
90
* extension classes, that is, jar files placed into any of the usual extension
91
* directories. Installed providers are loaded using the service-provider loading
92
* facility defined by the {@link ServiceLoader} class. A ZoneRulesProvider
93
* identifies itself with a provider configuration file named
94
* {@code java.time.zone.ZoneRulesProvider} in the resource directory
95
* {@code META-INF/services}. The file should contain a line that specifies the
96
* fully qualified concrete zonerules-provider class name.
97
* Providers may also be made available by adding them to the class path or by
98
* registering themselves via {@link #registerProvider} method.
99
* <p>
100
* The Java virtual machine has a default provider that provides zone rules
101
* for the time-zones defined by IANA Time Zone Database (TZDB). If the system
102
* property {@systemProperty java.time.zone.DefaultZoneRulesProvider} is defined then
103
* it is taken to be the fully-qualified name of a concrete ZoneRulesProvider
104
* class to be loaded as the default provider, using the system class loader.
105
* If this system property is not defined, a system-default provider will be
106
* loaded to serve as the default provider.
107
* <p>
108
* Rules are looked up primarily by zone ID, as used by {@link ZoneId}.
109
* Only zone region IDs may be used, zone offset IDs are not used here.
110
* <p>
111
* Time-zone rules are political, thus the data can change at any time.
112
* Each provider will provide the latest rules for each zone ID, but they
113
* may also provide the history of how the rules changed.
114
*
115
* @implSpec
116
* This interface is a service provider that can be called by multiple threads.
117
* Implementations must be immutable and thread-safe.
118
* <p>
119
* Providers must ensure that once a rule has been seen by the application, the
120
* rule must continue to be available.
121
* <p>
122
* Providers are encouraged to implement a meaningful {@code toString} method.
123
* <p>
124
* Many systems would like to update time-zone rules dynamically without stopping the JVM.
125
* When examined in detail, this is a complex problem.
126
* Providers may choose to handle dynamic updates, however the default provider does not.
127
*
128
* @since 1.8
129
*/
130
@SuppressWarnings("removal")
131
public abstract class ZoneRulesProvider {
132
133
/**
134
* The set of loaded providers.
135
*/
136
private static final CopyOnWriteArrayList<ZoneRulesProvider> PROVIDERS = new CopyOnWriteArrayList<>();
137
/**
138
* The lookup from zone ID to provider.
139
*/
140
private static final ConcurrentMap<String, ZoneRulesProvider> ZONES = new ConcurrentHashMap<>(512, 0.75f, 2);
141
142
/**
143
* The zone ID data
144
*/
145
private static volatile Set<String> ZONE_IDS;
146
147
static {
148
// if the property java.time.zone.DefaultZoneRulesProvider is
149
// set then its value is the class name of the default provider
150
final List<ZoneRulesProvider> loaded = new ArrayList<>();
151
AccessController.doPrivileged(new PrivilegedAction<>() {
152
public Object run() {
153
String prop = System.getProperty("java.time.zone.DefaultZoneRulesProvider");
154
if (prop != null) {
155
try {
156
Class<?> c = Class.forName(prop, true, ClassLoader.getSystemClassLoader());
157
@SuppressWarnings("deprecation")
158
ZoneRulesProvider provider = ZoneRulesProvider.class.cast(c.newInstance());
159
registerProvider(provider);
160
loaded.add(provider);
161
} catch (Exception x) {
162
throw new Error(x);
163
}
164
} else {
165
registerProvider(new TzdbZoneRulesProvider());
166
}
167
return null;
168
}
169
});
170
171
ServiceLoader<ZoneRulesProvider> sl = ServiceLoader.load(ZoneRulesProvider.class, ClassLoader.getSystemClassLoader());
172
Iterator<ZoneRulesProvider> it = sl.iterator();
173
while (it.hasNext()) {
174
ZoneRulesProvider provider;
175
try {
176
provider = it.next();
177
} catch (ServiceConfigurationError ex) {
178
if (ex.getCause() instanceof SecurityException) {
179
continue; // ignore the security exception, try the next provider
180
}
181
throw ex;
182
}
183
boolean found = false;
184
for (ZoneRulesProvider p : loaded) {
185
if (p.getClass() == provider.getClass()) {
186
found = true;
187
}
188
}
189
if (!found) {
190
registerProvider0(provider);
191
loaded.add(provider);
192
}
193
}
194
// CopyOnWriteList could be slow if lots of providers and each added individually
195
PROVIDERS.addAll(loaded);
196
}
197
198
//-------------------------------------------------------------------------
199
/**
200
* Gets the set of available zone IDs.
201
* <p>
202
* These IDs are the string form of a {@link ZoneId}.
203
*
204
* @return the unmodifiable set of zone IDs, not null
205
*/
206
public static Set<String> getAvailableZoneIds() {
207
return ZONE_IDS;
208
}
209
210
/**
211
* Gets the rules for the zone ID.
212
* <p>
213
* This returns the latest available rules for the zone ID.
214
* <p>
215
* This method relies on time-zone data provider files that are configured.
216
* These are loaded using a {@code ServiceLoader}.
217
* <p>
218
* The caching flag is designed to allow provider implementations to
219
* prevent the rules being cached in {@code ZoneId}.
220
* Under normal circumstances, the caching of zone rules is highly desirable
221
* as it will provide greater performance. However, there is a use case where
222
* the caching would not be desirable, see {@link #provideRules}.
223
*
224
* @param zoneId the zone ID as defined by {@code ZoneId}, not null
225
* @param forCaching whether the rules are being queried for caching,
226
* true if the returned rules will be cached by {@code ZoneId},
227
* false if they will be returned to the user without being cached in {@code ZoneId}
228
* @return the rules, null if {@code forCaching} is true and this
229
* is a dynamic provider that wants to prevent caching in {@code ZoneId},
230
* otherwise not null
231
* @throws ZoneRulesException if rules cannot be obtained for the zone ID
232
*/
233
public static ZoneRules getRules(String zoneId, boolean forCaching) {
234
Objects.requireNonNull(zoneId, "zoneId");
235
return getProvider(zoneId).provideRules(zoneId, forCaching);
236
}
237
238
/**
239
* Gets the history of rules for the zone ID.
240
* <p>
241
* Time-zones are defined by governments and change frequently.
242
* This method allows applications to find the history of changes to the
243
* rules for a single zone ID. The map is keyed by a string, which is the
244
* version string associated with the rules.
245
* <p>
246
* The exact meaning and format of the version is provider specific.
247
* The version must follow lexicographical order, thus the returned map will
248
* be order from the oldest known rules to the newest available rules.
249
* The default 'TZDB' group uses version numbering consisting of the year
250
* followed by a letter, such as '2009e' or '2012f'.
251
* <p>
252
* Implementations must provide a result for each valid zone ID, however
253
* they do not have to provide a history of rules.
254
* Thus the map will always contain one element, and will only contain more
255
* than one element if historical rule information is available.
256
*
257
* @param zoneId the zone ID as defined by {@code ZoneId}, not null
258
* @return a modifiable copy of the history of the rules for the ID, sorted
259
* from oldest to newest, not null
260
* @throws ZoneRulesException if history cannot be obtained for the zone ID
261
*/
262
public static NavigableMap<String, ZoneRules> getVersions(String zoneId) {
263
Objects.requireNonNull(zoneId, "zoneId");
264
return getProvider(zoneId).provideVersions(zoneId);
265
}
266
267
/**
268
* Gets the provider for the zone ID.
269
*
270
* @param zoneId the zone ID as defined by {@code ZoneId}, not null
271
* @return the provider, not null
272
* @throws ZoneRulesException if the zone ID is unknown
273
*/
274
private static ZoneRulesProvider getProvider(String zoneId) {
275
ZoneRulesProvider provider = ZONES.get(zoneId);
276
if (provider == null) {
277
if (ZONES.isEmpty()) {
278
throw new ZoneRulesException("No time-zone data files registered");
279
}
280
throw new ZoneRulesException("Unknown time-zone ID: " + zoneId);
281
}
282
return provider;
283
}
284
285
//-------------------------------------------------------------------------
286
/**
287
* Registers a zone rules provider.
288
* <p>
289
* This adds a new provider to those currently available.
290
* A provider supplies rules for one or more zone IDs.
291
* A provider cannot be registered if it supplies a zone ID that has already been
292
* registered. See the notes on time-zone IDs in {@link ZoneId}, especially
293
* the section on using the concept of a "group" to make IDs unique.
294
* <p>
295
* To ensure the integrity of time-zones already created, there is no way
296
* to deregister providers.
297
*
298
* @param provider the provider to register, not null
299
* @throws ZoneRulesException if a zone ID is already registered
300
*/
301
public static void registerProvider(ZoneRulesProvider provider) {
302
Objects.requireNonNull(provider, "provider");
303
registerProvider0(provider);
304
PROVIDERS.add(provider);
305
}
306
307
/**
308
* Registers the provider.
309
*
310
* @param provider the provider to register, not null
311
* @throws ZoneRulesException if unable to complete the registration
312
*/
313
private static synchronized void registerProvider0(ZoneRulesProvider provider) {
314
for (String zoneId : provider.provideZoneIds()) {
315
Objects.requireNonNull(zoneId, "zoneId");
316
ZoneRulesProvider old = ZONES.putIfAbsent(zoneId, provider);
317
if (old != null) {
318
throw new ZoneRulesException(
319
"Unable to register zone as one already registered with that ID: " + zoneId +
320
", currently loading from provider: " + provider);
321
}
322
}
323
Set<String> combinedSet = new HashSet<String>(ZONES.keySet());
324
ZONE_IDS = Collections.unmodifiableSet(combinedSet);
325
}
326
327
/**
328
* Refreshes the rules from the underlying data provider.
329
* <p>
330
* This method allows an application to request that the providers check
331
* for any updates to the provided rules.
332
* After calling this method, the offset stored in any {@link ZonedDateTime}
333
* may be invalid for the zone ID.
334
* <p>
335
* Dynamic update of rules is a complex problem and most applications
336
* should not use this method or dynamic rules.
337
* To achieve dynamic rules, a provider implementation will have to be written
338
* as per the specification of this class.
339
* In addition, instances of {@code ZoneRules} must not be cached in the
340
* application as they will become stale. However, the boolean flag on
341
* {@link #provideRules(String, boolean)} allows provider implementations
342
* to control the caching of {@code ZoneId}, potentially ensuring that
343
* all objects in the system see the new rules.
344
* Note that there is likely to be a cost in performance of a dynamic rules
345
* provider. Note also that no dynamic rules provider is in this specification.
346
*
347
* @return true if the rules were updated
348
* @throws ZoneRulesException if an error occurs during the refresh
349
*/
350
public static boolean refresh() {
351
boolean changed = false;
352
for (ZoneRulesProvider provider : PROVIDERS) {
353
changed |= provider.provideRefresh();
354
}
355
return changed;
356
}
357
358
/**
359
* Constructor.
360
*/
361
protected ZoneRulesProvider() {
362
}
363
364
//-----------------------------------------------------------------------
365
/**
366
* SPI method to get the available zone IDs.
367
* <p>
368
* This obtains the IDs that this {@code ZoneRulesProvider} provides.
369
* A provider should provide data for at least one zone ID.
370
* <p>
371
* The returned zone IDs remain available and valid for the lifetime of the application.
372
* A dynamic provider may increase the set of IDs as more data becomes available.
373
*
374
* @return the set of zone IDs being provided, not null
375
* @throws ZoneRulesException if a problem occurs while providing the IDs
376
*/
377
protected abstract Set<String> provideZoneIds();
378
379
/**
380
* SPI method to get the rules for the zone ID.
381
* <p>
382
* This loads the rules for the specified zone ID.
383
* The provider implementation must validate that the zone ID is valid and
384
* available, throwing a {@code ZoneRulesException} if it is not.
385
* The result of the method in the valid case depends on the caching flag.
386
* <p>
387
* If the provider implementation is not dynamic, then the result of the
388
* method must be the non-null set of rules selected by the ID.
389
* <p>
390
* If the provider implementation is dynamic, then the flag gives the option
391
* of preventing the returned rules from being cached in {@link ZoneId}.
392
* When the flag is true, the provider is permitted to return null, where
393
* null will prevent the rules from being cached in {@code ZoneId}.
394
* When the flag is false, the provider must return non-null rules.
395
*
396
* @param zoneId the zone ID as defined by {@code ZoneId}, not null
397
* @param forCaching whether the rules are being queried for caching,
398
* true if the returned rules will be cached by {@code ZoneId},
399
* false if they will be returned to the user without being cached in {@code ZoneId}
400
* @return the rules, null if {@code forCaching} is true and this
401
* is a dynamic provider that wants to prevent caching in {@code ZoneId},
402
* otherwise not null
403
* @throws ZoneRulesException if rules cannot be obtained for the zone ID
404
*/
405
protected abstract ZoneRules provideRules(String zoneId, boolean forCaching);
406
407
/**
408
* SPI method to get the history of rules for the zone ID.
409
* <p>
410
* This returns a map of historical rules keyed by a version string.
411
* The exact meaning and format of the version is provider specific.
412
* The version must follow lexicographical order, thus the returned map will
413
* be order from the oldest known rules to the newest available rules.
414
* The default 'TZDB' group uses version numbering consisting of the year
415
* followed by a letter, such as '2009e' or '2012f'.
416
* <p>
417
* Implementations must provide a result for each valid zone ID, however
418
* they do not have to provide a history of rules.
419
* Thus the map will contain at least one element, and will only contain
420
* more than one element if historical rule information is available.
421
* <p>
422
* The returned versions remain available and valid for the lifetime of the application.
423
* A dynamic provider may increase the set of versions as more data becomes available.
424
*
425
* @param zoneId the zone ID as defined by {@code ZoneId}, not null
426
* @return a modifiable copy of the history of the rules for the ID, sorted
427
* from oldest to newest, not null
428
* @throws ZoneRulesException if history cannot be obtained for the zone ID
429
*/
430
protected abstract NavigableMap<String, ZoneRules> provideVersions(String zoneId);
431
432
/**
433
* SPI method to refresh the rules from the underlying data provider.
434
* <p>
435
* This method provides the opportunity for a provider to dynamically
436
* recheck the underlying data provider to find the latest rules.
437
* This could be used to load new rules without stopping the JVM.
438
* Dynamic behavior is entirely optional and most providers do not support it.
439
* <p>
440
* This implementation returns false.
441
*
442
* @return true if the rules were updated
443
* @throws ZoneRulesException if an error occurs during the refresh
444
*/
445
protected boolean provideRefresh() {
446
return false;
447
}
448
449
}
450
451