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