Files
android_development/tools/zoneinfo/ZoneInfo.java
Eric Fischer 1f9f586c83 Correct the raw offsets in the time zone index for a few zones.
ZoneCompactor was using the system time zone database to get the offsets
instead of using the data it was compiling, so for newly added or recently
changed zones the index could be inconsistent with the data.

Affected zones: San_Luis, Casey, Davis, Mawson, Kathmandu, Novokuznetsk
2009-11-23 16:45:09 -08:00

273 lines
8.3 KiB
Java

/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.TimeZone;
/**
* Copied from ZoneInfo and ZoneInfoDB in dalvik.
* {@hide}
*/
public class ZoneInfo extends TimeZone {
private static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
private static final long MILLISECONDS_PER_400_YEARS =
MILLISECONDS_PER_DAY * (400 * 365 + 100 - 3);
private static final long UNIX_OFFSET = 62167219200000L;
private static final int[] NORMAL = new int[] {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
};
private static final int[] LEAP = new int[] {
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
};
private static String nullName(byte[] data, int where, int off) {
if (off < 0)
return null;
int end = where + off;
while (end < data.length && data[end] != '\0')
end++;
return new String(data, where + off, end - (where + off));
}
public static ZoneInfo make(String name, byte[] data) {
int ntransition = read4(data, 32);
int ngmtoff = read4(data, 36);
int base = 44;
int[] transitions = new int[ntransition];
for (int i = 0; i < ntransition; i++)
transitions[i] = read4(data, base + 4 * i);
base += 4 * ntransition;
byte[] type = new byte[ntransition];
for (int i = 0; i < ntransition; i++)
type[i] = data[base + i];
base += ntransition;
int[] gmtoff = new int[ngmtoff];
byte[] isdst = new byte[ngmtoff];
byte[] abbrev = new byte[ngmtoff];
for (int i = 0; i < ngmtoff; i++) {
gmtoff[i] = read4(data, base + 6 * i);
isdst[i] = data[base + 6 * i + 4];
abbrev[i] = data[base + 6 * i + 5];
}
base += 6 * ngmtoff;
return new ZoneInfo(name, transitions, type, gmtoff, isdst, abbrev, data, base);
}
private static int read4(byte[] data, int off) {
return ((data[off ] & 0xFF) << 24) |
((data[off + 1] & 0xFF) << 16) |
((data[off + 2] & 0xFF) << 8) |
((data[off + 3] & 0xFF) << 0);
}
/*package*/ ZoneInfo(String name, int[] transitions, byte[] type,
int[] gmtoff, byte[] isdst, byte[] abbrev,
byte[] data, int abbrevoff) {
mTransitions = transitions;
mTypes = type;
mGmtOffs = gmtoff;
mIsDsts = isdst;
mUseDst = false;
setID(name);
// Find the latest GMT and non-GMT offsets for their abbreviations
int lastdst;
for (lastdst = mTransitions.length - 1; lastdst >= 0; lastdst--) {
if (mIsDsts[mTypes[lastdst] & 0xFF] != 0)
break;
}
int laststd;
for (laststd = mTransitions.length - 1; laststd >= 0; laststd--) {
if (mIsDsts[mTypes[laststd] & 0xFF] == 0)
break;
}
if (lastdst >= 0) {
mDaylightName = nullName(data, abbrevoff,
abbrev[mTypes[lastdst] & 0xFF]);
}
if (laststd >= 0) {
mStandardName = nullName(data, abbrevoff,
abbrev[mTypes[laststd] & 0xFF]);
}
// Use the latest non-DST offset if any as the raw offset
if (laststd < 0) {
laststd = 0;
}
if (laststd >= mTypes.length) {
mRawOffset = mGmtOffs[0];
} else {
mRawOffset = mGmtOffs[mTypes[laststd] & 0xFF];
}
// Subtract the raw offset from all offsets so it can be changed
// and affect them too.
// Find whether there exist any observances of DST.
for (int i = 0; i < mGmtOffs.length; i++) {
mGmtOffs[i] -= mRawOffset;
if (mIsDsts[i] != 0) {
mUseDst = true;
}
}
mRawOffset *= 1000;
}
@Override
public int getOffset(@SuppressWarnings("unused") int era,
int year, int month, int day,
@SuppressWarnings("unused") int dayOfWeek,
int millis) {
// XXX This assumes Gregorian always; Calendar switches from
// Julian to Gregorian in 1582. What calendar system are the
// arguments supposed to come from?
long calc = (year / 400) * MILLISECONDS_PER_400_YEARS;
year %= 400;
calc += year * (365 * MILLISECONDS_PER_DAY);
calc += ((year + 3) / 4) * MILLISECONDS_PER_DAY;
if (year > 0)
calc -= ((year - 1) / 100) * MILLISECONDS_PER_DAY;
boolean isLeap = (year == 0 || (year % 4 == 0 && year % 100 != 0));
int[] mlen = isLeap ? LEAP : NORMAL;
calc += mlen[month] * MILLISECONDS_PER_DAY;
calc += (day - 1) * MILLISECONDS_PER_DAY;
calc += millis;
calc -= mRawOffset;
calc -= UNIX_OFFSET;
return getOffset(calc);
}
@Override
public int getOffset(long when) {
int unix = (int) (when / 1000);
int trans = Arrays.binarySearch(mTransitions, unix);
if (trans == ~0) {
return mGmtOffs[0] * 1000 + mRawOffset;
}
if (trans < 0) {
trans = ~trans - 1;
}
return mGmtOffs[mTypes[trans] & 0xFF] * 1000 + mRawOffset;
}
@Override
public int getRawOffset() {
return mRawOffset;
}
@Override
public void setRawOffset(int off) {
mRawOffset = off;
}
@Override
public boolean inDaylightTime(Date when) {
int unix = (int) (when.getTime() / 1000);
int trans = Arrays.binarySearch(mTransitions, unix);
if (trans == ~0) {
return mIsDsts[0] != 0;
}
if (trans < 0) {
trans = ~trans - 1;
}
return mIsDsts[mTypes[trans] & 0xFF] != 0;
}
@Override
public boolean useDaylightTime() {
return mUseDst;
}
private int mRawOffset;
private int[] mTransitions;
private int[] mGmtOffs;
private byte[] mTypes;
private byte[] mIsDsts;
private boolean mUseDst;
private String mDaylightName;
private String mStandardName;
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ZoneInfo)) {
return false;
}
ZoneInfo other = (ZoneInfo) obj;
return mUseDst == other.mUseDst
&& (mDaylightName == null ? other.mDaylightName == null :
mDaylightName.equals(other.mDaylightName))
&& (mStandardName == null ? other.mStandardName == null :
mStandardName.equals(other.mStandardName))
&& mRawOffset == other.mRawOffset
// Arrays.equals returns true if both arrays are null
&& Arrays.equals(mGmtOffs, other.mGmtOffs)
&& Arrays.equals(mIsDsts, other.mIsDsts)
&& Arrays.equals(mTypes, other.mTypes)
&& Arrays.equals(mTransitions, other.mTransitions);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((mDaylightName == null) ? 0 :
mDaylightName.hashCode());
result = prime * result + Arrays.hashCode(mGmtOffs);
result = prime * result + Arrays.hashCode(mIsDsts);
result = prime * result + mRawOffset;
result = prime * result + ((mStandardName == null) ? 0 :
mStandardName.hashCode());
result = prime * result + Arrays.hashCode(mTransitions);
result = prime * result + Arrays.hashCode(mTypes);
result = prime * result + (mUseDst ? 1231 : 1237);
return result;
}
}