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