001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.text;
019:
020: import java.io.InvalidObjectException;
021: import java.util.Calendar;
022: import java.util.Date;
023: import java.util.Hashtable;
024: import java.util.Locale;
025: import java.util.TimeZone;
026:
027: import org.apache.harmony.text.internal.nls.Messages;
028:
029: /**
030: * DateFormat is the abstract superclass of formats which format and parse
031: * Dates.
032: */
033: public abstract class DateFormat extends Format {
034:
035: private static final long serialVersionUID = 7218322306649953788L;
036:
037: protected Calendar calendar;
038:
039: protected NumberFormat numberFormat;
040:
041: /**
042: * Format style constant.
043: */
044: public final static int DEFAULT = 2;
045:
046: /**
047: * Format style constant.
048: */
049: public final static int FULL = 0;
050:
051: /**
052: * Format style constant.
053: */
054: public final static int LONG = 1;
055:
056: /**
057: * Format style constant.
058: */
059: public final static int MEDIUM = 2;
060:
061: /**
062: * Format style constant.
063: */
064: public final static int SHORT = 3;
065:
066: /**
067: * Field constant.
068: */
069: public final static int ERA_FIELD = 0;
070:
071: /**
072: * Field constant.
073: */
074: public final static int YEAR_FIELD = 1;
075:
076: /**
077: * Field constant.
078: */
079: public final static int MONTH_FIELD = 2;
080:
081: /**
082: * Field constant.
083: */
084: public final static int DATE_FIELD = 3;
085:
086: /**
087: * Field constant.
088: */
089: public final static int HOUR_OF_DAY1_FIELD = 4;
090:
091: /**
092: * Field constant.
093: */
094: public final static int HOUR_OF_DAY0_FIELD = 5;
095:
096: /**
097: * Field constant.
098: */
099: public final static int MINUTE_FIELD = 6;
100:
101: /**
102: * Field constant.
103: */
104: public final static int SECOND_FIELD = 7;
105:
106: /**
107: * Field constant.
108: */
109: public final static int MILLISECOND_FIELD = 8;
110:
111: /**
112: * Field constant.
113: */
114: public final static int DAY_OF_WEEK_FIELD = 9;
115:
116: /**
117: * Field constant.
118: */
119: public final static int DAY_OF_YEAR_FIELD = 10;
120:
121: /**
122: * Field constant.
123: */
124: public final static int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
125:
126: /**
127: * Field constant.
128: */
129: public final static int WEEK_OF_YEAR_FIELD = 12;
130:
131: /**
132: * Field constant.
133: */
134: public final static int WEEK_OF_MONTH_FIELD = 13;
135:
136: /**
137: * Field constant.
138: */
139: public final static int AM_PM_FIELD = 14;
140:
141: /**
142: * Field constant.
143: */
144: public final static int HOUR1_FIELD = 15;
145:
146: /**
147: * Field constant.
148: */
149: public final static int HOUR0_FIELD = 16;
150:
151: /**
152: * Field constant.
153: */
154: public final static int TIMEZONE_FIELD = 17;
155:
156: /**
157: * Constructs a new instance of DateFormat.
158: *
159: */
160: protected DateFormat() {
161: }
162:
163: /**
164: * Answers a new instance of DateFormat with the same properties.
165: *
166: * @return a shallow copy of this DateFormat
167: *
168: * @see java.lang.Cloneable
169: */
170: @Override
171: public Object clone() {
172: DateFormat clone = (DateFormat) super .clone();
173: clone.calendar = (Calendar) calendar.clone();
174: clone.numberFormat = (NumberFormat) numberFormat.clone();
175: return clone;
176: }
177:
178: /**
179: * Compares the specified object to this DateFormat and answer if they are
180: * equal. The object must be an instance of DateFormat with the same
181: * properties.
182: *
183: * @param object
184: * the object to compare with this object
185: * @return true if the specified object is equal to this DateFormat, false
186: * otherwise
187: *
188: * @see #hashCode
189: */
190: @Override
191: public boolean equals(Object object) {
192: if (this == object) {
193: return true;
194: }
195: if (!(object instanceof DateFormat)) {
196: return false;
197: }
198: DateFormat dateFormat = (DateFormat) object;
199: return numberFormat.equals(dateFormat.numberFormat)
200: && calendar.getTimeZone().equals(
201: dateFormat.calendar.getTimeZone())
202: && calendar.getFirstDayOfWeek() == dateFormat.calendar
203: .getFirstDayOfWeek()
204: && calendar.getMinimalDaysInFirstWeek() == dateFormat.calendar
205: .getMinimalDaysInFirstWeek()
206: && calendar.isLenient() == dateFormat.calendar
207: .isLenient();
208: }
209:
210: /**
211: * Formats the specified object into the specified StringBuffer using the
212: * rules of this DateFormat. If the field specified by the FieldPosition is
213: * formatted, set the begin and end index of the formatted field in the
214: * FieldPosition.
215: *
216: * @param object
217: * the object to format, must be a Date or a Number. If the
218: * object is a Number, a Date is constructed using the
219: * <code>longValue()</code> of the Number.
220: * @param buffer
221: * the StringBuffer
222: * @param field
223: * the FieldPosition
224: * @return the StringBuffer parameter <code>buffer</code>
225: *
226: * @exception IllegalArgumentException
227: * when the object is not a Date or a Number
228: */
229: @Override
230: public final StringBuffer format(Object object,
231: StringBuffer buffer, FieldPosition field) {
232: if (object instanceof Date) {
233: return format((Date) object, buffer, field);
234: }
235: if (object instanceof Number) {
236: return format(new Date(((Number) object).longValue()),
237: buffer, field);
238: }
239: throw new IllegalArgumentException();
240: }
241:
242: /**
243: * Formats the specified Date using the rules of this DateFormat.
244: *
245: * @param date
246: * the Date to format
247: * @return the formatted String
248: */
249: public final String format(Date date) {
250: return format(date, new StringBuffer(), new FieldPosition(0))
251: .toString();
252: }
253:
254: /**
255: * Formats the specified Date into the specified StringBuffer using the
256: * rules of this DateFormat. If the field specified by the FieldPosition is
257: * formatted, set the begin and end index of the formatted field in the
258: * FieldPosition.
259: *
260: * @param date
261: * the Date to format
262: * @param buffer
263: * the StringBuffer
264: * @param field
265: * the FieldPosition
266: * @return the StringBuffer parameter <code>buffer</code>
267: */
268: public abstract StringBuffer format(Date date, StringBuffer buffer,
269: FieldPosition field);
270:
271: /**
272: * Gets the list of installed Locales which support DateFormat.
273: *
274: * @return an array of Locale
275: */
276: public static Locale[] getAvailableLocales() {
277: return Locale.getAvailableLocales();
278: }
279:
280: /**
281: * Answers the Calendar used by this DateFormat.
282: *
283: * @return a Calendar
284: */
285: public Calendar getCalendar() {
286: return calendar;
287: }
288:
289: /**
290: * Answers a DateFormat instance for formatting and parsing dates in the
291: * DEFAULT style for the default Locale.
292: *
293: * @return a DateFormat
294: */
295: public final static DateFormat getDateInstance() {
296: return getDateInstance(DEFAULT);
297: }
298:
299: /**
300: * Answers a DateFormat instance for formatting and parsing dates in the
301: * specified style for the default Locale.
302: *
303: * @param style
304: * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT
305: * @return a DateFormat
306: */
307: public final static DateFormat getDateInstance(int style) {
308: checkDateStyle(style);
309: return getDateInstance(style, Locale.getDefault());
310: }
311:
312: /**
313: * Answers a DateFormat instance for formatting and parsing dates in the
314: * specified style for the specified Locale.
315: *
316: * @param style
317: * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT
318: * @param locale
319: * the Locale
320: * @return a DateFormat
321: */
322: public final static DateFormat getDateInstance(int style,
323: Locale locale) {
324: checkDateStyle(style);
325: com.ibm.icu.text.DateFormat icuFormat = com.ibm.icu.text.DateFormat
326: .getDateInstance(style, locale);
327: return new SimpleDateFormat(locale,
328: (com.ibm.icu.text.SimpleDateFormat) icuFormat);
329: }
330:
331: /**
332: * Answers a DateFormat instance for formatting and parsing dates and times
333: * in the DEFAULT style for the default Locale.
334: *
335: * @return a DateFormat
336: */
337: public final static DateFormat getDateTimeInstance() {
338: return getDateTimeInstance(DEFAULT, DEFAULT);
339: }
340:
341: /**
342: * Answers a <code>DateFormat</code> instance for the formatting and
343: * parsing of both dates and times in the manner appropriate to the default
344: * Locale.
345: *
346: * @param dateStyle
347: * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT
348: * @param timeStyle
349: * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT
350: * @return a DateFormat
351: */
352: public final static DateFormat getDateTimeInstance(int dateStyle,
353: int timeStyle) {
354: checkTimeStyle(timeStyle);
355: checkDateStyle(dateStyle);
356: return getDateTimeInstance(dateStyle, timeStyle, Locale
357: .getDefault());
358: }
359:
360: /**
361: * Answers a DateFormat instance for formatting and parsing dates and times
362: * in the specified styles for the specified Locale.
363: *
364: * @param dateStyle
365: * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT
366: * @param timeStyle
367: * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT
368: * @param locale
369: * the Locale
370: * @return a DateFormat
371: */
372: public final static DateFormat getDateTimeInstance(int dateStyle,
373: int timeStyle, Locale locale) {
374: checkTimeStyle(timeStyle);
375: checkDateStyle(dateStyle);
376: com.ibm.icu.text.DateFormat icuFormat = com.ibm.icu.text.DateFormat
377: .getDateTimeInstance(dateStyle, timeStyle, locale);
378: return new SimpleDateFormat(locale,
379: (com.ibm.icu.text.SimpleDateFormat) icuFormat);
380: }
381:
382: /**
383: * Answers a DateFormat instance for formatting and parsing dates and times
384: * in the SHORT style for the default Locale.
385: *
386: * @return a DateFormat
387: */
388: public final static DateFormat getInstance() {
389: return getDateTimeInstance(SHORT, SHORT);
390: }
391:
392: /**
393: * Answers the NumberFormat used by this DateFormat.
394: *
395: * @return a NumberFormat
396: */
397: public NumberFormat getNumberFormat() {
398: return numberFormat;
399: }
400:
401: static String getStyleName(int style) {
402: String styleName;
403: switch (style) {
404: case SHORT:
405: styleName = "SHORT"; //$NON-NLS-1$
406: break;
407: case MEDIUM:
408: styleName = "MEDIUM"; //$NON-NLS-1$
409: break;
410: case LONG:
411: styleName = "LONG"; //$NON-NLS-1$
412: break;
413: case FULL:
414: styleName = "FULL"; //$NON-NLS-1$
415: break;
416: default:
417: styleName = ""; //$NON-NLS-1$
418: }
419: return styleName;
420: }
421:
422: /**
423: * Answers a DateFormat instance for formatting and parsing times in the
424: * DEFAULT style for the default Locale.
425: *
426: * @return a DateFormat
427: */
428: public final static DateFormat getTimeInstance() {
429: return getTimeInstance(DEFAULT);
430: }
431:
432: /**
433: * Answers a DateFormat instance for formatting and parsing times in the
434: * specified style for the default Locale.
435: *
436: * @param style
437: * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT
438: * @return a DateFormat
439: */
440: public final static DateFormat getTimeInstance(int style) {
441: checkTimeStyle(style);
442: return getTimeInstance(style, Locale.getDefault());
443: }
444:
445: /**
446: * Answers a DateFormat instance for formatting and parsing times in the
447: * specified style for the specified Locale.
448: *
449: * @param style
450: * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT
451: * @param locale
452: * the Locale
453: * @return a DateFormat
454: */
455: public final static DateFormat getTimeInstance(int style,
456: Locale locale) {
457: checkTimeStyle(style);
458: com.ibm.icu.text.DateFormat icuFormat = com.ibm.icu.text.DateFormat
459: .getTimeInstance(style, locale);
460: return new SimpleDateFormat(locale,
461: (com.ibm.icu.text.SimpleDateFormat) icuFormat);
462: }
463:
464: /**
465: * Answers the TimeZone of the Calendar used by this DateFormat.
466: *
467: * @return a TimeZone
468: */
469: public TimeZone getTimeZone() {
470: return calendar.getTimeZone();
471: }
472:
473: /**
474: * Answers an integer hash code for the receiver. Objects which are equal
475: * answer the same value for this method.
476: *
477: * @return the receiver's hash
478: *
479: * @see #equals
480: */
481: @Override
482: public int hashCode() {
483: return calendar.getFirstDayOfWeek()
484: + calendar.getMinimalDaysInFirstWeek()
485: + calendar.getTimeZone().hashCode()
486: + (calendar.isLenient() ? 1231 : 1237)
487: + numberFormat.hashCode();
488: }
489:
490: /**
491: * Answers if the Calendar used by this DateFormat is lenient.
492: *
493: * @return true when the Calendar is lenient, false otherwise
494: */
495: public boolean isLenient() {
496: return calendar.isLenient();
497: }
498:
499: /**
500: * Parse a Date from the specified String using the rules of this
501: * DateFormat.
502: *
503: * @param string
504: * the String to parse
505: * @return the Date resulting from the parse
506: *
507: * @exception ParseException
508: * when an error occurs during parsing
509: */
510: public Date parse(String string) throws ParseException {
511: ParsePosition position = new ParsePosition(0);
512: Date date = parse(string, position);
513: if (position.getErrorIndex() != -1 || position.getIndex() == 0) {
514: // text.19=Unparseable date: {0}
515: throw new ParseException(Messages.getString(
516: "text.19", string), position.getErrorIndex()); //$NON-NLS-1$
517: }
518: return date;
519: }
520:
521: /**
522: * Parse a Date from the specified String starting at the index specified by
523: * the ParsePosition. If the string is successfully parsed, the index of the
524: * ParsePosition is updated to the index following the parsed text.
525: *
526: * @param string
527: * the String to parse
528: * @param position
529: * the ParsePosition, updated on return with the index following
530: * the parsed text, or on error the index is unchanged and the
531: * error index is set to the index where the error occurred
532: * @return the Date resulting from the parse, or null if there is an error
533: */
534: public abstract Date parse(String string, ParsePosition position);
535:
536: /**
537: * Parse a Date from the specified String starting at the index specified by
538: * the ParsePosition. If the string is successfully parsed, the index of the
539: * ParsePosition is updated to the index following the parsed text.
540: *
541: * @param string
542: * the String to parse
543: * @param position
544: * the ParsePosition, updated on return with the index following
545: * the parsed text, or on error the index is unchanged and the
546: * error index is set to the index where the error occurred
547: * @return the Date resulting from the parse, or null if there is an error
548: */
549: @Override
550: public Object parseObject(String string, ParsePosition position) {
551: return parse(string, position);
552: }
553:
554: /**
555: * Sets the Calendar used by this DateFormat.
556: *
557: * @param cal
558: * the Calendar
559: */
560: public void setCalendar(Calendar cal) {
561: calendar = cal;
562: }
563:
564: /**
565: * Sets if the Calendar used by this DateFormat is lenient.
566: *
567: * @param value
568: * true to set the Calendar to be lenient, false otherwise
569: */
570: public void setLenient(boolean value) {
571: calendar.setLenient(value);
572: }
573:
574: /**
575: * Sets the NumberFormat used by this DateFormat.
576: *
577: * @param format
578: * the NumberFormat
579: */
580: public void setNumberFormat(NumberFormat format) {
581: numberFormat = format;
582: }
583:
584: /**
585: * Sets the TimeZone of the Calendar used by this DateFormat.
586: *
587: * @param timezone
588: * the TimeZone
589: */
590: public void setTimeZone(TimeZone timezone) {
591: calendar.setTimeZone(timezone);
592: }
593:
594: /**
595: * The instances of this inner class are used as attribute keys and values
596: * in AttributedCharacterIterator that
597: * SimpleDateFormat.formatToCharacterIterator() method returns.
598: * <p>
599: * There is no public constructor to this class, the only instances are the
600: * constants defined here.
601: * <p>
602: */
603: public static class Field extends Format.Field {
604:
605: private static final long serialVersionUID = 7441350119349544720L;
606:
607: private static Hashtable<Integer, Field> table = new Hashtable<Integer, Field>();
608:
609: public final static Field ERA = new Field("era", Calendar.ERA); //$NON-NLS-1$
610:
611: public final static Field YEAR = new Field(
612: "year", Calendar.YEAR); //$NON-NLS-1$
613:
614: public final static Field MONTH = new Field(
615: "month", Calendar.MONTH); //$NON-NLS-1$
616:
617: public final static Field HOUR_OF_DAY0 = new Field(
618: "hour of day", //$NON-NLS-1$
619: Calendar.HOUR_OF_DAY);
620:
621: public final static Field HOUR_OF_DAY1 = new Field(
622: "hour of day 1", -1); //$NON-NLS-1$
623:
624: public final static Field MINUTE = new Field(
625: "minute", Calendar.MINUTE); //$NON-NLS-1$
626:
627: public final static Field SECOND = new Field(
628: "second", Calendar.SECOND); //$NON-NLS-1$
629:
630: public final static Field MILLISECOND = new Field(
631: "millisecond", //$NON-NLS-1$
632: Calendar.MILLISECOND);
633:
634: public final static Field DAY_OF_WEEK = new Field(
635: "day of week", //$NON-NLS-1$
636: Calendar.DAY_OF_WEEK);
637:
638: public final static Field DAY_OF_MONTH = new Field(
639: "day of month", //$NON-NLS-1$
640: Calendar.DAY_OF_MONTH);
641:
642: public final static Field DAY_OF_YEAR = new Field(
643: "day of year", //$NON-NLS-1$
644: Calendar.DAY_OF_YEAR);
645:
646: public final static Field DAY_OF_WEEK_IN_MONTH = new Field(
647: "day of week in month", Calendar.DAY_OF_WEEK_IN_MONTH); //$NON-NLS-1$
648:
649: public final static Field WEEK_OF_YEAR = new Field(
650: "week of year", //$NON-NLS-1$
651: Calendar.WEEK_OF_YEAR);
652:
653: public final static Field WEEK_OF_MONTH = new Field(
654: "week of month", //$NON-NLS-1$
655: Calendar.WEEK_OF_MONTH);
656:
657: public final static Field AM_PM = new Field(
658: "am pm", Calendar.AM_PM); //$NON-NLS-1$
659:
660: public final static Field HOUR0 = new Field(
661: "hour", Calendar.HOUR); //$NON-NLS-1$
662:
663: public final static Field HOUR1 = new Field("hour 1", -1); //$NON-NLS-1$
664:
665: public final static Field TIME_ZONE = new Field("time zone", -1); //$NON-NLS-1$
666:
667: /**
668: * The Calendar field that this Field represents.
669: */
670: private int calendarField = -1;
671:
672: /**
673: * Constructs a new instance of DateFormat.Field with the given
674: * fieldName and calendar field.
675: */
676: protected Field(String fieldName, int calendarField) {
677: super (fieldName);
678: this .calendarField = calendarField;
679: if (calendarField != -1
680: && table.get(new Integer(calendarField)) == null) {
681: table.put(new Integer(calendarField), this );
682: }
683: }
684:
685: /**
686: * Answers the Calendar field this Field represents
687: *
688: * @return int calendar field
689: */
690: public int getCalendarField() {
691: return calendarField;
692: }
693:
694: /**
695: * Answers the DateFormat.Field instance for the given calendar field
696: *
697: * @param calendarField
698: * a calendar field constant
699: * @return null if there is no Field for this calendar field
700: */
701: public static Field ofCalendarField(int calendarField) {
702: if (calendarField < 0
703: || calendarField >= Calendar.FIELD_COUNT) {
704: throw new IllegalArgumentException();
705: }
706:
707: return table.get(new Integer(calendarField));
708: }
709:
710: /**
711: * Serialization method resolve instances to the constant
712: * DateFormat.Field values
713: */
714: @Override
715: protected Object readResolve() throws InvalidObjectException {
716: if (calendarField != -1) {
717: try {
718: Field result = ofCalendarField(calendarField);
719: if (result != null && this .equals(result)) {
720: return result;
721: }
722: } catch (IllegalArgumentException e) {
723: // text.02=Unknown attribute
724: throw new InvalidObjectException(Messages
725: .getString("text.02")); //$NON-NLS-1$
726: }
727: } else {
728: if (this .equals(TIME_ZONE)) {
729: return TIME_ZONE;
730: }
731: if (this .equals(HOUR1)) {
732: return HOUR1;
733: }
734: if (this .equals(HOUR_OF_DAY1)) {
735: return HOUR_OF_DAY1;
736: }
737: }
738: // text.02=Unknown attribute
739: throw new InvalidObjectException(Messages
740: .getString("text.02")); //$NON-NLS-1$
741: }
742: }
743:
744: private static void checkDateStyle(int style) {
745: if (!(style == SHORT || style == MEDIUM || style == LONG
746: || style == FULL || style == DEFAULT)) {
747: // text.0E=Illegal date style: {0}
748: throw new IllegalArgumentException(Messages.getString(
749: "text.0E", style)); //$NON-NLS-1$
750: }
751: }
752:
753: private static void checkTimeStyle(int style) {
754: if (!(style == SHORT || style == MEDIUM || style == LONG
755: || style == FULL || style == DEFAULT)) {
756: // text.0F=Illegal time style: {0}
757: throw new IllegalArgumentException(Messages.getString(
758: "text.0F", style)); //$NON-NLS-1$
759: }
760: }
761: }
|