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:   public static class Field extends Format.Field
 224:   {
 225:     static final long serialVersionUID = 7441350119349544720L;
 226: 
 227:     private int calendarField;
 228: 
 229:     public static final DateFormat.Field ERA
 230:         = new Field("era", Calendar.ERA);
 231:     public static final DateFormat.Field YEAR
 232:         = new Field("year", Calendar.YEAR);
 233:     public static final DateFormat.Field MONTH
 234:         = new Field("month", Calendar.MONTH);
 235:     public static final DateFormat.Field DAY_OF_MONTH
 236:         = new Field("day of month", Calendar.DAY_OF_MONTH);
 237:     public static final DateFormat.Field HOUR_OF_DAY1
 238:         = new Field("hour of day 1", Calendar.HOUR_OF_DAY);
 239:     public static final DateFormat.Field HOUR_OF_DAY0
 240:         = new Field("hour of day 0", Calendar.HOUR_OF_DAY);
 241:     public static final DateFormat.Field MINUTE
 242:         = new Field("minute", Calendar.MINUTE);
 243:     public static final DateFormat.Field SECOND
 244:         = new Field("second", Calendar.SECOND);
 245:     public static final DateFormat.Field MILLISECOND
 246:         = new Field("millisecond", Calendar.MILLISECOND);
 247:     public static final DateFormat.Field DAY_OF_WEEK
 248:         = new Field("day of week", Calendar.DAY_OF_WEEK);
 249:     public static final DateFormat.Field DAY_OF_YEAR
 250:         = new Field("day of year", Calendar.DAY_OF_YEAR);
 251:     public static final DateFormat.Field DAY_OF_WEEK_IN_MONTH
 252:         = new Field("day of week in month", Calendar.DAY_OF_WEEK_IN_MONTH);
 253:     public static final DateFormat.Field WEEK_OF_YEAR
 254:         = new Field("week of year", Calendar.WEEK_OF_YEAR);
 255:     public static final DateFormat.Field WEEK_OF_MONTH
 256:         = new Field("week of month", Calendar.WEEK_OF_MONTH);
 257:     public static final DateFormat.Field AM_PM
 258:         = new Field("am/pm", Calendar.AM_PM);
 259:     public static final DateFormat.Field HOUR1
 260:         = new Field("hour1", Calendar.HOUR);
 261:     public static final DateFormat.Field HOUR0
 262:         = new Field("hour0", Calendar.HOUR);
 263:     public static final DateFormat.Field TIME_ZONE
 264:         = new Field("timezone", Calendar.ZONE_OFFSET);
 265: 
 266:     static final DateFormat.Field[] allFields =
 267:     {
 268:       ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY1,
 269:       HOUR_OF_DAY0, MINUTE, SECOND, MILLISECOND,
 270:       DAY_OF_WEEK, DAY_OF_YEAR, DAY_OF_WEEK_IN_MONTH,
 271:       WEEK_OF_YEAR, WEEK_OF_MONTH, AM_PM, HOUR1, HOUR0,
 272:       TIME_ZONE
 273:     };
 274: 
 275:     // For deserialization
 276:     private Field()
 277:     {
 278:       super("");
 279:     }
 280: 
 281:     protected Field(String name, int calendarField)
 282:     {
 283:       super(name);
 284:       this.calendarField = calendarField;
 285:     }
 286: 
 287:     public int getCalendarField()
 288:     {
 289:       return calendarField;
 290:     }
 291: 
 292:     public static Field ofCalendarField(int calendarField)
 293:     {
 294:       if (calendarField >= allFields.length || calendarField < 0)
 295:         throw new IllegalArgumentException("no such calendar field ("
 296:                                            + calendarField + ")");
 297: 
 298:       return allFields[calendarField];
 299:     }
 300: 
 301:     protected Object readResolve() throws InvalidObjectException
 302:     {
 303:       String s = getName();
 304: 
 305:       for (int i=0;i<allFields.length;i++)
 306:         if (s.equals(allFields[i].getName()))
 307:           return allFields[i];
 308: 
 309:       throw new InvalidObjectException("no such DateFormat field called " + s);
 310:     }
 311:   }
 312: 
 313:   /**
 314:    * This method initializes a new instance of <code>DateFormat</code>.
 315:    */
 316:   protected DateFormat ()
 317:   {
 318:   }
 319: 
 320:   /**
 321:    * This method tests this object for equality against the specified object.
 322:    * The two objects will be considered equal if an only if the specified
 323:    * object:
 324:    * <P>
 325:    * <ul>
 326:    * <li>Is not <code>null</code>.</li>
 327:    * <li>Is an instance of <code>DateFormat</code>.</li>
 328:    * <li>Has equal numberFormat field as this object.</li>
 329:    * <li>Has equal (Calendar) TimeZone rules as this object.</li>
 330:    * <li>Has equal (Calendar) isLenient results.</li>
 331:    * <li>Has equal Calendar first day of week and minimal days in week
 332:    * values.</li>
 333:    * </ul>
 334:    * Note that not all properties of the Calendar are relevant for a
 335:    * DateFormat. For formatting only the fact whether or not the
 336:    * TimeZone has the same rules and whether the calendar is lenient
 337:    * and has the same week rules is compared for this implementation
 338:    * of equals. Other properties of the Calendar (such as the time)
 339:    * are not taken into account.
 340:    *
 341:    * @param obj The object to test for equality against.
 342:    *
 343:    * @return <code>true</code> if the specified object is equal to this object,
 344:    * <code>false</code> otherwise.
 345:    */
 346:   public boolean equals (Object obj)
 347:   {
 348:     if (!(obj instanceof DateFormat))
 349:       return false;
 350: 
 351:     DateFormat d = (DateFormat) obj;
 352:     TimeZone tz = getTimeZone();
 353:     TimeZone tzd = d.getTimeZone();
 354:     if (tz.hasSameRules(tzd))
 355:       if (isLenient() == d.isLenient())
 356:         {
 357:           Calendar c = getCalendar();
 358:           Calendar cd = d.getCalendar();
 359:           if ((c == null && cd == null)
 360:               ||
 361:               (c.getFirstDayOfWeek() == cd.getFirstDayOfWeek()
 362:                &&
 363:                c.getMinimalDaysInFirstWeek()
 364:                == cd.getMinimalDaysInFirstWeek()))
 365:             return ((numberFormat == null && d.numberFormat == null)
 366:                     || numberFormat.equals(d.numberFormat));
 367:         }
 368: 
 369:     return false;
 370:   }
 371: 
 372:   /**
 373:    * This method returns a copy of this object.
 374:    *
 375:    * @return A copy of this object.
 376:    */
 377:   public Object clone ()
 378:   {
 379:     // We know the superclass just call's Object's generic cloner.
 380:     return super.clone ();
 381:   }
 382: 
 383:   /**
 384:    * This method formats the specified <code>Object</code> into a date string
 385:    * and appends it to the specified <code>StringBuffer</code>.
 386:    * The specified object must be an instance of <code>Number</code> or
 387:    * <code>Date</code> or an <code>IllegalArgumentException</code> will be
 388:    * thrown.
 389:    *
 390:    * @param obj The <code>Object</code> to format.
 391:    * @param buf The <code>StringBuffer</code> to append the resultant
 392:    * <code>String</code> to.
 393:    * @param pos Is updated to the start and end index of the
 394:    * specified field.
 395:    *
 396:    * @return The <code>StringBuffer</code> supplied on input, with the
 397:    * formatted date/time appended.
 398:    */
 399:   public final StringBuffer format (Object obj,
 400:                                     StringBuffer buf, FieldPosition pos)
 401:   {
 402:     if (obj instanceof Number)
 403:       obj = new Date(((Number) obj).longValue());
 404:     else if (! (obj instanceof Date))
 405:       throw new IllegalArgumentException
 406:         ("Cannot format given Object as a Date");
 407: 
 408:     return format ((Date) obj, buf, pos);
 409:   }
 410: 
 411:   /**
 412:     * Formats the date argument according to the pattern specified.
 413:     *
 414:     * @param date The formatted date.
 415:     */
 416:   public final String format (Date date)
 417:   {
 418:     StringBuffer sb = new StringBuffer ();
 419:     format (date, sb, new FieldPosition (MONTH_FIELD));
 420:     return sb.toString();
 421:   }
 422: 
 423:   /**
 424:    * This method formats a <code>Date</code> into a string and appends it
 425:    * to the specified <code>StringBuffer</code>.
 426:    *
 427:    * @param date The <code>Date</code> value to format.
 428:    * @param buf The <code>StringBuffer</code> to append the resultant
 429:    * <code>String</code> to.
 430:    * @param pos Is updated to the start and end index of the
 431:    * specified field.
 432:    *
 433:    * @return The <code>StringBuffer</code> supplied on input, with the
 434:    * formatted date/time appended.
 435:    */
 436:   public abstract StringBuffer format (Date date,
 437:                                        StringBuffer buf, FieldPosition pos);
 438: 
 439:   /**
 440:    * This method returns a list of available locales supported by this
 441:    * class.
 442:    */
 443:   public static Locale[] getAvailableLocales()
 444:   {
 445:     return Locale.getAvailableLocales();
 446:   }
 447: 
 448:   /**
 449:     * This method returns the <code>Calendar</code> object being used by
 450:     * this object to parse/format datetimes.
 451:     *
 452:     * @return The <code>Calendar</code> being used by this object.
 453:     *
 454:     * @see java.util.Calendar
 455:     */
 456:   public Calendar getCalendar ()
 457:   {
 458:     return calendar;
 459:   }
 460: 
 461:   private static DateFormat computeInstance (int style, Locale loc,
 462:                                              boolean use_date, boolean use_time)
 463:   {
 464:     return computeInstance (style, style, loc, use_date, use_time);
 465:   }
 466: 
 467:   private static DateFormat computeInstance (int dateStyle, int timeStyle,
 468:                                              Locale loc, boolean use_date,
 469:                                              boolean use_time)
 470:     throws MissingResourceException
 471:   {
 472:     if (loc.equals(Locale.ROOT))
 473:       return computeDefault(dateStyle,timeStyle,use_date,use_time);
 474: 
 475:     ResourceBundle res =
 476:       ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
 477:                                loc, ClassLoader.getSystemClassLoader());
 478: 
 479:     String pattern = null;
 480:     if (use_date)
 481:       {
 482:         String name, def;
 483:         switch (dateStyle)
 484:           {
 485:           case FULL:
 486:             name = "fullDateFormat";
 487:             def = "EEEE MMMM d, yyyy G";
 488:             break;
 489:           case LONG:
 490:             name = "longDateFormat";
 491:             def = "MMMM d, yyyy";
 492:             break;
 493:           case MEDIUM:
 494:             name = "mediumDateFormat";
 495:             def = "d-MMM-yy";
 496:             break;
 497:           case SHORT:
 498:             name = "shortDateFormat";
 499:             def = "M/d/yy";
 500:             break;
 501:           default:
 502:             throw new IllegalArgumentException ();
 503:           }
 504:         try
 505:           {
 506:             pattern = res == null ? def : res.getString(name);
 507:           }
 508:         catch (MissingResourceException x)
 509:           {
 510:             pattern = def;
 511:           }
 512:       }
 513: 
 514:     if (use_time)
 515:       {
 516:         if (pattern == null)
 517:           pattern = "";
 518:         else
 519:           pattern += " ";
 520: 
 521:         String name, def;
 522:         switch (timeStyle)
 523:           {
 524:           case FULL:
 525:             name = "fullTimeFormat";
 526:             def = "h:mm:ss;S 'o''clock' a z";
 527:             break;
 528:           case LONG:
 529:             name = "longTimeFormat";
 530:             def = "h:mm:ss a z";
 531:             break;
 532:           case MEDIUM:
 533:             name = "mediumTimeFormat";
 534:             def = "h:mm:ss a";
 535:             break;
 536:           case SHORT:
 537:             name = "shortTimeFormat";
 538:             def = "h:mm a";
 539:             break;
 540:           default:
 541:             throw new IllegalArgumentException ();
 542:           }
 543: 
 544:         String s;
 545:         try
 546:           {
 547:             s = res == null ? def : res.getString(name);
 548:           }
 549:         catch (MissingResourceException x)
 550:           {
 551:             s = def;
 552:           }
 553:         pattern += s;
 554:       }
 555: 
 556:     return new SimpleDateFormat (pattern, loc);
 557:   }
 558: 
 559:   private static DateFormat computeDefault (int dateStyle, int timeStyle,
 560:                                             boolean use_date, boolean use_time)
 561:   {
 562:     String pattern = null;
 563:     if (use_date)
 564:       {
 565:         switch (dateStyle)
 566:           {
 567:           case FULL:
 568:             pattern = "EEEE MMMM d, yyyy G";
 569:             break;
 570:           case LONG:
 571:             pattern = "MMMM d, yyyy";
 572:             break;
 573:           case MEDIUM:
 574:             pattern = "d-MMM-yy";
 575:             break;
 576:           case SHORT:
 577:             pattern = "M/d/yy";
 578:           default:
 579:             throw new IllegalArgumentException ();
 580:           }
 581:       }
 582: 
 583:     if (use_time)
 584:       {
 585:         if (pattern == null)
 586:           pattern = "";
 587:         else
 588:           pattern += " ";
 589: 
 590:         switch (timeStyle)
 591:           {
 592:           case FULL:
 593:             pattern += "h:mm:ss;S 'o''clock' a z";
 594:             break;
 595:           case LONG:
 596:             pattern += "h:mm:ss a z";
 597:             break;
 598:           case MEDIUM:
 599:             pattern += "h:mm:ss a";
 600:             break;
 601:           case SHORT:
 602:             pattern += "h:mm a";
 603:             break;
 604:           default:
 605:             throw new IllegalArgumentException ();
 606:           }
 607:       }
 608: 
 609:     return new SimpleDateFormat (pattern, Locale.ROOT);
 610:   }
 611: 
 612:  /**
 613:    * This method returns an instance of <code>DateFormat</code> that will
 614:    * format using the default formatting style for dates.
 615:    *
 616:    * @return A new <code>DateFormat</code> instance.
 617:    */
 618:   public static final DateFormat getDateInstance ()
 619:   {
 620:     return getDateInstance (DEFAULT, Locale.getDefault());
 621:   }
 622: 
 623:   /**
 624:    * This method returns an instance of <code>DateFormat</code> that will
 625:    * format using the specified formatting style for dates.
 626:    *
 627:    * @param style The type of formatting to perform.
 628:    *
 629:    * @return A new <code>DateFormat</code> instance.
 630:    */
 631:   public static final DateFormat getDateInstance (int style)
 632:   {
 633:     return getDateInstance (style, Locale.getDefault());
 634:   }
 635: 
 636:   /**
 637:    * This method returns an instance of <code>DateFormat</code> that will
 638:    * format using the specified formatting style for dates.  The specified
 639:    * localed will be used in place of the default.
 640:    *
 641:    * @param style The type of formatting to perform.
 642:    * @param loc The desired locale.
 643:    *
 644:    * @return A new <code>DateFormat</code> instance.
 645:    */
 646:   public static final DateFormat getDateInstance (int style, Locale loc)
 647:   {
 648:     try
 649:       {
 650:         return computeInstance (style, loc, true, false);
 651:       }
 652:     catch (MissingResourceException e)
 653:       {
 654:         for (DateFormatProvider p :
 655:                ServiceLoader.load(DateFormatProvider.class))
 656:           {
 657:             for (Locale l : p.getAvailableLocales())
 658:               {
 659:                 if (l.equals(loc))
 660:                   {
 661:                     DateFormat df = p.getDateInstance(style, loc);
 662:                     if (df != null)
 663:                       return df;
 664:                     break;
 665:                   }
 666:               }
 667:           }
 668:         return getDateInstance(style,
 669:                                LocaleHelper.getFallbackLocale(loc));
 670:       }
 671:   }
 672: 
 673:   /**
 674:    * This method returns a new instance of <code>DateFormat</code> that
 675:    * formats both dates and times using the <code>SHORT</code> style.
 676:    *
 677:    * @return A new <code>DateFormat</code>instance.
 678:    */
 679:   public static final DateFormat getDateTimeInstance ()
 680:   {
 681:     return getDateTimeInstance (DEFAULT, DEFAULT, Locale.getDefault());
 682:   }
 683: 
 684:   /**
 685:    * This method returns a new instance of <code>DateFormat</code> that
 686:    * formats both dates and times using the <code>DEFAULT</code> style.
 687:    *
 688:    * @return A new <code>DateFormat</code>instance.
 689:    */
 690:   public static final DateFormat getDateTimeInstance (int dateStyle,
 691:                                                       int timeStyle)
 692:   {
 693:     return getDateTimeInstance (dateStyle, timeStyle, Locale.getDefault());
 694:   }
 695: 
 696:   /**
 697:    * This method returns a new instance of <code>DateFormat</code> that
 698:    * formats both dates and times using the specified styles.
 699:    *
 700:    * @param dateStyle The desired style for date formatting.
 701:    * @param timeStyle The desired style for time formatting
 702:    *
 703:    * @return A new <code>DateFormat</code>instance.
 704:    */
 705:   public static final DateFormat getDateTimeInstance (int dateStyle,
 706:                                                       int timeStyle,
 707:                                                       Locale loc)
 708:   {
 709:     try
 710:       {
 711:         return computeInstance (dateStyle, timeStyle, loc, true, true);
 712:       }
 713:     catch (MissingResourceException e)
 714:       {
 715:         for (DateFormatProvider p :
 716:                ServiceLoader.load(DateFormatProvider.class))
 717:           {
 718:             for (Locale l : p.getAvailableLocales())
 719:               {
 720:                 if (l.equals(loc))
 721:                   {
 722:                     DateFormat df = p.getDateTimeInstance(dateStyle,
 723:                                                           timeStyle, loc);
 724:                     if (df != null)
 725:                       return df;
 726:                     break;
 727:                   }
 728:               }
 729:           }
 730:         return getDateTimeInstance(dateStyle, timeStyle,
 731:                                    LocaleHelper.getFallbackLocale(loc));
 732:       }
 733:   }
 734: 
 735:   /**
 736:    * This method returns a new instance of <code>DateFormat</code> that
 737:    * formats both dates and times using the <code>SHORT</code> style.
 738:    *
 739:    * @return A new <code>DateFormat</code>instance.
 740:    */
 741:   public static final DateFormat getInstance ()
 742:   {
 743:     // JCL book says SHORT.
 744:     return getDateTimeInstance (SHORT, SHORT, Locale.getDefault());
 745:   }
 746: 
 747:   /**
 748:    * This method returns the <code>NumberFormat</code> object being used
 749:    * by this object to parse/format time values.
 750:    *
 751:    * @return The <code>NumberFormat</code> in use by this object.
 752:    */
 753:   public NumberFormat getNumberFormat ()
 754:   {
 755:     return numberFormat;
 756:   }
 757: 
 758:  /**
 759:    * This method returns an instance of <code>DateFormat</code> that will
 760:    * format using the default formatting style for times.
 761:    *
 762:    * @return A new <code>DateFormat</code> instance.
 763:    */
 764:   public static final DateFormat getTimeInstance ()
 765:   {
 766:     return getTimeInstance (DEFAULT, Locale.getDefault());
 767:   }
 768: 
 769:   /**
 770:    * This method returns an instance of <code>DateFormat</code> that will
 771:    * format using the specified formatting style for times.
 772:    *
 773:    * @param style The type of formatting to perform.
 774:    *
 775:    * @return A new <code>DateFormat</code> instance.
 776:    */
 777:   public static final DateFormat getTimeInstance (int style)
 778:   {
 779:     return getTimeInstance (style, Locale.getDefault());
 780:   }
 781: 
 782:   /**
 783:    * This method returns an instance of <code>DateFormat</code> that will
 784:    * format using the specified formatting style for times.  The specified
 785:    * localed will be used in place of the default.
 786:    *
 787:    * @param style The type of formatting to perform.
 788:    * @param loc The desired locale.
 789:    *
 790:    * @return A new <code>DateFormat</code> instance.
 791:    */
 792:   public static final DateFormat getTimeInstance (int style, Locale loc)
 793:   {
 794:     try
 795:       {
 796:         return computeInstance (style, loc, false, true);
 797:       }
 798:     catch (MissingResourceException e)
 799:       {
 800:         for (DateFormatProvider p :
 801:                ServiceLoader.load(DateFormatProvider.class))
 802:           {
 803:             for (Locale l : p.getAvailableLocales())
 804:               {
 805:                 if (l.equals(loc))
 806:                   {
 807:                     DateFormat df = p.getTimeInstance(style, loc);
 808:                     if (df != null)
 809:                       return df;
 810:                     break;
 811:                   }
 812:               }
 813:           }
 814:         return getTimeInstance(style,
 815:                                LocaleHelper.getFallbackLocale(loc));
 816:       }
 817:   }
 818: 
 819:   /**
 820:    * This method returns the <code>TimeZone</code> object being used by
 821:    * this instance.
 822:    *
 823:    * @return The time zone in use.
 824:    */
 825:   public TimeZone getTimeZone ()
 826:   {
 827:     return calendar.getTimeZone();
 828:   }
 829: 
 830:   /**
 831:    * This method returns a hash value for this object.
 832:    *
 833:    * @return A hash value for this object.
 834:    */
 835:   public int hashCode ()
 836:   {
 837:     if (numberFormat != null)
 838:       return numberFormat.hashCode();
 839:     else
 840:       return 0;
 841:   }
 842: 
 843:   /**
 844:    * This method indicates whether or not the parsing of date and time
 845:    * values should be done in a lenient value.
 846:    *
 847:    * @return <code>true</code> if date/time parsing is lenient,
 848:    * <code>false</code> otherwise.
 849:    */
 850:   public boolean isLenient ()
 851:   {
 852:     return calendar.isLenient();
 853:   }
 854: 
 855:   /**
 856:    * This method parses the specified date/time string.
 857:    *
 858:    * @param source The string to parse.
 859:    * @return The resultant date.
 860:    *
 861:    * @exception ParseException If the specified string cannot be parsed.
 862:    */
 863:   public Date parse (String source) throws ParseException
 864:   {
 865:     ParsePosition pos = new ParsePosition(0);
 866:     Date result = parse (source, pos);
 867:     if (result == null)
 868:       {
 869:         int index = pos.getErrorIndex();
 870:         if (index < 0)
 871:           index = pos.getIndex();
 872:         throw new ParseException("invalid Date syntax in \""
 873:                                  + source + '\"', index);
 874:       }
 875:     return result;
 876:   }
 877: 
 878:   /**
 879:    * This method parses the specified <code>String</code> into a
 880:    * <code>Date</code>.  The <code>pos</code> argument contains the
 881:    * starting parse position on method entry and the ending parse
 882:    * position on method exit.
 883:    *
 884:    * @param source The string to parse.
 885:    * @param pos The starting parse position in entry, the ending parse
 886:    * position on exit.
 887:    *
 888:    * @return The parsed date, or <code>null</code> if the string cannot
 889:    * be parsed.
 890:    */
 891:   public abstract Date parse (String source, ParsePosition pos);
 892: 
 893:   /**
 894:    * This method is identical to <code>parse(String, ParsePosition)</code>,
 895:    * but returns its result as an <code>Object</code> instead of a
 896:    * <code>Date</code>.
 897:    *
 898:    * @param source The string to parse.
 899:    * @param pos The starting parse position in entry, the ending parse
 900:    * position on exit.
 901:    *
 902:    * @return The parsed date, or <code>null</code> if the string cannot
 903:    * be parsed.
 904:    */
 905:   public Object parseObject (String source, ParsePosition pos)
 906:   {
 907:     return parse(source, pos);
 908:   }
 909: 
 910:   /**
 911:    * This method specified the <code>Calendar</code> that should be used
 912:    * by this object to parse/format datetimes.
 913:    *
 914:    * @param calendar The new <code>Calendar</code> for this object.
 915:    *
 916:    * @see java.util.Calendar
 917:    */
 918:   public void setCalendar (Calendar calendar)
 919:   {
 920:     this.calendar = calendar;
 921:   }
 922: 
 923:   /**
 924:    * This method specifies whether or not this object should be lenient in
 925:    * the syntax it accepts while parsing date/time values.
 926:    *
 927:    * @param lenient <code>true</code> if parsing should be lenient,
 928:    * <code>false</code> otherwise.
 929:    */
 930:   public void setLenient (boolean lenient)
 931:   {
 932:     calendar.setLenient(lenient);
 933:   }
 934: 
 935:   /**
 936:    * This method specifies the <code>NumberFormat</code> object that should
 937:    * be used by this object to parse/format times.
 938:    *
 939:    * @param numberFormat The <code>NumberFormat</code> in use by this object.
 940:    */
 941:   public void setNumberFormat (NumberFormat numberFormat)
 942:   {
 943:     this.numberFormat = numberFormat;
 944:   }
 945: 
 946:   /**
 947:    * This method sets the time zone that should be used by this object.
 948:    *
 949:    * @param timeZone The new time zone.
 950:    */
 951:   public void setTimeZone (TimeZone timeZone)
 952:   {
 953:     calendar.setTimeZone(timeZone);
 954:   }
 955: }