1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * This code is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 only, as
6  * published by the Free Software Foundation.  The Android Open Source
7  * Project designates this particular file as subject to the "Classpath"
8  * exception as provided by The Android Open Source Project in the LICENSE
9  * 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 
22 package java.time.zone;
23 
24 
25 import android.icu.util.TimeZone;
26 
27 import com.android.icu.util.ExtendedTimeZone;
28 
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.NavigableMap;
32 import java.util.Set;
33 import java.util.TreeMap;
34 import libcore.util.BasicLruCache;
35 
36 /**
37  * A ZoneRulesProvider that generates rules from ICU4J TimeZones.
38  * This provider ensures that classes in {@link java.time} use the same time zone information
39  * as ICU4J.
40  */
41 public class IcuZoneRulesProvider extends ZoneRulesProvider {
42 
43     private final BasicLruCache<String, ZoneRules> cache = new ZoneRulesCache(8);
44 
45     @Override
provideZoneIds()46     protected Set<String> provideZoneIds() {
47         Set<String> zoneIds = TimeZone.getAvailableIDs(TimeZone.SystemTimeZoneType.ANY, null, null);
48         zoneIds = new HashSet<>(zoneIds);
49         // java.time assumes ZoneId that start with "GMT" fit the pattern "GMT+HH:mm:ss" which these
50         // do not. Since they are equivalent to GMT, just remove these aliases.
51         zoneIds.remove("GMT+0");
52         zoneIds.remove("GMT-0");
53         return zoneIds;
54     }
55 
56     @Override
provideRules(String zoneId, boolean forCaching)57     protected ZoneRules provideRules(String zoneId, boolean forCaching) {
58         // Ignore forCaching, as this is a static provider.
59         return cache.get(zoneId);
60     }
61 
62     @Override
provideVersions(String zoneId)63     protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
64         return new TreeMap<>(
65                 Collections.singletonMap(TimeZone.getTZDataVersion(),
66                         provideRules(zoneId, /* forCaching */ false)));
67     }
68 
generateZoneRules(String zoneId)69     static ZoneRules generateZoneRules(String zoneId) {
70         return ExtendedTimeZone.getInstance(zoneId).createZoneRules();
71     }
72 
73     private static class ZoneRulesCache extends BasicLruCache<String, ZoneRules> {
74 
ZoneRulesCache(int maxSize)75         ZoneRulesCache(int maxSize) {
76             super(maxSize);
77         }
78 
79         @Override
create(String zoneId)80         protected ZoneRules create(String zoneId) {
81             String canonicalId = TimeZone.getCanonicalID(zoneId);
82             if (!canonicalId.equals(zoneId)) {
83                 // Return the same object as the canonical one, to avoid wasting space, but cache
84                 // it under the non-cannonical name as well, to avoid future getCanonicalID calls.
85                 return get(canonicalId);
86             }
87             return generateZoneRules(zoneId);
88         }
89     }
90 }
91