1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.joda.time.tz;
17
18 import org.joda.time.DateTimeZone;
19
20
21
22
23
24
25
26
27
28
29
30 public class CachedDateTimeZone extends DateTimeZone {
31
32 private static final long serialVersionUID = 5472298452022250685L;
33
34 private static final int cInfoCacheMask;
35
36 static {
37 Integer i;
38 try {
39 i = Integer.getInteger("org.joda.time.tz.CachedDateTimeZone.size");
40 } catch (SecurityException e) {
41 i = null;
42 }
43
44 int cacheSize;
45 if (i == null) {
46
47
48 cacheSize = 512;
49 } else {
50 cacheSize = i.intValue();
51
52 cacheSize--;
53 int shift = 0;
54 while (cacheSize > 0) {
55 shift++;
56 cacheSize >>= 1;
57 }
58 cacheSize = 1 << shift;
59 }
60
61 cInfoCacheMask = cacheSize - 1;
62 }
63
64
65
66
67 public static CachedDateTimeZone forZone(DateTimeZone zone) {
68 if (zone instanceof CachedDateTimeZone) {
69 return (CachedDateTimeZone)zone;
70 }
71 return new CachedDateTimeZone(zone);
72 }
73
74
75
76
77
78
79
80
81
82 private final DateTimeZone iZone;
83
84 private final Info[] iInfoCache = new Info[cInfoCacheMask + 1];
85
86 private CachedDateTimeZone(DateTimeZone zone) {
87 super(zone.getID());
88 iZone = zone;
89 }
90
91
92
93
94 public DateTimeZone getUncachedZone() {
95 return iZone;
96 }
97
98 public String getNameKey(long instant) {
99 return getInfo(instant).getNameKey(instant);
100 }
101
102 public int getOffset(long instant) {
103 return getInfo(instant).getOffset(instant);
104 }
105
106 public int getStandardOffset(long instant) {
107 return getInfo(instant).getStandardOffset(instant);
108 }
109
110 public boolean isFixed() {
111 return iZone.isFixed();
112 }
113
114 public long nextTransition(long instant) {
115 return iZone.nextTransition(instant);
116 }
117
118 public long previousTransition(long instant) {
119 return iZone.previousTransition(instant);
120 }
121
122 public int hashCode() {
123 return iZone.hashCode();
124 }
125
126 public boolean equals(Object obj) {
127 if (this == obj) {
128 return true;
129 }
130 if (obj instanceof CachedDateTimeZone) {
131 return iZone.equals(((CachedDateTimeZone)obj).iZone);
132 }
133 return false;
134 }
135
136
137
138
139 private Info getInfo(long millis) {
140 int period = (int)(millis >> 32);
141 Info[] cache = iInfoCache;
142 int index = period & cInfoCacheMask;
143 Info info = cache[index];
144 if (info == null || (int)((info.iPeriodStart >> 32)) != period) {
145 info = createInfo(millis);
146 cache[index] = info;
147 }
148 return info;
149 }
150
151 private Info createInfo(long millis) {
152 long periodStart = millis & (0xffffffffL << 32);
153 Info info = new Info(iZone, periodStart);
154
155 long end = periodStart | 0xffffffffL;
156 Info chain = info;
157 while (true) {
158 long next = iZone.nextTransition(periodStart);
159 if (next == periodStart || next > end) {
160 break;
161 }
162 periodStart = next;
163 chain = (chain.iNextInfo = new Info(iZone, periodStart));
164 }
165
166 return info;
167 }
168
169 private final static class Info {
170
171 public final long iPeriodStart;
172 public final DateTimeZone iZoneRef;
173
174 Info iNextInfo;
175
176 private String iNameKey;
177 private int iOffset = Integer.MIN_VALUE;
178 private int iStandardOffset = Integer.MIN_VALUE;
179
180 Info(DateTimeZone zone, long periodStart) {
181 iPeriodStart = periodStart;
182 iZoneRef = zone;
183 }
184
185 public String getNameKey(long millis) {
186 if (iNextInfo == null || millis < iNextInfo.iPeriodStart) {
187 if (iNameKey == null) {
188 iNameKey = iZoneRef.getNameKey(iPeriodStart);
189 }
190 return iNameKey;
191 }
192 return iNextInfo.getNameKey(millis);
193 }
194
195 public int getOffset(long millis) {
196 if (iNextInfo == null || millis < iNextInfo.iPeriodStart) {
197 if (iOffset == Integer.MIN_VALUE) {
198 iOffset = iZoneRef.getOffset(iPeriodStart);
199 }
200 return iOffset;
201 }
202 return iNextInfo.getOffset(millis);
203 }
204
205 public int getStandardOffset(long millis) {
206 if (iNextInfo == null || millis < iNextInfo.iPeriodStart) {
207 if (iStandardOffset == Integer.MIN_VALUE) {
208 iStandardOffset = iZoneRef.getStandardOffset(iPeriodStart);
209 }
210 return iStandardOffset;
211 }
212 return iNextInfo.getStandardOffset(millis);
213 }
214 }
215 }