| 1 7 8 20 21 package java.text; 22 23 import java.util.TimeZone ; 24 import java.util.Calendar ; 25 import java.util.Date ; 26 import java.util.Locale ; 27 import java.util.ResourceBundle ; 28 import java.util.SimpleTimeZone ; 29 import java.util.GregorianCalendar ; 30 import java.io.ObjectInputStream ; 31 import java.io.InvalidObjectException ; 32 import java.io.IOException ; 33 import java.lang.ClassNotFoundException ; 34 import java.util.Hashtable ; 35 import java.lang.StringIndexOutOfBoundsException ; 36 import sun.text.resources.LocaleData; 37 import sun.util.calendar.CalendarUtils; 38 import sun.util.calendar.ZoneInfoFile; 39 40 315 public class SimpleDateFormat extends DateFormat { 316 317 static final long serialVersionUID = 4774881970558875024L; 320 321 static final int currentSerialVersion = 1; 325 326 340 private int serialVersionOnStream = currentSerialVersion; 341 342 347 private String pattern; 348 349 352 transient private char[] compiledPattern; 353 354 357 private final static int TAG_QUOTE_ASCII_CHAR = 100; 358 private final static int TAG_QUOTE_CHARS = 101; 359 360 365 transient private char zeroDigit; 366 367 373 private DateFormatSymbols formatData; 374 375 382 private Date defaultCenturyStart; 383 384 transient private int defaultCenturyStartYear; 385 386 private static final int millisPerHour = 60 * 60 * 1000; 387 private static final int millisPerMinute = 60 * 1000; 388 389 private static final String GMT = "GMT"; 392 393 396 private static Hashtable cachedLocaleData = new Hashtable (3); 397 398 401 private static Hashtable cachedNumberFormatData = new Hashtable (3); 402 403 410 public SimpleDateFormat() { 411 this(SHORT, SHORT, Locale.getDefault()); 412 } 413 414 425 public SimpleDateFormat(String pattern) 426 { 427 this(pattern, Locale.getDefault()); 428 } 429 430 442 public SimpleDateFormat(String pattern, Locale locale) 443 { 444 this.pattern = pattern; 445 this.formatData = new DateFormatSymbols (locale); 446 initialize(locale); 447 } 448 449 458 public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols) 459 { 460 this.pattern = pattern; 461 this.formatData = (DateFormatSymbols ) formatSymbols.clone(); 462 initialize(Locale.getDefault()); 463 } 464 465 466 SimpleDateFormat(int timeStyle, int dateStyle, Locale loc) { 467 468 String [] dateTimePatterns = (String []) cachedLocaleData.get(loc); 469 if (dateTimePatterns == null) { 470 ResourceBundle r = LocaleData.getLocaleElements(loc); 471 dateTimePatterns = r.getStringArray("DateTimePatterns"); 472 473 cachedLocaleData.put(loc, dateTimePatterns); 474 } 475 formatData = new DateFormatSymbols (loc); 476 if ((timeStyle >= 0) && (dateStyle >= 0)) { 477 Object [] dateTimeArgs = {dateTimePatterns[timeStyle], 478 dateTimePatterns[dateStyle + 4]}; 479 pattern = MessageFormat.format(dateTimePatterns[8], dateTimeArgs); 480 } 481 else if (timeStyle >= 0) { 482 pattern = dateTimePatterns[timeStyle]; 483 } 484 else if (dateStyle >= 0) { 485 pattern = dateTimePatterns[dateStyle + 4]; 486 } 487 else { 488 throw new IllegalArgumentException ("No date or time style specified"); 489 } 490 491 initialize(loc); 492 } 493 494 495 private void initialize(Locale loc) { 496 compiledPattern = compile(pattern); 498 499 calendar = Calendar.getInstance(TimeZone.getDefault(), loc); 504 505 506 numberFormat = (NumberFormat ) cachedNumberFormatData.get(loc); 507 if (numberFormat == null) { 508 numberFormat = NumberFormat.getIntegerInstance(loc); 509 numberFormat.setGroupingUsed(false); 510 511 512 cachedNumberFormatData.put(loc, numberFormat); 513 } 514 numberFormat = (NumberFormat ) numberFormat.clone(); 515 516 initializeDefaultCentury(); 517 } 518 519 583 private char[] compile(String pattern) { 584 int length = pattern.length(); 585 boolean inQuote = false; 586 StringBuilder compiledPattern = new StringBuilder (length * 2); 587 StringBuilder tmpBuffer = null; 588 int count = 0; 589 int lastTag = -1; 590 591 for (int i = 0; i < length; i++) { 592 char c = pattern.charAt(i); 593 594 if (c == '\'') { 595 if ((i + 1) < length) { 598 c = pattern.charAt(i + 1); 599 if (c == '\'') { 600 i++; 601 if (count != 0) { 602 encode(lastTag, count, compiledPattern); 603 lastTag = -1; 604 count = 0; 605 } 606 if (inQuote) { 607 tmpBuffer.append(c); 608 } else { 609 compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c)); 610 } 611 continue; 612 } 613 } 614 if (!inQuote) { 615 if (count != 0) { 616 encode(lastTag, count, compiledPattern); 617 lastTag = -1; 618 count = 0; 619 } 620 if (tmpBuffer == null) { 621 tmpBuffer = new StringBuilder (length); 622 } else { 623 tmpBuffer.setLength(0); 624 } 625 inQuote = true; 626 } else { 627 int len = tmpBuffer.length(); 628 if (len == 1) { 629 char ch = tmpBuffer.charAt(0); 630 if (ch < 128) { 631 compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | ch)); 632 } else { 633 compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | 1)); 634 compiledPattern.append(ch); 635 } 636 } else { 637 encode(TAG_QUOTE_CHARS, len, compiledPattern); 638 compiledPattern.append(tmpBuffer); 639 } 640 inQuote = false; 641 } 642 continue; 643 } 644 if (inQuote) { 645 tmpBuffer.append(c); 646 continue; 647 } 648 if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) { 649 if (count != 0) { 650 encode(lastTag, count, compiledPattern); 651 lastTag = -1; 652 count = 0; 653 } 654 if (c < 128) { 655 compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c)); 657 } else { 658 int j; 661 for (j = i + 1; j < length; j++) { 662 char d = pattern.charAt(j); 663 if (d == '\'' || (d >= 'a' && d <= 'z' || d >= 'A' && d <= 'Z')) { 664 break; 665 } 666 } 667 compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | (j - i))); 668 for (; i < j; i++) { 669 compiledPattern.append(pattern.charAt(i)); 670 } 671 i--; 672 } 673 continue; 674 } 675 676 int tag; 677 if ((tag = formatData.patternChars.indexOf(c)) == -1) { 678 throw new IllegalArgumentException ("Illegal pattern character " + 679 "'" + c + "'"); 680 } 681 if (lastTag == -1 || lastTag == tag) { 682 lastTag = tag; 683 count++; 684 continue; 685 } 686 encode(lastTag, count, compiledPattern); 687 lastTag = tag; 688 count = 1; 689 } 690 691 if (inQuote) { 692 throw new IllegalArgumentException ("Unterminated quote"); 693 } 694 695 if (count != 0) { 696 encode(lastTag, count, compiledPattern); 697 } 698 699 int len = compiledPattern.length(); 701 char[] r = new char[len]; 702 compiledPattern.getChars(0, len, r, 0); 703 return r; 704 } 705 706 709 private static final void encode(int tag, int length, StringBuilder buffer) { 710 if (length < 255) { 711 buffer.append((char)(tag << 8 | length)); 712 } else { 713 buffer.append((char)((tag << 8) | 0xff)); 714 buffer.append((char)(length >>> 16)); 715 buffer.append((char)(length & 0xffff)); 716 } 717 } 718 719 722 private void initializeDefaultCentury() { 723 calendar.setTime( new Date () ); 724 calendar.add( Calendar.YEAR, -80 ); 725 parseAmbiguousDatesAsAfter(calendar.getTime()); 726 } 727 728 731 private void parseAmbiguousDatesAsAfter(Date startDate) { 732 defaultCenturyStart = startDate; 733 calendar.setTime(startDate); 734 defaultCenturyStartYear = calendar.get(Calendar.YEAR); 735 } 736 737 746 public void set2DigitYearStart(Date startDate) { 747 parseAmbiguousDatesAsAfter(startDate); 748 } 749 750 759 public Date get2DigitYearStart() { 760 return defaultCenturyStart; 761 } 762 763 774 public StringBuffer format(Date date, StringBuffer toAppendTo, 775 FieldPosition pos) 776 { 777 pos.beginIndex = pos.endIndex = 0; 778 return format(date, toAppendTo, pos.getFieldDelegate()); 779 } 780 781 private StringBuffer format(Date date, StringBuffer toAppendTo, 783 FieldDelegate delegate) { 784 calendar.setTime(date); 786 787 for (int i = 0; i < compiledPattern.length; ) { 788 int tag = compiledPattern[i] >>> 8; 789 int count = compiledPattern[i++] & 0xff; 790 if (count == 255) { 791 count = compiledPattern[i++] << 16; 792 count |= compiledPattern[i++]; 793 } 794 795 switch (tag) { 796 case TAG_QUOTE_ASCII_CHAR: 797 toAppendTo.append((char)count); 798 break; 799 800 case TAG_QUOTE_CHARS: 801 toAppendTo.append(compiledPattern, i, count); 802 i += count; 803 break; 804 805 default: 806 subFormat(tag, count, delegate, toAppendTo); 807 break; 808 } 809 } 810 return toAppendTo; 811 } 812 813 830 public AttributedCharacterIterator formatToCharacterIterator(Object obj) { 831 StringBuffer sb = new StringBuffer (); 832 CharacterIteratorFieldDelegate delegate = new 833 CharacterIteratorFieldDelegate (); 834 835 if (obj instanceof Date ) { 836 format((Date )obj, sb, delegate); 837 } 838 else if (obj instanceof Number ) { 839 format(new Date (((Number )obj).longValue()), sb, delegate); 840 } 841 else if (obj == null) { 842 throw new NullPointerException ( 843 "formatToCharacterIterator must be passed non-null object"); 844 } 845 else { 846 throw new IllegalArgumentException ( 847 "Cannot format given Object as a Date"); 848 } 849 return delegate.getIterator(sb.toString()); 850 } 851 852 private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD = 854 { 855 Calendar.ERA, Calendar.YEAR, Calendar.MONTH, Calendar.DATE, 856 Calendar.HOUR_OF_DAY, Calendar.HOUR_OF_DAY, Calendar.MINUTE, 857 Calendar.SECOND, Calendar.MILLISECOND, Calendar.DAY_OF_WEEK, 858 Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK_IN_MONTH, 859 Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH, 860 Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR, Calendar.ZONE_OFFSET, 861 Calendar.ZONE_OFFSET 862 }; 863 864 private static final int[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD = { 866 DateFormat.ERA_FIELD, DateFormat.YEAR_FIELD, DateFormat.MONTH_FIELD, 867 DateFormat.DATE_FIELD, DateFormat.HOUR_OF_DAY1_FIELD, 868 DateFormat.HOUR_OF_DAY0_FIELD, DateFormat.MINUTE_FIELD, 869 DateFormat.SECOND_FIELD, DateFormat.MILLISECOND_FIELD, 870 DateFormat.DAY_OF_WEEK_FIELD, DateFormat.DAY_OF_YEAR_FIELD, 871 DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD, DateFormat.WEEK_OF_YEAR_FIELD, 872 DateFormat.WEEK_OF_MONTH_FIELD, DateFormat.AM_PM_FIELD, 873 DateFormat.HOUR1_FIELD, DateFormat.HOUR0_FIELD, 874 DateFormat.TIMEZONE_FIELD, DateFormat.TIMEZONE_FIELD, 875 }; 876 877 private static final Field[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID = { 879 Field.ERA, Field.YEAR, Field.MONTH, Field.DAY_OF_MONTH, 880 Field.HOUR_OF_DAY1, Field.HOUR_OF_DAY0, Field.MINUTE, 881 Field.SECOND, Field.MILLISECOND, Field.DAY_OF_WEEK, 882 Field.DAY_OF_YEAR, Field.DAY_OF_WEEK_IN_MONTH, 883 Field.WEEK_OF_YEAR, Field.WEEK_OF_MONTH, 884 Field.AM_PM, Field.HOUR1, Field.HOUR0, Field.TIME_ZONE, 885 Field.TIME_ZONE, 886 }; 887 888 891 private void subFormat(int patternCharIndex, int count, 892 FieldDelegate delegate, StringBuffer buffer) 893 { 894 int maxIntCount = Integer.MAX_VALUE; 895 String current = null; 896 int beginOffset = buffer.length(); 897 898 int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex]; 899 int value = calendar.get(field); 900 901 905 switch (patternCharIndex) { 906 case 0: current = formatData.eras[value]; 908 break; 909 case 1: if (count >= 4) 911 zeroPaddingNumber(value, count, maxIntCount, buffer); 912 else zeroPaddingNumber(value, 2, 2, buffer); break; 915 case 2: if (count >= 4) 917 current = formatData.months[value]; 918 else if (count == 3) 919 current = formatData.shortMonths[value]; 920 else 921 zeroPaddingNumber(value+1, count, maxIntCount, buffer); 922 break; 923 case 4: if (value == 0) 925 zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY)+1, 926 count, maxIntCount, buffer); 927 else 928 zeroPaddingNumber(value, count, maxIntCount, buffer); 929 break; 930 case 9: if (count >= 4) 932 current = formatData.weekdays[value]; 933 else current = formatData.shortWeekdays[value]; 935 break; 936 case 14: current = formatData.ampms[value]; 938 break; 939 case 15: if (value == 0) 941 zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR)+1, 942 count, maxIntCount, buffer); 943 else 944 zeroPaddingNumber(value, count, maxIntCount, buffer); 945 break; 946 case 17: int zoneIndex = 948 formatData.getZoneIndex(calendar.get
|