• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import java.io.IOException;
18 import java.util.Arrays;
19 import java.util.Date;
20 import java.util.TimeZone;
21 
22 /**
23  * Copied from ZoneInfo and ZoneInfoDB in dalvik.
24  * {@hide}
25  */
26 public class ZoneInfo extends TimeZone {
27 
28     private static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
29     private static final long MILLISECONDS_PER_400_YEARS =
30         MILLISECONDS_PER_DAY * (400 * 365 + 100 - 3);
31 
32     private static final long UNIX_OFFSET = 62167219200000L;
33 
34     private static final int[] NORMAL = new int[] {
35         0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
36     };
37 
38     private static final int[] LEAP = new int[] {
39         0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
40     };
41 
nullName(byte[] data, int where, int off)42     private static String nullName(byte[] data, int where, int off) {
43         if (off < 0)
44             return null;
45 
46         int end = where + off;
47         while (end < data.length && data[end] != '\0')
48             end++;
49 
50         return new String(data, where + off, end - (where + off));
51     }
52 
make(String name, byte[] data)53     public static ZoneInfo make(String name, byte[] data) {
54         int ntransition = read4(data, 32);
55         int ngmtoff = read4(data, 36);
56         int base = 44;
57 
58         int[] transitions = new int[ntransition];
59         for (int i = 0; i < ntransition; i++)
60             transitions[i] = read4(data, base + 4 * i);
61         base += 4 * ntransition;
62 
63         byte[] type = new byte[ntransition];
64         for (int i = 0; i < ntransition; i++)
65             type[i] = data[base + i];
66         base += ntransition;
67 
68         int[] gmtoff = new int[ngmtoff];
69         byte[] isdst = new byte[ngmtoff];
70         byte[] abbrev = new byte[ngmtoff];
71         for (int i = 0; i < ngmtoff; i++) {
72             gmtoff[i] = read4(data, base + 6 * i);
73             isdst[i] = data[base + 6 * i + 4];
74             abbrev[i] = data[base + 6 * i + 5];
75         }
76 
77         base += 6 * ngmtoff;
78 
79         return new ZoneInfo(name, transitions, type, gmtoff, isdst, abbrev, data, base);
80     }
81 
read4(byte[] data, int off)82     private static int read4(byte[] data, int off) {
83         return ((data[off    ] & 0xFF) << 24) |
84                ((data[off + 1] & 0xFF) << 16) |
85                ((data[off + 2] & 0xFF) <<  8) |
86                ((data[off + 3] & 0xFF) <<  0);
87     }
88 
ZoneInfo(String name, int[] transitions, byte[] type, int[] gmtoff, byte[] isdst, byte[] abbrev, byte[] data, int abbrevoff)89     /*package*/ ZoneInfo(String name, int[] transitions, byte[] type,
90                      int[] gmtoff, byte[] isdst, byte[] abbrev,
91                      byte[] data, int abbrevoff) {
92         mTransitions = transitions;
93         mTypes = type;
94         mGmtOffs = gmtoff;
95         mIsDsts = isdst;
96         mUseDst = false;
97         setID(name);
98 
99         // Find the latest GMT and non-GMT offsets for their abbreviations
100 
101         int lastdst;
102         for (lastdst = mTransitions.length - 1; lastdst >= 0; lastdst--) {
103             if (mIsDsts[mTypes[lastdst] & 0xFF] != 0)
104                 break;
105         }
106 
107         int laststd;
108         for (laststd = mTransitions.length - 1; laststd >= 0; laststd--) {
109             if (mIsDsts[mTypes[laststd] & 0xFF] == 0)
110                 break;
111         }
112 
113         if (lastdst >= 0) {
114             mDaylightName = nullName(data, abbrevoff,
115                                      abbrev[mTypes[lastdst] & 0xFF]);
116         }
117         if (laststd >= 0) {
118             mStandardName = nullName(data, abbrevoff,
119                                      abbrev[mTypes[laststd] & 0xFF]);
120         }
121 
122         // Use the latest non-DST offset if any as the raw offset
123 
124         if (laststd < 0) {
125             laststd = 0;
126         }
127 
128         if (laststd >= mTypes.length) {
129             mRawOffset = mGmtOffs[0];
130         } else {
131             mRawOffset = mGmtOffs[mTypes[laststd] & 0xFF];
132         }
133 
134         // Subtract the raw offset from all offsets so it can be changed
135         // and affect them too.
136         // Find whether there exist any observances of DST.
137 
138         for (int i = 0; i < mGmtOffs.length; i++) {
139             mGmtOffs[i] -= mRawOffset;
140 
141             if (mIsDsts[i] != 0) {
142                 mUseDst = true;
143             }
144         }
145 
146         mRawOffset *= 1000;
147     }
148 
149     @Override
getOffset(@uppressWarnings"unused") int era, int year, int month, int day, @SuppressWarnings("unused") int dayOfWeek, int millis)150     public int getOffset(@SuppressWarnings("unused") int era,
151         int year, int month, int day,
152         @SuppressWarnings("unused") int dayOfWeek,
153         int millis) {
154         // XXX This assumes Gregorian always; Calendar switches from
155         // Julian to Gregorian in 1582.  What calendar system are the
156         // arguments supposed to come from?
157 
158         long calc = (year / 400) * MILLISECONDS_PER_400_YEARS;
159         year %= 400;
160 
161         calc += year * (365 * MILLISECONDS_PER_DAY);
162         calc += ((year + 3) / 4) * MILLISECONDS_PER_DAY;
163 
164         if (year > 0)
165             calc -= ((year - 1) / 100) * MILLISECONDS_PER_DAY;
166 
167         boolean isLeap = (year == 0 || (year % 4 == 0 && year % 100 != 0));
168         int[] mlen = isLeap ? LEAP : NORMAL;
169 
170         calc += mlen[month] * MILLISECONDS_PER_DAY;
171         calc += (day - 1) * MILLISECONDS_PER_DAY;
172         calc += millis;
173 
174         calc -= mRawOffset;
175         calc -= UNIX_OFFSET;
176 
177         return getOffset(calc);
178     }
179 
180     @Override
getOffset(long when)181     public int getOffset(long when) {
182         int unix = (int) (when / 1000);
183         int trans = Arrays.binarySearch(mTransitions, unix);
184 
185         if (trans == ~0) {
186             return mGmtOffs[0] * 1000 + mRawOffset;
187         }
188         if (trans < 0) {
189             trans = ~trans - 1;
190         }
191 
192         return mGmtOffs[mTypes[trans] & 0xFF] * 1000 + mRawOffset;
193     }
194 
195     @Override
getRawOffset()196     public int getRawOffset() {
197         return mRawOffset;
198     }
199 
200     @Override
setRawOffset(int off)201     public void setRawOffset(int off) {
202         mRawOffset = off;
203     }
204 
205     @Override
inDaylightTime(Date when)206     public boolean inDaylightTime(Date when) {
207         int unix = (int) (when.getTime() / 1000);
208         int trans = Arrays.binarySearch(mTransitions, unix);
209 
210         if (trans == ~0) {
211             return mIsDsts[0] != 0;
212         }
213         if (trans < 0) {
214             trans = ~trans - 1;
215         }
216 
217         return mIsDsts[mTypes[trans] & 0xFF] != 0;
218     }
219 
220     @Override
useDaylightTime()221     public boolean useDaylightTime() {
222         return mUseDst;
223     }
224 
225     private int mRawOffset;
226     private int[] mTransitions;
227     private int[] mGmtOffs;
228     private byte[] mTypes;
229     private byte[] mIsDsts;
230     private boolean mUseDst;
231     private String mDaylightName;
232     private String mStandardName;
233 
234     @Override
equals(Object obj)235     public boolean equals(Object obj) {
236         if (this == obj) {
237             return true;
238         }
239         if (!(obj instanceof ZoneInfo)) {
240            return false;
241         }
242         ZoneInfo other = (ZoneInfo) obj;
243         return mUseDst == other.mUseDst
244                 && (mDaylightName == null ? other.mDaylightName == null :
245                         mDaylightName.equals(other.mDaylightName))
246                 && (mStandardName == null ? other.mStandardName == null :
247                         mStandardName.equals(other.mStandardName))
248                 && mRawOffset == other.mRawOffset
249                 // Arrays.equals returns true if both arrays are null
250                 && Arrays.equals(mGmtOffs, other.mGmtOffs)
251                 && Arrays.equals(mIsDsts, other.mIsDsts)
252                 && Arrays.equals(mTypes, other.mTypes)
253                 && Arrays.equals(mTransitions, other.mTransitions);
254     }
255 
256     @Override
hashCode()257     public int hashCode() {
258         final int prime = 31;
259         int result = 1;
260         result = prime * result + ((mDaylightName == null) ? 0 :
261                 mDaylightName.hashCode());
262         result = prime * result + Arrays.hashCode(mGmtOffs);
263         result = prime * result + Arrays.hashCode(mIsDsts);
264         result = prime * result + mRawOffset;
265         result = prime * result + ((mStandardName == null) ? 0 :
266                 mStandardName.hashCode());
267         result = prime * result + Arrays.hashCode(mTransitions);
268         result = prime * result + Arrays.hashCode(mTypes);
269         result = prime * result + (mUseDst ? 1231 : 1237);
270         return result;
271     }
272 }
273