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