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 package java.util;
     19 
     20 import java.io.IOException;
     21 import java.io.ObjectInputStream;
     22 import java.io.ObjectOutputStream;
     23 import java.io.Serializable;
     24 import java.text.DateFormat;
     25 import java.text.DateFormatSymbols;
     26 import java.text.SimpleDateFormat;
     27 import libcore.icu.LocaleData;
     28 
     29 /**
     30  * A specific moment in time, with millisecond precision. Values typically come
     31  * from {@link System#currentTimeMillis}, and are always UTC, regardless of the
     32  * system's time zone. This is often called "Unix time" or "epoch time".
     33  *
     34  * <p>Instances of this class are suitable for comparison, but little else.
     35  * Use {@link java.text.DateFormat} to format a {@code Date} for display to a human.
     36  * Use {@link Calendar} to break down a {@code Date} if you need to extract fields such
     37  * as the current month or day of week, or to construct a {@code Date} from a broken-down
     38  * time. That is: this class' deprecated display-related functionality is now provided
     39  * by {@code DateFormat}, and this class' deprecated computational functionality is
     40  * now provided by {@code Calendar}. Both of these other classes (and their subclasses)
     41  * allow you to interpret a {@code Date} in a given time zone.
     42  *
     43  * <p>Note that, surprisingly, instances of this class are mutable.
     44  */
     45 public class Date implements Serializable, Cloneable, Comparable<Date> {
     46 
     47     private static final long serialVersionUID = 7523967970034938905L;
     48 
     49     // Used by parse()
     50     private static final int CREATION_YEAR = new Date().getYear();
     51 
     52     private transient long milliseconds;
     53 
     54     /**
     55      * Initializes this {@code Date} instance to the current time.
     56      */
     57     public Date() {
     58         this(System.currentTimeMillis());
     59     }
     60 
     61     /**
     62      * Constructs a new {@code Date} initialized to midnight in the default {@code TimeZone} on
     63      * the specified date.
     64      *
     65      * @param year
     66      *            the year, 0 is 1900.
     67      * @param month
     68      *            the month, 0 - 11.
     69      * @param day
     70      *            the day of the month, 1 - 31.
     71      *
     72      * @deprecated Use {@link GregorianCalendar#GregorianCalendar(int, int, int)} instead.
     73      */
     74     @Deprecated
     75     public Date(int year, int month, int day) {
     76         GregorianCalendar cal = new GregorianCalendar(false);
     77         cal.set(1900 + year, month, day);
     78         milliseconds = cal.getTimeInMillis();
     79     }
     80 
     81     /**
     82      * Constructs a new {@code Date} initialized to the specified date and time in the
     83      * default {@code TimeZone}.
     84      *
     85      * @param year
     86      *            the year, 0 is 1900.
     87      * @param month
     88      *            the month, 0 - 11.
     89      * @param day
     90      *            the day of the month, 1 - 31.
     91      * @param hour
     92      *            the hour of day, 0 - 23.
     93      * @param minute
     94      *            the minute of the hour, 0 - 59.
     95      *
     96      * @deprecated Use {@link GregorianCalendar#GregorianCalendar(int, int, int, int, int)} instead.
     97      */
     98     @Deprecated
     99     public Date(int year, int month, int day, int hour, int minute) {
    100         GregorianCalendar cal = new GregorianCalendar(false);
    101         cal.set(1900 + year, month, day, hour, minute);
    102         milliseconds = cal.getTimeInMillis();
    103     }
    104 
    105     /**
    106      * Constructs a new {@code Date} initialized to the specified date and time in the
    107      * default {@code TimeZone}.
    108      *
    109      * @param year
    110      *            the year, 0 is 1900.
    111      * @param month
    112      *            the month, 0 - 11.
    113      * @param day
    114      *            the day of the month, 1 - 31.
    115      * @param hour
    116      *            the hour of day, 0 - 23.
    117      * @param minute
    118      *            the minute of the hour, 0 - 59.
    119      * @param second
    120      *            the second of the minute, 0 - 59.
    121      *
    122      * @deprecated Use {@link GregorianCalendar#GregorianCalendar(int, int, int, int, int, int)}
    123      * instead.
    124      */
    125     @Deprecated
    126     public Date(int year, int month, int day, int hour, int minute, int second) {
    127         GregorianCalendar cal = new GregorianCalendar(false);
    128         cal.set(1900 + year, month, day, hour, minute, second);
    129         milliseconds = cal.getTimeInMillis();
    130     }
    131 
    132     /**
    133      * Initializes this {@code Date} instance using the specified millisecond value. The
    134      * value is the number of milliseconds since Jan. 1, 1970 GMT.
    135      *
    136      * @param milliseconds
    137      *            the number of milliseconds since Jan. 1, 1970 GMT.
    138      */
    139     public Date(long milliseconds) {
    140         this.milliseconds = milliseconds;
    141     }
    142 
    143     /**
    144      * Constructs a new {@code Date} initialized to the date and time parsed from the
    145      * specified String.
    146      *
    147      * @param string
    148      *            the String to parse.
    149      *
    150      * @deprecated Use {@link DateFormat} instead.
    151      */
    152     @Deprecated
    153     public Date(String string) {
    154         milliseconds = parse(string);
    155     }
    156 
    157     /**
    158      * Returns if this {@code Date} is after the specified Date.
    159      *
    160      * @param date
    161      *            a Date instance to compare.
    162      * @return {@code true} if this {@code Date} is after the specified {@code Date},
    163      *         {@code false} otherwise.
    164      */
    165     public boolean after(Date date) {
    166         return milliseconds > date.milliseconds;
    167     }
    168 
    169     /**
    170      * Returns if this {@code Date} is before the specified Date.
    171      *
    172      * @param date
    173      *            a {@code Date} instance to compare.
    174      * @return {@code true} if this {@code Date} is before the specified {@code Date},
    175      *         {@code false} otherwise.
    176      */
    177     public boolean before(Date date) {
    178         return milliseconds < date.milliseconds;
    179     }
    180 
    181     /**
    182      * Returns a new {@code Date} with the same millisecond value as this {@code Date}.
    183      *
    184      * @return a shallow copy of this {@code Date}.
    185      *
    186      * @see java.lang.Cloneable
    187      */
    188     @Override
    189     public Object clone() {
    190         try {
    191             return super.clone();
    192         } catch (CloneNotSupportedException e) {
    193             throw new AssertionError(e);
    194         }
    195     }
    196 
    197     /**
    198      * Compare the receiver to the specified {@code Date} to determine the relative
    199      * ordering.
    200      *
    201      * @param date
    202      *            a {@code Date} to compare against.
    203      * @return an {@code int < 0} if this {@code Date} is less than the specified {@code Date}, {@code 0} if
    204      *         they are equal, and an {@code int > 0} if this {@code Date} is greater.
    205      */
    206     public int compareTo(Date date) {
    207         if (milliseconds < date.milliseconds) {
    208             return -1;
    209         }
    210         if (milliseconds == date.milliseconds) {
    211             return 0;
    212         }
    213         return 1;
    214     }
    215 
    216     /**
    217      * Compares the specified object to this {@code Date} and returns if they are equal.
    218      * To be equal, the object must be an instance of {@code Date} and have the same millisecond
    219      * value.
    220      *
    221      * @param object
    222      *            the object to compare with this object.
    223      * @return {@code true} if the specified object is equal to this {@code Date}, {@code false}
    224      *         otherwise.
    225      *
    226      * @see #hashCode
    227      */
    228     @Override
    229     public boolean equals(Object object) {
    230         return (object == this) || (object instanceof Date)
    231                 && (milliseconds == ((Date) object).milliseconds);
    232     }
    233 
    234     /**
    235      * Returns the gregorian calendar day of the month for this {@code Date} object.
    236      *
    237      * @return the day of the month.
    238      *
    239      * @deprecated Use {@code Calendar.get(Calendar.DATE)} instead.
    240      */
    241     @Deprecated
    242     public int getDate() {
    243         return new GregorianCalendar(milliseconds).get(Calendar.DATE);
    244     }
    245 
    246     /**
    247      * Returns the gregorian calendar day of the week for this {@code Date} object.
    248      *
    249      * @return the day of the week.
    250      *
    251      * @deprecated Use {@code Calendar.get(Calendar.DAY_OF_WEEK)} instead.
    252      */
    253     @Deprecated
    254     public int getDay() {
    255         return new GregorianCalendar(milliseconds).get(Calendar.DAY_OF_WEEK) - 1;
    256     }
    257 
    258     /**
    259      * Returns the gregorian calendar hour of the day for this {@code Date} object.
    260      *
    261      * @return the hour of the day.
    262      *
    263      * @deprecated Use {@code Calendar.get(Calendar.HOUR_OF_DAY)} instead.
    264      */
    265     @Deprecated
    266     public int getHours() {
    267         return new GregorianCalendar(milliseconds).get(Calendar.HOUR_OF_DAY);
    268     }
    269 
    270     /**
    271      * Returns the gregorian calendar minute of the hour for this {@code Date} object.
    272      *
    273      * @return the minutes.
    274      *
    275      * @deprecated Use {@code Calendar.get(Calendar.MINUTE)} instead.
    276      */
    277     @Deprecated
    278     public int getMinutes() {
    279         return new GregorianCalendar(milliseconds).get(Calendar.MINUTE);
    280     }
    281 
    282     /**
    283      * Returns the gregorian calendar month for this {@code Date} object.
    284      *
    285      * @return the month.
    286      *
    287      * @deprecated Use {@code Calendar.get(Calendar.MONTH)} instead.
    288      */
    289     @Deprecated
    290     public int getMonth() {
    291         return new GregorianCalendar(milliseconds).get(Calendar.MONTH);
    292     }
    293 
    294     /**
    295      * Returns the gregorian calendar second of the minute for this {@code Date} object.
    296      *
    297      * @return the seconds.
    298      *
    299      * @deprecated Use {@code Calendar.get(Calendar.SECOND)} instead.
    300      */
    301     @Deprecated
    302     public int getSeconds() {
    303         return new GregorianCalendar(milliseconds).get(Calendar.SECOND);
    304     }
    305 
    306     /**
    307      * Returns this {@code Date} as a millisecond value. The value is the number of
    308      * milliseconds since Jan. 1, 1970, midnight GMT.
    309      *
    310      * @return the number of milliseconds since Jan. 1, 1970, midnight GMT.
    311      */
    312     public long getTime() {
    313         return milliseconds;
    314     }
    315 
    316     /**
    317      * Returns the timezone offset in minutes of the default {@code TimeZone}.
    318      *
    319      * @return the timezone offset in minutes of the default {@code TimeZone}.
    320      *
    321      * @deprecated Use {@code (Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / 60000} instead.
    322      */
    323     @Deprecated
    324     public int getTimezoneOffset() {
    325         GregorianCalendar cal = new GregorianCalendar(milliseconds);
    326         return -(cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / 60000;
    327     }
    328 
    329     /**
    330      * Returns the gregorian calendar year since 1900 for this {@code Date} object.
    331      *
    332      * @return the year - 1900.
    333      *
    334      * @deprecated Use {@code Calendar.get(Calendar.YEAR) - 1900} instead.
    335      */
    336     @Deprecated
    337     public int getYear() {
    338         return new GregorianCalendar(milliseconds).get(Calendar.YEAR) - 1900;
    339     }
    340 
    341     /**
    342      * Returns an integer hash code for the receiver. Objects which are equal
    343      * return the same value for this method.
    344      *
    345      * @return this {@code Date}'s hash.
    346      *
    347      * @see #equals
    348      */
    349     @Override
    350     public int hashCode() {
    351         return (int) (milliseconds >>> 32) ^ (int) milliseconds;
    352     }
    353 
    354     private static int parse(String string, String[] array) {
    355         for (int i = 0, alength = array.length, slength = string.length(); i < alength; i++) {
    356             if (string.regionMatches(true, 0, array[i], 0, slength)) {
    357                 return i;
    358             }
    359         }
    360         return -1;
    361     }
    362 
    363     private static IllegalArgumentException parseError(String string) {
    364         throw new IllegalArgumentException("Parse error: " + string);
    365     }
    366 
    367     /**
    368      * Returns the millisecond value of the date and time parsed from the
    369      * specified {@code String}. Many date/time formats are recognized, including IETF
    370      * standard syntax, i.e. Tue, 22 Jun 1999 12:16:00 GMT-0500
    371      *
    372      * @param string
    373      *            the String to parse.
    374      * @return the millisecond value parsed from the String.
    375      *
    376      * @deprecated Use {@link DateFormat} instead.
    377      */
    378     @Deprecated
    379     public static long parse(String string) {
    380         if (string == null) {
    381             throw new IllegalArgumentException("The string argument is null");
    382         }
    383 
    384         char sign = 0;
    385         int commentLevel = 0;
    386         int offset = 0, length = string.length(), state = 0;
    387         int year = -1, month = -1, date = -1;
    388         int hour = -1, minute = -1, second = -1, zoneOffset = 0, minutesOffset = 0;
    389         boolean zone = false;
    390         final int PAD = 0, LETTERS = 1, NUMBERS = 2;
    391         StringBuilder buffer = new StringBuilder();
    392 
    393         while (offset <= length) {
    394             char next = offset < length ? string.charAt(offset) : '\r';
    395             offset++;
    396 
    397             if (next == '(') {
    398                 commentLevel++;
    399             }
    400             if (commentLevel > 0) {
    401                 if (next == ')') {
    402                     commentLevel--;
    403                 }
    404                 if (commentLevel == 0) {
    405                     next = ' ';
    406                 } else {
    407                     continue;
    408                 }
    409             }
    410 
    411             int nextState = PAD;
    412             if ('a' <= next && next <= 'z' || 'A' <= next && next <= 'Z') {
    413                 nextState = LETTERS;
    414             } else if ('0' <= next && next <= '9') {
    415                 nextState = NUMBERS;
    416             } else if (!Character.isSpace(next) && ",+-:/".indexOf(next) == -1) {
    417                 throw parseError(string);
    418             }
    419 
    420             if (state == NUMBERS && nextState != NUMBERS) {
    421                 int digit = Integer.parseInt(buffer.toString());
    422                 buffer.setLength(0);
    423                 if (sign == '+' || sign == '-') {
    424                     if (zoneOffset == 0) {
    425                         zone = true;
    426                         if (next == ':') {
    427                             minutesOffset = sign == '-' ? -Integer
    428                                     .parseInt(string.substring(offset,
    429                                             offset + 2)) : Integer
    430                                     .parseInt(string.substring(offset,
    431                                             offset + 2));
    432                             offset += 2;
    433                         }
    434                         zoneOffset = sign == '-' ? -digit : digit;
    435                         sign = 0;
    436                     } else {
    437                         throw parseError(string);
    438                     }
    439                 } else if (digit >= 70) {
    440                     if (year == -1
    441                             && (Character.isSpace(next) || next == ','
    442                                     || next == '/' || next == '\r')) {
    443                         year = digit;
    444                     } else {
    445                         throw parseError(string);
    446                     }
    447                 } else if (next == ':') {
    448                     if (hour == -1) {
    449                         hour = digit;
    450                     } else if (minute == -1) {
    451                         minute = digit;
    452                     } else {
    453                         throw parseError(string);
    454                     }
    455                 } else if (next == '/') {
    456                     if (month == -1) {
    457                         month = digit - 1;
    458                     } else if (date == -1) {
    459                         date = digit;
    460                     } else {
    461                         throw parseError(string);
    462                     }
    463                 } else if (Character.isSpace(next) || next == ','
    464                         || next == '-' || next == '\r') {
    465                     if (hour != -1 && minute == -1) {
    466                         minute = digit;
    467                     } else if (minute != -1 && second == -1) {
    468                         second = digit;
    469                     } else if (date == -1) {
    470                         date = digit;
    471                     } else if (year == -1) {
    472                         year = digit;
    473                     } else {
    474                         throw parseError(string);
    475                     }
    476                 } else if (year == -1 && month != -1 && date != -1) {
    477                     year = digit;
    478                 } else {
    479                     throw parseError(string);
    480                 }
    481             } else if (state == LETTERS && nextState != LETTERS) {
    482                 String text = buffer.toString().toUpperCase(Locale.US);
    483                 buffer.setLength(0);
    484                 if (text.length() == 1) {
    485                     throw parseError(string);
    486                 }
    487                 if (text.equals("AM")) {
    488                     if (hour == 12) {
    489                         hour = 0;
    490                     } else if (hour < 1 || hour > 12) {
    491                         throw parseError(string);
    492                     }
    493                 } else if (text.equals("PM")) {
    494                     if (hour == 12) {
    495                         hour = 0;
    496                     } else if (hour < 1 || hour > 12) {
    497                         throw parseError(string);
    498                     }
    499                     hour += 12;
    500                 } else {
    501                     DateFormatSymbols symbols = new DateFormatSymbols(Locale.US);
    502                     String[] weekdays = symbols.getWeekdays(), months = symbols
    503                             .getMonths();
    504                     int value;
    505                     if (parse(text, weekdays) != -1) {/* empty */
    506                     } else if (month == -1 && (month = parse(text, months)) != -1) {/* empty */
    507                     } else if (text.equals("GMT") || text.equals("UT") || text.equals("UTC")) {
    508                         zone = true;
    509                         zoneOffset = 0;
    510                     } else if ((value = zone(text)) != 0) {
    511                         zone = true;
    512                         zoneOffset = value;
    513                     } else {
    514                         throw parseError(string);
    515                     }
    516                 }
    517             }
    518 
    519             if (next == '+' || (year != -1 && next == '-')) {
    520                 sign = next;
    521             } else if (!Character.isSpace(next) && next != ','
    522                     && nextState != NUMBERS) {
    523                 sign = 0;
    524             }
    525 
    526             if (nextState == LETTERS || nextState == NUMBERS) {
    527                 buffer.append(next);
    528             }
    529             state = nextState;
    530         }
    531 
    532         if (year != -1 && month != -1 && date != -1) {
    533             if (hour == -1) {
    534                 hour = 0;
    535             }
    536             if (minute == -1) {
    537                 minute = 0;
    538             }
    539             if (second == -1) {
    540                 second = 0;
    541             }
    542             if (year < (CREATION_YEAR - 80)) {
    543                 year += 2000;
    544             } else if (year < 100) {
    545                 year += 1900;
    546             }
    547             minute -= minutesOffset;
    548             if (zone) {
    549                 if (zoneOffset >= 24 || zoneOffset <= -24) {
    550                     hour -= zoneOffset / 100;
    551                     minute -= zoneOffset % 100;
    552                 } else {
    553                     hour -= zoneOffset;
    554                 }
    555                 return UTC(year - 1900, month, date, hour, minute, second);
    556             }
    557             return new Date(year - 1900, month, date, hour, minute, second)
    558                     .getTime();
    559         }
    560         throw parseError(string);
    561     }
    562 
    563     /**
    564      * Sets the gregorian calendar day of the month for this {@code Date} object.
    565      *
    566      * @param day
    567      *            the day of the month.
    568      *
    569      * @deprecated Use {@code Calendar.set(Calendar.DATE, day)} instead.
    570      */
    571     @Deprecated
    572     public void setDate(int day) {
    573         GregorianCalendar cal = new GregorianCalendar(milliseconds);
    574         cal.set(Calendar.DATE, day);
    575         milliseconds = cal.getTimeInMillis();
    576     }
    577 
    578     /**
    579      * Sets the gregorian calendar hour of the day for this {@code Date} object.
    580      *
    581      * @param hour
    582      *            the hour of the day.
    583      *
    584      * @deprecated Use {@code Calendar.set(Calendar.HOUR_OF_DAY, hour)} instead.
    585      */
    586     @Deprecated
    587     public void setHours(int hour) {
    588         GregorianCalendar cal = new GregorianCalendar(milliseconds);
    589         cal.set(Calendar.HOUR_OF_DAY, hour);
    590         milliseconds = cal.getTimeInMillis();
    591     }
    592 
    593     /**
    594      * Sets the gregorian calendar minute of the hour for this {@code Date} object.
    595      *
    596      * @param minute
    597      *            the minutes.
    598      *
    599      * @deprecated Use {@code Calendar.set(Calendar.MINUTE, minute)} instead.
    600      */
    601     @Deprecated
    602     public void setMinutes(int minute) {
    603         GregorianCalendar cal = new GregorianCalendar(milliseconds);
    604         cal.set(Calendar.MINUTE, minute);
    605         milliseconds = cal.getTimeInMillis();
    606     }
    607 
    608     /**
    609      * Sets the gregorian calendar month for this {@code Date} object.
    610      *
    611      * @param month
    612      *            the month.
    613      *
    614      * @deprecated Use {@code Calendar.set(Calendar.MONTH, month)} instead.
    615      */
    616     @Deprecated
    617     public void setMonth(int month) {
    618         GregorianCalendar cal = new GregorianCalendar(milliseconds);
    619         cal.set(Calendar.MONTH, month);
    620         milliseconds = cal.getTimeInMillis();
    621     }
    622 
    623     /**
    624      * Sets the gregorian calendar second of the minute for this {@code Date} object.
    625      *
    626      * @param second
    627      *            the seconds.
    628      *
    629      * @deprecated Use {@code Calendar.set(Calendar.SECOND, second)} instead.
    630      */
    631     @Deprecated
    632     public void setSeconds(int second) {
    633         GregorianCalendar cal = new GregorianCalendar(milliseconds);
    634         cal.set(Calendar.SECOND, second);
    635         milliseconds = cal.getTimeInMillis();
    636     }
    637 
    638     /**
    639      * Sets this {@code Date} to the specified millisecond value. The value is the
    640      * number of milliseconds since Jan. 1, 1970 GMT.
    641      *
    642      * @param milliseconds
    643      *            the number of milliseconds since Jan. 1, 1970 GMT.
    644      */
    645     public void setTime(long milliseconds) {
    646         this.milliseconds = milliseconds;
    647     }
    648 
    649     /**
    650      * Sets the gregorian calendar year since 1900 for this {@code Date} object.
    651      *
    652      * @param year
    653      *            the year since 1900.
    654      *
    655      * @deprecated Use {@code Calendar.set(Calendar.YEAR, year + 1900)} instead.
    656      */
    657     @Deprecated
    658     public void setYear(int year) {
    659         GregorianCalendar cal = new GregorianCalendar(milliseconds);
    660         cal.set(Calendar.YEAR, year + 1900);
    661         milliseconds = cal.getTimeInMillis();
    662     }
    663 
    664     /**
    665      * Returns the string representation of this {@code Date} in GMT in the format
    666      * {@code "22 Jun 1999 13:02:00 GMT"}.
    667      *
    668      * @deprecated Use {@link DateFormat} instead.
    669      */
    670     @Deprecated
    671     public String toGMTString() {
    672         SimpleDateFormat sdf = new SimpleDateFormat("d MMM y HH:mm:ss 'GMT'", Locale.US);
    673         TimeZone gmtZone = TimeZone.getTimeZone("GMT");
    674         sdf.setTimeZone(gmtZone);
    675         GregorianCalendar gc = new GregorianCalendar(gmtZone);
    676         gc.setTimeInMillis(milliseconds);
    677         return sdf.format(this);
    678     }
    679 
    680     /**
    681      * Returns the string representation of this {@code Date} for the default {@code Locale}.
    682      *
    683      * @deprecated Use {@link DateFormat} instead.
    684      */
    685     @Deprecated
    686     public String toLocaleString() {
    687         return DateFormat.getDateTimeInstance().format(this);
    688     }
    689 
    690     /**
    691      * Returns a string representation of this {@code Date}.
    692      * The formatting is equivalent to using a {@code SimpleDateFormat} with
    693      * the format string "EEE MMM dd HH:mm:ss zzz yyyy", which looks something
    694      * like "Tue Jun 22 13:07:00 PDT 1999". The current default time zone and
    695      * locale are used. If you need control over the time zone or locale,
    696      * use {@code SimpleDateFormat} instead.
    697      */
    698     @Override
    699     public String toString() {
    700         // TODO: equivalent to the following one-liner, though that's slower on stingray
    701         // at 476us versus 69us...
    702         //   return new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy").format(d);
    703         LocaleData localeData = LocaleData.get(Locale.US);
    704         Calendar cal = new GregorianCalendar(milliseconds);
    705         TimeZone tz = cal.getTimeZone();
    706         StringBuilder result = new StringBuilder();
    707         result.append(localeData.shortWeekdayNames[cal.get(Calendar.DAY_OF_WEEK)]);
    708         result.append(' ');
    709         result.append(localeData.shortMonthNames[cal.get(Calendar.MONTH)]);
    710         result.append(' ');
    711         appendTwoDigits(result, cal.get(Calendar.DAY_OF_MONTH));
    712         result.append(' ');
    713         appendTwoDigits(result, cal.get(Calendar.HOUR_OF_DAY));
    714         result.append(':');
    715         appendTwoDigits(result, cal.get(Calendar.MINUTE));
    716         result.append(':');
    717         appendTwoDigits(result, cal.get(Calendar.SECOND));
    718         result.append(' ');
    719         result.append(tz.getDisplayName(tz.inDaylightTime(this), TimeZone.SHORT));
    720         result.append(' ');
    721         result.append(cal.get(Calendar.YEAR));
    722         return result.toString();
    723     }
    724 
    725     private static void appendTwoDigits(StringBuilder sb, int n) {
    726         if (n < 10) {
    727             sb.append('0');
    728         }
    729         sb.append(n);
    730     }
    731 
    732     /**
    733      * Returns the millisecond value of the specified date and time in GMT.
    734      *
    735      * @param year
    736      *            the year, 0 is 1900.
    737      * @param month
    738      *            the month, 0 - 11.
    739      * @param day
    740      *            the day of the month, 1 - 31.
    741      * @param hour
    742      *            the hour of day, 0 - 23.
    743      * @param minute
    744      *            the minute of the hour, 0 - 59.
    745      * @param second
    746      *            the second of the minute, 0 - 59.
    747      * @return the date and time in GMT in milliseconds.
    748      *
    749      * @deprecated Use code like this instead:<code>
    750      *  Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
    751      *  cal.set(year + 1900, month, day, hour, minute, second);
    752      *  cal.getTime().getTime();</code>
    753      */
    754     @Deprecated
    755     public static long UTC(int year, int month, int day, int hour, int minute,
    756             int second) {
    757         GregorianCalendar cal = new GregorianCalendar(false);
    758         cal.setTimeZone(TimeZone.getTimeZone("GMT"));
    759         cal.set(1900 + year, month, day, hour, minute, second);
    760         return cal.getTimeInMillis();
    761     }
    762 
    763     private static int zone(String text) {
    764         if (text.equals("EST")) {
    765             return -5;
    766         }
    767         if (text.equals("EDT")) {
    768             return -4;
    769         }
    770         if (text.equals("CST")) {
    771             return -6;
    772         }
    773         if (text.equals("CDT")) {
    774             return -5;
    775         }
    776         if (text.equals("MST")) {
    777             return -7;
    778         }
    779         if (text.equals("MDT")) {
    780             return -6;
    781         }
    782         if (text.equals("PST")) {
    783             return -8;
    784         }
    785         if (text.equals("PDT")) {
    786             return -7;
    787         }
    788         return 0;
    789     }
    790 
    791     private void writeObject(ObjectOutputStream stream) throws IOException {
    792         stream.defaultWriteObject();
    793         stream.writeLong(getTime());
    794     }
    795 
    796     private void readObject(ObjectInputStream stream) throws IOException,
    797             ClassNotFoundException {
    798         stream.defaultReadObject();
    799         setTime(stream.readLong());
    800     }
    801 }
    802