1 /* DateFormat.java -- Class for formatting/parsing date/times
2 Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005
3 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library. Thus, the terms and
24 conditions of the GNU General Public License cover the whole
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module. An independent module is a module which is not derived from
34 or based on this library. If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so. If you do not wish to do so, delete this
37 exception statement from your version. */
42 import gnu
.java
.locale
.LocaleHelper
;
44 import java
.text
.spi
.DateFormatProvider
;
46 import java
.io
.InvalidObjectException
;
47 import java
.util
.Calendar
;
48 import java
.util
.Date
;
49 import java
.util
.Locale
;
50 import java
.util
.MissingResourceException
;
51 import java
.util
.ResourceBundle
;
52 import java
.util
.ServiceLoader
;
53 import java
.util
.TimeZone
;
56 * @author Per Bothner (bothner@cygnus.com)
57 * @date October 25, 1998.
59 /* Written using "Java Class Libraries", 2nd edition, plus online
60 * API docs for JDK 1.2 beta from http://www.javasoft.com.
61 * Status: Mostly complete; search for FIXME to see omissions.
64 public abstract class DateFormat
extends Format
implements Cloneable
66 private static final long serialVersionUID
= 7218322306649953788L;
68 // Names fixed by serialization spec.
69 protected Calendar calendar
;
70 protected NumberFormat numberFormat
;
72 // (Values determined using a test program.)
73 public static final int FULL
= 0;
74 public static final int LONG
= 1;
75 public static final int MEDIUM
= 2;
76 public static final int SHORT
= 3;
77 public static final int DEFAULT
= MEDIUM
;
79 /* These constants need to have these exact values. They
80 * correspond to index positions within the localPatternChars
81 * string for a given locale. Each locale may specify its
82 * own character for a particular field, but the position
83 * of these characters must correspond to an appropriate field
84 * number (as listed below), in order for their meaning to
85 * be determined. For example, the US locale uses
86 * the string "GyMdkHmsSEDFwWahKzYeugAZ", where 'G' is the character
87 * for era, 'y' for year, and so on down to 'Z' for time zone.
90 * Represents the position of the era
91 * pattern character in the array of
92 * localized pattern characters.
93 * For example, 'AD' is an era used
94 * in the Gregorian calendar system.
95 * In the U.S. locale, this is 'G'.
97 public static final int ERA_FIELD
= 0;
99 * Represents the position of the year
100 * pattern character in the array of
101 * localized pattern characters.
102 * In the U.S. locale, this is 'y'.
104 public static final int YEAR_FIELD
= 1;
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'.
111 public static final int MONTH_FIELD
= 2;
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,
119 public static final int DATE_FIELD
= 3;
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.
127 public static final int HOUR_OF_DAY1_FIELD
= 4;
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.
135 public static final int HOUR_OF_DAY0_FIELD
= 5;
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'.
142 public static final int MINUTE_FIELD
= 6;
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'.
149 public static final int SECOND_FIELD
= 7;
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'.
156 public static final int MILLISECOND_FIELD
= 8;
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'.
163 public static final int DAY_OF_WEEK_FIELD
= 9;
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'.
170 public static final int DAY_OF_YEAR_FIELD
= 10;
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'.
177 public static final int DAY_OF_WEEK_IN_MONTH_FIELD
= 11;
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'.
184 public static final int WEEK_OF_YEAR_FIELD
= 12;
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'.
191 public static final int WEEK_OF_MONTH_FIELD
= 13;
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'.
198 public static final int AM_PM_FIELD
= 14;
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.
206 public static final int HOUR1_FIELD
= 15;
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.
214 public static final int HOUR0_FIELD
= 16;
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'.
221 public static final int TIMEZONE_FIELD
= 17;
223 public static class Field
extends Format
.Field
225 static final long serialVersionUID
= 7441350119349544720L;
227 private int calendarField
;
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
);
266 static final DateFormat
.Field
[] allFields
=
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
,
275 // For deserialization
281 protected Field(String name
, int calendarField
)
284 this.calendarField
= calendarField
;
287 public int getCalendarField()
289 return calendarField
;
292 public static Field
ofCalendarField(int calendarField
)
294 if (calendarField
>= allFields
.length
|| calendarField
< 0)
295 throw new IllegalArgumentException("no such calendar field ("
296 + calendarField
+ ")");
298 return allFields
[calendarField
];
301 protected Object
readResolve() throws InvalidObjectException
303 String s
= getName();
305 for (int i
=0;i
<allFields
.length
;i
++)
306 if (s
.equals(allFields
[i
].getName()))
309 throw new InvalidObjectException("no such DateFormat field called " + s
);
314 * This method initializes a new instance of <code>DateFormat</code>.
316 protected DateFormat ()
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
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
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.
341 * @param obj The object to test for equality against.
343 * @return <code>true</code> if the specified object is equal to this object,
344 * <code>false</code> otherwise.
346 public boolean equals (Object obj
)
348 if (!(obj
instanceof DateFormat
))
351 DateFormat d
= (DateFormat
) obj
;
352 TimeZone tz
= getTimeZone();
353 TimeZone tzd
= d
.getTimeZone();
354 if (tz
.hasSameRules(tzd
))
355 if (isLenient() == d
.isLenient())
357 Calendar c
= getCalendar();
358 Calendar cd
= d
.getCalendar();
359 if ((c
== null && cd
== null)
361 (c
.getFirstDayOfWeek() == cd
.getFirstDayOfWeek()
363 c
.getMinimalDaysInFirstWeek()
364 == cd
.getMinimalDaysInFirstWeek()))
365 return ((numberFormat
== null && d
.numberFormat
== null)
366 || numberFormat
.equals(d
.numberFormat
));
373 * This method returns a copy of this object.
375 * @return A copy of this object.
377 public Object
clone ()
379 // We know the superclass just call's Object's generic cloner.
380 return super.clone ();
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
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
396 * @return The <code>StringBuffer</code> supplied on input, with the
397 * formatted date/time appended.
399 public final StringBuffer
format (Object obj
,
400 StringBuffer buf
, FieldPosition pos
)
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");
408 return format ((Date
) obj
, buf
, pos
);
412 * Formats the date argument according to the pattern specified.
414 * @param date The formatted date.
416 public final String
format (Date date
)
418 StringBuffer sb
= new StringBuffer ();
419 format (date
, sb
, new FieldPosition (MONTH_FIELD
));
420 return sb
.toString();
424 * This method formats a <code>Date</code> into a string and appends it
425 * to the specified <code>StringBuffer</code>.
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
433 * @return The <code>StringBuffer</code> supplied on input, with the
434 * formatted date/time appended.
436 public abstract StringBuffer
format (Date date
,
437 StringBuffer buf
, FieldPosition pos
);
440 * This method returns a list of available locales supported by this
443 public static Locale
[] getAvailableLocales()
445 return Locale
.getAvailableLocales();
449 * This method returns the <code>Calendar</code> object being used by
450 * this object to parse/format datetimes.
452 * @return The <code>Calendar</code> being used by this object.
454 * @see java.util.Calendar
456 public Calendar
getCalendar ()
461 private static DateFormat
computeInstance (int style
, Locale loc
,
462 boolean use_date
, boolean use_time
)
464 return computeInstance (style
, style
, loc
, use_date
, use_time
);
467 private static DateFormat
computeInstance (int dateStyle
, int timeStyle
,
468 Locale loc
, boolean use_date
,
470 throws MissingResourceException
472 if (loc
.equals(Locale
.ROOT
))
473 return computeDefault(dateStyle
,timeStyle
,use_date
,use_time
);
476 ResourceBundle
.getBundle("gnu.java.locale.LocaleInformation",
477 loc
, ClassLoader
.getSystemClassLoader());
479 String pattern
= null;
486 name
= "fullDateFormat";
487 def
= "EEEE MMMM d, yyyy G";
490 name
= "longDateFormat";
491 def
= "MMMM d, yyyy";
494 name
= "mediumDateFormat";
498 name
= "shortDateFormat";
502 throw new IllegalArgumentException ();
506 pattern
= res
== null ? def
: res
.getString(name
);
508 catch (MissingResourceException x
)
525 name
= "fullTimeFormat";
526 def
= "h:mm:ss;S 'o''clock' a z";
529 name
= "longTimeFormat";
533 name
= "mediumTimeFormat";
537 name
= "shortTimeFormat";
541 throw new IllegalArgumentException ();
547 s
= res
== null ? def
: res
.getString(name
);
549 catch (MissingResourceException x
)
556 return new SimpleDateFormat (pattern
, loc
);
559 private static DateFormat
computeDefault (int dateStyle
, int timeStyle
,
560 boolean use_date
, boolean use_time
)
562 String pattern
= null;
568 pattern
= "EEEE MMMM d, yyyy G";
571 pattern
= "MMMM d, yyyy";
574 pattern
= "d-MMM-yy";
579 throw new IllegalArgumentException ();
593 pattern
+= "h:mm:ss;S 'o''clock' a z";
596 pattern
+= "h:mm:ss a z";
599 pattern
+= "h:mm:ss a";
605 throw new IllegalArgumentException ();
609 return new SimpleDateFormat (pattern
, Locale
.ROOT
);
613 * This method returns an instance of <code>DateFormat</code> that will
614 * format using the default formatting style for dates.
616 * @return A new <code>DateFormat</code> instance.
618 public static final DateFormat
getDateInstance ()
620 return getDateInstance (DEFAULT
, Locale
.getDefault());
624 * This method returns an instance of <code>DateFormat</code> that will
625 * format using the specified formatting style for dates.
627 * @param style The type of formatting to perform.
629 * @return A new <code>DateFormat</code> instance.
631 public static final DateFormat
getDateInstance (int style
)
633 return getDateInstance (style
, Locale
.getDefault());
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.
641 * @param style The type of formatting to perform.
642 * @param loc The desired locale.
644 * @return A new <code>DateFormat</code> instance.
646 public static final DateFormat
getDateInstance (int style
, Locale loc
)
650 return computeInstance (style
, loc
, true, false);
652 catch (MissingResourceException e
)
654 for (DateFormatProvider p
:
655 ServiceLoader
.load(DateFormatProvider
.class))
657 for (Locale l
: p
.getAvailableLocales())
661 DateFormat df
= p
.getDateInstance(style
, loc
);
668 return getDateInstance(style
,
669 LocaleHelper
.getFallbackLocale(loc
));
674 * This method returns a new instance of <code>DateFormat</code> that
675 * formats both dates and times using the <code>SHORT</code> style.
677 * @return A new <code>DateFormat</code>instance.
679 public static final DateFormat
getDateTimeInstance ()
681 return getDateTimeInstance (DEFAULT
, DEFAULT
, Locale
.getDefault());
685 * This method returns a new instance of <code>DateFormat</code> that
686 * formats both dates and times using the <code>DEFAULT</code> style.
688 * @return A new <code>DateFormat</code>instance.
690 public static final DateFormat
getDateTimeInstance (int dateStyle
,
693 return getDateTimeInstance (dateStyle
, timeStyle
, Locale
.getDefault());
697 * This method returns a new instance of <code>DateFormat</code> that
698 * formats both dates and times using the specified styles.
700 * @param dateStyle The desired style for date formatting.
701 * @param timeStyle The desired style for time formatting
703 * @return A new <code>DateFormat</code>instance.
705 public static final DateFormat
getDateTimeInstance (int dateStyle
,
711 return computeInstance (dateStyle
, timeStyle
, loc
, true, true);
713 catch (MissingResourceException e
)
715 for (DateFormatProvider p
:
716 ServiceLoader
.load(DateFormatProvider
.class))
718 for (Locale l
: p
.getAvailableLocales())
722 DateFormat df
= p
.getDateTimeInstance(dateStyle
,
730 return getDateTimeInstance(dateStyle
, timeStyle
,
731 LocaleHelper
.getFallbackLocale(loc
));
736 * This method returns a new instance of <code>DateFormat</code> that
737 * formats both dates and times using the <code>SHORT</code> style.
739 * @return A new <code>DateFormat</code>instance.
741 public static final DateFormat
getInstance ()
743 // JCL book says SHORT.
744 return getDateTimeInstance (SHORT
, SHORT
, Locale
.getDefault());
748 * This method returns the <code>NumberFormat</code> object being used
749 * by this object to parse/format time values.
751 * @return The <code>NumberFormat</code> in use by this object.
753 public NumberFormat
getNumberFormat ()
759 * This method returns an instance of <code>DateFormat</code> that will
760 * format using the default formatting style for times.
762 * @return A new <code>DateFormat</code> instance.
764 public static final DateFormat
getTimeInstance ()
766 return getTimeInstance (DEFAULT
, Locale
.getDefault());
770 * This method returns an instance of <code>DateFormat</code> that will
771 * format using the specified formatting style for times.
773 * @param style The type of formatting to perform.
775 * @return A new <code>DateFormat</code> instance.
777 public static final DateFormat
getTimeInstance (int style
)
779 return getTimeInstance (style
, Locale
.getDefault());
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.
787 * @param style The type of formatting to perform.
788 * @param loc The desired locale.
790 * @return A new <code>DateFormat</code> instance.
792 public static final DateFormat
getTimeInstance (int style
, Locale loc
)
796 return computeInstance (style
, loc
, false, true);
798 catch (MissingResourceException e
)
800 for (DateFormatProvider p
:
801 ServiceLoader
.load(DateFormatProvider
.class))
803 for (Locale l
: p
.getAvailableLocales())
807 DateFormat df
= p
.getTimeInstance(style
, loc
);
814 return getTimeInstance(style
,
815 LocaleHelper
.getFallbackLocale(loc
));
820 * This method returns the <code>TimeZone</code> object being used by
823 * @return The time zone in use.
825 public TimeZone
getTimeZone ()
827 return calendar
.getTimeZone();
831 * This method returns a hash value for this object.
833 * @return A hash value for this object.
835 public int hashCode ()
837 if (numberFormat
!= null)
838 return numberFormat
.hashCode();
844 * This method indicates whether or not the parsing of date and time
845 * values should be done in a lenient value.
847 * @return <code>true</code> if date/time parsing is lenient,
848 * <code>false</code> otherwise.
850 public boolean isLenient ()
852 return calendar
.isLenient();
856 * This method parses the specified date/time string.
858 * @param source The string to parse.
859 * @return The resultant date.
861 * @exception ParseException If the specified string cannot be parsed.
863 public Date
parse (String source
) throws ParseException
865 ParsePosition pos
= new ParsePosition(0);
866 Date result
= parse (source
, pos
);
869 int index
= pos
.getErrorIndex();
871 index
= pos
.getIndex();
872 throw new ParseException("invalid Date syntax in \""
873 + source
+ '\"', index
);
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.
884 * @param source The string to parse.
885 * @param pos The starting parse position in entry, the ending parse
888 * @return The parsed date, or <code>null</code> if the string cannot
891 public abstract Date
parse (String source
, ParsePosition pos
);
894 * This method is identical to <code>parse(String, ParsePosition)</code>,
895 * but returns its result as an <code>Object</code> instead of a
898 * @param source The string to parse.
899 * @param pos The starting parse position in entry, the ending parse
902 * @return The parsed date, or <code>null</code> if the string cannot
905 public Object
parseObject (String source
, ParsePosition pos
)
907 return parse(source
, pos
);
911 * This method specified the <code>Calendar</code> that should be used
912 * by this object to parse/format datetimes.
914 * @param calendar The new <code>Calendar</code> for this object.
916 * @see java.util.Calendar
918 public void setCalendar (Calendar calendar
)
920 this.calendar
= calendar
;
924 * This method specifies whether or not this object should be lenient in
925 * the syntax it accepts while parsing date/time values.
927 * @param lenient <code>true</code> if parsing should be lenient,
928 * <code>false</code> otherwise.
930 public void setLenient (boolean lenient
)
932 calendar
.setLenient(lenient
);
936 * This method specifies the <code>NumberFormat</code> object that should
937 * be used by this object to parse/format times.
939 * @param numberFormat The <code>NumberFormat</code> in use by this object.
941 public void setNumberFormat (NumberFormat numberFormat
)
943 this.numberFormat
= numberFormat
;
947 * This method sets the time zone that should be used by this object.
949 * @param timeZone The new time zone.
951 public void setTimeZone (TimeZone timeZone
)
953 calendar
.setTimeZone(timeZone
);