Home | History | Annotate | Download | only in util
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 // BEGIN android-note
     19 // This implementation is based on an old version of Apache Harmony. The current
     20 // Harmony uses ICU4J, which makes it much simpler. We should consider updating
     21 // this implementation to leverage ICU4JNI.
     22 // END android-note
     23 
     24 package java.util;
     25 
     26 import java.io.IOException;
     27 import java.io.ObjectInputStream;
     28 import java.io.ObjectOutputStream;
     29 import java.io.ObjectStreamField;
     30 // BEGIN android-removed
     31 // import java.security.AccessController;
     32 // import java.security.PrivilegedAction;
     33 // END android-removed
     34 
     35 import org.apache.harmony.luni.util.Msg;
     36 
     37 /**
     38  * {@code SimpleTimeZone} is a concrete subclass of {@code TimeZone}
     39  * that represents a time zone for use with a Gregorian calendar. This class
     40  * does not handle historical changes.
     41  * <p>
     42  * Use a negative value for {@code dayOfWeekInMonth} to indicate that
     43  * {@code SimpleTimeZone} should count from the end of the month
     44  * backwards. For example, Daylight Savings Time ends at the last
     45  * (dayOfWeekInMonth = -1) Sunday in October, at 2 AM in standard time.
     46  *
     47  * @see Calendar
     48  * @see GregorianCalendar
     49  * @see TimeZone
     50  */
     51 public class SimpleTimeZone extends TimeZone {
     52 
     53     private static final long serialVersionUID = -403250971215465050L;
     54 
     55     // BEGIN android-removed
     56     // private static com.ibm.icu.util.TimeZone getICUTimeZone(final String name){
     57     //     return AccessController.doPrivileged(new PrivilegedAction<com.ibm.icu.util.TimeZone>(){
     58     //         public com.ibm.icu.util.TimeZone run() {
     59     //             return com.ibm.icu.util.TimeZone.getTimeZone(name);
     60     //         }
     61     //     });
     62     // }
     63     // END android-removed
     64 
     65     private int rawOffset;
     66 
     67     private int startYear, startMonth, startDay, startDayOfWeek, startTime;
     68 
     69     private int endMonth, endDay, endDayOfWeek, endTime;
     70 
     71     private int startMode, endMode;
     72 
     73     private static final int DOM_MODE = 1, DOW_IN_MONTH_MODE = 2,
     74             DOW_GE_DOM_MODE = 3, DOW_LE_DOM_MODE = 4;
     75 
     76     /**
     77      * The constant for representing a start or end time in GMT time mode.
     78      */
     79     public static final int UTC_TIME = 2;
     80 
     81     /**
     82      * The constant for representing a start or end time in standard local time mode,
     83      * based on timezone's raw offset from GMT; does not include Daylight
     84      * savings.
     85      */
     86     public static final int STANDARD_TIME = 1;
     87 
     88     /**
     89      * The constant for representing a start or end time in local wall clock time
     90      * mode, based on timezone's adjusted offset from GMT; includes
     91      * Daylight savings.
     92      */
     93     public static final int WALL_TIME = 0;
     94 
     95     private boolean useDaylight;
     96 
     97     private GregorianCalendar daylightSavings;
     98 
     99     private int dstSavings = 3600000;
    100 
    101     // BEGIN android-removed
    102     // private final transient com.ibm.icu.util.TimeZone icuTZ;
    103     //
    104     // private final transient boolean isSimple;
    105     // END android-removed
    106 
    107     /**
    108      * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT
    109      * and time zone ID. Timezone IDs can be obtained from
    110      * {@code TimeZone.getAvailableIDs}. Normally you should use {@code TimeZone.getDefault} to
    111      * construct a {@code TimeZone}.
    112      *
    113      * @param offset
    114      *            the given base time zone offset to GMT.
    115      * @param name
    116      *            the time zone ID which is obtained from
    117      *            {@code TimeZone.getAvailableIDs}.
    118      */
    119     public SimpleTimeZone(int offset, final String name) {
    120         setID(name);
    121         rawOffset = offset;
    122         // BEGIN android-removed
    123         // icuTZ = getICUTimeZone(name);
    124         // if (icuTZ instanceof com.ibm.icu.util.SimpleTimeZone) {
    125         //     isSimple = true;
    126         //     icuTZ.setRawOffset(offset);
    127         // } else {
    128         //     isSimple = false;
    129         // }
    130         // useDaylight = icuTZ.useDaylightTime();
    131         // END android-removed
    132     }
    133 
    134     /**
    135      * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT,
    136      * time zone ID, and times to start and end the daylight savings time. Timezone IDs can
    137      * be obtained from {@code TimeZone.getAvailableIDs}. Normally you should use
    138      * {@code TimeZone.getDefault} to create a {@code TimeZone}. For a time zone that does not
    139      * use daylight saving time, do not use this constructor; instead you should
    140      * use {@code SimpleTimeZone(rawOffset, ID)}.
    141      * <p>
    142      * By default, this constructor specifies day-of-week-in-month rules. That
    143      * is, if the {@code startDay} is 1, and the {@code startDayOfWeek} is {@code SUNDAY}, then this
    144      * indicates the first Sunday in the {@code startMonth}. A {@code startDay} of -1 likewise
    145      * indicates the last Sunday. However, by using negative or zero values for
    146      * certain parameters, other types of rules can be specified.
    147      * <p>
    148      * Day of month: To specify an exact day of the month, such as March 1, set
    149      * {@code startDayOfWeek} to zero.
    150      * <p>
    151      * Day of week after day of month: To specify the first day of the week
    152      * occurring on or after an exact day of the month, make the day of the week
    153      * negative. For example, if {@code startDay} is 5 and {@code startDayOfWeek} is {@code -MONDAY},
    154      * this indicates the first Monday on or after the 5th day of the
    155      * {@code startMonth}.
    156      * <p>
    157      * Day of week before day of month: To specify the last day of the week
    158      * occurring on or before an exact day of the month, make the day of the
    159      * week and the day of the month negative. For example, if {@code startDay} is {@code -21}
    160      * and {@code startDayOfWeek} is {@code -WEDNESDAY}, this indicates the last Wednesday on or
    161      * before the 21st of the {@code startMonth}.
    162      * <p>
    163      * The above examples refer to the {@code startMonth}, {@code startDay}, and {@code startDayOfWeek};
    164      * the same applies for the {@code endMonth}, {@code endDay}, and {@code endDayOfWeek}.
    165      * <p>
    166      * The daylight savings time difference is set to the default value: one hour.
    167      *
    168      * @param offset
    169      *            the given base time zone offset to GMT.
    170      * @param name
    171      *            the time zone ID which is obtained from
    172      *            {@code TimeZone.getAvailableIDs}.
    173      * @param startMonth
    174      *            the daylight savings starting month. The month indexing is 0-based. eg, 0
    175      *            for January.
    176      * @param startDay
    177      *            the daylight savings starting day-of-week-in-month. Please see
    178      *            the member description for an example.
    179      * @param startDayOfWeek
    180      *            the daylight savings starting day-of-week. Please see the
    181      *            member description for an example.
    182      * @param startTime
    183      *            the daylight savings starting time in local wall time, which
    184      *            is standard time in this case. Please see the member
    185      *            description for an example.
    186      * @param endMonth
    187      *            the daylight savings ending month. The month indexing is 0-based. eg, 0 for
    188      *            January.
    189      * @param endDay
    190      *            the daylight savings ending day-of-week-in-month. Please see
    191      *            the member description for an example.
    192      * @param endDayOfWeek
    193      *            the daylight savings ending day-of-week. Please see the member
    194      *            description for an example.
    195      * @param endTime
    196      *            the daylight savings ending time in local wall time, which is
    197      *            daylight time in this case. Please see the member description
    198      *            for an example.
    199      * @throws IllegalArgumentException
    200      *             if the month, day, dayOfWeek, or time parameters are out of
    201      *             range for the start or end rule.
    202      */
    203     public SimpleTimeZone(int offset, String name, int startMonth,
    204             int startDay, int startDayOfWeek, int startTime, int endMonth,
    205             int endDay, int endDayOfWeek, int endTime) {
    206         this(offset, name, startMonth, startDay, startDayOfWeek, startTime,
    207                 endMonth, endDay, endDayOfWeek, endTime, 3600000);
    208     }
    209 
    210     /**
    211      * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT,
    212      * time zone ID, times to start and end the daylight savings time, and
    213      * the daylight savings time difference in milliseconds.
    214      *
    215      * @param offset
    216      *            the given base time zone offset to GMT.
    217      * @param name
    218      *            the time zone ID which is obtained from
    219      *            {@code TimeZone.getAvailableIDs}.
    220      * @param startMonth
    221      *            the daylight savings starting month. Month is 0-based. eg, 0
    222      *            for January.
    223      * @param startDay
    224      *            the daylight savings starting day-of-week-in-month. Please see
    225      *            the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
    226      * @param startDayOfWeek
    227      *            the daylight savings starting day-of-week. Please see the
    228      *            description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
    229      * @param startTime
    230      *            The daylight savings starting time in local wall time, which
    231      *            is standard time in this case. Please see the description of
    232      *            {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
    233      * @param endMonth
    234      *            the daylight savings ending month. Month is 0-based. eg, 0 for
    235      *            January.
    236      * @param endDay
    237      *            the daylight savings ending day-of-week-in-month. Please see
    238      *            the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
    239      * @param endDayOfWeek
    240      *            the daylight savings ending day-of-week. Please see the description of
    241      *            {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
    242      * @param endTime
    243      *            the daylight savings ending time in local wall time, which is
    244      *            daylight time in this case. Please see the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)}
    245      *            for an example.
    246      * @param daylightSavings
    247      *            the daylight savings time difference in milliseconds.
    248      * @throws IllegalArgumentException
    249      *                if the month, day, dayOfWeek, or time parameters are out of
    250      *                range for the start or end rule.
    251      */
    252     public SimpleTimeZone(int offset, String name, int startMonth,
    253             int startDay, int startDayOfWeek, int startTime, int endMonth,
    254             int endDay, int endDayOfWeek, int endTime, int daylightSavings) {
    255         // BEGIN android-changed
    256         // icuTZ = getICUTimeZone(name);
    257         // if (icuTZ instanceof com.ibm.icu.util.SimpleTimeZone) {
    258         //     isSimple = true;
    259         //     com.ibm.icu.util.SimpleTimeZone tz = (com.ibm.icu.util.SimpleTimeZone)icuTZ;
    260         //     tz.setRawOffset(offset);
    261         //     tz.setStartRule(startMonth, startDay, startDayOfWeek, startTime);
    262         //     tz.setEndRule(endMonth, endDay, endDayOfWeek, endTime);
    263         //     tz.setDSTSavings(daylightSavings);
    264         // } else {
    265         //     isSimple = false;
    266         // }
    267         // setID(name);
    268         // rawOffset = offset;
    269         this(offset, name);
    270         // END android-changed
    271         if (daylightSavings <= 0) {
    272             throw new IllegalArgumentException(Msg.getString(
    273                     "K00e9", daylightSavings)); //$NON-NLS-1$
    274         }
    275         dstSavings = daylightSavings;
    276 
    277         setStartRule(startMonth, startDay, startDayOfWeek, startTime);
    278         setEndRule(endMonth, endDay, endDayOfWeek, endTime);
    279 
    280         // BEGIN android-removed
    281         // useDaylight = daylightSavings > 0 || icuTZ.useDaylightTime();
    282         // END android-removed
    283     }
    284 
    285     /**
    286      * Construct a {@code SimpleTimeZone} with the given base time zone offset from GMT,
    287      * time zone ID, times to start and end the daylight savings time including a
    288      * mode specifier, the daylight savings time difference in milliseconds.
    289      * The mode specifies either {@link #WALL_TIME}, {@link #STANDARD_TIME}, or
    290      * {@link #UTC_TIME}.
    291      *
    292      * @param offset
    293      *            the given base time zone offset to GMT.
    294      * @param name
    295      *            the time zone ID which is obtained from
    296      *            {@code TimeZone.getAvailableIDs}.
    297      * @param startMonth
    298      *            the daylight savings starting month. The month indexing is 0-based. eg, 0
    299      *            for January.
    300      * @param startDay
    301      *            the daylight savings starting day-of-week-in-month. Please see
    302      *            the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
    303      * @param startDayOfWeek
    304      *            the daylight savings starting day-of-week. Please see the
    305      *            description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
    306      * @param startTime
    307      *            the time of day in milliseconds on which daylight savings
    308      *            time starts, based on the {@code startTimeMode}.
    309      * @param startTimeMode
    310      *            the mode (UTC, standard, or wall time) of the start time
    311      *            value.
    312      * @param endDay
    313      *            the day of the week on which daylight savings time ends.
    314      * @param endMonth
    315      *            the daylight savings ending month. The month indexing is 0-based. eg, 0 for
    316      *            January.
    317      * @param endDayOfWeek
    318      *            the daylight savings ending day-of-week. Please see the description of
    319      *            {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
    320      * @param endTime
    321      *            the time of day in milliseconds on which daylight savings
    322      *            time ends, based on the {@code endTimeMode}.
    323      * @param endTimeMode
    324      *            the mode (UTC, standard, or wall time) of the end time value.
    325      * @param daylightSavings
    326      *            the daylight savings time difference in milliseconds.
    327      * @throws IllegalArgumentException
    328      *             if the month, day, dayOfWeek, or time parameters are out of
    329      *             range for the start or end rule.
    330      */
    331     public SimpleTimeZone(int offset, String name, int startMonth,
    332             int startDay, int startDayOfWeek, int startTime, int startTimeMode,
    333             int endMonth, int endDay, int endDayOfWeek, int endTime,
    334             int endTimeMode, int daylightSavings) {
    335 
    336         this(offset, name, startMonth, startDay, startDayOfWeek, startTime,
    337                 endMonth, endDay, endDayOfWeek, endTime, daylightSavings);
    338         startMode = startTimeMode;
    339         endMode = endTimeMode;
    340     }
    341 
    342     /**
    343      * Returns a new {@code SimpleTimeZone} with the same ID, {@code rawOffset} and daylight
    344      * savings time rules as this SimpleTimeZone.
    345      *
    346      * @return a shallow copy of this {@code SimpleTimeZone}.
    347      * @see java.lang.Cloneable
    348      */
    349     @Override
    350     public Object clone() {
    351         SimpleTimeZone zone = (SimpleTimeZone) super.clone();
    352         if (daylightSavings != null) {
    353             zone.daylightSavings = (GregorianCalendar) daylightSavings.clone();
    354         }
    355         return zone;
    356     }
    357 
    358     /**
    359      * Compares the specified object to this {@code SimpleTimeZone} and returns whether they
    360      * are equal. The object must be an instance of {@code SimpleTimeZone} and have the
    361      * same internal data.
    362      *
    363      * @param object
    364      *            the object to compare with this object.
    365      * @return {@code true} if the specified object is equal to this
    366      *         {@code SimpleTimeZone}, {@code false} otherwise.
    367      * @see #hashCode
    368      */
    369     @Override
    370     public boolean equals(Object object) {
    371         if (!(object instanceof SimpleTimeZone)) {
    372             return false;
    373         }
    374         SimpleTimeZone tz = (SimpleTimeZone) object;
    375         return getID().equals(tz.getID())
    376                 && rawOffset == tz.rawOffset
    377                 && useDaylight == tz.useDaylight
    378                 && (!useDaylight || (startYear == tz.startYear
    379                         && startMonth == tz.startMonth
    380                         && startDay == tz.startDay && startMode == tz.startMode
    381                         && startDayOfWeek == tz.startDayOfWeek
    382                         && startTime == tz.startTime && endMonth == tz.endMonth
    383                         && endDay == tz.endDay
    384                         && endDayOfWeek == tz.endDayOfWeek
    385                         && endTime == tz.endTime && endMode == tz.endMode && dstSavings == tz.dstSavings));
    386     }
    387 
    388     @Override
    389     public int getDSTSavings() {
    390         if (!useDaylight) {
    391             return 0;
    392         }
    393         return dstSavings;
    394     }
    395 
    396     @Override
    397     public int getOffset(int era, int year, int month, int day, int dayOfWeek,
    398             int time) {
    399         if (era != GregorianCalendar.BC && era != GregorianCalendar.AD) {
    400             throw new IllegalArgumentException(Msg.getString("K00ea", era)); //$NON-NLS-1$
    401         }
    402         checkRange(month, dayOfWeek, time);
    403         if (month != Calendar.FEBRUARY || day != 29 || !isLeapYear(year)) {
    404             checkDay(month, day);
    405         }
    406 
    407         // BEGIN android-changed
    408         // return icuTZ.getOffset(era, year, month, day, dayOfWeek, time);
    409         if (!useDaylightTime() || era != GregorianCalendar.AD
    410                 || year < startYear) {
    411             return rawOffset;
    412         }
    413         if (endMonth < startMonth) {
    414             if (month > endMonth && month < startMonth) {
    415                 return rawOffset;
    416             }
    417         } else {
    418             if (month < startMonth || month > endMonth) {
    419                 return rawOffset;
    420             }
    421         }
    422 
    423         int ruleDay = 0, daysInMonth, firstDayOfMonth = mod7(dayOfWeek - day);
    424         if (month == startMonth) {
    425             switch (startMode) {
    426                 case DOM_MODE:
    427                     ruleDay = startDay;
    428                     break;
    429                 case DOW_IN_MONTH_MODE:
    430                     if (startDay >= 0) {
    431                         ruleDay = mod7(startDayOfWeek - firstDayOfMonth) + 1
    432                                 + (startDay - 1) * 7;
    433                     } else {
    434                         daysInMonth = GregorianCalendar.DaysInMonth[startMonth];
    435                         if (startMonth == Calendar.FEBRUARY && isLeapYear(
    436                                 year)) {
    437                             daysInMonth += 1;
    438                         }
    439                         ruleDay = daysInMonth
    440                                 + 1
    441                                 + mod7(startDayOfWeek
    442                                 - (firstDayOfMonth + daysInMonth))
    443                                 + startDay * 7;
    444                     }
    445                     break;
    446                 case DOW_GE_DOM_MODE:
    447                     ruleDay = startDay
    448                             + mod7(startDayOfWeek
    449                             - (firstDayOfMonth + startDay - 1));
    450                     break;
    451                 case DOW_LE_DOM_MODE:
    452                     ruleDay = startDay
    453                             + mod7(startDayOfWeek
    454                             - (firstDayOfMonth + startDay - 1));
    455                     if (ruleDay != startDay) {
    456                         ruleDay -= 7;
    457                     }
    458                     break;
    459             }
    460             if (ruleDay > day || ruleDay == day && time < startTime) {
    461                 return rawOffset;
    462             }
    463         }
    464 
    465         int ruleTime = endTime - dstSavings;
    466         int nextMonth = (month + 1) % 12;
    467         if (month == endMonth || (ruleTime < 0 && nextMonth == endMonth)) {
    468             switch (endMode) {
    469                 case DOM_MODE:
    470                     ruleDay = endDay;
    471                     break;
    472                 case DOW_IN_MONTH_MODE:
    473                     if (endDay >= 0) {
    474                         ruleDay = mod7(endDayOfWeek - firstDayOfMonth) + 1
    475                                 + (endDay - 1) * 7;
    476                     } else {
    477                         daysInMonth = GregorianCalendar.DaysInMonth[endMonth];
    478                         if (endMonth == Calendar.FEBRUARY && isLeapYear(year)) {
    479                             daysInMonth++;
    480                         }
    481                         ruleDay = daysInMonth
    482                                 + 1
    483                                 + mod7(endDayOfWeek
    484                                 - (firstDayOfMonth + daysInMonth)) + endDay
    485                                 * 7;
    486                     }
    487                     break;
    488                 case DOW_GE_DOM_MODE:
    489                     ruleDay = endDay
    490                             + mod7(
    491                             endDayOfWeek - (firstDayOfMonth + endDay - 1));
    492                     break;
    493                 case DOW_LE_DOM_MODE:
    494                     ruleDay = endDay
    495                             + mod7(
    496                             endDayOfWeek - (firstDayOfMonth + endDay - 1));
    497                     if (ruleDay != endDay) {
    498                         ruleDay -= 7;
    499                     }
    500                     break;
    501             }
    502 
    503             int ruleMonth = endMonth;
    504             if (ruleTime < 0) {
    505                 int changeDays = 1 - (ruleTime / 86400000);
    506                 ruleTime = (ruleTime % 86400000) + 86400000;
    507                 ruleDay -= changeDays;
    508                 if (ruleDay <= 0) {
    509                     if (--ruleMonth < Calendar.JANUARY) {
    510                         ruleMonth = Calendar.DECEMBER;
    511                     }
    512                     ruleDay += GregorianCalendar.DaysInMonth[ruleMonth];
    513                     if (ruleMonth == Calendar.FEBRUARY && isLeapYear(year)) {
    514                         ruleDay++;
    515                     }
    516                 }
    517             }
    518 
    519             if (month == ruleMonth) {
    520                 if (ruleDay < day || ruleDay == day && time >= ruleTime) {
    521                     return rawOffset;
    522                 }
    523             } else if (nextMonth != ruleMonth) {
    524                 return rawOffset;
    525             }
    526         }
    527         return rawOffset + dstSavings;
    528         // END android-changed
    529     }
    530 
    531     @Override
    532     public int getOffset(long time) {
    533         // BEGIN android-changed
    534         // return icuTZ.getOffset(time);
    535         if (!useDaylightTime()) {
    536             return rawOffset;
    537         }
    538         if (daylightSavings == null) {
    539             daylightSavings = new GregorianCalendar(this);
    540         }
    541         return daylightSavings.getOffset(time + rawOffset);
    542         // END android-changed
    543     }
    544 
    545     @Override
    546     public int getRawOffset() {
    547         return rawOffset;
    548     }
    549 
    550     /**
    551      * Returns an integer hash code for the receiver. Objects which are equal
    552      * return the same value for this method.
    553      *
    554      * @return the receiver's hash.
    555      * @see #equals
    556      */
    557     @Override
    558     public synchronized int hashCode() {
    559         int hashCode = getID().hashCode() + rawOffset;
    560         if (useDaylight) {
    561             hashCode += startYear + startMonth + startDay + startDayOfWeek
    562                     + startTime + startMode + endMonth + endDay + endDayOfWeek
    563                     + endTime + endMode + dstSavings;
    564         }
    565         return hashCode;
    566     }
    567 
    568     @Override
    569     public boolean hasSameRules(TimeZone zone) {
    570         if (!(zone instanceof SimpleTimeZone)) {
    571             return false;
    572         }
    573         SimpleTimeZone tz = (SimpleTimeZone) zone;
    574         if (useDaylight != tz.useDaylight) {
    575             return false;
    576         }
    577         if (!useDaylight) {
    578             return rawOffset == tz.rawOffset;
    579         }
    580         return rawOffset == tz.rawOffset && dstSavings == tz.dstSavings
    581                 && startYear == tz.startYear && startMonth == tz.startMonth
    582                 && startDay == tz.startDay && startMode == tz.startMode
    583                 && startDayOfWeek == tz.startDayOfWeek
    584                 && startTime == tz.startTime && endMonth == tz.endMonth
    585                 && endDay == tz.endDay && endDayOfWeek == tz.endDayOfWeek
    586                 && endTime == tz.endTime && endMode == tz.endMode;
    587     }
    588 
    589     @Override
    590     public boolean inDaylightTime(Date time) {
    591         // BEGIN android-changed
    592         // return icuTZ.inDaylightTime(time);
    593         // check for null pointer
    594         long millis = time.getTime();
    595         if (!useDaylightTime()) {
    596             return false;
    597         }
    598         if (daylightSavings == null) {
    599             daylightSavings = new GregorianCalendar(this);
    600         }
    601         return daylightSavings.getOffset(millis + rawOffset) != rawOffset;
    602         // END android-changed
    603     }
    604 
    605     private boolean isLeapYear(int year) {
    606         if (year > 1582) {
    607             return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
    608         }
    609         return year % 4 == 0;
    610     }
    611 
    612     // BEGIN android-added
    613     private int mod7(int num1) {
    614         int rem = num1 % 7;
    615         return (num1 < 0 && rem < 0) ? 7 + rem : rem;
    616     }
    617     // END android-added
    618 
    619     /**
    620      * Sets the daylight savings offset in milliseconds for this {@code SimpleTimeZone}.
    621      *
    622      * @param milliseconds
    623      *            the daylight savings offset in milliseconds.
    624      */
    625     public void setDSTSavings(int milliseconds) {
    626         if (milliseconds > 0) {
    627             dstSavings = milliseconds;
    628         } else {
    629             throw new IllegalArgumentException();
    630         }
    631     }
    632 
    633     private void checkRange(int month, int dayOfWeek, int time) {
    634         if (month < Calendar.JANUARY || month > Calendar.DECEMBER) {
    635             throw new IllegalArgumentException(Msg.getString("K00e5", month)); //$NON-NLS-1$
    636         }
    637         if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) {
    638             throw new IllegalArgumentException(Msg
    639                     .getString("K00e7", dayOfWeek)); //$NON-NLS-1$
    640         }
    641         if (time < 0 || time >= 24 * 3600000) {
    642             throw new IllegalArgumentException(Msg.getString("K00e8", time)); //$NON-NLS-1$
    643         }
    644     }
    645 
    646     private void checkDay(int month, int day) {
    647         if (day <= 0 || day > GregorianCalendar.DaysInMonth[month]) {
    648             throw new IllegalArgumentException(Msg.getString("K00e6", day)); //$NON-NLS-1$
    649         }
    650     }
    651 
    652     private void setEndMode() {
    653         if (endDayOfWeek == 0) {
    654             endMode = DOM_MODE;
    655         } else if (endDayOfWeek < 0) {
    656             endDayOfWeek = -endDayOfWeek;
    657             if (endDay < 0) {
    658                 endDay = -endDay;
    659                 endMode = DOW_LE_DOM_MODE;
    660             } else {
    661                 endMode = DOW_GE_DOM_MODE;
    662             }
    663         } else {
    664             endMode = DOW_IN_MONTH_MODE;
    665         }
    666         useDaylight = startDay != 0 && endDay != 0;
    667         if (endDay != 0) {
    668             checkRange(endMonth, endMode == DOM_MODE ? 1 : endDayOfWeek,
    669                     endTime);
    670             if (endMode != DOW_IN_MONTH_MODE) {
    671                 checkDay(endMonth, endDay);
    672             } else {
    673                 if (endDay < -5 || endDay > 5) {
    674                     throw new IllegalArgumentException(Msg.getString(
    675                             "K00f8", endDay)); //$NON-NLS-1$
    676                 }
    677             }
    678         }
    679         if (endMode != DOM_MODE) {
    680             endDayOfWeek--;
    681         }
    682     }
    683 
    684     /**
    685      * Sets the rule which specifies the end of daylight savings time.
    686      *
    687      * @param month
    688      *            the {@code Calendar} month in which daylight savings time ends.
    689      * @param dayOfMonth
    690      *            the {@code Calendar} day of the month on which daylight savings time
    691      *            ends.
    692      * @param time
    693      *            the time of day in milliseconds standard time on which
    694      *            daylight savings time ends.
    695      */
    696     public void setEndRule(int month, int dayOfMonth, int time) {
    697         endMonth = month;
    698         endDay = dayOfMonth;
    699         endDayOfWeek = 0; // Initialize this value for hasSameRules()
    700         endTime = time;
    701         setEndMode();
    702         // BEGIN android-removed
    703         // if (isSimple) {
    704         //     ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setEndRule(month,
    705         //             dayOfMonth, time);
    706         // }
    707         // END android-removed
    708     }
    709 
    710     /**
    711      * Sets the rule which specifies the end of daylight savings time.
    712      *
    713      * @param month
    714      *            the {@code Calendar} month in which daylight savings time ends.
    715      * @param day
    716      *            the occurrence of the day of the week on which daylight
    717      *            savings time ends.
    718      * @param dayOfWeek
    719      *            the {@code Calendar} day of the week on which daylight savings time
    720      *            ends.
    721      * @param time
    722      *            the time of day in milliseconds standard time on which
    723      *            daylight savings time ends.
    724      */
    725     public void setEndRule(int month, int day, int dayOfWeek, int time) {
    726         endMonth = month;
    727         endDay = day;
    728         endDayOfWeek = dayOfWeek;
    729         endTime = time;
    730         setEndMode();
    731         // BEGIN android-removed
    732         // if (isSimple) {
    733         //     ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setEndRule(month, day,
    734         //             dayOfWeek, time);
    735         // }
    736         // END android-removed
    737     }
    738 
    739     /**
    740      * Sets the rule which specifies the end of daylight savings time.
    741      *
    742      * @param month
    743      *            the {@code Calendar} month in which daylight savings time ends.
    744      * @param day
    745      *            the {@code Calendar} day of the month.
    746      * @param dayOfWeek
    747      *            the {@code Calendar} day of the week on which daylight savings time
    748      *            ends.
    749      * @param time
    750      *            the time of day in milliseconds on which daylight savings time
    751      *            ends.
    752      * @param after
    753      *            selects the day after or before the day of month.
    754      */
    755     public void setEndRule(int month, int day, int dayOfWeek, int time,
    756             boolean after) {
    757         endMonth = month;
    758         endDay = after ? day : -day;
    759         endDayOfWeek = -dayOfWeek;
    760         endTime = time;
    761         setEndMode();
    762         // BEGIN android-removed
    763         // if (isSimple) {
    764         //     ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setEndRule(month, day,
    765         //             dayOfWeek, time, after);
    766         // }
    767         // END android-removed
    768     }
    769 
    770     /**
    771      * Sets the offset for standard time from GMT for this {@code SimpleTimeZone}.
    772      *
    773      * @param offset
    774      *            the offset from GMT of standard time in milliseconds.
    775      */
    776     @Override
    777     public void setRawOffset(int offset) {
    778         rawOffset = offset;
    779         // BEGIN android-removed
    780         // icuTZ.setRawOffset(offset);
    781         // END android-removed
    782     }
    783 
    784     private void setStartMode() {
    785         if (startDayOfWeek == 0) {
    786             startMode = DOM_MODE;
    787         } else if (startDayOfWeek < 0) {
    788             startDayOfWeek = -startDayOfWeek;
    789             if (startDay < 0) {
    790                 startDay = -startDay;
    791                 startMode = DOW_LE_DOM_MODE;
    792             } else {
    793                 startMode = DOW_GE_DOM_MODE;
    794             }
    795         } else {
    796             startMode = DOW_IN_MONTH_MODE;
    797         }
    798         useDaylight = startDay != 0 && endDay != 0;
    799         if (startDay != 0) {
    800             checkRange(startMonth, startMode == DOM_MODE ? 1 : startDayOfWeek,
    801                     startTime);
    802             if (startMode != DOW_IN_MONTH_MODE) {
    803                 checkDay(startMonth, startDay);
    804             } else {
    805                 if (startDay < -5 || startDay > 5) {
    806                     throw new IllegalArgumentException(Msg.getString(
    807                             "K00f8", startDay)); //$NON-NLS-1$
    808                 }
    809             }
    810         }
    811         if (startMode != DOM_MODE) {
    812             startDayOfWeek--;
    813         }
    814     }
    815 
    816     /**
    817      * Sets the rule which specifies the start of daylight savings time.
    818      *
    819      * @param month
    820      *            the {@code Calendar} month in which daylight savings time starts.
    821      * @param dayOfMonth
    822      *            the {@code Calendar} day of the month on which daylight savings time
    823      *            starts.
    824      * @param time
    825      *            the time of day in milliseconds on which daylight savings time
    826      *            starts.
    827      */
    828     public void setStartRule(int month, int dayOfMonth, int time) {
    829         startMonth = month;
    830         startDay = dayOfMonth;
    831         startDayOfWeek = 0; // Initialize this value for hasSameRules()
    832         startTime = time;
    833         setStartMode();
    834         // BEGIN android-removed
    835         // if (isSimple) {
    836         //     ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setStartRule(month,
    837         //             dayOfMonth, time);
    838         // }
    839         // END android-removed
    840     }
    841 
    842     /**
    843      * Sets the rule which specifies the start of daylight savings time.
    844      *
    845      * @param month
    846      *            the {@code Calendar} month in which daylight savings time starts.
    847      * @param day
    848      *            the occurrence of the day of the week on which daylight
    849      *            savings time starts.
    850      * @param dayOfWeek
    851      *            the {@code Calendar} day of the week on which daylight savings time
    852      *            starts.
    853      * @param time
    854      *            the time of day in milliseconds on which daylight savings time
    855      *            starts.
    856      */
    857     public void setStartRule(int month, int day, int dayOfWeek, int time) {
    858         startMonth = month;
    859         startDay = day;
    860         startDayOfWeek = dayOfWeek;
    861         startTime = time;
    862         setStartMode();
    863         // BEGIN android-removed
    864         // if (isSimple) {
    865         //     ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setStartRule(month, day,
    866         //             dayOfWeek, time);
    867         // }
    868         // END android-removed
    869     }
    870 
    871     /**
    872      * Sets the rule which specifies the start of daylight savings time.
    873      *
    874      * @param month
    875      *            the {@code Calendar} month in which daylight savings time starts.
    876      * @param day
    877      *            the {@code Calendar} day of the month.
    878      * @param dayOfWeek
    879      *            the {@code Calendar} day of the week on which daylight savings time
    880      *            starts.
    881      * @param time
    882      *            the time of day in milliseconds on which daylight savings time
    883      *            starts.
    884      * @param after
    885      *            selects the day after or before the day of month.
    886      */
    887     public void setStartRule(int month, int day, int dayOfWeek, int time,
    888             boolean after) {
    889         startMonth = month;
    890         startDay = after ? day : -day;
    891         startDayOfWeek = -dayOfWeek;
    892         startTime = time;
    893         setStartMode();
    894         // BEGIN android-removed
    895         // if (isSimple) {
    896         //     ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setStartRule(month, day,
    897         //             dayOfWeek, time, after);
    898         // }
    899         // END android-removed
    900     }
    901 
    902     /**
    903      * Sets the starting year for daylight savings time in this {@code SimpleTimeZone}.
    904      * Years before this start year will always be in standard time.
    905      *
    906      * @param year
    907      *            the starting year.
    908      */
    909     public void setStartYear(int year) {
    910         startYear = year;
    911         useDaylight = true;
    912     }
    913 
    914     /**
    915      * Returns the string representation of this {@code SimpleTimeZone}.
    916      *
    917      * @return the string representation of this {@code SimpleTimeZone}.
    918      */
    919     @Override
    920     public String toString() {
    921         return getClass().getName()
    922                 + "[id=" //$NON-NLS-1$
    923                 + getID()
    924                 + ",offset=" //$NON-NLS-1$
    925                 + rawOffset
    926                 + ",dstSavings=" //$NON-NLS-1$
    927                 + dstSavings
    928                 + ",useDaylight=" //$NON-NLS-1$
    929                 + useDaylight
    930                 + ",startYear=" //$NON-NLS-1$
    931                 + startYear
    932                 + ",startMode=" //$NON-NLS-1$
    933                 + startMode
    934                 + ",startMonth=" //$NON-NLS-1$
    935                 + startMonth
    936                 + ",startDay=" //$NON-NLS-1$
    937                 + startDay
    938                 + ",startDayOfWeek=" //$NON-NLS-1$
    939                 + (useDaylight && (startMode != DOM_MODE) ? startDayOfWeek + 1
    940                         : 0) + ",startTime=" + startTime + ",endMode=" //$NON-NLS-1$ //$NON-NLS-2$
    941                 + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay //$NON-NLS-1$ //$NON-NLS-2$
    942                 + ",endDayOfWeek=" //$NON-NLS-1$
    943                 + (useDaylight && (endMode != DOM_MODE) ? endDayOfWeek + 1 : 0)
    944                 + ",endTime=" + endTime + "]"; //$NON-NLS-1$//$NON-NLS-2$
    945     }
    946 
    947     @Override
    948     public boolean useDaylightTime() {
    949         return useDaylight;
    950     }
    951 
    952     private static final ObjectStreamField[] serialPersistentFields = {
    953             new ObjectStreamField("dstSavings", Integer.TYPE), //$NON-NLS-1$
    954             new ObjectStreamField("endDay", Integer.TYPE), //$NON-NLS-1$
    955             new ObjectStreamField("endDayOfWeek", Integer.TYPE), //$NON-NLS-1$
    956             new ObjectStreamField("endMode", Integer.TYPE), //$NON-NLS-1$
    957             new ObjectStreamField("endMonth", Integer.TYPE), //$NON-NLS-1$
    958             new ObjectStreamField("endTime", Integer.TYPE), //$NON-NLS-1$
    959             new ObjectStreamField("monthLength", byte[].class), //$NON-NLS-1$
    960             new ObjectStreamField("rawOffset", Integer.TYPE), //$NON-NLS-1$
    961             new ObjectStreamField("serialVersionOnStream", Integer.TYPE), //$NON-NLS-1$
    962             new ObjectStreamField("startDay", Integer.TYPE), //$NON-NLS-1$
    963             new ObjectStreamField("startDayOfWeek", Integer.TYPE), //$NON-NLS-1$
    964             new ObjectStreamField("startMode", Integer.TYPE), //$NON-NLS-1$
    965             new ObjectStreamField("startMonth", Integer.TYPE), //$NON-NLS-1$
    966             new ObjectStreamField("startTime", Integer.TYPE), //$NON-NLS-1$
    967             new ObjectStreamField("startYear", Integer.TYPE), //$NON-NLS-1$
    968             new ObjectStreamField("useDaylight", Boolean.TYPE), }; //$NON-NLS-1$
    969 
    970     private void writeObject(ObjectOutputStream stream) throws IOException {
    971         int sEndDay = endDay, sEndDayOfWeek = endDayOfWeek + 1, sStartDay = startDay, sStartDayOfWeek = startDayOfWeek + 1;
    972         if (useDaylight
    973                 && (startMode != DOW_IN_MONTH_MODE || endMode != DOW_IN_MONTH_MODE)) {
    974             Calendar cal = new GregorianCalendar(this);
    975             if (endMode != DOW_IN_MONTH_MODE) {
    976                 cal.set(Calendar.MONTH, endMonth);
    977                 cal.set(Calendar.DATE, endDay);
    978                 sEndDay = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH);
    979                 if (endMode == DOM_MODE) {
    980                     sEndDayOfWeek = cal.getFirstDayOfWeek();
    981                 }
    982             }
    983             if (startMode != DOW_IN_MONTH_MODE) {
    984                 cal.set(Calendar.MONTH, startMonth);
    985                 cal.set(Calendar.DATE, startDay);
    986                 sStartDay = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH);
    987                 if (startMode == DOM_MODE) {
    988                     sStartDayOfWeek = cal.getFirstDayOfWeek();
    989                 }
    990             }
    991         }
    992         ObjectOutputStream.PutField fields = stream.putFields();
    993         fields.put("dstSavings", dstSavings); //$NON-NLS-1$
    994         fields.put("endDay", sEndDay); //$NON-NLS-1$
    995         fields.put("endDayOfWeek", sEndDayOfWeek); //$NON-NLS-1$
    996         fields.put("endMode", endMode); //$NON-NLS-1$
    997         fields.put("endMonth", endMonth); //$NON-NLS-1$
    998         fields.put("endTime", endTime); //$NON-NLS-1$
    999         fields.put("monthLength", GregorianCalendar.DaysInMonth); //$NON-NLS-1$
   1000         fields.put("rawOffset", rawOffset); //$NON-NLS-1$
   1001         fields.put("serialVersionOnStream", 1); //$NON-NLS-1$
   1002         fields.put("startDay", sStartDay); //$NON-NLS-1$
   1003         fields.put("startDayOfWeek", sStartDayOfWeek); //$NON-NLS-1$
   1004         fields.put("startMode", startMode); //$NON-NLS-1$
   1005         fields.put("startMonth", startMonth); //$NON-NLS-1$
   1006         fields.put("startTime", startTime); //$NON-NLS-1$
   1007         fields.put("startYear", startYear); //$NON-NLS-1$
   1008         fields.put("useDaylight", useDaylight); //$NON-NLS-1$
   1009         stream.writeFields();
   1010         stream.writeInt(4);
   1011         byte[] values = new byte[4];
   1012         values[0] = (byte) startDay;
   1013         values[1] = (byte) (startMode == DOM_MODE ? 0 : startDayOfWeek + 1);
   1014         values[2] = (byte) endDay;
   1015         values[3] = (byte) (endMode == DOM_MODE ? 0 : endDayOfWeek + 1);
   1016         stream.write(values);
   1017     }
   1018 
   1019     private void readObject(ObjectInputStream stream) throws IOException,
   1020             ClassNotFoundException {
   1021         ObjectInputStream.GetField fields = stream.readFields();
   1022         rawOffset = fields.get("rawOffset", 0); //$NON-NLS-1$
   1023         useDaylight = fields.get("useDaylight", false); //$NON-NLS-1$
   1024         if (useDaylight) {
   1025             endMonth = fields.get("endMonth", 0); //$NON-NLS-1$
   1026             endTime = fields.get("endTime", 0); //$NON-NLS-1$
   1027             startMonth = fields.get("startMonth", 0); //$NON-NLS-1$
   1028             startTime = fields.get("startTime", 0); //$NON-NLS-1$
   1029             startYear = fields.get("startYear", 0); //$NON-NLS-1$
   1030         }
   1031         if (fields.get("serialVersionOnStream", 0) == 0) { //$NON-NLS-1$
   1032             if (useDaylight) {
   1033                 startMode = endMode = DOW_IN_MONTH_MODE;
   1034                 endDay = fields.get("endDay", 0); //$NON-NLS-1$
   1035                 endDayOfWeek = fields.get("endDayOfWeek", 0) - 1; //$NON-NLS-1$
   1036                 startDay = fields.get("startDay", 0); //$NON-NLS-1$
   1037                 startDayOfWeek = fields.get("startDayOfWeek", 0) - 1; //$NON-NLS-1$
   1038             }
   1039         } else {
   1040             dstSavings = fields.get("dstSavings", 0); //$NON-NLS-1$
   1041             if (useDaylight) {
   1042                 endMode = fields.get("endMode", 0); //$NON-NLS-1$
   1043                 startMode = fields.get("startMode", 0); //$NON-NLS-1$
   1044                 int length = stream.readInt();
   1045                 byte[] values = new byte[length];
   1046                 stream.readFully(values);
   1047                 if (length >= 4) {
   1048                     startDay = values[0];
   1049                     startDayOfWeek = values[1];
   1050                     if (startMode != DOM_MODE) {
   1051                         startDayOfWeek--;
   1052                     }
   1053                     endDay = values[2];
   1054                     endDayOfWeek = values[3];
   1055                     if (endMode != DOM_MODE) {
   1056                         endDayOfWeek--;
   1057                     }
   1058                 }
   1059             }
   1060         }
   1061     }
   1062 
   1063 }
   1064