1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * you may not use this file except in compliance with the License.
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * You may obtain a copy of the License at
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License.
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
176d82ce5d35a5e84aedf08528fd98b849f3f565a6Elliott Hughespackage libcore.util;
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Arrays;
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Date;
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.TimeZone;
2253d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughesimport libcore.io.BufferIterator;
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
246d82ce5d35a5e84aedf08528fd98b849f3f565a6Elliott Hughes/**
256d82ce5d35a5e84aedf08528fd98b849f3f565a6Elliott Hughes * Our concrete TimeZone implementation, backed by zoneinfo data.
266d82ce5d35a5e84aedf08528fd98b849f3f565a6Elliott Hughes *
276d82ce5d35a5e84aedf08528fd98b849f3f565a6Elliott Hughes * @hide - used to implement TimeZone
286d82ce5d35a5e84aedf08528fd98b849f3f565a6Elliott Hughes */
2965c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughespublic final class ZoneInfo extends TimeZone {
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final long MILLISECONDS_PER_400_YEARS =
32f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes            MILLISECONDS_PER_DAY * (400 * 365 + 100 - 3);
33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final long UNIX_OFFSET = 62167219200000L;
35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int[] NORMAL = new int[] {
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    };
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int[] LEAP = new int[] {
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    };
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
44e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson    private int mRawOffset;
4565c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes    private final int mEarliestRawOffset;
46de3df0418aff29b06ea022b200fbcc687de63a7cElliott Hughes    private final boolean mUseDst;
47de3df0418aff29b06ea022b200fbcc687de63a7cElliott Hughes    private final int mDstSavings; // Implements TimeZone.getDSTSavings.
4865c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes
49f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes    private final int[] mTransitions;
50102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes    private final int[] mOffsets;
51f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes    private final byte[] mTypes;
52f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes    private final byte[] mIsDsts;
53e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson
5453d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes    public static TimeZone makeTimeZone(String id, BufferIterator it) {
5553d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        // Variable names beginning tzh_ correspond to those in "tzfile.h".
5653d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes
5753d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        // Check tzh_magic.
5853d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        if (it.readInt() != 0x545a6966) { // "TZif"
5953d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes            return null;
6053d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        }
6153d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes
6253d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        // Skip the uninteresting part of the header.
6353d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        it.skip(28);
6453d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes
6553d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        // Read the sizes of the arrays we're about to read.
6653d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        int tzh_timecnt = it.readInt();
6753d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        int tzh_typecnt = it.readInt();
6853d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes
6953d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        it.skip(4); // Skip tzh_charcnt.
7053d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes
7153d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        int[] transitions = new int[tzh_timecnt];
7253d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        it.readIntArray(transitions, 0, transitions.length);
7353d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes
7453d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        byte[] type = new byte[tzh_timecnt];
7553d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        it.readByteArray(type, 0, type.length);
7653d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes
7753d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        int[] gmtOffsets = new int[tzh_typecnt];
7853d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        byte[] isDsts = new byte[tzh_typecnt];
7953d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        for (int i = 0; i < tzh_typecnt; ++i) {
8053d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes            gmtOffsets[i] = it.readInt();
8153d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes            isDsts[i] = it.readByte();
8253d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes            // We skip the abbreviation index. This would let us provide historically-accurate
8353d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes            // time zone abbreviations (such as "AHST", "YST", and "AKST" for standard time in
8453d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes            // America/Anchorage in 1982, 1983, and 1984 respectively). ICU only knows the current
8553d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes            // names, though, so even if we did use this data to provide the correct abbreviations
8653d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes            // for en_US, we wouldn't be able to provide correct abbreviations for other locales,
8753d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes            // nor would we be able to provide correct long forms (such as "Yukon Standard Time")
8853d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes            // for any locale. (The RI doesn't do any better than us here either.)
8953d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes            it.skip(1);
9053d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        }
9153d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes
9253d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes        return new ZoneInfo(id, transitions, type, gmtOffsets, isDsts);
9353d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes    }
9453d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes
9553d000ca7ea62d0e36c9ba7be8c54c4777cabae6Elliott Hughes    private ZoneInfo(String name, int[] transitions, byte[] types, int[] gmtOffsets, byte[] isDsts) {
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        mTransitions = transitions;
9778c3de051d68b703af480778c100ca335690b250Elliott Hughes        mTypes = types;
98102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes        mIsDsts = isDsts;
99adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        setID(name);
100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
10178c3de051d68b703af480778c100ca335690b250Elliott Hughes        // Find the latest daylight and standard offsets (if any).
10278c3de051d68b703af480778c100ca335690b250Elliott Hughes        int lastStd = 0;
10378c3de051d68b703af480778c100ca335690b250Elliott Hughes        boolean haveStd = false;
10478c3de051d68b703af480778c100ca335690b250Elliott Hughes        int lastDst = 0;
10578c3de051d68b703af480778c100ca335690b250Elliott Hughes        boolean haveDst = false;
10678c3de051d68b703af480778c100ca335690b250Elliott Hughes        for (int i = mTransitions.length - 1; (!haveStd || !haveDst) && i >= 0; --i) {
10778c3de051d68b703af480778c100ca335690b250Elliott Hughes            int type = mTypes[i] & 0xff;
10878c3de051d68b703af480778c100ca335690b250Elliott Hughes            if (!haveStd && mIsDsts[type] == 0) {
10978c3de051d68b703af480778c100ca335690b250Elliott Hughes                haveStd = true;
11078c3de051d68b703af480778c100ca335690b250Elliott Hughes                lastStd = i;
11178c3de051d68b703af480778c100ca335690b250Elliott Hughes            }
11278c3de051d68b703af480778c100ca335690b250Elliott Hughes            if (!haveDst && mIsDsts[type] != 0) {
11378c3de051d68b703af480778c100ca335690b250Elliott Hughes                haveDst = true;
11478c3de051d68b703af480778c100ca335690b250Elliott Hughes                lastDst = i;
115f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes            }
116adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
11778c3de051d68b703af480778c100ca335690b250Elliott Hughes
11878c3de051d68b703af480778c100ca335690b250Elliott Hughes        // Use the latest non-daylight offset (if any) as the raw offset.
119102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes        if (lastStd >= mTypes.length) {
120102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes            mRawOffset = gmtOffsets[0];
121adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else {
12265c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes            mRawOffset = gmtOffsets[mTypes[lastStd] & 0xff];
12365c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        }
12465c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes
12578c3de051d68b703af480778c100ca335690b250Elliott Hughes        // Use the latest transition's pair of offsets to compute the DST savings.
12678c3de051d68b703af480778c100ca335690b250Elliott Hughes        // This isn't generally useful, but it's exposed by TimeZone.getDSTSavings.
12778c3de051d68b703af480778c100ca335690b250Elliott Hughes        if (lastDst >= mTypes.length) {
12878c3de051d68b703af480778c100ca335690b250Elliott Hughes            mDstSavings = 0;
12978c3de051d68b703af480778c100ca335690b250Elliott Hughes        } else {
13078c3de051d68b703af480778c100ca335690b250Elliott Hughes            mDstSavings = Math.abs(gmtOffsets[mTypes[lastStd] & 0xff] - gmtOffsets[mTypes[lastDst] & 0xff]) * 1000;
13178c3de051d68b703af480778c100ca335690b250Elliott Hughes        }
13278c3de051d68b703af480778c100ca335690b250Elliott Hughes
13365c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        // Cache the oldest known raw offset, in case we're asked about times that predate our
13465c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        // transition data.
13565c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        int firstStd = -1;
13665c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        for (int i = 0; i < mTransitions.length; ++i) {
13765c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes            if (mIsDsts[mTypes[i] & 0xff] == 0) {
13865c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes                firstStd = i;
13965c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes                break;
14065c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes            }
141adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
14265c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        int earliestRawOffset = (firstStd != -1) ? gmtOffsets[mTypes[firstStd] & 0xff] : mRawOffset;
143adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
144102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes        // Rather than keep offsets from UTC, we use offsets from local time, so the raw offset
145102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes        // can be changed and automatically affect all the offsets.
146102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes        mOffsets = gmtOffsets;
147102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes        for (int i = 0; i < mOffsets.length; i++) {
148102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes            mOffsets[i] -= mRawOffset;
1498ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes        }
150adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1518ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes        // Is this zone still observing DST?
1528ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes        // We don't care if they've historically used it: most places have at least once.
1538ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes        // We want to know whether the last "schedule info" (the unix times in the mTransitions
1548ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes        // array) is in the future. If it is, DST is still relevant.
1558ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes        // See http://code.google.com/p/android/issues/detail?id=877.
1568ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes        // This test means that for somewhere like Morocco, which tried DST in 2009 but has
1578ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes        // no future plans (and thus no future schedule info) will report "true" from
1588ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes        // useDaylightTime at the start of 2009 but "false" at the end. This seems appropriate.
159f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes        boolean usesDst = false;
1608ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes        long currentUnixTime = System.currentTimeMillis() / 1000;
1618ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes        if (mTransitions.length > 0) {
1628ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes            // (We're really dealing with uint32_t values, so long is most convenient in Java.)
1636aa068b481cc4cca7765ce90fdf32f3eb2b5a77cElliott Hughes            long latestScheduleTime = ((long) mTransitions[mTransitions.length - 1]) & 0xffffffff;
1648ed4ad0c5b361f7a196eb97bc3ec210ab086623bElliott Hughes            if (currentUnixTime < latestScheduleTime) {
165f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes                usesDst = true;
166adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
168f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes        mUseDst = usesDst;
169adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
17078c3de051d68b703af480778c100ca335690b250Elliott Hughes        // tzdata uses seconds, but Java uses milliseconds.
171adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        mRawOffset *= 1000;
17265c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        mEarliestRawOffset = earliestRawOffset * 1000;
173adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
174adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
175adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
176e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson    public int getOffset(int era, int year, int month, int day, int dayOfWeek, int millis) {
177adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // XXX This assumes Gregorian always; Calendar switches from
178adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // Julian to Gregorian in 1582.  What calendar system are the
179adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // arguments supposed to come from?
180adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        long calc = (year / 400) * MILLISECONDS_PER_400_YEARS;
182adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        year %= 400;
183adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
184adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        calc += year * (365 * MILLISECONDS_PER_DAY);
185adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        calc += ((year + 3) / 4) * MILLISECONDS_PER_DAY;
186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
187102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes        if (year > 0) {
188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            calc -= ((year - 1) / 100) * MILLISECONDS_PER_DAY;
189102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes        }
190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
191adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        boolean isLeap = (year == 0 || (year % 4 == 0 && year % 100 != 0));
192adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int[] mlen = isLeap ? LEAP : NORMAL;
193adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        calc += mlen[month] * MILLISECONDS_PER_DAY;
195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        calc += (day - 1) * MILLISECONDS_PER_DAY;
196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        calc += millis;
197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
198adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        calc -= mRawOffset;
199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        calc -= UNIX_OFFSET;
200adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return getOffset(calc);
202adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
203adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
204adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
205adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getOffset(long when) {
206adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int unix = (int) (when / 1000);
20765c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        int transition = Arrays.binarySearch(mTransitions, unix);
20865c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        if (transition < 0) {
20965c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes            transition = ~transition - 1;
21065c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes            if (transition < 0) {
21165c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes                // Assume that all times before our first transition correspond to the
21265c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes                // oldest-known non-daylight offset. The obvious alternative would be to
21365c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes                // use the current raw offset, but that seems like a greater leap of faith.
21465c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes                return mEarliestRawOffset;
21565c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes            }
216adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
21765c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        return mRawOffset + mOffsets[mTypes[transition] & 0xff] * 1000;
21865c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes    }
219adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
22065c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes    @Override public boolean inDaylightTime(Date time) {
22165c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        long when = time.getTime();
22265c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        int unix = (int) (when / 1000);
22365c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        int transition = Arrays.binarySearch(mTransitions, unix);
22465c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        if (transition < 0) {
22565c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes            transition = ~transition - 1;
22665c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes            if (transition < 0) {
22765c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes                // Assume that all times before our first transition are non-daylight.
22865c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes                // Transition data tends to start with a transition to daylight, so just
22965c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes                // copying the first transition would assume the opposite.
23065c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes                // http://code.google.com/p/android/issues/detail?id=14395
23165c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes                return false;
23265c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes            }
23365c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        }
23465c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes        return mIsDsts[mTypes[transition] & 0xff] == 1;
235adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
236adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
23765c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes    @Override public int getRawOffset() {
238adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return mRawOffset;
239adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
240adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
24165c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes    @Override public void setRawOffset(int off) {
242adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        mRawOffset = off;
243adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
244adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
24578c3de051d68b703af480778c100ca335690b250Elliott Hughes    @Override public int getDSTSavings() {
24678c3de051d68b703af480778c100ca335690b250Elliott Hughes        return mUseDst ? mDstSavings: 0;
24778c3de051d68b703af480778c100ca335690b250Elliott Hughes    }
24878c3de051d68b703af480778c100ca335690b250Elliott Hughes
24965c53ceda4279ee7c19cf42d7c265cd601340ca0Elliott Hughes    @Override public boolean useDaylightTime() {
250adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return mUseDst;
251adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
252adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
253e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson    @Override public boolean hasSameRules(TimeZone timeZone) {
254e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson        if (!(timeZone instanceof ZoneInfo)) {
255e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson            return false;
256adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
257e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson        ZoneInfo other = (ZoneInfo) timeZone;
258e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson        if (mUseDst != other.mUseDst) {
259e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson            return false;
260adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
261e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson        if (!mUseDst) {
262e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson            return mRawOffset == other.mRawOffset;
263e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson        }
264e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson        return mRawOffset == other.mRawOffset
265adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                // Arrays.equals returns true if both arrays are null
266102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes                && Arrays.equals(mOffsets, other.mOffsets)
267adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                && Arrays.equals(mIsDsts, other.mIsDsts)
268adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                && Arrays.equals(mTypes, other.mTypes)
269adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                && Arrays.equals(mTransitions, other.mTransitions);
270adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
271f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
272e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson    @Override public boolean equals(Object obj) {
273e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson        if (!(obj instanceof ZoneInfo)) {
274e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson            return false;
275e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson        }
276e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson        ZoneInfo other = (ZoneInfo) obj;
277d6b9f2e0e9640ff9c896b394716cf5e7eee6e257Elliott Hughes        return getID().equals(other.getID()) && hasSameRules(other);
278e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson    }
279e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilson
280adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
281adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int hashCode() {
282adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        final int prime = 31;
283adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int result = 1;
284d6b9f2e0e9640ff9c896b394716cf5e7eee6e257Elliott Hughes        result = prime * result + getID().hashCode();
285102b3a86bde04a4cffcbecd5b6d175d7790b132aElliott Hughes        result = prime * result + Arrays.hashCode(mOffsets);
286adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        result = prime * result + Arrays.hashCode(mIsDsts);
287adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        result = prime * result + mRawOffset;
288adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        result = prime * result + Arrays.hashCode(mTransitions);
289adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        result = prime * result + Arrays.hashCode(mTypes);
290adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        result = prime * result + (mUseDst ? 1231 : 1237);
291adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return result;
292adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
293d3012a3bff31557a81c74c1ef1b50ef556b38967Elliott Hughes
294d3012a3bff31557a81c74c1ef1b50ef556b38967Elliott Hughes    @Override
295d3012a3bff31557a81c74c1ef1b50ef556b38967Elliott Hughes    public String toString() {
296de3df0418aff29b06ea022b200fbcc687de63a7cElliott Hughes        return getClass().getName() + "[id=\"" + getID() + "\"" +
297de3df0418aff29b06ea022b200fbcc687de63a7cElliott Hughes            ",mRawOffset=" + mRawOffset +
298de3df0418aff29b06ea022b200fbcc687de63a7cElliott Hughes            ",mEarliestRawOffset=" + mEarliestRawOffset +
299de3df0418aff29b06ea022b200fbcc687de63a7cElliott Hughes            ",mUseDst=" + mUseDst +
300de3df0418aff29b06ea022b200fbcc687de63a7cElliott Hughes            ",mDstSavings=" + mDstSavings +
301de3df0418aff29b06ea022b200fbcc687de63a7cElliott Hughes            ",transitions=" + mTransitions.length +
302de3df0418aff29b06ea022b200fbcc687de63a7cElliott Hughes            "]";
303d3012a3bff31557a81c74c1ef1b50ef556b38967Elliott Hughes    }
304adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
305