OSDN Git Service

5559d8c53f41936501819cdee85710b389e71aee
[pf3gnuchains/gcc-fork.git] / libjava / java / util / Calendar.java
1 /* Calendar.java --
2    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package java.util;
40
41 import java.io.IOException;
42 import java.io.ObjectInputStream;
43 import java.io.ObjectOutputStream;
44 import java.io.Serializable;
45 import java.lang.reflect.Constructor;
46 import java.lang.reflect.InvocationTargetException;
47
48 /**
49  * This class is an abstract base class for Calendars, which can be
50  * used to convert between <code>Date</code> objects and a set of
51  * integer fields which represent <code>YEAR</code>,
52  * <code>MONTH</code>, <code>DAY</code>, etc.  The <code>Date</code>
53  * object represents a time in milliseconds since the Epoch. <br>
54  *
55  * This class is locale sensitive.  To get the Object matching the
56  * current locale you can use <code>getInstance</code>.  You can even provide
57  * a locale or a timezone.  <code>getInstance</code> returns currently
58  * a <code>GregorianCalendar</code> for the current date. <br>
59  *
60  * If you want to convert a date from the Year, Month, Day, DayOfWeek,
61  * etc.  Representation to a <code>Date</code>-Object, you can create
62  * a new Calendar with <code>getInstance()</code>,
63  * <code>clear()</code> all fields, <code>set(int,int)</code> the
64  * fields you need and convert it with <code>getTime()</code>. <br>
65  *
66  * If you want to convert a <code>Date</code>-object to the Calendar
67  * representation, create a new Calendar, assign the
68  * <code>Date</code>-Object with <code>setTime()</code>, and read the
69  * fields with <code>get(int)</code>. <br>
70  *
71  * When computing the date from time fields, it may happen, that there
72  * are either two few fields set, or some fields are inconsistent.  This
73  * cases will handled in a calendar specific way.  Missing fields are
74  * replaced by the fields of the epoch: 1970 January 1 00:00. <br>
75  *
76  * To understand, how the day of year is computed out of the fields
77  * look at the following table.  It is traversed from top to bottom,
78  * and for the first line all fields are set, that line is used to
79  * compute the day. <br>
80  *
81  *
82 <pre>month + day_of_month
83 month + week_of_month + day_of_week
84 month + day_of_week_of_month + day_of_week
85 day_of_year
86 day_of_week + week_of_year</pre>
87  *
88  * The hour_of_day-field takes precedence over the ampm and
89  * hour_of_ampm fields. <br>
90  *
91  * <STRONG>Note:</STRONG> This can differ for non-Gregorian calendar. <br>
92  *
93  * To convert a calendar to a human readable form and vice versa,  use
94  * the <code>java.text.DateFormat</code> class. <br>
95  *
96  * Other useful things you can do with an calendar, is
97  * <code>roll</code>ing fields (that means increase/decrease a
98  * specific field by one, propagating overflows), or
99  * <code>add</code>ing/substracting a fixed amount to a field.
100  *
101  * @see Date
102  * @see GregorianCalendar
103  * @see TimeZone
104  * @see java.text.DateFormat
105  */
106 public abstract class Calendar implements Serializable, Cloneable
107 {
108   /**
109    * Constant representing the era time field.
110    */
111   public static final int ERA = 0;
112
113   /**
114    * Constant representing the year time field.
115    */
116   public static final int YEAR = 1;
117
118   /**
119    * Constant representing the month time field.  This field
120    * should contain one of the JANUARY,...,DECEMBER constants below.
121    */
122   public static final int MONTH = 2;
123
124   /**
125    * Constant representing the week of the year field.
126    * @see #setFirstDayOfWeek(int)
127    */
128   public static final int WEEK_OF_YEAR = 3;
129
130   /**
131    * Constant representing the week of the month time field.
132    * @see #setFirstDayOfWeek(int)
133    */
134   public static final int WEEK_OF_MONTH = 4;
135
136   /**
137    * Constant representing the day time field, synonym for DAY_OF_MONTH.
138    */
139   public static final int DATE = 5;
140
141   /**
142    * Constant representing the day time field.
143    */
144   public static final int DAY_OF_MONTH = 5;
145
146   /**
147    * Constant representing the day of year time field.  This is
148    * 1 for the first day in month.
149    */
150   public static final int DAY_OF_YEAR = 6;
151
152   /**
153    * Constant representing the day of week time field.  This field
154    * should contain one of the SUNDAY,...,SATURDAY constants below.
155    */
156   public static final int DAY_OF_WEEK = 7;
157
158   /**
159    * Constant representing the day-of-week-in-month field.  For
160    * instance this field contains 2 for the second thursday in a
161    * month.  If you give a negative number here, the day will count
162    * from the end of the month.
163    */
164   public static final int DAY_OF_WEEK_IN_MONTH = 8;
165
166   /**
167    * Constant representing the part of the day for 12-hour clock.  This
168    * should be one of AM or PM.
169    */
170   public static final int AM_PM = 9;
171
172   /**
173    * Constant representing the hour time field for 12-hour clock.
174    */
175   public static final int HOUR = 10;
176
177   /**
178    * Constant representing the hour of day time field for 24-hour clock.
179    */
180   public static final int HOUR_OF_DAY = 11;
181
182   /**
183    * Constant representing the minute of hour time field.
184    */
185   public static final int MINUTE = 12;
186
187   /**
188    * Constant representing the second time field.
189    */
190   public static final int SECOND = 13;
191
192   /**
193    * Constant representing the millisecond time field.
194    */
195   public static final int MILLISECOND = 14;
196
197   /**
198    * Constant representing the time zone offset time field for the
199    * time given in the other fields.  It is measured in
200    * milliseconds.  The default is the offset of the time zone.
201    */
202   public static final int ZONE_OFFSET = 15;
203
204   /**
205    * Constant representing the daylight saving time offset in
206    * milliseconds.  The default is the value given by the time zone.
207    */
208   public static final int DST_OFFSET = 16;
209
210   /**
211    * Number of time fields.
212    */
213   public static final int FIELD_COUNT = 17;
214
215   /**
216    * Constant representing Sunday.
217    */
218   public static final int SUNDAY = 1;
219
220   /**
221    * Constant representing Monday.
222    */
223   public static final int MONDAY = 2;
224
225   /**
226    * Constant representing Tuesday.
227    */
228   public static final int TUESDAY = 3;
229
230   /**
231    * Constant representing Wednesday.
232    */
233   public static final int WEDNESDAY = 4;
234
235   /**
236    * Constant representing Thursday.
237    */
238   public static final int THURSDAY = 5;
239
240   /**
241    * Constant representing Friday.
242    */
243   public static final int FRIDAY = 6;
244
245   /**
246    * Constant representing Saturday.
247    */
248   public static final int SATURDAY = 7;
249
250   /**
251    * Constant representing January.
252    */
253   public static final int JANUARY = 0;
254
255   /**
256    * Constant representing February.
257    */
258   public static final int FEBRUARY = 1;
259
260   /**
261    * Constant representing March.
262    */
263   public static final int MARCH = 2;
264
265   /**
266    * Constant representing April.
267    */
268   public static final int APRIL = 3;
269
270   /**
271    * Constant representing May.
272    */
273   public static final int MAY = 4;
274
275   /**
276    * Constant representing June.
277    */
278   public static final int JUNE = 5;
279
280   /**
281    * Constant representing July.
282    */
283   public static final int JULY = 6;
284
285   /**
286    * Constant representing August.
287    */
288   public static final int AUGUST = 7;
289
290   /**
291    * Constant representing September.
292    */
293   public static final int SEPTEMBER = 8;
294
295   /**
296    * Constant representing October.
297    */
298   public static final int OCTOBER = 9;
299
300   /**
301    * Constant representing November.
302    */
303   public static final int NOVEMBER = 10;
304
305   /**
306    * Constant representing December.
307    */
308   public static final int DECEMBER = 11;
309
310   /**
311    * Constant representing Undecimber. This is an artificial name useful
312    * for lunar calendars.
313    */
314   public static final int UNDECIMBER = 12;
315
316   /**
317    * Useful constant for 12-hour clock.
318    */
319   public static final int AM = 0;
320
321   /**
322    * Useful constant for 12-hour clock.
323    */
324   public static final int PM = 1;
325
326   /**
327    * The time fields.  The array is indexed by the constants YEAR to
328    * DST_OFFSET.
329    * @serial
330    */
331   protected int[] fields = new int[FIELD_COUNT];
332
333   /**
334    * The flags which tell if the fields above have a value.
335    * @serial
336    */
337   protected boolean[] isSet = new boolean[FIELD_COUNT];
338
339   /**
340    * The time in milliseconds since the epoch.
341    * @serial
342    */
343   protected long time;
344
345   /**
346    * Tells if the above field has a valid value.
347    * @serial
348    */
349   protected boolean isTimeSet;
350
351   /**
352    * Tells if the fields have a valid value.  This superseeds the isSet
353    * array.
354    * @serial
355    */
356   protected boolean areFieldsSet;
357
358   /**
359    * The time zone of this calendar.  Used by sub classes to do UTC / local
360    * time conversion.  Sub classes can access this field with getTimeZone().
361    * @serial
362    */
363   private TimeZone zone;
364
365   /**
366    * Specifies if the date/time interpretation should be lenient.
367    * If the flag is set, a date such as "February 30, 1996" will be
368    * treated as the 29th day after the February 1.  If this flag
369    * is false, such dates will cause an exception.
370    * @serial
371    */
372   private boolean lenient;
373
374   /**
375    * Sets what the first day of week is.  This is used for
376    * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
377    * @serial
378    */
379   private int firstDayOfWeek;
380
381   /**
382    * Sets how many days are required in the first week of the year.
383    * If the first day of the year should be the first week you should
384    * set this value to 1.  If the first week must be a full week, set
385    * it to 7.
386    * @serial
387    */
388   private int minimalDaysInFirstWeek;
389
390   /**
391    * Is set to true if DST_OFFSET is explicitly set. In that case
392    * it's value overrides the value computed from the current
393    * time and the timezone.
394    */
395   private boolean explicitDSTOffset = false;
396
397   /**
398    * The version of the serialized data on the stream.
399    * <dl><dt>0 or not present</dt>
400    * <dd> JDK 1.1.5 or later.</dd>
401    * <dt>1</dt>
402    * <dd>JDK 1.1.6 or later.  This always writes a correct `time' value
403    * on the stream, as well as the other fields, to be compatible with
404    * earlier versions</dd></dl>
405    * @since JDK1.1.6
406    * @serial
407    */
408   private int serialVersionOnStream = 1;
409
410   /**
411    * XXX - I have not checked the compatibility.  The documentation of
412    * the serialized-form is quite hairy...
413    */
414   static final long serialVersionUID = -1807547505821590642L;
415
416   /**
417    * The name of the resource bundle. Used only by getBundle()
418    */
419   private static final String bundleName = "gnu.java.locale.Calendar";
420
421   /**
422    * get resource bundle:
423    * The resources should be loaded via this method only. Iff an application
424    * uses this method, the resourcebundle is required.
425    */
426   private static ResourceBundle getBundle(Locale locale)
427   {
428     return ResourceBundle.getBundle(bundleName, locale,
429                                     ClassLoader.getSystemClassLoader());
430   }
431
432   /**
433    * Constructs a new Calendar with the default time zone and the default
434    * locale.
435    */
436   protected Calendar()
437   {
438     this(TimeZone.getDefault(), Locale.getDefault());
439   }
440
441   /**
442    * Constructs a new Calendar with the given time zone and the given
443    * locale.
444    * @param zone a time zone.
445    * @param locale a locale.
446    */
447   protected Calendar(TimeZone zone, Locale locale)
448   {
449     this.zone = zone;
450     lenient = true;
451
452     ResourceBundle rb = getBundle(locale);
453
454     firstDayOfWeek = ((Integer) rb.getObject("firstDayOfWeek")).intValue();
455     minimalDaysInFirstWeek = ((Integer) rb.getObject("minimalDaysInFirstWeek"))
456                              .intValue();
457     clear();
458   }
459
460   /**
461    * Creates a calendar representing the actual time, using the default
462    * time zone and locale.
463    */
464   public static synchronized Calendar getInstance()
465   {
466     return getInstance(TimeZone.getDefault(), Locale.getDefault());
467   }
468
469   /**
470    * Creates a calendar representing the actual time, using the given
471    * time zone and the default locale.
472    * @param zone a time zone.
473    */
474   public static synchronized Calendar getInstance(TimeZone zone)
475   {
476     return getInstance(zone, Locale.getDefault());
477   }
478
479   /**
480    * Creates a calendar representing the actual time, using the default
481    * time zone and the given locale.
482    * @param locale a locale.
483    */
484   public static synchronized Calendar getInstance(Locale locale)
485   {
486     return getInstance(TimeZone.getDefault(), locale);
487   }
488
489   /**
490    * Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle
491    * lookup for every getInstance call.
492    */
493   private static HashMap cache = new HashMap();
494
495   /** Preset argument types for calendar-class constructor lookup.  */
496   private static Class[] ctorArgTypes = new Class[]
497                                         {
498                                           TimeZone.class, Locale.class
499                                         };
500
501   /**
502    * Creates a calendar representing the actual time, using the given
503    * time zone and locale.
504    * @param zone a time zone.
505    * @param locale a locale.
506    */
507   public static synchronized Calendar getInstance(TimeZone zone, Locale locale)
508   {
509     Class calendarClass = (Class) cache.get(locale);
510     Throwable exception = null;
511
512     try
513       {
514         if (calendarClass == null)
515           {
516             ResourceBundle rb = getBundle(locale);
517             String calendarClassName = rb.getString("calendarClass");
518
519             if (calendarClassName != null)
520               {
521                 calendarClass = Class.forName(calendarClassName);
522                 if (Calendar.class.isAssignableFrom(calendarClass))
523                   cache.put(locale, calendarClass);
524               }
525           }
526
527         // GregorianCalendar is by far the most common case. Optimize by 
528         // avoiding reflection.
529         if (calendarClass == GregorianCalendar.class)
530           return new GregorianCalendar(zone, locale);
531
532         if (Calendar.class.isAssignableFrom(calendarClass))
533           {
534             Constructor ctor = calendarClass.getConstructor(ctorArgTypes);
535             return (Calendar) ctor.newInstance(new Object[] { zone, locale });
536           }
537       }
538     catch (ClassNotFoundException ex)
539       {
540         exception = ex;
541       }
542     catch (IllegalAccessException ex)
543       {
544         exception = ex;
545       }
546     catch (NoSuchMethodException ex)
547       {
548         exception = ex;
549       }
550     catch (InstantiationException ex)
551       {
552         exception = ex;
553       }
554     catch (InvocationTargetException ex)
555       {
556         exception = ex;
557       }
558
559     throw new RuntimeException("Error instantiating calendar for locale "
560                                + locale, exception);
561   }
562
563   /**
564    * Gets the set of locales for which a Calendar is available.
565    * @exception MissingResourceException if locale data couldn't be found.
566    * @return the set of locales.
567    */
568   public static synchronized Locale[] getAvailableLocales()
569   {
570     ResourceBundle rb = getBundle(new Locale("", ""));
571     return (Locale[]) rb.getObject("availableLocales");
572   }
573
574   /**
575    * Converts the time field values (<code>fields</code>) to
576    * milliseconds since the epoch UTC (<code>time</code>).  Override
577    * this method if you write your own Calendar.  */
578   protected abstract void computeTime();
579
580   /**
581    * Converts the milliseconds since the epoch UTC
582    * (<code>time</code>) to time fields
583    * (<code>fields</code>). Override this method if you write your
584    * own Calendar.
585    */
586   protected abstract void computeFields();
587
588   /**
589    * Converts the time represented by this object to a
590    * <code>Date</code>-Object.
591    * @return the Date.
592    */
593   public final Date getTime()
594   {
595     if (! isTimeSet)
596       computeTime();
597     return new Date(time);
598   }
599
600   /**
601    * Sets this Calendar's time to the given Date.  All time fields
602    * are invalidated by this method.
603    */
604   public final void setTime(Date date)
605   {
606     setTimeInMillis(date.getTime());
607   }
608
609   /**
610    * Returns the time represented by this Calendar.
611    * @return the time in milliseconds since the epoch.
612    * @specnote This was made public in 1.4.
613    */
614   public long getTimeInMillis()
615   {
616     if (! isTimeSet)
617       computeTime();
618     return time;
619   }
620
621   /**
622    * Sets this Calendar's time to the given Time.  All time fields
623    * are invalidated by this method.
624    * @param time the time in milliseconds since the epoch
625    * @specnote This was made public in 1.4.
626    */
627   public void setTimeInMillis(long time)
628   {
629     clear();
630     this.time = time;
631     isTimeSet = true;
632     computeFields();
633   }
634
635   /**
636    * Gets the value of the specified field.  They are recomputed
637    * if they are invalid.
638    * @param field the time field. One of the time field constants.
639    * @return the value of the specified field
640    * @throws ArrayIndexOutOfBoundsException if the field is outside
641    *         the valid range.  The value of field must be >= 0 and
642    *         <= <code>FIELD_COUNT</code>.
643    * @specnote Not final since JDK 1.4
644    */
645   public int get(int field)
646   {
647     // If the requested field is invalid, force all fields to be recomputed.
648     if (! isSet[field])
649       areFieldsSet = false;
650     complete();
651     return fields[field];
652   }
653
654   /**
655    * Gets the value of the specified field. This method doesn't
656    * recompute the fields, if they are invalid.
657    * @param field the time field. One of the time field constants.
658    * @return the value of the specified field, undefined if
659    * <code>areFieldsSet</code> or <code>isSet[field]</code> is false.
660    * @throws ArrayIndexOutOfBoundsException if the field is outside
661    *         the valid range.  The value of field must be >= 0 and
662    *         <= <code>FIELD_COUNT</code>.
663    */
664   protected final int internalGet(int field)
665   {
666     return fields[field];
667   }
668
669   /**
670    * Sets the time field with the given value.  This does invalidate
671    * the time in milliseconds.
672    * @param field the time field. One of the time field constants
673    * @param value the value to be set.
674    * @throws ArrayIndexOutOfBoundsException if field is outside
675    *         the valid range.  The value of field must be >= 0 and
676    *         <= <code>FIELD_COUNT</code>.
677    * @specnote Not final since JDK 1.4
678    */
679   public void set(int field, int value)
680   {
681     if (isTimeSet)
682       for (int i = 0; i < FIELD_COUNT; i++)
683         isSet[i] = false;
684     isTimeSet = false;
685     fields[field] = value;
686     isSet[field] = true;
687
688     // The five valid date patterns, in order of priority
689     // 1  YEAR + MONTH + DAY_OF_MONTH
690     // 2  YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
691     // 3  YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
692     // 4  YEAR + DAY_OF_YEAR
693     // 5  YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
694     switch (field)
695       {
696       case MONTH: // pattern 1,2 or 3
697         isSet[DAY_OF_YEAR] = false;
698         isSet[WEEK_OF_YEAR] = false;
699         break;
700       case DAY_OF_MONTH: // pattern 1
701         isSet[YEAR] = true;
702         isSet[MONTH] = true;
703         isSet[WEEK_OF_MONTH] = true;
704         isSet[DAY_OF_WEEK] = false;
705         isSet[DAY_OF_WEEK_IN_MONTH] = false;
706         isSet[DAY_OF_YEAR] = false;
707         isSet[WEEK_OF_YEAR] = false;
708         break;
709       case WEEK_OF_MONTH: // pattern 2
710         if (! isSet[DAY_OF_WEEK])
711           fields[DAY_OF_WEEK] = getFirstDayOfWeek();
712         isSet[YEAR] = true;
713         isSet[MONTH] = true;
714         isSet[DAY_OF_WEEK] = true;
715         isSet[DAY_OF_MONTH] = false;
716         isSet[DAY_OF_WEEK_IN_MONTH] = false;
717         isSet[DAY_OF_YEAR] = false;
718         isSet[WEEK_OF_YEAR] = false;
719         break;
720       case DAY_OF_WEEK_IN_MONTH: // pattern 3
721         if (! isSet[DAY_OF_WEEK])
722           fields[DAY_OF_WEEK] = getFirstDayOfWeek();
723         isSet[YEAR] = true;
724         isSet[MONTH] = true;
725         isSet[DAY_OF_WEEK] = true;
726         isSet[DAY_OF_YEAR] = false;
727         isSet[DAY_OF_MONTH] = false;
728         isSet[WEEK_OF_MONTH] = false;
729         isSet[WEEK_OF_YEAR] = false;
730         break;
731       case DAY_OF_YEAR: // pattern 4
732         isSet[YEAR] = true;
733         isSet[MONTH] = false;
734         isSet[WEEK_OF_MONTH] = false;
735         isSet[DAY_OF_MONTH] = false;
736         isSet[DAY_OF_WEEK] = false;
737         isSet[WEEK_OF_YEAR] = false;
738         isSet[DAY_OF_WEEK_IN_MONTH] = false;
739         break;
740       case WEEK_OF_YEAR: // pattern 5
741         if (! isSet[DAY_OF_WEEK])
742           fields[DAY_OF_WEEK] = getFirstDayOfWeek();
743         isSet[YEAR] = true;
744         isSet[DAY_OF_WEEK] = true;
745         isSet[MONTH] = false;
746         isSet[DAY_OF_MONTH] = false;
747         isSet[WEEK_OF_MONTH] = false;
748         isSet[DAY_OF_YEAR] = false;
749         isSet[DAY_OF_WEEK_IN_MONTH] = false;
750         break;
751       case AM_PM:
752         isSet[HOUR] = true;
753         isSet[HOUR_OF_DAY] = false;
754         break;
755       case HOUR_OF_DAY:
756         isSet[AM_PM] = false;
757         isSet[HOUR] = false;
758         break;
759       case HOUR:
760         isSet[AM_PM] = true;
761         isSet[HOUR_OF_DAY] = false;
762         break;
763       case DST_OFFSET:
764         explicitDSTOffset = true;
765       }
766
767     // May have crossed over a DST boundary.
768     if (! explicitDSTOffset && (field != DST_OFFSET && field != ZONE_OFFSET))
769       isSet[DST_OFFSET] = false;
770   }
771
772   /**
773    * Sets the fields for year, month, and date
774    * @param year the year.
775    * @param month the month, one of the constants JANUARY..UNDICEMBER.
776    * @param date the day of the month
777    */
778   public final void set(int year, int month, int date)
779   {
780     isTimeSet = false;
781     fields[YEAR] = year;
782     fields[MONTH] = month;
783     fields[DATE] = date;
784     isSet[YEAR] = isSet[MONTH] = isSet[DATE] = true;
785     isSet[WEEK_OF_YEAR] = false;
786     isSet[DAY_OF_YEAR] = false;
787     isSet[WEEK_OF_MONTH] = false;
788     isSet[DAY_OF_WEEK] = false;
789     isSet[DAY_OF_WEEK_IN_MONTH] = false;
790     isSet[ERA] = false;
791
792     if (! explicitDSTOffset)
793       isSet[DST_OFFSET] = false; // May have crossed a DST boundary.
794   }
795
796   /**
797    * Sets the fields for year, month, date, hour, and minute
798    * @param year the year.
799    * @param month the month, one of the constants JANUARY..UNDICEMBER.
800    * @param date the day of the month
801    * @param hour the hour of day.
802    * @param minute the minute.
803    */
804   public final void set(int year, int month, int date, int hour, int minute)
805   {
806     set(year, month, date);
807     fields[HOUR_OF_DAY] = hour;
808     fields[MINUTE] = minute;
809     isSet[HOUR_OF_DAY] = isSet[MINUTE] = true;
810     isSet[AM_PM] = false;
811     isSet[HOUR] = false;
812   }
813
814   /**
815    * Sets the fields for year, month, date, hour, and minute
816    * @param year the year.
817    * @param month the month, one of the constants JANUARY..UNDICEMBER.
818    * @param date the day of the month
819    * @param hour the hour of day.
820    * @param minute the minute.
821    * @param second the second.
822    */
823   public final void set(int year, int month, int date, int hour, int minute,
824                         int second)
825   {
826     set(year, month, date, hour, minute);
827     fields[SECOND] = second;
828     isSet[SECOND] = true;
829   }
830
831   /**
832    * Clears the values of all the time fields.
833    */
834   public final void clear()
835   {
836     isTimeSet = false;
837     areFieldsSet = false;
838     int zoneOffs = zone.getRawOffset();
839     int[] tempFields = 
840                        {
841                          1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0,
842                          0, 0, zoneOffs, 0
843                        };
844     fields = tempFields;
845     for (int i = 0; i < FIELD_COUNT; i++)
846       isSet[i] = false;
847   }
848
849   /**
850    * Clears the values of the specified time field.
851    * @param field the time field. One of the time field constants.
852    * @throws ArrayIndexOutOfBoundsException if field is outside
853    *         the valid range.  The value of field must be >= 0 and
854    *         <= <code>FIELD_COUNT</code>.
855    */
856   public final void clear(int field)
857   {
858     int[] tempFields = 
859                        {
860                          1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0,
861                          0, 0, zone.getRawOffset(), 0
862                        };
863     isTimeSet = false;
864     areFieldsSet = false;
865     isSet[field] = false;
866     fields[field] = tempFields[field];
867   }
868
869   /**
870    * Determines if the specified field has a valid value.
871    * @return true if the specified field has a value.
872    * @throws ArrayIndexOutOfBoundsException if the field is outside
873    *         the valid range.  The value of field must be >= 0 and
874    *         <= <code>FIELD_COUNT</code>.
875    */
876   public final boolean isSet(int field)
877   {
878     return isSet[field];
879   }
880
881   /**
882    * Fills any unset fields in the time field list
883    */
884   protected void complete()
885   {
886     if (! isTimeSet)
887       computeTime();
888     if (! areFieldsSet)
889       computeFields();
890   }
891
892   /**
893    * Compares the given calendar with this.
894    * @param o the object to that we should compare.
895    * @return true, if the given object is a calendar, that represents
896    * the same time (but doesn't necessary have the same fields).
897    */
898   public boolean equals(Object o)
899   {
900     if (! (o instanceof Calendar))
901       return false;
902     Calendar cal = (Calendar) o;
903     if (getTimeInMillis() == ((Calendar) o).getTimeInMillis()
904         && cal.getFirstDayOfWeek() == getFirstDayOfWeek()
905         && cal.isLenient() == isLenient()
906         && cal.getMinimalDaysInFirstWeek() == getMinimalDaysInFirstWeek())
907       {
908         TimeZone self = getTimeZone();
909         TimeZone oth = cal.getTimeZone();
910         return self == null ? oth == null : self.equals(oth);
911       }
912     return false;
913   }
914
915   /**
916    * Returns a hash code for this calendar.
917    * @return a hash code, which fullfits the general contract of
918    * <code>hashCode()</code>
919    */
920   public int hashCode()
921   {
922     long time = getTimeInMillis();
923     int val = (int) ((time & 0xffffffffL) ^ (time >> 32));
924     val += (getFirstDayOfWeek() + (isLenient() ? 1230 : 1237)
925             + getMinimalDaysInFirstWeek());
926     TimeZone self = getTimeZone();
927     if (self != null)
928       val ^= self.hashCode();
929     return val;
930   }
931
932   /**
933    * Compares the given calendar with this.
934    * @param o the object to that we should compare.
935    * @return true, if the given object is a calendar, and this calendar
936    * represents a smaller time than the calendar o.
937    * @exception ClassCastException if o is not an calendar.
938    * @since JDK1.2 you don't need to override this method
939    */
940   public boolean before(Object o)
941   {
942     return getTimeInMillis() < ((Calendar) o).getTimeInMillis();
943   }
944
945   /**
946    * Compares the given calendar with this.
947    * @param o the object to that we should compare.
948    * @return true, if the given object is a calendar, and this calendar
949    * represents a bigger time than the calendar o.
950    * @exception ClassCastException if o is not an calendar.
951    * @since JDK1.2 you don't need to override this method
952    */
953   public boolean after(Object o)
954   {
955     return getTimeInMillis() > ((Calendar) o).getTimeInMillis();
956   }
957
958   /**
959    * Adds the specified amount of time to the given time field.  The
960    * amount may be negative to subtract the time.  If the field overflows
961    * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
962    * @param field the time field. One of the time field constants.
963    * @param amount the amount of time.
964    * @throws ArrayIndexOutOfBoundsException if the field is outside
965    *         the valid range.  The value of field must be >= 0 and
966    *         <= <code>FIELD_COUNT</code>.
967    */
968   public abstract void add(int field, int amount);
969
970   /**
971    * Rolls the specified time field up or down.  This means add one
972    * to the specified field, but don't change the other fields.  If
973    * the maximum for this field is reached, start over with the
974    * minimum value.  <br>
975    *
976    * <strong>Note:</strong> There may be situation, where the other
977    * fields must be changed, e.g rolling the month on May, 31.
978    * The date June, 31 is automatically converted to July, 1.
979    * @param field the time field. One of the time field constants.
980    * @param up the direction, true for up, false for down.
981    * @throws ArrayIndexOutOfBoundsException if the field is outside
982    *         the valid range.  The value of field must be >= 0 and
983    *         <= <code>FIELD_COUNT</code>.
984    */
985   public abstract void roll(int field, boolean up);
986
987   /**
988    * Rolls up or down the specified time field by the given amount.
989    * A negative amount rolls down.  The default implementation is
990    * call <code>roll(int, boolean)</code> for the specified amount.
991    *
992    * Subclasses should override this method to do more intuitiv things.
993    *
994    * @param field the time field. One of the time field constants.
995    * @param amount the amount to roll by, positive for rolling up,
996    * negative for rolling down.
997    * @throws ArrayIndexOutOfBoundsException if the field is outside
998    *         the valid range.  The value of field must be >= 0 and
999    *         <= <code>FIELD_COUNT</code>.
1000    * @since JDK1.2
1001    */
1002   public void roll(int field, int amount)
1003   {
1004     while (amount > 0)
1005       {
1006         roll(field, true);
1007         amount--;
1008       }
1009     while (amount < 0)
1010       {
1011         roll(field, false);
1012         amount++;
1013       }
1014   }
1015
1016   /**
1017    * Sets the time zone to the specified value.
1018    * @param zone the new time zone
1019    */
1020   public void setTimeZone(TimeZone zone)
1021   {
1022     this.zone = zone;
1023   }
1024
1025   /**
1026    * Gets the time zone of this calendar
1027    * @return the current time zone.
1028    */
1029   public TimeZone getTimeZone()
1030   {
1031     return zone;
1032   }
1033
1034   /**
1035    * Specifies if the date/time interpretation should be lenient.
1036    * If the flag is set, a date such as "February 30, 1996" will be
1037    * treated as the 29th day after the February 1.  If this flag
1038    * is false, such dates will cause an exception.
1039    * @param lenient true, if the date should be interpreted linient,
1040    * false if it should be interpreted strict.
1041    */
1042   public void setLenient(boolean lenient)
1043   {
1044     this.lenient = lenient;
1045   }
1046
1047   /**
1048    * Tells if the date/time interpretation is lenient.
1049    * @return true, if the date should be interpreted linient,
1050    * false if it should be interpreted strict.
1051    */
1052   public boolean isLenient()
1053   {
1054     return lenient;
1055   }
1056
1057   /**
1058    * Sets what the first day of week is.  This is used for
1059    * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
1060    * @param value the first day of week.  One of SUNDAY to SATURDAY.
1061    */
1062   public void setFirstDayOfWeek(int value)
1063   {
1064     firstDayOfWeek = value;
1065   }
1066
1067   /**
1068    * Gets what the first day of week is.  This is used for
1069    * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
1070    * @return the first day of week.  One of SUNDAY to SATURDAY.
1071    */
1072   public int getFirstDayOfWeek()
1073   {
1074     return firstDayOfWeek;
1075   }
1076
1077   /**
1078    * Sets how many days are required in the first week of the year.
1079    * If the first day of the year should be the first week you should
1080    * set this value to 1.  If the first week must be a full week, set
1081    * it to 7.
1082    * @param value the minimal days required in the first week.
1083    */
1084   public void setMinimalDaysInFirstWeek(int value)
1085   {
1086     minimalDaysInFirstWeek = value;
1087   }
1088
1089   /**
1090    * Gets how many days are required in the first week of the year.
1091    * @return the minimal days required in the first week.
1092    * @see #setMinimalDaysInFirstWeek
1093    */
1094   public int getMinimalDaysInFirstWeek()
1095   {
1096     return minimalDaysInFirstWeek;
1097   }
1098
1099   /**
1100    * Gets the smallest value that is allowed for the specified field.
1101    * @param field the time field. One of the time field constants.
1102    * @return the smallest value.
1103    */
1104   public abstract int getMinimum(int field);
1105
1106   /**
1107    * Gets the biggest value that is allowed for the specified field.
1108    * @param field the time field. One of the time field constants.
1109    * @return the biggest value.
1110    */
1111   public abstract int getMaximum(int field);
1112
1113   /**
1114    * Gets the greatest minimum value that is allowed for the specified field.
1115    * @param field the time field. One of the time field constants.
1116    * @return the greatest minimum value.
1117    */
1118   public abstract int getGreatestMinimum(int field);
1119
1120   /**
1121    * Gets the smallest maximum value that is allowed for the
1122    * specified field.  For example this is 28 for DAY_OF_MONTH.
1123    * @param field the time field. One of the time field constants.
1124    * @return the least maximum value.
1125    */
1126   public abstract int getLeastMaximum(int field);
1127
1128   /**
1129    * Gets the actual minimum value that is allowed for the specified field.
1130    * This value is dependent on the values of the other fields.
1131    * @param field the time field. One of the time field constants.
1132    * @return the actual minimum value.
1133    * @throws ArrayIndexOutOfBoundsException if the field is outside
1134    *         the valid range.  The value of field must be >= 0 and
1135    *         <= <code>FIELD_COUNT</code>.
1136    * @since jdk1.2
1137    */
1138   public int getActualMinimum(int field)
1139   {
1140     Calendar tmp = (Calendar) clone(); // To avoid restoring state
1141     int min = tmp.getGreatestMinimum(field);
1142     int end = tmp.getMinimum(field);
1143     tmp.set(field, min);
1144     for (; min > end; min--)
1145       {
1146         tmp.add(field, -1); // Try to get smaller
1147         if (tmp.get(field) != min - 1)
1148           break; // Done if not successful
1149       }
1150     return min;
1151   }
1152
1153   /**
1154    * Gets the actual maximum value that is allowed for the specified field.
1155    * This value is dependent on the values of the other fields.
1156    * @param field the time field. One of the time field constants.
1157    * @return the actual maximum value.
1158    * @throws ArrayIndexOutOfBoundsException if the field is outside
1159    *         the valid range.  The value of field must be >= 0 and
1160    *         <= <code>FIELD_COUNT</code>.
1161    * @since jdk1.2
1162    */
1163   public int getActualMaximum(int field)
1164   {
1165     Calendar tmp = (Calendar) clone(); // To avoid restoring state
1166     int max = tmp.getLeastMaximum(field);
1167     int end = tmp.getMaximum(field);
1168     tmp.set(field, max);
1169     for (; max < end; max++)
1170       {
1171         tmp.add(field, 1);
1172         if (tmp.get(field) != max + 1)
1173           break;
1174       }
1175     return max;
1176   }
1177
1178   /**
1179    * Return a clone of this object.
1180    */
1181   public Object clone()
1182   {
1183     try
1184       {
1185         Calendar cal = (Calendar) super.clone();
1186         cal.fields = (int[]) fields.clone();
1187         cal.isSet = (boolean[]) isSet.clone();
1188         return cal;
1189       }
1190     catch (CloneNotSupportedException ex)
1191       {
1192         return null;
1193       }
1194   }
1195
1196   private static final String[] fieldNames = 
1197                                              {
1198                                                ",ERA=", ",YEAR=", ",MONTH=",
1199                                                ",WEEK_OF_YEAR=",
1200                                                ",WEEK_OF_MONTH=",
1201                                                ",DAY_OF_MONTH=",
1202                                                ",DAY_OF_YEAR=", ",DAY_OF_WEEK=",
1203                                                ",DAY_OF_WEEK_IN_MONTH=",
1204                                                ",AM_PM=", ",HOUR=",
1205                                                ",HOUR_OF_DAY=", ",MINUTE=",
1206                                                ",SECOND=", ",MILLISECOND=",
1207                                                ",ZONE_OFFSET=", ",DST_OFFSET="
1208                                              };
1209
1210   /**
1211    * Returns a string representation of this object.  It is mainly
1212    * for debugging purposes and its content is implementation
1213    * specific.
1214    */
1215   public String toString()
1216   {
1217     StringBuffer sb = new StringBuffer();
1218     sb.append(getClass().getName()).append('[');
1219     sb.append("time=");
1220     if (isTimeSet)
1221       sb.append(time);
1222     else
1223       sb.append("?");
1224     sb.append(",zone=" + zone);
1225     sb.append(",areFieldsSet=" + areFieldsSet);
1226     for (int i = 0; i < FIELD_COUNT; i++)
1227       {
1228         sb.append(fieldNames[i]);
1229         if (isSet[i])
1230           sb.append(fields[i]);
1231         else
1232           sb.append("?");
1233       }
1234     sb.append(",lenient=").append(lenient);
1235     sb.append(",firstDayOfWeek=").append(firstDayOfWeek);
1236     sb.append(",minimalDaysInFirstWeek=").append(minimalDaysInFirstWeek);
1237     sb.append("]");
1238     return sb.toString();
1239   }
1240
1241   /**
1242    * Saves the state of the object to the stream.  Ideally we would
1243    * only write the time field, but we need to be compatible with
1244    * earlier versions. <br>
1245    *
1246    * This doesn't write the JDK1.1 field nextStamp to the stream, as
1247    * I don't know what it is good for, and because the documentation
1248    * says, that it could be omitted.  */
1249   private void writeObject(ObjectOutputStream stream) throws IOException
1250   {
1251     if (! isTimeSet)
1252       computeTime();
1253     stream.defaultWriteObject();
1254   }
1255
1256   /**
1257    * Reads the object back from stream (deserialization).
1258    */
1259   private void readObject(ObjectInputStream stream)
1260     throws IOException, ClassNotFoundException
1261   {
1262     stream.defaultReadObject();
1263     if (! isTimeSet)
1264       computeTime();
1265
1266     if (serialVersionOnStream > 1)
1267       {
1268         // This is my interpretation of the serial number:
1269         // Sun wants to remove all fields from the stream someday
1270         // and will then increase the serialVersion number again.
1271         // We prepare to be compatible.
1272         fields = new int[FIELD_COUNT];
1273         isSet = new boolean[FIELD_COUNT];
1274         areFieldsSet = false;
1275       }
1276   }
1277 }