Source for java.util.Calendar

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