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    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.text;
041    
042    import gnu.java.locale.LocaleHelper;
043    
044    import java.text.spi.DateFormatProvider;
045    
046    import java.io.InvalidObjectException;
047    import java.util.Calendar;
048    import java.util.Date;
049    import java.util.Locale;
050    import java.util.MissingResourceException;
051    import java.util.ResourceBundle;
052    import java.util.ServiceLoader;
053    import 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    
064    public 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    }