575 public SimpleDateFormat() {
576 this("", Locale.getDefault(Locale.Category.FORMAT));
577 applyPatternImpl(LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(locale)
578 .getDateTimePattern(SHORT, SHORT, calendar));
579 }
580
581 /**
582 * Constructs a <code>SimpleDateFormat</code> using the given pattern and
583 * the default date format symbols for the default
584 * {@link java.util.Locale.Category#FORMAT FORMAT} locale.
585 * <b>Note:</b> This constructor may not support all locales.
586 * For full coverage, use the factory methods in the {@link DateFormat}
587 * class.
588 * <p>This is equivalent to calling
589 * {@link #SimpleDateFormat(String, Locale)
590 * SimpleDateFormat(pattern, Locale.getDefault(Locale.Category.FORMAT))}.
591 *
592 * @see java.util.Locale#getDefault(java.util.Locale.Category)
593 * @see java.util.Locale.Category#FORMAT
594 * @param pattern the pattern describing the date and time format
595 * @exception NullPointerException if the given pattern is null
596 * @exception IllegalArgumentException if the given pattern is invalid
597 */
598 public SimpleDateFormat(String pattern)
599 {
600 this(pattern, Locale.getDefault(Locale.Category.FORMAT));
601 }
602
603 /**
604 * Constructs a <code>SimpleDateFormat</code> using the given pattern and
605 * the default date format symbols for the given locale.
606 * <b>Note:</b> This constructor may not support all locales.
607 * For full coverage, use the factory methods in the {@link DateFormat}
608 * class.
609 *
610 * @param pattern the pattern describing the date and time format
611 * @param locale the locale whose date format symbols should be used
612 * @exception NullPointerException if the given pattern or locale is null
613 * @exception IllegalArgumentException if the given pattern is invalid
614 */
615 public SimpleDateFormat(String pattern, Locale locale)
616 {
617 if (pattern == null || locale == null) {
618 throw new NullPointerException();
619 }
620
621 initializeCalendar(locale);
622 this.pattern = pattern;
623 this.formatData = DateFormatSymbols.getInstanceRef(locale);
624 this.locale = locale;
625 initialize(locale);
626 }
627
628 /**
629 * Constructs a <code>SimpleDateFormat</code> using the given pattern and
630 * date format symbols.
631 *
632 * @param pattern the pattern describing the date and time format
633 * @param formatSymbols the date format symbols to be used for formatting
634 * @exception NullPointerException if the given pattern or formatSymbols is null
635 * @exception IllegalArgumentException if the given pattern is invalid
636 */
637 public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols)
638 {
639 if (pattern == null || formatSymbols == null) {
640 throw new NullPointerException();
641 }
642
643 this.pattern = pattern;
644 this.formatData = (DateFormatSymbols) formatSymbols.clone();
645 this.locale = Locale.getDefault(Locale.Category.FORMAT);
646 initializeCalendar(this.locale);
647 initialize(this.locale);
648 useDateFormatSymbols = true;
649 }
650
651 /* Initialize compiledPattern and numberFormat fields */
652 private void initialize(Locale loc) {
653 // Verify and compile the given pattern.
654 compiledPattern = compile(pattern);
655
722 * <blockquote>
723 * char[0] = (Tag << 8) | 255;
724 * char[1] = (char) (long_length >>> 16);
725 * char[2] = (char) (long_length & 0xffff);
726 * </blockquote>
727 * <p>
728 * If Tag is a pattern_char_index, its Length is the number of
729 * pattern characters. For example, if the given pattern is
730 * "yyyy", Tag is 1 and Length is 4, followed by no data.
731 * <p>
732 * If Tag is TAG_QUOTE_CHARS, its Length is the number of char's
733 * following the TagField. For example, if the given pattern is
734 * "'o''clock'", Length is 7 followed by a char sequence of
735 * <code>o&nbs;'&nbs;c&nbs;l&nbs;o&nbs;c&nbs;k</code>.
736 * <p>
737 * TAG_QUOTE_ASCII_CHAR is a special tag and has an ASCII
738 * character in place of Length. For example, if the given pattern
739 * is "'o'", the TaggedData entry is
740 * <code>((TAG_QUOTE_ASCII_CHAR&nbs;<<&nbs;8)&nbs;|&nbs;'o')</code>.
741 *
742 * @exception NullPointerException if the given pattern is null
743 * @exception IllegalArgumentException if the given pattern is invalid
744 */
745 private char[] compile(String pattern) {
746 int length = pattern.length();
747 boolean inQuote = false;
748 StringBuilder compiledCode = new StringBuilder(length * 2);
749 StringBuilder tmpBuffer = null;
750 int count = 0, tagcount = 0;
751 int lastTag = -1, prevTag = -1;
752
753 for (int i = 0; i < length; i++) {
754 char c = pattern.charAt(i);
755
756 if (c == '\'') {
757 // '' is treated as a single quote regardless of being
758 // in a quoted section.
759 if ((i + 1) < length) {
760 c = pattern.charAt(i + 1);
761 if (c == '\'') {
762 i++;
763 if (count != 0) {
941 /**
942 * Formats the given <code>Date</code> into a date/time string and appends
943 * the result to the given <code>StringBuffer</code>.
944 *
945 * @param date the date-time value to be formatted into a date-time string.
946 * @param toAppendTo where the new date-time text is to be appended.
947 * @param pos keeps track on the position of the field within
948 * the returned string. For example, given a date-time text
949 * {@code "1996.07.10 AD at 15:08:56 PDT"}, if the given {@code fieldPosition}
950 * is {@link DateFormat#YEAR_FIELD}, the begin index and end index of
951 * {@code fieldPosition} will be set to 0 and 4, respectively.
952 * Notice that if the same date-time field appears more than once in a
953 * pattern, the {@code fieldPosition} will be set for the first occurrence
954 * of that date-time field. For instance, formatting a {@code Date} to the
955 * date-time string {@code "1 PM PDT (Pacific Daylight Time)"} using the
956 * pattern {@code "h a z (zzzz)"} and the alignment field
957 * {@link DateFormat#TIMEZONE_FIELD}, the begin index and end index of
958 * {@code fieldPosition} will be set to 5 and 8, respectively, for the
959 * first occurrence of the timezone pattern character {@code 'z'}.
960 * @return the formatted date-time string.
961 * @exception NullPointerException if any of the parameters is {@code null}.
962 */
963 @Override
964 public StringBuffer format(Date date, StringBuffer toAppendTo,
965 FieldPosition pos)
966 {
967 pos.beginIndex = pos.endIndex = 0;
968 return format(date, toAppendTo, pos.getFieldDelegate());
969 }
970
971 // Called from Format after creating a FieldDelegate
972 private StringBuffer format(Date date, StringBuffer toAppendTo,
973 FieldDelegate delegate) {
974 // Convert input date to time field list
975 calendar.setTime(date);
976
977 boolean useDateFormatSymbols = useDateFormatSymbols();
978
979 for (int i = 0; i < compiledPattern.length; ) {
980 int tag = compiledPattern[i] >>> 8;
981 int count = compiledPattern[i++] & 0xff;
995 break;
996
997 default:
998 subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
999 break;
1000 }
1001 }
1002 return toAppendTo;
1003 }
1004
1005 /**
1006 * Formats an Object producing an <code>AttributedCharacterIterator</code>.
1007 * You can use the returned <code>AttributedCharacterIterator</code>
1008 * to build the resulting String, as well as to determine information
1009 * about the resulting String.
1010 * <p>
1011 * Each attribute key of the AttributedCharacterIterator will be of type
1012 * <code>DateFormat.Field</code>, with the corresponding attribute value
1013 * being the same as the attribute key.
1014 *
1015 * @exception NullPointerException if obj is null.
1016 * @exception IllegalArgumentException if the Format cannot format the
1017 * given object, or if the Format's pattern string is invalid.
1018 * @param obj The object to format
1019 * @return AttributedCharacterIterator describing the formatted value.
1020 * @since 1.4
1021 */
1022 @Override
1023 public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
1024 StringBuffer sb = new StringBuffer();
1025 CharacterIteratorFieldDelegate delegate = new
1026 CharacterIteratorFieldDelegate();
1027
1028 if (obj instanceof Date) {
1029 format((Date)obj, sb, delegate);
1030 }
1031 else if (obj instanceof Number) {
1032 format(new Date(((Number)obj).longValue()), sb, delegate);
1033 }
1034 else if (obj == null) {
1035 throw new NullPointerException(
1036 "formatToCharacterIterator must be passed non-null object");
1442 *
1443 * <p>This parsing operation uses the {@link DateFormat#calendar
1444 * calendar} to produce a {@code Date}. All of the {@code
1445 * calendar}'s date-time fields are {@linkplain Calendar#clear()
1446 * cleared} before parsing, and the {@code calendar}'s default
1447 * values of the date-time fields are used for any missing
1448 * date-time information. For example, the year value of the
1449 * parsed {@code Date} is 1970 with {@link GregorianCalendar} if
1450 * no year value is given from the parsing operation. The {@code
1451 * TimeZone} value may be overwritten, depending on the given
1452 * pattern and the time zone value in {@code text}. Any {@code
1453 * TimeZone} value that has previously been set by a call to
1454 * {@link #setTimeZone(java.util.TimeZone) setTimeZone} may need
1455 * to be restored for further operations.
1456 *
1457 * @param text A <code>String</code>, part of which should be parsed.
1458 * @param pos A <code>ParsePosition</code> object with index and error
1459 * index information as described above.
1460 * @return A <code>Date</code> parsed from the string. In case of
1461 * error, returns null.
1462 * @exception NullPointerException if <code>text</code> or <code>pos</code> is null.
1463 */
1464 @Override
1465 public Date parse(String text, ParsePosition pos)
1466 {
1467 checkNegativeNumberExpression();
1468
1469 int start = pos.index;
1470 int oldStart = start;
1471 int textLength = text.length();
1472
1473 boolean[] ambiguousYear = {false};
1474
1475 CalendarBuilder calb = new CalendarBuilder();
1476
1477 for (int i = 0; i < compiledPattern.length; ) {
1478 int tag = compiledPattern[i] >>> 8;
1479 int count = compiledPattern[i++] & 0xff;
1480 if (count == 255) {
1481 count = compiledPattern[i++] << 16;
1482 count |= compiledPattern[i++];
2259 }
2260 }
2261
2262 // Parsing failed.
2263 origPos.errorIndex = pos.index;
2264 return -1;
2265 }
2266
2267 /**
2268 * Returns true if the DateFormatSymbols has been set explicitly or locale
2269 * is null.
2270 */
2271 private boolean useDateFormatSymbols() {
2272 return useDateFormatSymbols || locale == null;
2273 }
2274
2275 /**
2276 * Translates a pattern, mapping each character in the from string to the
2277 * corresponding character in the to string.
2278 *
2279 * @exception IllegalArgumentException if the given pattern is invalid
2280 */
2281 private String translatePattern(String pattern, String from, String to) {
2282 StringBuilder result = new StringBuilder();
2283 boolean inQuote = false;
2284 for (int i = 0; i < pattern.length(); ++i) {
2285 char c = pattern.charAt(i);
2286 if (inQuote) {
2287 if (c == '\'') {
2288 inQuote = false;
2289 }
2290 }
2291 else {
2292 if (c == '\'') {
2293 inQuote = true;
2294 } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2295 int ci = from.indexOf(c);
2296 if (ci >= 0) {
2297 // patternChars is longer than localPatternChars due
2298 // to serialization compatibility. The pattern letters
2299 // unsupported by localPatternChars pass through.
2322 */
2323 public String toPattern() {
2324 return pattern;
2325 }
2326
2327 /**
2328 * Returns a localized pattern string describing this date format.
2329 *
2330 * @return a localized pattern string describing this date format.
2331 */
2332 public String toLocalizedPattern() {
2333 return translatePattern(pattern,
2334 DateFormatSymbols.patternChars,
2335 formatData.getLocalPatternChars());
2336 }
2337
2338 /**
2339 * Applies the given pattern string to this date format.
2340 *
2341 * @param pattern the new date and time pattern for this date format
2342 * @exception NullPointerException if the given pattern is null
2343 * @exception IllegalArgumentException if the given pattern is invalid
2344 */
2345 public void applyPattern(String pattern)
2346 {
2347 applyPatternImpl(pattern);
2348 }
2349
2350 private void applyPatternImpl(String pattern) {
2351 compiledPattern = compile(pattern);
2352 this.pattern = pattern;
2353 }
2354
2355 /**
2356 * Applies the given localized pattern string to this date format.
2357 *
2358 * @param pattern a String to be mapped to the new date and time format
2359 * pattern for this format
2360 * @exception NullPointerException if the given pattern is null
2361 * @exception IllegalArgumentException if the given pattern is invalid
2362 */
2363 public void applyLocalizedPattern(String pattern) {
2364 String p = translatePattern(pattern,
2365 formatData.getLocalPatternChars(),
2366 DateFormatSymbols.patternChars);
2367 compiledPattern = compile(p);
2368 this.pattern = p;
2369 }
2370
2371 /**
2372 * Gets a copy of the date and time format symbols of this date format.
2373 *
2374 * @return the date and time format symbols of this date format
2375 * @see #setDateFormatSymbols
2376 */
2377 public DateFormatSymbols getDateFormatSymbols()
2378 {
2379 return (DateFormatSymbols)formatData.clone();
2380 }
2381
2382 /**
2383 * Sets the date and time format symbols of this date format.
2384 *
2385 * @param newFormatSymbols the new date and time format symbols
2386 * @exception NullPointerException if the given newFormatSymbols is null
2387 * @see #getDateFormatSymbols
2388 */
2389 public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols)
2390 {
2391 this.formatData = (DateFormatSymbols)newFormatSymbols.clone();
2392 useDateFormatSymbols = true;
2393 }
2394
2395 /**
2396 * Creates a copy of this <code>SimpleDateFormat</code>. This also
2397 * clones the format's date format symbols.
2398 *
2399 * @return a clone of this <code>SimpleDateFormat</code>
2400 */
2401 @Override
2402 public Object clone() {
2403 SimpleDateFormat other = (SimpleDateFormat) super.clone();
2404 other.formatData = (DateFormatSymbols) formatData.clone();
2405 return other;
2406 }
2453 /**
2454 * Obtains display names map, taking the context into account. Currently only
2455 * the month name pattern 'M' is context dependent.
2456 */
2457 private Map<String, Integer> getDisplayContextNamesMap(int field, Locale locale) {
2458 Map<String, Integer> map = calendar.getDisplayNames(field,
2459 forceStandaloneForm ? Calendar.SHORT_STANDALONE : Calendar.SHORT_FORMAT, locale);
2460 // Get the LONG style
2461 Map<String, Integer> m = calendar.getDisplayNames(field,
2462 forceStandaloneForm ? Calendar.LONG_STANDALONE : Calendar.LONG_FORMAT, locale);
2463 if (m != null) {
2464 map.putAll(m);
2465 }
2466 return map;
2467 }
2468
2469 /**
2470 * After reading an object from the input stream, the format
2471 * pattern in the object is verified.
2472 *
2473 * @exception InvalidObjectException if the pattern is invalid
2474 */
2475 @java.io.Serial
2476 private void readObject(ObjectInputStream stream)
2477 throws IOException, ClassNotFoundException {
2478 stream.defaultReadObject();
2479
2480 try {
2481 compiledPattern = compile(pattern);
2482 } catch (Exception e) {
2483 throw new InvalidObjectException("invalid pattern");
2484 }
2485
2486 if (serialVersionOnStream < 1) {
2487 // didn't have defaultCenturyStart field
2488 initializeDefaultCentury();
2489 }
2490 else {
2491 // fill in dependent transient field
2492 parseAmbiguousDatesAsAfter(defaultCenturyStart);
2493 }
|
575 public SimpleDateFormat() {
576 this("", Locale.getDefault(Locale.Category.FORMAT));
577 applyPatternImpl(LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(locale)
578 .getDateTimePattern(SHORT, SHORT, calendar));
579 }
580
581 /**
582 * Constructs a <code>SimpleDateFormat</code> using the given pattern and
583 * the default date format symbols for the default
584 * {@link java.util.Locale.Category#FORMAT FORMAT} locale.
585 * <b>Note:</b> This constructor may not support all locales.
586 * For full coverage, use the factory methods in the {@link DateFormat}
587 * class.
588 * <p>This is equivalent to calling
589 * {@link #SimpleDateFormat(String, Locale)
590 * SimpleDateFormat(pattern, Locale.getDefault(Locale.Category.FORMAT))}.
591 *
592 * @see java.util.Locale#getDefault(java.util.Locale.Category)
593 * @see java.util.Locale.Category#FORMAT
594 * @param pattern the pattern describing the date and time format
595 * @throws NullPointerException if the given pattern is null
596 * @throws IllegalArgumentException if the given pattern is invalid
597 */
598 public SimpleDateFormat(String pattern)
599 {
600 this(pattern, Locale.getDefault(Locale.Category.FORMAT));
601 }
602
603 /**
604 * Constructs a <code>SimpleDateFormat</code> using the given pattern and
605 * the default date format symbols for the given locale.
606 * <b>Note:</b> This constructor may not support all locales.
607 * For full coverage, use the factory methods in the {@link DateFormat}
608 * class.
609 *
610 * @param pattern the pattern describing the date and time format
611 * @param locale the locale whose date format symbols should be used
612 * @throws NullPointerException if the given pattern or locale is null
613 * @throws IllegalArgumentException if the given pattern is invalid
614 */
615 public SimpleDateFormat(String pattern, Locale locale)
616 {
617 if (pattern == null || locale == null) {
618 throw new NullPointerException();
619 }
620
621 initializeCalendar(locale);
622 this.pattern = pattern;
623 this.formatData = DateFormatSymbols.getInstanceRef(locale);
624 this.locale = locale;
625 initialize(locale);
626 }
627
628 /**
629 * Constructs a <code>SimpleDateFormat</code> using the given pattern and
630 * date format symbols.
631 *
632 * @param pattern the pattern describing the date and time format
633 * @param formatSymbols the date format symbols to be used for formatting
634 * @throws NullPointerException if the given pattern or formatSymbols is null
635 * @throws IllegalArgumentException if the given pattern is invalid
636 */
637 public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols)
638 {
639 if (pattern == null || formatSymbols == null) {
640 throw new NullPointerException();
641 }
642
643 this.pattern = pattern;
644 this.formatData = (DateFormatSymbols) formatSymbols.clone();
645 this.locale = Locale.getDefault(Locale.Category.FORMAT);
646 initializeCalendar(this.locale);
647 initialize(this.locale);
648 useDateFormatSymbols = true;
649 }
650
651 /* Initialize compiledPattern and numberFormat fields */
652 private void initialize(Locale loc) {
653 // Verify and compile the given pattern.
654 compiledPattern = compile(pattern);
655
722 * <blockquote>
723 * char[0] = (Tag << 8) | 255;
724 * char[1] = (char) (long_length >>> 16);
725 * char[2] = (char) (long_length & 0xffff);
726 * </blockquote>
727 * <p>
728 * If Tag is a pattern_char_index, its Length is the number of
729 * pattern characters. For example, if the given pattern is
730 * "yyyy", Tag is 1 and Length is 4, followed by no data.
731 * <p>
732 * If Tag is TAG_QUOTE_CHARS, its Length is the number of char's
733 * following the TagField. For example, if the given pattern is
734 * "'o''clock'", Length is 7 followed by a char sequence of
735 * <code>o&nbs;'&nbs;c&nbs;l&nbs;o&nbs;c&nbs;k</code>.
736 * <p>
737 * TAG_QUOTE_ASCII_CHAR is a special tag and has an ASCII
738 * character in place of Length. For example, if the given pattern
739 * is "'o'", the TaggedData entry is
740 * <code>((TAG_QUOTE_ASCII_CHAR&nbs;<<&nbs;8)&nbs;|&nbs;'o')</code>.
741 *
742 * @throws NullPointerException if the given pattern is null
743 * @throws IllegalArgumentException if the given pattern is invalid
744 */
745 private char[] compile(String pattern) {
746 int length = pattern.length();
747 boolean inQuote = false;
748 StringBuilder compiledCode = new StringBuilder(length * 2);
749 StringBuilder tmpBuffer = null;
750 int count = 0, tagcount = 0;
751 int lastTag = -1, prevTag = -1;
752
753 for (int i = 0; i < length; i++) {
754 char c = pattern.charAt(i);
755
756 if (c == '\'') {
757 // '' is treated as a single quote regardless of being
758 // in a quoted section.
759 if ((i + 1) < length) {
760 c = pattern.charAt(i + 1);
761 if (c == '\'') {
762 i++;
763 if (count != 0) {
941 /**
942 * Formats the given <code>Date</code> into a date/time string and appends
943 * the result to the given <code>StringBuffer</code>.
944 *
945 * @param date the date-time value to be formatted into a date-time string.
946 * @param toAppendTo where the new date-time text is to be appended.
947 * @param pos keeps track on the position of the field within
948 * the returned string. For example, given a date-time text
949 * {@code "1996.07.10 AD at 15:08:56 PDT"}, if the given {@code fieldPosition}
950 * is {@link DateFormat#YEAR_FIELD}, the begin index and end index of
951 * {@code fieldPosition} will be set to 0 and 4, respectively.
952 * Notice that if the same date-time field appears more than once in a
953 * pattern, the {@code fieldPosition} will be set for the first occurrence
954 * of that date-time field. For instance, formatting a {@code Date} to the
955 * date-time string {@code "1 PM PDT (Pacific Daylight Time)"} using the
956 * pattern {@code "h a z (zzzz)"} and the alignment field
957 * {@link DateFormat#TIMEZONE_FIELD}, the begin index and end index of
958 * {@code fieldPosition} will be set to 5 and 8, respectively, for the
959 * first occurrence of the timezone pattern character {@code 'z'}.
960 * @return the formatted date-time string.
961 * @throws NullPointerException if any of the parameters is {@code null}.
962 */
963 @Override
964 public StringBuffer format(Date date, StringBuffer toAppendTo,
965 FieldPosition pos)
966 {
967 pos.beginIndex = pos.endIndex = 0;
968 return format(date, toAppendTo, pos.getFieldDelegate());
969 }
970
971 // Called from Format after creating a FieldDelegate
972 private StringBuffer format(Date date, StringBuffer toAppendTo,
973 FieldDelegate delegate) {
974 // Convert input date to time field list
975 calendar.setTime(date);
976
977 boolean useDateFormatSymbols = useDateFormatSymbols();
978
979 for (int i = 0; i < compiledPattern.length; ) {
980 int tag = compiledPattern[i] >>> 8;
981 int count = compiledPattern[i++] & 0xff;
995 break;
996
997 default:
998 subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
999 break;
1000 }
1001 }
1002 return toAppendTo;
1003 }
1004
1005 /**
1006 * Formats an Object producing an <code>AttributedCharacterIterator</code>.
1007 * You can use the returned <code>AttributedCharacterIterator</code>
1008 * to build the resulting String, as well as to determine information
1009 * about the resulting String.
1010 * <p>
1011 * Each attribute key of the AttributedCharacterIterator will be of type
1012 * <code>DateFormat.Field</code>, with the corresponding attribute value
1013 * being the same as the attribute key.
1014 *
1015 * @throws NullPointerException if obj is null.
1016 * @throws IllegalArgumentException if the Format cannot format the
1017 * given object, or if the Format's pattern string is invalid.
1018 * @param obj The object to format
1019 * @return AttributedCharacterIterator describing the formatted value.
1020 * @since 1.4
1021 */
1022 @Override
1023 public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
1024 StringBuffer sb = new StringBuffer();
1025 CharacterIteratorFieldDelegate delegate = new
1026 CharacterIteratorFieldDelegate();
1027
1028 if (obj instanceof Date) {
1029 format((Date)obj, sb, delegate);
1030 }
1031 else if (obj instanceof Number) {
1032 format(new Date(((Number)obj).longValue()), sb, delegate);
1033 }
1034 else if (obj == null) {
1035 throw new NullPointerException(
1036 "formatToCharacterIterator must be passed non-null object");
1442 *
1443 * <p>This parsing operation uses the {@link DateFormat#calendar
1444 * calendar} to produce a {@code Date}. All of the {@code
1445 * calendar}'s date-time fields are {@linkplain Calendar#clear()
1446 * cleared} before parsing, and the {@code calendar}'s default
1447 * values of the date-time fields are used for any missing
1448 * date-time information. For example, the year value of the
1449 * parsed {@code Date} is 1970 with {@link GregorianCalendar} if
1450 * no year value is given from the parsing operation. The {@code
1451 * TimeZone} value may be overwritten, depending on the given
1452 * pattern and the time zone value in {@code text}. Any {@code
1453 * TimeZone} value that has previously been set by a call to
1454 * {@link #setTimeZone(java.util.TimeZone) setTimeZone} may need
1455 * to be restored for further operations.
1456 *
1457 * @param text A <code>String</code>, part of which should be parsed.
1458 * @param pos A <code>ParsePosition</code> object with index and error
1459 * index information as described above.
1460 * @return A <code>Date</code> parsed from the string. In case of
1461 * error, returns null.
1462 * @throws NullPointerException if <code>text</code> or <code>pos</code> is null.
1463 */
1464 @Override
1465 public Date parse(String text, ParsePosition pos)
1466 {
1467 checkNegativeNumberExpression();
1468
1469 int start = pos.index;
1470 int oldStart = start;
1471 int textLength = text.length();
1472
1473 boolean[] ambiguousYear = {false};
1474
1475 CalendarBuilder calb = new CalendarBuilder();
1476
1477 for (int i = 0; i < compiledPattern.length; ) {
1478 int tag = compiledPattern[i] >>> 8;
1479 int count = compiledPattern[i++] & 0xff;
1480 if (count == 255) {
1481 count = compiledPattern[i++] << 16;
1482 count |= compiledPattern[i++];
2259 }
2260 }
2261
2262 // Parsing failed.
2263 origPos.errorIndex = pos.index;
2264 return -1;
2265 }
2266
2267 /**
2268 * Returns true if the DateFormatSymbols has been set explicitly or locale
2269 * is null.
2270 */
2271 private boolean useDateFormatSymbols() {
2272 return useDateFormatSymbols || locale == null;
2273 }
2274
2275 /**
2276 * Translates a pattern, mapping each character in the from string to the
2277 * corresponding character in the to string.
2278 *
2279 * @throws IllegalArgumentException if the given pattern is invalid
2280 */
2281 private String translatePattern(String pattern, String from, String to) {
2282 StringBuilder result = new StringBuilder();
2283 boolean inQuote = false;
2284 for (int i = 0; i < pattern.length(); ++i) {
2285 char c = pattern.charAt(i);
2286 if (inQuote) {
2287 if (c == '\'') {
2288 inQuote = false;
2289 }
2290 }
2291 else {
2292 if (c == '\'') {
2293 inQuote = true;
2294 } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2295 int ci = from.indexOf(c);
2296 if (ci >= 0) {
2297 // patternChars is longer than localPatternChars due
2298 // to serialization compatibility. The pattern letters
2299 // unsupported by localPatternChars pass through.
2322 */
2323 public String toPattern() {
2324 return pattern;
2325 }
2326
2327 /**
2328 * Returns a localized pattern string describing this date format.
2329 *
2330 * @return a localized pattern string describing this date format.
2331 */
2332 public String toLocalizedPattern() {
2333 return translatePattern(pattern,
2334 DateFormatSymbols.patternChars,
2335 formatData.getLocalPatternChars());
2336 }
2337
2338 /**
2339 * Applies the given pattern string to this date format.
2340 *
2341 * @param pattern the new date and time pattern for this date format
2342 * @throws NullPointerException if the given pattern is null
2343 * @throws IllegalArgumentException if the given pattern is invalid
2344 */
2345 public void applyPattern(String pattern)
2346 {
2347 applyPatternImpl(pattern);
2348 }
2349
2350 private void applyPatternImpl(String pattern) {
2351 compiledPattern = compile(pattern);
2352 this.pattern = pattern;
2353 }
2354
2355 /**
2356 * Applies the given localized pattern string to this date format.
2357 *
2358 * @param pattern a String to be mapped to the new date and time format
2359 * pattern for this format
2360 * @throws NullPointerException if the given pattern is null
2361 * @throws IllegalArgumentException if the given pattern is invalid
2362 */
2363 public void applyLocalizedPattern(String pattern) {
2364 String p = translatePattern(pattern,
2365 formatData.getLocalPatternChars(),
2366 DateFormatSymbols.patternChars);
2367 compiledPattern = compile(p);
2368 this.pattern = p;
2369 }
2370
2371 /**
2372 * Gets a copy of the date and time format symbols of this date format.
2373 *
2374 * @return the date and time format symbols of this date format
2375 * @see #setDateFormatSymbols
2376 */
2377 public DateFormatSymbols getDateFormatSymbols()
2378 {
2379 return (DateFormatSymbols)formatData.clone();
2380 }
2381
2382 /**
2383 * Sets the date and time format symbols of this date format.
2384 *
2385 * @param newFormatSymbols the new date and time format symbols
2386 * @throws NullPointerException if the given newFormatSymbols is null
2387 * @see #getDateFormatSymbols
2388 */
2389 public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols)
2390 {
2391 this.formatData = (DateFormatSymbols)newFormatSymbols.clone();
2392 useDateFormatSymbols = true;
2393 }
2394
2395 /**
2396 * Creates a copy of this <code>SimpleDateFormat</code>. This also
2397 * clones the format's date format symbols.
2398 *
2399 * @return a clone of this <code>SimpleDateFormat</code>
2400 */
2401 @Override
2402 public Object clone() {
2403 SimpleDateFormat other = (SimpleDateFormat) super.clone();
2404 other.formatData = (DateFormatSymbols) formatData.clone();
2405 return other;
2406 }
2453 /**
2454 * Obtains display names map, taking the context into account. Currently only
2455 * the month name pattern 'M' is context dependent.
2456 */
2457 private Map<String, Integer> getDisplayContextNamesMap(int field, Locale locale) {
2458 Map<String, Integer> map = calendar.getDisplayNames(field,
2459 forceStandaloneForm ? Calendar.SHORT_STANDALONE : Calendar.SHORT_FORMAT, locale);
2460 // Get the LONG style
2461 Map<String, Integer> m = calendar.getDisplayNames(field,
2462 forceStandaloneForm ? Calendar.LONG_STANDALONE : Calendar.LONG_FORMAT, locale);
2463 if (m != null) {
2464 map.putAll(m);
2465 }
2466 return map;
2467 }
2468
2469 /**
2470 * After reading an object from the input stream, the format
2471 * pattern in the object is verified.
2472 *
2473 * @throws InvalidObjectException if the pattern is invalid
2474 */
2475 @java.io.Serial
2476 private void readObject(ObjectInputStream stream)
2477 throws IOException, ClassNotFoundException {
2478 stream.defaultReadObject();
2479
2480 try {
2481 compiledPattern = compile(pattern);
2482 } catch (Exception e) {
2483 throw new InvalidObjectException("invalid pattern");
2484 }
2485
2486 if (serialVersionOnStream < 1) {
2487 // didn't have defaultCenturyStart field
2488 initializeDefaultCentury();
2489 }
2490 else {
2491 // fill in dependent transient field
2492 parseAmbiguousDatesAsAfter(defaultCenturyStart);
2493 }
|