Source for java.text.DateFormat

   1: /* DateFormat.java -- Class for formatting/parsing date/times
   2:    Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005
   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.text;
  41: 
  42: import gnu.java.locale.LocaleHelper;
  43: 
  44: import java.text.spi.DateFormatProvider;
  45: 
  46: import java.io.InvalidObjectException;
  47: import java.util.Calendar;
  48: import java.util.Date;
  49: import java.util.Locale;
  50: import java.util.MissingResourceException;
  51: import java.util.ResourceBundle;
  52: import java.util.ServiceLoader;
  53: import java.util.TimeZone;
  54: 
  55: /**
  56:  * @author Per Bothner (bothner@cygnus.com)
  57:  * @date October 25, 1998.
  58:  */
  59: /* Written using "Java Class Libraries", 2nd edition, plus online
  60:  * API docs for JDK 1.2 beta from http://www.javasoft.com.
  61:  * Status:  Mostly complete; search for FIXME to see omissions.
  62:  */
  63: 
  64: public abstract class DateFormat extends Format implements Cloneable
  65: {
  66:   private static final long serialVersionUID = 7218322306649953788L;
  67: 
  68:   // Names fixed by serialization spec.
  69:   protected Calendar calendar;
  70:   protected NumberFormat numberFormat;
  71: 
  72:   // (Values determined using a test program.)
  73:   public static final int FULL = 0;
  74:   public static final int LONG = 1;
  75:   public static final int MEDIUM = 2;
  76:   public static final int SHORT = 3;
  77:   public static final int DEFAULT = MEDIUM;
  78: 
  79:   /* These constants need to have these exact values.  They
  80:    * correspond to index positions within the localPatternChars
  81:    * string for a given locale.  Each locale may specify its
  82:    * own character for a particular field, but the position
  83:    * of these characters must correspond to an appropriate field
  84:    * number (as listed below), in order for their meaning to
  85:    * be determined.  For example, the US locale uses
  86:    * the string "GyMdkHmsSEDFwWahKzYeugAZ", where 'G' is the character
  87:    * for era, 'y' for year, and so on down to 'Z' for time zone.
  88:    */
  89:   /**
  90:    * Represents the position of the era
  91:    * pattern character in the array of
  92:    * localized pattern characters. 
  93:    * For example, 'AD' is an era used
  94:    * in the Gregorian calendar system.
  95:    * In the U.S. locale, this is 'G'.
  96:    */  
  97:   public static final int ERA_FIELD = 0;
  98:   /**
  99:    * Represents the position of the year
 100:    * pattern character in the array of
 101:    * localized pattern characters.
 102:    * In the U.S. locale, this is 'y'.
 103:    */
 104:   public static final int YEAR_FIELD = 1;
 105:   /**
 106:    * Represents the position of the month
 107:    * pattern character in the array of
 108:    * localized pattern characters.
 109:    * In the U.S. locale, this is 'M'.
 110:    */
 111:   public static final int MONTH_FIELD = 2;
 112:   /**
 113:    * Represents the position of the date
 114:    * or day of the month pattern character
 115:    * in the array of localized pattern
 116:    * characters.  In the U.S. locale,
 117:    * this is 'd'.
 118:    */
 119:   public static final int DATE_FIELD = 3;
 120:   /**
 121:    * Represents the position of the 24
 122:    * hour pattern character in the array of
 123:    * localized pattern characters.
 124:    * In the U.S. locale, this is 'k'.
 125:    * This field numbers hours from 1 to 24.
 126:    */
 127:   public static final int HOUR_OF_DAY1_FIELD = 4;
 128:   /**
 129:    * Represents the position of the 24
 130:    * hour pattern character in the array of
 131:    * localized pattern characters.
 132:    * In the U.S. locale, this is 'H'.
 133:    * This field numbers hours from 0 to 23.
 134:    */
 135:   public static final int HOUR_OF_DAY0_FIELD = 5;
 136:   /**
 137:    * Represents the position of the minute
 138:    * pattern character in the array of
 139:    * localized pattern characters.
 140:    * In the U.S. locale, this is 'm'.
 141:    */
 142:   public static final int MINUTE_FIELD = 6;
 143:   /**
 144:    * Represents the position of the second
 145:    * pattern character in the array of
 146:    * localized pattern characters.
 147:    * In the U.S. locale, this is 's'.
 148:    */
 149:   public static final int SECOND_FIELD = 7;
 150:   /**
 151:    * Represents the position of the millisecond
 152:    * pattern character in the array of
 153:    * localized pattern characters.
 154:    * In the U.S. locale, this is 'S'.
 155:    */
 156:   public static final int MILLISECOND_FIELD = 8;
 157:   /**
 158:    * Represents the position of the day of the
 159:    * week pattern character in the array of
 160:    * localized pattern characters.
 161:    * In the U.S. locale, this is 'E'.
 162:    */
 163:   public static final int DAY_OF_WEEK_FIELD = 9;
 164:   /**
 165:    * Represents the position of the day of the
 166:    * year pattern character in the array of
 167:    * localized pattern characters.
 168:    * In the U.S. locale, this is 'D'.
 169:    */
 170:   public static final int DAY_OF_YEAR_FIELD = 10;
 171:   /**
 172:    * Represents the position of the day of the
 173:    * week in the month pattern character in the
 174:    * array of localized pattern characters.
 175:    * In the U.S. locale, this is 'F'.
 176:    */
 177:   public static final int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
 178:   /**
 179:    * Represents the position of the week of the
 180:    * year pattern character in the array of
 181:    * localized pattern characters.
 182:    * In the U.S. locale, this is 'w'.
 183:    */
 184:   public static final int WEEK_OF_YEAR_FIELD = 12;
 185:   /**
 186:    * Represents the position of the week of the
 187:    * month pattern character in the array of
 188:    * localized pattern characters.
 189:    * In the U.S. locale, this is 'W'.
 190:    */
 191:   public static final int WEEK_OF_MONTH_FIELD = 13;
 192:   /**
 193:    * Represents the position of the am/pm
 194:    * pattern character in the array of
 195:    * localized pattern characters.
 196:    * In the U.S. locale, this is 'a'.
 197:    */
 198:   public static final int AM_PM_FIELD = 14;
 199:   /**
 200:    * Represents the position of the 12 
 201:    * hour pattern character in the array of
 202:    * localized pattern characters.
 203:    * In the U.S. locale, this is 'h'.
 204:    * This field numbers hours from 1 to 12.
 205:    */
 206:   public static final int HOUR1_FIELD = 15;
 207:   /**
 208:    * Represents the position of the 12 
 209:    * hour pattern character in the array of
 210:    * localized pattern characters.
 211:    * In the U.S. locale, this is 'K'.
 212:    * This field numbers hours from 0 to 11.
 213:    */
 214:   public static final int HOUR0_FIELD = 16;
 215:   /**
 216:    * Represents the position of the generic
 217:    * timezone pattern character in the array of
 218:    * localized pattern characters.
 219:    * In the U.S. locale, this is 'z'.
 220:    */
 221:   public static final int TIMEZONE_FIELD = 17;
 222:   /**
 223:    * Represents the position of the ISO year
 224:    * pattern character in the array of
 225:    * localized pattern characters.
 226:    * In the U.S. locale, this is 'Y'.
 227:    * This is a GNU extension in accordance with
 228:    * the CLDR data used.  This value may
 229:    * differ from the normal year value.
 230:    */
 231:   public static final int ISO_YEAR_FIELD = 18;
 232:   /**
 233:    * Represents the position of the localized
 234:    * day of the week pattern character in the
 235:    * array of localized pattern characters.
 236:    * In the U.S. locale, this is 'e'.
 237:    * This is a GNU extension in accordance with
 238:    * the CLDR data used.  This value only
 239:    * differs from the day of the week with
 240:    * numeric formatting, in which case the
 241:    * locale's first day of the week is used.
 242:    */
 243:   public static final int LOCALIZED_DAY_OF_WEEK_FIELD = 19;
 244:   /**
 245:    * Represents the position of the extended year
 246:    * pattern character in the array of
 247:    * localized pattern characters.
 248:    * In the U.S. locale, this is 'u'.
 249:    * This is a GNU extension in accordance with
 250:    * the CLDR data used.  This value modifies
 251:    * the year value, so as to incorporate the era.
 252:    * For example, in the Gregorian calendar system,
 253:    * the extended year is negative instead of being
 254:    * marked as BC.
 255:    */
 256:   public static final int EXTENDED_YEAR_FIELD = 20;
 257:   /**
 258:    * Represents the position of the modified Julian
 259:    * day pattern character in the array of
 260:    * localized pattern characters.
 261:    * In the U.S. locale, this is 'g'.
 262:    * This is a GNU extension in accordance with
 263:    * the CLDR data used.  This value differs
 264:    * from the standard Julian day in that days
 265:    * are marked from midnight onwards rather than
 266:    * noon, and the local time zone affects the value.
 267:    * In simple terms, it can be thought of as all
 268:    * the date fields represented as a single number.
 269:    */
 270:   public static final int MODIFIED_JULIAN_DAY_FIELD = 21;
 271:   /**
 272:    * Represents the position of the millisecond
 273:    * in the day pattern character in the array of
 274:    * localized pattern characters.
 275:    * In the U.S. locale, this is 'A'.
 276:    * This is a GNU extension in accordance with
 277:    * the CLDR data used.  This value represents
 278:    * all the time fields (excluding the time zone)
 279:    * numerically, giving the number of milliseconds
 280:    * into the day (e.g. 10 in the morning would
 281:    * be 10 * 60 * 60 * 1000).  Any daylight savings
 282:    * offset also affects this value.
 283:    */
 284:   public static final int MILLISECOND_IN_DAY_FIELD = 22;
 285:   /**
 286:    * Represents the position of the RFC822
 287:    * timezone pattern character in the array of
 288:    * localized pattern characters.
 289:    * In the U.S. locale, this is 'Z'.
 290:    * This is a GNU extension in accordance with
 291:    * the CLDR data used.  The value is the offset
 292:    * of the current time from GMT e.g. -0500 would
 293:    * be five hours prior to GMT.
 294:    */
 295:   public static final int RFC822_TIMEZONE_FIELD = 23;
 296: 
 297:   public static class Field extends Format.Field
 298:   {
 299:     static final long serialVersionUID = 7441350119349544720L;
 300:     
 301:     private int calendarField;
 302: 
 303:     public static final DateFormat.Field ERA
 304:     = new Field("era", Calendar.ERA);
 305:     public static final DateFormat.Field YEAR
 306:     = new Field("year", Calendar.YEAR);
 307:     public static final DateFormat.Field MONTH
 308:     = new Field("month", Calendar.MONTH);
 309:     public static final DateFormat.Field DAY_OF_MONTH
 310:     = new Field("day of month", Calendar.DAY_OF_MONTH);
 311:     public static final DateFormat.Field HOUR_OF_DAY1
 312:     = new Field("hour of day 1", Calendar.HOUR_OF_DAY);
 313:     public static final DateFormat.Field HOUR_OF_DAY0
 314:     = new Field("hour of day 0", Calendar.HOUR_OF_DAY);
 315:     public static final DateFormat.Field MINUTE
 316:     = new Field("minute", Calendar.MINUTE);
 317:     public static final DateFormat.Field SECOND
 318:     = new Field("second", Calendar.SECOND);
 319:     public static final DateFormat.Field MILLISECOND
 320:     = new Field("millisecond", Calendar.MILLISECOND);
 321:     public static final DateFormat.Field DAY_OF_WEEK
 322:     = new Field("day of week", Calendar.DAY_OF_WEEK);
 323:     public static final DateFormat.Field DAY_OF_YEAR
 324:     = new Field("day of year", Calendar.DAY_OF_YEAR);
 325:     public static final DateFormat.Field DAY_OF_WEEK_IN_MONTH
 326:     = new Field("day of week in month", Calendar.DAY_OF_WEEK_IN_MONTH);
 327:     public static final DateFormat.Field WEEK_OF_YEAR
 328:     = new Field("week of year", Calendar.WEEK_OF_YEAR);
 329:     public static final DateFormat.Field WEEK_OF_MONTH
 330:     = new Field("week of month", Calendar.WEEK_OF_MONTH);
 331:     public static final DateFormat.Field AM_PM
 332:     = new Field("am/pm", Calendar.AM_PM);
 333:     public static final DateFormat.Field HOUR1
 334:     = new Field("hour1", Calendar.HOUR);
 335:     public static final DateFormat.Field HOUR0
 336:     = new Field("hour0", Calendar.HOUR);
 337:     public static final DateFormat.Field TIME_ZONE
 338:     = new Field("timezone", Calendar.ZONE_OFFSET);
 339:     public static final DateFormat.Field ISO_YEAR
 340:     = new Field("iso year", Calendar.YEAR);
 341:     public static final DateFormat.Field LOCALIZED_DAY_OF_WEEK
 342:     = new Field("localized day of week", Calendar.DAY_OF_WEEK);
 343:     public static final DateFormat.Field EXTENDED_YEAR
 344:       = new Field("extended year", Calendar.YEAR);
 345:     public static final DateFormat.Field MODIFIED_JULIAN_DAY
 346:     = new Field("julian day", -1);
 347:     public static final DateFormat.Field MILLISECOND_IN_DAY
 348:     = new Field("millisecond in day", -1);
 349:     public static final DateFormat.Field RFC822_TIME_ZONE
 350:     = new Field("rfc822 timezone", Calendar.ZONE_OFFSET);
 351: 
 352:     static final DateFormat.Field[] allFields =
 353:     {
 354:       ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY1,
 355:       HOUR_OF_DAY0, MINUTE, SECOND, MILLISECOND,
 356:       DAY_OF_WEEK, DAY_OF_YEAR, DAY_OF_WEEK_IN_MONTH,
 357:       WEEK_OF_YEAR, WEEK_OF_MONTH, AM_PM, HOUR1, HOUR0,
 358:       TIME_ZONE, ISO_YEAR, LOCALIZED_DAY_OF_WEEK,
 359:       EXTENDED_YEAR, MODIFIED_JULIAN_DAY, MILLISECOND_IN_DAY,
 360:       RFC822_TIME_ZONE
 361:     };
 362: 
 363:     // For deserialization
 364:     private Field()
 365:     {
 366:       super("");
 367:     }
 368: 
 369:     protected Field(String name, int calendarField)
 370:     {
 371:       super(name);
 372:       this.calendarField = calendarField;
 373:     }
 374:     
 375:     public int getCalendarField()
 376:     {
 377:       return calendarField;
 378:     }
 379: 
 380:     public static Field ofCalendarField(int calendarField)
 381:     {
 382:       if (calendarField >= allFields.length || calendarField < 0)
 383:     throw new IllegalArgumentException("no such calendar field ("
 384:                        + calendarField + ")");
 385:       
 386:       return allFields[calendarField];
 387:     }
 388:     
 389:     protected Object readResolve() throws InvalidObjectException
 390:     {
 391:       String s = getName();
 392: 
 393:       for (int i=0;i<allFields.length;i++)
 394:     if (s.equals(allFields[i].getName()))
 395:       return allFields[i];
 396:       
 397:       throw new InvalidObjectException("no such DateFormat field called " + s);
 398:     }
 399:   }
 400: 
 401:   /**
 402:    * This method initializes a new instance of <code>DateFormat</code>.
 403:    */
 404:   protected DateFormat ()
 405:   {
 406:   }
 407: 
 408:   /**
 409:    * This method tests this object for equality against the specified object.
 410:    * The two objects will be considered equal if an only if the specified
 411:    * object:
 412:    * <P>
 413:    * <ul>
 414:    * <li>Is not <code>null</code>.</li>
 415:    * <li>Is an instance of <code>DateFormat</code>.</li>
 416:    * <li>Has equal numberFormat field as this object.</li>
 417:    * <li>Has equal (Calendar) TimeZone rules as this object.</li>
 418:    * <li>Has equal (Calendar) isLenient results.</li> 
 419:    * <li>Has equal Calendar first day of week and minimal days in week
 420:    * values.</li>
 421:    * </ul>
 422:    * Note that not all properties of the Calendar are relevant for a
 423:    * DateFormat. For formatting only the fact whether or not the
 424:    * TimeZone has the same rules and whether the calendar is lenient
 425:    * and has the same week rules is compared for this implementation
 426:    * of equals. Other properties of the Calendar (such as the time)
 427:    * are not taken into account.
 428:    *
 429:    * @param obj The object to test for equality against.
 430:    * 
 431:    * @return <code>true</code> if the specified object is equal to this object,
 432:    * <code>false</code> otherwise.
 433:    */
 434:   public boolean equals (Object obj)
 435:   {
 436:     if (!(obj instanceof DateFormat))
 437:       return false;
 438: 
 439:     DateFormat d = (DateFormat) obj;
 440:     TimeZone tz = getTimeZone();
 441:     TimeZone tzd = d.getTimeZone();
 442:     if (tz.hasSameRules(tzd))
 443:       if (isLenient() == d.isLenient())
 444:     {
 445:       Calendar c = getCalendar();
 446:       Calendar cd = d.getCalendar();
 447:       if ((c == null && cd == null)
 448:           ||
 449:           (c.getFirstDayOfWeek() == cd.getFirstDayOfWeek()
 450:            &&
 451:            c.getMinimalDaysInFirstWeek()
 452:            == cd.getMinimalDaysInFirstWeek()))
 453:         return ((numberFormat == null && d.numberFormat == null)
 454:             || numberFormat.equals(d.numberFormat));
 455:     }
 456: 
 457:     return false;
 458:   }
 459: 
 460:   /**
 461:    * This method returns a copy of this object.
 462:    *
 463:    * @return A copy of this object.
 464:    */
 465:   public Object clone ()
 466:   {
 467:     // We know the superclass just call's Object's generic cloner.
 468:     return super.clone ();
 469:   }
 470: 
 471:   /**
 472:    * This method formats the specified <code>Object</code> into a date string
 473:    * and appends it to the specified <code>StringBuffer</code>.
 474:    * The specified object must be an instance of <code>Number</code> or
 475:    * <code>Date</code> or an <code>IllegalArgumentException</code> will be
 476:    * thrown.
 477:    *
 478:    * @param obj The <code>Object</code> to format.
 479:    * @param buf The <code>StringBuffer</code> to append the resultant
 480:    * <code>String</code> to.
 481:    * @param pos Is updated to the start and end index of the
 482:    * specified field.
 483:    *
 484:    * @return The <code>StringBuffer</code> supplied on input, with the
 485:    * formatted date/time appended.
 486:    */
 487:   public final StringBuffer format (Object obj,
 488:                     StringBuffer buf, FieldPosition pos)
 489:   {
 490:     if (obj instanceof Number)
 491:       obj = new Date(((Number) obj).longValue());
 492:     else if (! (obj instanceof Date))
 493:       throw new IllegalArgumentException
 494:     ("Cannot format given Object as a Date");
 495: 
 496:     return format ((Date) obj, buf, pos);
 497:   }
 498: 
 499:   /**  
 500:     * Formats the date argument according to the pattern specified. 
 501:     *
 502:     * @param date The formatted date.
 503:     */
 504:   public final String format (Date date)
 505:   {
 506:     StringBuffer sb = new StringBuffer ();
 507:     format (date, sb, new FieldPosition (MONTH_FIELD));
 508:     return sb.toString();
 509:   }
 510: 
 511:   /**
 512:    * This method formats a <code>Date</code> into a string and appends it
 513:    * to the specified <code>StringBuffer</code>.
 514:    *
 515:    * @param date The <code>Date</code> value to format.
 516:    * @param buf The <code>StringBuffer</code> to append the resultant
 517:    * <code>String</code> to.
 518:    * @param pos Is updated to the start and end index of the
 519:    * specified field.
 520:    *
 521:    * @return The <code>StringBuffer</code> supplied on input, with the
 522:    * formatted date/time appended.
 523:    */
 524:   public abstract StringBuffer format (Date date,
 525:                        StringBuffer buf, FieldPosition pos);
 526: 
 527:   /**
 528:    * This method returns a list of available locales supported by this
 529:    * class.
 530:    */
 531:   public static Locale[] getAvailableLocales()
 532:   {
 533:     return Locale.getAvailableLocales();
 534:   }
 535: 
 536:   /**
 537:     * This method returns the <code>Calendar</code> object being used by
 538:     * this object to parse/format datetimes.
 539:     *
 540:     * @return The <code>Calendar</code> being used by this object.
 541:     *
 542:     * @see java.util.Calendar
 543:     */
 544:   public Calendar getCalendar ()
 545:   {
 546:     return calendar;
 547:   }
 548: 
 549:   private static DateFormat computeInstance (int style, Locale loc,
 550:                                              boolean use_date, boolean use_time)
 551:   {
 552:     return computeInstance (style, style, loc, use_date, use_time);
 553:   }
 554: 
 555:   private static DateFormat computeInstance (int dateStyle, int timeStyle,
 556:                                              Locale loc, boolean use_date,
 557:                                              boolean use_time)
 558:     throws MissingResourceException
 559:   {
 560:     if (loc.equals(Locale.ROOT))
 561:       return computeDefault(dateStyle,timeStyle,use_date,use_time);
 562: 
 563:     ResourceBundle res =
 564:       ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
 565:                    loc, ClassLoader.getSystemClassLoader());
 566: 
 567:     String pattern = null;
 568:     if (use_date)
 569:       {
 570:     String name, def;
 571:     switch (dateStyle)
 572:       {
 573:       case FULL:
 574:         name = "fullDateFormat";
 575:         def = "EEEE MMMM d, yyyy G";
 576:         break;
 577:       case LONG:
 578:         name = "longDateFormat";
 579:         def = "MMMM d, yyyy";
 580:         break;
 581:       case MEDIUM:
 582:         name = "mediumDateFormat";
 583:         def = "d-MMM-yy";
 584:         break;
 585:       case SHORT:
 586:         name = "shortDateFormat";
 587:         def = "M/d/yy";
 588:         break;
 589:       default:
 590:         throw new IllegalArgumentException ();
 591:       }
 592:     try
 593:       {
 594:         pattern = res == null ? def : res.getString(name);
 595:       }
 596:     catch (MissingResourceException x)
 597:       {
 598:         pattern = def;
 599:       }
 600:       }
 601: 
 602:     if (use_time)
 603:       {
 604:     if (pattern == null)
 605:       pattern = "";
 606:     else
 607:       pattern += " ";
 608: 
 609:     String name, def;
 610:     switch (timeStyle)
 611:       {
 612:       case FULL:
 613:         name = "fullTimeFormat";
 614:         def = "h:mm:ss;S 'o''clock' a z";
 615:         break;
 616:       case LONG:
 617:         name = "longTimeFormat";
 618:         def = "h:mm:ss a z";
 619:         break;
 620:       case MEDIUM:
 621:         name = "mediumTimeFormat";
 622:         def = "h:mm:ss a";
 623:         break;
 624:       case SHORT:
 625:         name = "shortTimeFormat";
 626:         def = "h:mm a";
 627:         break;
 628:       default:
 629:         throw new IllegalArgumentException ();
 630:       }
 631: 
 632:     String s;
 633:     try
 634:       {
 635:         s = res == null ? def : res.getString(name);
 636:       }
 637:     catch (MissingResourceException x)
 638:       {
 639:         s = def;
 640:       }
 641:     pattern += s;
 642:       }
 643: 
 644:     return new SimpleDateFormat (pattern, loc);
 645:   }
 646: 
 647:   private static DateFormat computeDefault (int dateStyle, int timeStyle,
 648:                         boolean use_date, boolean use_time)
 649:   {
 650:     String pattern = null;
 651:     if (use_date)
 652:       {
 653:     switch (dateStyle)
 654:       {
 655:       case FULL:
 656:         pattern = "EEEE MMMM d, yyyy G";
 657:         break;
 658:       case LONG:
 659:         pattern = "MMMM d, yyyy";
 660:         break;
 661:       case MEDIUM:
 662:         pattern = "d-MMM-yy";
 663:         break;
 664:       case SHORT:
 665:         pattern = "M/d/yy";
 666:       default:
 667:         throw new IllegalArgumentException ();
 668:       }
 669:       }
 670:     
 671:     if (use_time)
 672:       {
 673:     if (pattern == null)
 674:       pattern = "";
 675:     else
 676:       pattern += " ";
 677: 
 678:     switch (timeStyle)
 679:       {
 680:       case FULL:
 681:         pattern += "h:mm:ss;S 'o''clock' a z";
 682:         break;
 683:       case LONG:
 684:         pattern += "h:mm:ss a z";
 685:         break;
 686:       case MEDIUM:
 687:         pattern += "h:mm:ss a";
 688:         break;
 689:       case SHORT:
 690:         pattern += "h:mm a";
 691:         break;
 692:       default:
 693:         throw new IllegalArgumentException ();
 694:       }
 695:       }
 696: 
 697:     return new SimpleDateFormat (pattern, Locale.ROOT);
 698:   }
 699: 
 700:  /**
 701:    * This method returns an instance of <code>DateFormat</code> that will
 702:    * format using the default formatting style for dates.
 703:    *
 704:    * @return A new <code>DateFormat</code> instance.
 705:    */
 706:   public static final DateFormat getDateInstance ()
 707:   {
 708:     return getDateInstance (DEFAULT, Locale.getDefault());
 709:   }
 710: 
 711:   /**
 712:    * This method returns an instance of <code>DateFormat</code> that will
 713:    * format using the specified formatting style for dates.
 714:    *
 715:    * @param style The type of formatting to perform. 
 716:    * 
 717:    * @return A new <code>DateFormat</code> instance.
 718:    */
 719:   public static final DateFormat getDateInstance (int style)
 720:   {
 721:     return getDateInstance (style, Locale.getDefault());
 722:   }
 723: 
 724:   /**
 725:    * This method returns an instance of <code>DateFormat</code> that will
 726:    * format using the specified formatting style for dates.  The specified
 727:    * localed will be used in place of the default.
 728:    *
 729:    * @param style The type of formatting to perform. 
 730:    * @param loc The desired locale.
 731:    * 
 732:    * @return A new <code>DateFormat</code> instance.
 733:    */
 734:   public static final DateFormat getDateInstance (int style, Locale loc)
 735:   {
 736:     try
 737:       {
 738:     return computeInstance (style, loc, true, false);
 739:       }
 740:     catch (MissingResourceException e)
 741:       {
 742:     for (DateFormatProvider p :
 743:            ServiceLoader.load(DateFormatProvider.class))
 744:       {
 745:         for (Locale l : p.getAvailableLocales())
 746:           {
 747:         if (l.equals(loc))
 748:           {
 749:             DateFormat df = p.getDateInstance(style, loc);
 750:             if (df != null)
 751:               return df;
 752:             break;
 753:           }
 754:           }
 755:       }
 756:     return getDateInstance(style,
 757:                    LocaleHelper.getFallbackLocale(loc));
 758:       }
 759:   }
 760: 
 761:   /**
 762:    * This method returns a new instance of <code>DateFormat</code> that
 763:    * formats both dates and times using the <code>SHORT</code> style.
 764:    *
 765:    * @return A new <code>DateFormat</code>instance.
 766:    */
 767:   public static final DateFormat getDateTimeInstance ()
 768:   {
 769:     return getDateTimeInstance (DEFAULT, DEFAULT, Locale.getDefault());
 770:   }
 771: 
 772:   /**
 773:    * This method returns a new instance of <code>DateFormat</code> that
 774:    * formats both dates and times using the <code>DEFAULT</code> style.
 775:    *
 776:    * @return A new <code>DateFormat</code>instance.
 777:    */
 778:   public static final DateFormat getDateTimeInstance (int dateStyle, 
 779:                               int timeStyle)
 780:   {
 781:     return getDateTimeInstance (dateStyle, timeStyle, Locale.getDefault());
 782:   }
 783: 
 784:   /**
 785:    * This method returns a new instance of <code>DateFormat</code> that
 786:    * formats both dates and times using the specified styles.
 787:    * 
 788:    * @param dateStyle The desired style for date formatting.
 789:    * @param timeStyle The desired style for time formatting
 790:    *
 791:    * @return A new <code>DateFormat</code>instance.
 792:    */
 793:   public static final DateFormat getDateTimeInstance (int dateStyle, 
 794:                               int timeStyle, 
 795:                               Locale loc)
 796:   {
 797:     try
 798:       {
 799:     return computeInstance (dateStyle, timeStyle, loc, true, true);
 800:       }
 801:     catch (MissingResourceException e)
 802:       {
 803:     for (DateFormatProvider p :
 804:            ServiceLoader.load(DateFormatProvider.class))
 805:       {
 806:         for (Locale l : p.getAvailableLocales())
 807:           {
 808:         if (l.equals(loc))
 809:           {
 810:             DateFormat df = p.getDateTimeInstance(dateStyle,
 811:                               timeStyle, loc);
 812:             if (df != null)
 813:               return df;
 814:             break;
 815:           }
 816:           }
 817:       }
 818:     return getDateTimeInstance(dateStyle, timeStyle,
 819:                    LocaleHelper.getFallbackLocale(loc));
 820:       }
 821:   }
 822: 
 823:   /**
 824:    * This method returns a new instance of <code>DateFormat</code> that
 825:    * formats both dates and times using the <code>SHORT</code> style.
 826:    *
 827:    * @return A new <code>DateFormat</code>instance.
 828:    */
 829:   public static final DateFormat getInstance ()
 830:   {
 831:     // JCL book says SHORT.
 832:     return getDateTimeInstance (SHORT, SHORT, Locale.getDefault());
 833:   }
 834: 
 835:   /**
 836:    * This method returns the <code>NumberFormat</code> object being used
 837:    * by this object to parse/format time values.
 838:    *
 839:    * @return The <code>NumberFormat</code> in use by this object.
 840:    */
 841:   public NumberFormat getNumberFormat ()
 842:   {
 843:     return numberFormat;
 844:   }
 845: 
 846:  /**
 847:    * This method returns an instance of <code>DateFormat</code> that will
 848:    * format using the default formatting style for times.
 849:    *
 850:    * @return A new <code>DateFormat</code> instance.
 851:    */
 852:   public static final DateFormat getTimeInstance ()
 853:   {
 854:     return getTimeInstance (DEFAULT, Locale.getDefault());
 855:   }
 856: 
 857:   /**
 858:    * This method returns an instance of <code>DateFormat</code> that will
 859:    * format using the specified formatting style for times.
 860:    *
 861:    * @param style The type of formatting to perform. 
 862:    * 
 863:    * @return A new <code>DateFormat</code> instance.
 864:    */
 865:   public static final DateFormat getTimeInstance (int style)
 866:   {
 867:     return getTimeInstance (style, Locale.getDefault());
 868:   }
 869: 
 870:   /**
 871:    * This method returns an instance of <code>DateFormat</code> that will
 872:    * format using the specified formatting style for times.  The specified
 873:    * localed will be used in place of the default.
 874:    *
 875:    * @param style The type of formatting to perform. 
 876:    * @param loc The desired locale.
 877:    * 
 878:    * @return A new <code>DateFormat</code> instance.
 879:    */
 880:   public static final DateFormat getTimeInstance (int style, Locale loc)
 881:   {
 882:     try
 883:       {
 884:     return computeInstance (style, loc, false, true);
 885:       }
 886:     catch (MissingResourceException e)
 887:       {
 888:     for (DateFormatProvider p :
 889:            ServiceLoader.load(DateFormatProvider.class))
 890:       {
 891:         for (Locale l : p.getAvailableLocales())
 892:           {
 893:         if (l.equals(loc))
 894:           {
 895:             DateFormat df = p.getTimeInstance(style, loc);
 896:             if (df != null)
 897:               return df;
 898:             break;
 899:           }
 900:           }
 901:       }
 902:     return getTimeInstance(style,
 903:                    LocaleHelper.getFallbackLocale(loc));
 904:       }
 905:   }
 906: 
 907:   /**
 908:    * This method returns the <code>TimeZone</code> object being used by
 909:    * this instance.
 910:    *
 911:    * @return The time zone in use.
 912:    */
 913:   public TimeZone getTimeZone ()
 914:   {
 915:     return calendar.getTimeZone();
 916:   }
 917: 
 918:   /**
 919:    * This method returns a hash value for this object.
 920:    * 
 921:    * @return A hash value for this object.
 922:    */
 923:   public int hashCode ()
 924:   {
 925:     if (numberFormat != null)
 926:       return numberFormat.hashCode();
 927:     else
 928:       return 0;
 929:   }
 930: 
 931:   /**
 932:    * This method indicates whether or not the parsing of date and time
 933:    * values should be done in a lenient value.
 934:    *
 935:    * @return <code>true</code> if date/time parsing is lenient,
 936:    * <code>false</code> otherwise.
 937:    */
 938:   public boolean isLenient ()
 939:   {
 940:     return calendar.isLenient();
 941:   }
 942: 
 943:   /**
 944:    * This method parses the specified date/time string.
 945:    *
 946:    * @param source The string to parse.
 947:    * @return The resultant date.
 948:    *
 949:    * @exception ParseException If the specified string cannot be parsed.
 950:    */
 951:   public Date parse (String source) throws ParseException
 952:   {
 953:     ParsePosition pos = new ParsePosition(0);
 954:     Date result = parse (source, pos);
 955:     if (result == null)
 956:       {
 957:     int index = pos.getErrorIndex();
 958:     if (index < 0)
 959:       index = pos.getIndex();
 960:     throw new ParseException("invalid Date syntax in \""
 961:                  + source + '\"', index);
 962:       }
 963:     return result;
 964:   }
 965: 
 966:   /** 
 967:    * This method parses the specified <code>String</code> into a 
 968:    * <code>Date</code>.  The <code>pos</code> argument contains the
 969:    * starting parse position on method entry and the ending parse
 970:    * position on method exit.
 971:    *
 972:    * @param source The string to parse.
 973:    * @param pos The starting parse position in entry, the ending parse
 974:    * position on exit.
 975:    *
 976:    * @return The parsed date, or <code>null</code> if the string cannot
 977:    * be parsed.
 978:    */
 979:   public abstract Date parse (String source, ParsePosition pos);
 980: 
 981:   /**
 982:    * This method is identical to <code>parse(String, ParsePosition)</code>,
 983:    * but returns its result as an <code>Object</code> instead of a
 984:    * <code>Date</code>.
 985:    * 
 986:    * @param source The string to parse.
 987:    * @param pos The starting parse position in entry, the ending parse
 988:    * position on exit.
 989:    *
 990:    * @return The parsed date, or <code>null</code> if the string cannot
 991:    * be parsed.
 992:    */
 993:   public Object parseObject (String source, ParsePosition pos)
 994:   {
 995:     return parse(source, pos);
 996:   }
 997: 
 998:   /**
 999:    * This method specified the <code>Calendar</code> that should be used 
1000:    * by this object to parse/format datetimes.
1001:    *
1002:    * @param calendar The new <code>Calendar</code> for this object.
1003:    *
1004:    * @see java.util.Calendar
1005:    */
1006:   public void setCalendar (Calendar calendar)
1007:   {
1008:     this.calendar = calendar;
1009:   }
1010: 
1011:   /**
1012:    * This method specifies whether or not this object should be lenient in 
1013:    * the syntax it accepts while parsing date/time values.
1014:    *
1015:    * @param lenient <code>true</code> if parsing should be lenient,
1016:    * <code>false</code> otherwise.
1017:    */
1018:   public void setLenient (boolean lenient)
1019:   {
1020:     calendar.setLenient(lenient);
1021:   }
1022: 
1023:   /**
1024:    * This method specifies the <code>NumberFormat</code> object that should
1025:    * be used by this object to parse/format times.
1026:    *
1027:    * @param numberFormat The <code>NumberFormat</code> in use by this object.
1028:    */
1029:   public void setNumberFormat (NumberFormat numberFormat)
1030:   {
1031:     this.numberFormat = numberFormat;
1032:   }
1033: 
1034:   /**
1035:    * This method sets the time zone that should be used by this object.
1036:    *
1037:    * @param timeZone The new time zone.
1038:    */
1039:   public void setTimeZone (TimeZone timeZone)
1040:   {
1041:     calendar.setTimeZone(timeZone);
1042:   }
1043: }