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 * Elements of the WallTime class are a port of Bionic's localtime.c to Java. That code had the 18 * following header: 19 * 20 * This file is in the public domain, so clarified as of 21 * 1996-06-05 by Arthur David Olson. 22 */ 23 package libcore.util; 24 25 import android.compat.annotation.UnsupportedAppUsage; 26 27 import com.android.i18n.timezone.ZoneInfoData; 28 import java.io.IOException; 29 import java.io.ObjectInputStream; 30 import java.io.ObjectInputStream.GetField; 31 import java.io.ObjectOutputStream; 32 import java.io.ObjectOutputStream.PutField; 33 import java.io.ObjectStreamField; 34 import java.lang.reflect.Field; 35 import java.util.Date; 36 import java.util.TimeZone; 37 38 /** 39 * Our concrete TimeZone implementation, backed by a {@link ZoneInfoData}. 40 * 41 * This class exists in this package and has certain fields / a defined serialization footprint for 42 * app compatibility reasons. The knowledge of the underlying file format has been split out into 43 * {@link ZoneInfoData} which is intended to be updated independently of the classes in 44 * libcore.util. 45 * 46 * @hide - used to implement TimeZone 47 */ 48 public final class ZoneInfo extends TimeZone { 49 50 // Proclaim serialization compatibility with pre-OpenJDK AOSP 51 static final long serialVersionUID = -4598738130123921552L; 52 53 /** 54 * Keep the serialization compatibility even though the fields have been moved to 55 * {@link ZoneInfoData}. 56 */ 57 private static final ObjectStreamField[] serialPersistentFields = 58 ZoneInfoData.ZONEINFO_SERIALIZED_FIELDS; 59 60 /** 61 * Don't use it because the value is supposed used by mDelegate internally and this field 62 * is kept only for app compatibility indicated by @UnsupportedAppUsage. 63 */ 64 @UnsupportedAppUsage 65 private final long[] mTransitions; 66 67 /** 68 * Despite being transient, mDelegate is still serialized as part of this object. Please 69 * see {@link #readObject(ObjectInputStream)} and {@link #writeObject(ObjectOutputStream)} 70 */ 71 private transient ZoneInfoData mDelegate; 72 ZoneInfo(ZoneInfoData delegate)73 public ZoneInfo(ZoneInfoData delegate) { 74 mDelegate = delegate; 75 mTransitions = delegate.getTransitionsForAppCompat(); 76 setID(delegate.getID()); 77 } 78 readObject(ObjectInputStream in)79 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 80 GetField getField = in.readFields(); 81 // TimeZone#getID() should return the proper ID because the fields in the superclass should 82 // have been deserialized. 83 mDelegate = ZoneInfoData.createFromSerializationFields(getID(), getField); 84 85 // Set the final field mTransitions by reflection. 86 try { 87 Field mTransitionsField = ZoneInfo.class.getDeclaredField("mTransitions"); 88 mTransitionsField.setAccessible(true); 89 mTransitionsField.set(this, mDelegate.getTransitionsForAppCompat()); 90 } catch (ReflectiveOperationException e) { 91 // mTransitions should always exists because it's a member field in this class. 92 } 93 } 94 writeObject(ObjectOutputStream out)95 private void writeObject(ObjectOutputStream out) throws IOException { 96 PutField putField = out.putFields(); 97 mDelegate.writeToSerializationFields(putField); 98 out.writeFields(); 99 } 100 101 @Override getOffset(int era, int year, int month, int day, int dayOfWeek, int millis)102 public int getOffset(int era, int year, int month, int day, int dayOfWeek, int millis) { 103 return mDelegate.getOffset(era, year, month, day, dayOfWeek, millis); 104 } 105 106 @Override getOffset(long when)107 public int getOffset(long when) { 108 return mDelegate.getOffset(when); 109 } 110 111 @Override inDaylightTime(Date time)112 public boolean inDaylightTime(Date time) { 113 return mDelegate.inDaylightTime(time); 114 } 115 116 @Override getRawOffset()117 public int getRawOffset() { 118 return mDelegate.getRawOffset(); 119 } 120 121 @Override setRawOffset(int off)122 public void setRawOffset(int off) { 123 mDelegate.setRawOffset(off); 124 } 125 126 @Override getDSTSavings()127 public int getDSTSavings() { 128 return mDelegate.getDSTSavings(); 129 } 130 131 @Override useDaylightTime()132 public boolean useDaylightTime() { 133 return mDelegate.useDaylightTime(); 134 } 135 136 @Override hasSameRules(TimeZone timeZone)137 public boolean hasSameRules(TimeZone timeZone) { 138 if (!(timeZone instanceof ZoneInfo)) { 139 return false; 140 } 141 ZoneInfo other = (ZoneInfo) timeZone; 142 return mDelegate.hasSameRules(other.mDelegate); 143 } 144 145 @Override equals(Object obj)146 public boolean equals(Object obj) { 147 if (!(obj instanceof ZoneInfo)) { 148 return false; 149 } 150 ZoneInfo other = (ZoneInfo) obj; 151 return getID().equals(other.getID()) && hasSameRules(other); 152 } 153 154 @Override hashCode()155 public int hashCode() { 156 /* 157 * TODO Is it an existing bug? Can 2 ZoneInfo objects have different hashCode but equals? 158 * mDelegate.hashCode compares more fields than rules and ID. 159 */ 160 return mDelegate.hashCode(); 161 } 162 163 @Override toString()164 public String toString() { 165 return getClass().getName() + mDelegate.toString(); 166 } 167 168 @Override clone()169 public Object clone() { 170 return new ZoneInfo(new ZoneInfoData(mDelegate)); 171 } 172 getOffsetsByUtcTime(long utcTimeInMillis, int[] offsets)173 public int getOffsetsByUtcTime(long utcTimeInMillis, int[] offsets) { 174 return mDelegate.getOffsetsByUtcTime(utcTimeInMillis, offsets); 175 } 176 } 177