Home | History | Annotate | Download | only in zoneinfo
      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 
     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 
     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 
     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 
     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
    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
    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
    196     public int getRawOffset() {
    197         return mRawOffset;
    198     }
    199 
    200     @Override
    201     public void setRawOffset(int off) {
    202         mRawOffset = off;
    203     }
    204 
    205     @Override
    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
    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
    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
    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