1 16 package org.joda.time.format; 17 18 import java.io.IOException ; 19 import java.io.Writer ; 20 import java.text.DateFormat ; 21 import java.text.SimpleDateFormat ; 22 import java.util.HashMap ; 23 import java.util.Locale ; 24 import java.util.Map ; 25 26 import org.joda.time.Chronology; 27 import org.joda.time.DateTime; 28 import org.joda.time.DateTimeZone; 29 import org.joda.time.ReadablePartial; 30 31 124 public class DateTimeFormat { 125 126 127 static final int FULL = 0; 129 static final int LONG = 1; 131 static final int MEDIUM = 2; 133 static final int SHORT = 3; 135 static final int NONE = 4; 136 137 138 static final int DATE = 0; 139 140 static final int TIME = 1; 141 142 static final int DATETIME = 2; 143 144 145 private static final Map cPatternedCache = new HashMap (7); 146 147 private static final DateTimeFormatter[] cStyleCache = new DateTimeFormatter[25]; 148 149 166 public static DateTimeFormatter forPattern(String pattern) { 167 return createFormatterForPattern(pattern); 168 } 169 170 190 public static DateTimeFormatter forStyle(String style) { 191 return createFormatterForStyle(style); 192 } 193 194 208 public static String patternForStyle(String style, Locale locale) { 209 DateTimeFormatter formatter = createFormatterForStyle(style); 210 if (locale == null) { 211 locale = Locale.getDefault(); 212 } 213 return ((StyleFormatter) formatter.getPrinter()).getPattern(locale); 215 } 216 217 226 public static DateTimeFormatter shortDate() { 227 return createFormatterForStyleIndex(SHORT, NONE); 228 } 229 230 238 public static DateTimeFormatter shortTime() { 239 return createFormatterForStyleIndex(NONE, SHORT); 240 } 241 242 250 public static DateTimeFormatter shortDateTime() { 251 return createFormatterForStyleIndex(SHORT, SHORT); 252 } 253 254 263 public static DateTimeFormatter mediumDate() { 264 return createFormatterForStyleIndex(MEDIUM, NONE); 265 } 266 267 275 public static DateTimeFormatter mediumTime() { 276 return createFormatterForStyleIndex(NONE, MEDIUM); 277 } 278 279 287 public static DateTimeFormatter mediumDateTime() { 288 return createFormatterForStyleIndex(MEDIUM, MEDIUM); 289 } 290 291 300 public static DateTimeFormatter longDate() { 301 return createFormatterForStyleIndex(LONG, NONE); 302 } 303 304 312 public static DateTimeFormatter longTime() { 313 return createFormatterForStyleIndex(NONE, LONG); 314 } 315 316 324 public static DateTimeFormatter longDateTime() { 325 return createFormatterForStyleIndex(LONG, LONG); 326 } 327 328 337 public static DateTimeFormatter fullDate() { 338 return createFormatterForStyleIndex(FULL, NONE); 339 } 340 341 349 public static DateTimeFormatter fullTime() { 350 return createFormatterForStyleIndex(NONE, FULL); 351 } 352 353 361 public static DateTimeFormatter fullDateTime() { 362 return createFormatterForStyleIndex(FULL, FULL); 363 } 364 365 373 static void appendPatternTo(DateTimeFormatterBuilder builder, String pattern) { 374 parsePatternTo(builder, pattern); 375 } 376 377 383 protected DateTimeFormat() { 384 super(); 385 } 386 387 396 private static void parsePatternTo(DateTimeFormatterBuilder builder, String pattern) { 397 int length = pattern.length(); 398 int[] indexRef = new int[1]; 399 400 for (int i=0; i<length; i++) { 401 indexRef[0] = i; 402 String token = parseToken(pattern, indexRef); 403 i = indexRef[0]; 404 405 int tokenLen = token.length(); 406 if (tokenLen == 0) { 407 break; 408 } 409 char c = token.charAt(0); 410 411 switch (c) { 412 case 'G': builder.appendEraText(); 414 break; 415 case 'C': builder.appendCenturyOfEra(tokenLen, tokenLen); 417 break; 418 case 'x': case 'y': case 'Y': if (tokenLen == 2) { 422 boolean lenientParse = true; 423 424 if (i + 1 < length) { 426 indexRef[0]++; 427 if (isNumericToken(parseToken(pattern, indexRef))) { 428 lenientParse = false; 432 } 433 indexRef[0]--; 434 } 435 436 switch (c) { 438 case 'x': 439 builder.appendTwoDigitWeekyear 440 (new DateTime().getWeekyear() - 30, lenientParse); 441 break; 442 case 'y': 443 case 'Y': 444 default: 445 builder.appendTwoDigitYear(new DateTime().getYear() - 30, lenientParse); 446 break; 447 } 448 } else { 449 int maxDigits = 9; 451 452 if (i + 1 < length) { 454 indexRef[0]++; 455 if (isNumericToken(parseToken(pattern, indexRef))) { 456 maxDigits = tokenLen; 458 } 459 indexRef[0]--; 460 } 461 462 switch (c) { 463 case 'x': 464 builder.appendWeekyear(tokenLen, maxDigits); 465 break; 466 case 'y': 467 builder.appendYear(tokenLen, maxDigits); 468 break; 469 case 'Y': 470 builder.appendYearOfEra(tokenLen, maxDigits); 471 break; 472 } 473 } 474 break; 475 case 'M': if (tokenLen >= 3) { 477 if (tokenLen >= 4) { 478 builder.appendMonthOfYearText(); 479 } else { 480 builder.appendMonthOfYearShortText(); 481 } 482 } else { 483 builder.appendMonthOfYear(tokenLen); 484 } 485 break; 486 case 'd': builder.appendDayOfMonth(tokenLen); 488 break; 489 case 'a': builder.appendHalfdayOfDayText(); 491 break; 492 case 'h': builder.appendClockhourOfHalfday(tokenLen); 494 break; 495 case 'H': builder.appendHourOfDay(tokenLen); 497 break; 498 case 'k': builder.appendClockhourOfDay(tokenLen); 500 break; 501 case 'K': builder.appendHourOfHalfday(tokenLen); 503 break; 504 case 'm': builder.appendMinuteOfHour(tokenLen); 506 break; 507 case 's': builder.appendSecondOfMinute(tokenLen); 509 break; 510 case 'S': builder.appendFractionOfSecond(tokenLen, tokenLen); 512 break; 513 case 'e': builder.appendDayOfWeek(tokenLen); 515 break; 516 case 'E': if (tokenLen >= 4) { 518 builder.appendDayOfWeekText(); 519 } else { 520 builder.appendDayOfWeekShortText(); 521 } 522 break; 523 case 'D': builder.appendDayOfYear(tokenLen); 525 break; 526 case 'w': builder.appendWeekOfWeekyear(tokenLen); 528 break; 529 case 'z': if (tokenLen >= 4) { 531 builder.appendTimeZoneName(); 532 } else { 533 builder.appendTimeZoneShortName(); 534 } 535 break; 536 case 'Z': if (tokenLen == 1) { 538 builder.appendTimeZoneOffset(null, false, 2, 2); 539 } else if (tokenLen == 2) { 540 builder.appendTimeZoneOffset(null, true, 2, 2); 541 } else { 542 builder.appendTimeZoneId(); 543 } 544 break; 545 case '\'': String sub = token.substring(1); 547 if (sub.length() == 1) { 548 builder.appendLiteral(sub.charAt(0)); 549 } else { 550 builder.appendLiteral(new String (sub)); 553 } 554 break; 555 default: 556 throw new IllegalArgumentException 557 ("Illegal pattern component: " + token); 558 } 559 } 560 } 561 562 570 private static String parseToken(String pattern, int[] indexRef) { 571 StringBuffer buf = new StringBuffer (); 572 573 int i = indexRef[0]; 574 int length = pattern.length(); 575 576 char c = pattern.charAt(i); 577 if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') { 578 buf.append(c); 581 582 while (i + 1 < length) { 583 char peek = pattern.charAt(i + 1); 584 if (peek == c) { 585 buf.append(c); 586 i++; 587 } else { 588 break; 589 } 590 } 591 } else { 592 buf.append('\''); 594 595 boolean inLiteral = false; 596 597 for (; i < length; i++) { 598 c = pattern.charAt(i); 599 600 if (c == '\'') { 601 if (i + 1 < length && pattern.charAt(i + 1) == '\'') { 602 i++; 604 buf.append(c); 605 } else { 606 inLiteral = !inLiteral; 607 } 608 } else if (!inLiteral && 609 (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')) { 610 i--; 611 break; 612 } else { 613 buf.append(c); 614 } 615 } 616 } 617 618 indexRef[0] = i; 619 return buf.toString(); 620 } 621 622 628 private static boolean isNumericToken(String token) { 629 int tokenLen = token.length(); 630 if (tokenLen > 0) { 631 char c = token.charAt(0); 632 switch (c) { 633 case 'c': case 'C': case 'x': case 'y': case 'Y': case 'd': case 'h': case 'H': case 'm': case 's': case 'S': case 'e': case 'D': case 'F': case 'w': case 'W': case 'k': case 'K': return true; 652 case 'M': if (tokenLen <= 2) { 654 return true; 655 } 656 } 657 } 658 659 return false; 660 } 661 662 670 private static DateTimeFormatter createFormatterForPattern(String pattern) { 671 if (pattern == null || pattern.length() == 0) { 672 throw new IllegalArgumentException ("Invalid pattern specification"); 673 } 674 DateTimeFormatter formatter = null; 675 synchronized (cPatternedCache) { 676 formatter = (DateTimeFormatter) cPatternedCache.get(pattern); 677 if (formatter == null) { 678 DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); 679 parsePatternTo(builder, pattern); 680 formatter = builder.toFormatter(); 681 682 cPatternedCache.put(pattern, formatter); 683 } 684 } 685 return formatter; 686 } 687 688 697 private static DateTimeFormatter createFormatterForStyle(String style) { 698 if (style == null || style.length() != 2) { 699 throw new IllegalArgumentException ("Invalid style specification: " + style); 700 } 701 int dateStyle = selectStyle(style.charAt(0)); 702 int timeStyle = selectStyle(style.charAt(1)); 703 if (dateStyle == NONE && timeStyle == NONE) { 704 throw new IllegalArgumentException ("Style '--' is invalid"); 705 } 706 return createFormatterForStyleIndex(dateStyle, timeStyle); 707 } 708 709 716 private static DateTimeFormatter createFormatterForStyleIndex(int dateStyle, int timeStyle) { 717 int index = ((dateStyle << 2) + dateStyle) + timeStyle; 718 DateTimeFormatter f = null; 719 synchronized (cStyleCache) { 720 f = cStyleCache[index]; 721 if (f == null) { 722 int type = DATETIME; 723 if (dateStyle == NONE) { 724 type = TIME; 725 } else if (timeStyle == NONE) { 726 type = DATE; 727 } 728 StyleFormatter llf = new StyleFormatter( 729 dateStyle, timeStyle, type); 730 f = new DateTimeFormatter(llf, llf); 731 cStyleCache[index] = f; 732 } 733 } 734 return f; 735 } 736 737 743 private static int selectStyle(char ch) { 744 switch (ch) { 745 case 'S': 746 return SHORT; 747 case 'M': 748 return MEDIUM; 749 case 'L': 750 return LONG; 751 case 'F': 752 return FULL; 753 case '-': 754 return NONE; 755 default: 756 throw new IllegalArgumentException ("Invalid style character: " + ch); 757 } 758 } 759 760 static class StyleFormatter 762 implements DateTimePrinter, DateTimeParser { 763 764 private static final Map cCache = new HashMap (); 766 private final int iDateStyle; 767 private final int iTimeStyle; 768 private final int iType; 769 770 StyleFormatter(int dateStyle, int timeStyle, int type) { 771 super(); 772 iDateStyle = dateStyle; 773 iTimeStyle = timeStyle; 774 iType = type; 775 } 776 777 public int estimatePrintedLength() { 778 return 40; } 780 781 public void printTo( 782 StringBuffer buf, long instant, Chronology chrono, 783 int displayOffset, DateTimeZone displayZone, Locale locale) { 784 DateTimePrinter p = getFormatter(locale).getPrinter(); 785 p.printTo(buf, instant, chrono, displayOffset, displayZone, locale); 786 } 787 788 public void printTo( 789 Writer out, long instant, Chronology chrono, 790 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException { 791 DateTimePrinter p = getFormatter(locale).getPrinter(); 792 p.printTo(out, instant, chrono, displayOffset, displayZone, locale); 793 } 794 795 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { 796 DateTimePrinter p = getFormatter(locale).getPrinter(); 797 p.printTo(buf, partial, locale); 798 } 799 800 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { 801 DateTimePrinter p = getFormatter(locale).getPrinter(); 802 p.printTo(out, partial, locale); 803 } 804 805 public int estimateParsedLength() { 806 return 40; } 808 809 public int parseInto(DateTimeParserBucket bucket, String text, int position) { 810 DateTimeParser p = getFormatter(bucket.getLocale()).getParser(); 811 return p.parseInto(bucket, text, position); 812 } 813 814 private DateTimeFormatter getFormatter(Locale locale) { 815 locale = (locale == null ? Locale.getDefault() : locale); 816 String key = Integer.toString(iType + (iDateStyle << 4) + (iTimeStyle << 8)) + locale.toString(); 817 DateTimeFormatter f = null; 818 synchronized (cCache) { 819 f = (DateTimeFormatter) cCache.get(key); 820 if (f == null) { 821 String pattern = getPattern(locale); 822 f = DateTimeFormat.forPattern(pattern); 823 cCache.put(key, f); 824 } 825 } 826 return f; 827 } 828 829 String getPattern(Locale locale) { 830 DateFormat f = null; 831 switch (iType) { 832 case DATE: 833 f = DateFormat.getDateInstance(iDateStyle, locale); 834 break; 835 case TIME: 836 f = DateFormat.getTimeInstance(iTimeStyle, locale); 837 break; 838 case DATETIME: 839 f = DateFormat.getDateTimeInstance(iDateStyle, iTimeStyle, locale); 840 break; 841 } 842 if (f instanceof SimpleDateFormat == false) { 843 throw new IllegalArgumentException ("No datetime pattern for locale: " + locale); 844 } 845 return ((SimpleDateFormat ) f).toPattern(); 846 } 847 } 848 849 } 850 | Popular Tags |