001/* DateFormat.java -- Class for formatting/parsing date/times
002   Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005
003   Free Software Foundation, Inc.
004
005This file is part of GNU Classpath.
006
007GNU Classpath is free software; you can redistribute it and/or modify
008it under the terms of the GNU General Public License as published by
009the Free Software Foundation; either version 2, or (at your option)
010any later version.
011 
012GNU Classpath is distributed in the hope that it will be useful, but
013WITHOUT ANY WARRANTY; without even the implied warranty of
014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015General Public License for more details.
016
017You should have received a copy of the GNU General Public License
018along with GNU Classpath; see the file COPYING.  If not, write to the
019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02002110-1301 USA.
021
022Linking this library statically or dynamically with other modules is
023making a combined work based on this library.  Thus, the terms and
024conditions of the GNU General Public License cover the whole
025combination.
026
027As a special exception, the copyright holders of this library give you
028permission to link this library with independent modules to produce an
029executable, regardless of the license terms of these independent
030modules, and to copy and distribute the resulting executable under
031terms of your choice, provided that you also meet, for each linked
032independent module, the terms and conditions of the license of that
033module.  An independent module is a module which is not derived from
034or based on this library.  If you modify this library, you may extend
035this exception to your version of the library, but you are not
036obligated to do so.  If you do not wish to do so, delete this
037exception statement from your version. */
038
039
040package java.text;
041
042import gnu.java.locale.LocaleHelper;
043
044import java.text.spi.DateFormatProvider;
045
046import java.io.InvalidObjectException;
047import java.util.Calendar;
048import java.util.Date;
049import java.util.Locale;
050import java.util.MissingResourceException;
051import java.util.ResourceBundle;
052import java.util.ServiceLoader;
053import java.util.TimeZone;
054
055/**
056 * @author Per Bothner (bothner@cygnus.com)
057 * @date October 25, 1998.
058 */
059/* Written using "Java Class Libraries", 2nd edition, plus online
060 * API docs for JDK 1.2 beta from http://www.javasoft.com.
061 * Status:  Mostly complete; search for FIXME to see omissions.
062 */
063
064public abstract class DateFormat extends Format implements Cloneable
065{
066  private static final long serialVersionUID = 7218322306649953788L;
067
068  // Names fixed by serialization spec.
069  protected Calendar calendar;
070  protected NumberFormat numberFormat;
071
072  // (Values determined using a test program.)
073  public static final int FULL = 0;
074  public static final int LONG = 1;
075  public static final int MEDIUM = 2;
076  public static final int SHORT = 3;
077  public static final int DEFAULT = MEDIUM;
078
079  /* These constants need to have these exact values.  They
080   * correspond to index positions within the localPatternChars
081   * string for a given locale.  Each locale may specify its
082   * own character for a particular field, but the position
083   * of these characters must correspond to an appropriate field
084   * number (as listed below), in order for their meaning to
085   * be determined.  For example, the US locale uses
086   * the string "GyMdkHmsSEDFwWahKzYeugAZ", where 'G' is the character
087   * for era, 'y' for year, and so on down to 'Z' for time zone.
088   */
089  /**
090   * Represents the position of the era
091   * pattern character in the array of
092   * localized pattern characters. 
093   * For example, 'AD' is an era used
094   * in the Gregorian calendar system.
095   * In the U.S. locale, this is 'G'.
096   */  
097  public static final int ERA_FIELD = 0;
098  /**
099   * 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}