Source for java.util.Calendar

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