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;
27import 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 */
45public 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