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