KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > joda > time > format > DateTimeFormat


1 /*
2  * Copyright 2001-2005 Stephen Colebourne
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.joda.time.format;
17
18 import java.io.IOException JavaDoc;
19 import java.io.Writer JavaDoc;
20 import java.text.DateFormat JavaDoc;
21 import java.text.SimpleDateFormat JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Locale JavaDoc;
24 import java.util.Map JavaDoc;
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 /**
32  * Factory that creates instances of DateTimeFormatter from patterns and styles.
33  * <p>
34  * Datetime formatting is performed by the {@link DateTimeFormatter} class.
35  * Three classes provide factory methods to create formatters, and this is one.
36  * The others are {@link ISODateTimeFormat} and {@link DateTimeFormatterBuilder}.
37  * <p>
38  * This class provides two types of factory:
39  * <ul>
40  * <li>{@link #forPattern(String) Pattern} provides a DateTimeFormatter based on
41  * a pattern string that is compatible with the JDK date patterns.
42  * <li>{@link #forStyle(String) Style} provides a DateTimeFormatter based on a
43  * two character style, representing short, medium, long and full.
44  * </ul>
45  * <p>
46  * For example, to use a patterm:
47  * <pre>
48  * DateTime dt = new DateTime();
49  * DateTimeFormatter fmt = DateTimeFormat.forPattern("MMMM, yyyy");
50  * String str = fmt.print(dt);
51  * </pre>
52  *
53  * The pattern syntax is compatible with java.text.SimpleDateFormat, but a few
54  * more symbols are also supported. All ASCII letters are reserved as pattern
55  * letters, which are defined as the following:
56  * <blockquote>
57  * <pre>
58  * Symbol Meaning Presentation Examples
59  * ------ ------- ------------ -------
60  * G era text AD
61  * C century of era (&gt;=0) number 20
62  * Y year of era (&gt;=0) year 1996
63  *
64  * x weekyear year 1996
65  * w week of weekyear number 27
66  * e day of week number 2
67  * E day of week text Tuesday; Tue
68  *
69  * y year year 1996
70  * D day of year number 189
71  * M month of year month July; Jul; 07
72  * d day of month number 10
73  *
74  * a halfday of day text PM
75  * K hour of halfday (0~11) number 0
76  * h clockhour of halfday (1~12) number 12
77  *
78  * H hour of day (0~23) number 0
79  * k clockhour of day (1~24) number 24
80  * m minute of hour number 30
81  * s second of minute number 55
82  * S fraction of second number 978
83  *
84  * z time zone text Pacific Standard Time; PST
85  * Z time zone offset/id zone -0800; -08:00; America/Los_Angeles
86  *
87  * ' escape for text delimiter
88  * '' single quote literal '
89  * </pre>
90  * </blockquote>
91  * The count of pattern letters determine the format.
92  * <p>
93  * <strong>Text</strong>: If the number of pattern letters is 4 or more,
94  * the full form is used; otherwise a short or abbreviated form is used if
95  * available.
96  * <p>
97  * <strong>Number</strong>: The minimum number of digits. Shorter numbers
98  * are zero-padded to this amount.
99  * <p>
100  * <strong>Year</strong>: Numeric presentation for year and weekyear fields
101  * are handled specially. For example, if the count of 'y' is 2, the year
102  * will be displayed as the zero-based year of the century, which is two
103  * digits.
104  * <p>
105  * <strong>Month</strong>: 3 or over, use text, otherwise use number.
106  * <p>
107  * <strong>Zone</strong>: 'Z' outputs offset without a colon, 'ZZ' outputs
108  * the offset with a colon, 'ZZZ' or more outputs the zone id.
109  * <p>
110  * Any characters in the pattern that are not in the ranges of ['a'..'z']
111  * and ['A'..'Z'] will be treated as quoted text. For instance, characters
112  * like ':', '.', ' ', '#' and '?' will appear in the resulting time text
113  * even they are not embraced within single quotes.
114  * <p>
115  * DateTimeFormat is thread-safe and immutable, and the formatters it returns
116  * are as well.
117  *
118  * @author Brian S O'Neill
119  * @author Maxim Zhao
120  * @since 1.0
121  * @see ISODateTimeFormat
122  * @see DateTimeFormatterBuilder
123  */

124 public class DateTimeFormat {
125
126     /** Style constant for FULL. */
127     static final int FULL = 0; // DateFormat.FULL
128
/** Style constant for LONG. */
129     static final int LONG = 1; // DateFormat.LONG
130
/** Style constant for MEDIUM. */
131     static final int MEDIUM = 2; // DateFormat.MEDIUM
132
/** Style constant for SHORT. */
133     static final int SHORT = 3; // DateFormat.SHORT
134
/** Style constant for NONE. */
135     static final int NONE = 4;
136
137     /** Type constant for DATE only. */
138     static final int DATE = 0;
139     /** Type constant for TIME only. */
140     static final int TIME = 1;
141     /** Type constant for DATETIME. */
142     static final int DATETIME = 2;
143
144     /** Maps patterns to formatters, patterns don't vary by locale. */
145     private static final Map JavaDoc cPatternedCache = new HashMap JavaDoc(7);
146     /** Maps patterns to formatters, patterns don't vary by locale. */
147     private static final DateTimeFormatter[] cStyleCache = new DateTimeFormatter[25];
148
149     //-----------------------------------------------------------------------
150
/**
151      * Factory to create a formatter from a pattern string.
152      * The pattern string is described above, and is similar to SimpleDateFormat.
153      * <p>
154      * The format may contain locale specific output, and this will change as
155      * you change the locale of the formatter.
156      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
157      * For example:
158      * <pre>
159      * DateTimeFormat.forPattern(pattern).withLocale(Locale.FRANCE).print(dt);
160      * </pre>
161      *
162      * @param pattern pattern specification
163      * @return the formatter
164      * @throws IllegalArgumentException if the pattern is invalid
165      */

166     public static DateTimeFormatter forPattern(String JavaDoc pattern) {
167         return createFormatterForPattern(pattern);
168     }
169
170     /**
171      * Factory to create a format from a two character style pattern.
172      * <p>
173      * The first character is the date style, and the second character is the
174      * time style. Specify a character of 'S' for short style, 'M' for medium,
175      * 'L' for long, and 'F' for full.
176      * A date or time may be ommitted by specifying a style character '-'.
177      * <p>
178      * The returned formatter will dynamically adjust to the locale that
179      * the print/parse takes place in. Thus you just call
180      * {@link DateTimeFormatter#withLocale(Locale)} and the Short/Medium/Long/Full
181      * style for that locale will be output. For example:
182      * <pre>
183      * DateTimeFormat.forStyle(style).withLocale(Locale.FRANCE).print(dt);
184      * </pre>
185      *
186      * @param style two characters from the set {"S", "M", "L", "F", "-"}
187      * @return the formatter
188      * @throws IllegalArgumentException if the style is invalid
189      */

190     public static DateTimeFormatter forStyle(String JavaDoc style) {
191         return createFormatterForStyle(style);
192     }
193
194     /**
195      * Returns the pattern used by a particular style and locale.
196      * <p>
197      * The first character is the date style, and the second character is the
198      * time style. Specify a character of 'S' for short style, 'M' for medium,
199      * 'L' for long, and 'F' for full.
200      * A date or time may be ommitted by specifying a style character '-'.
201      *
202      * @param style two characters from the set {"S", "M", "L", "F", "-"}
203      * @param locale locale to use, null means default
204      * @return the formatter
205      * @throws IllegalArgumentException if the style is invalid
206      * @since 1.3
207      */

208     public static String JavaDoc patternForStyle(String JavaDoc style, Locale JavaDoc locale) {
209         DateTimeFormatter formatter = createFormatterForStyle(style);
210         if (locale == null) {
211             locale = Locale.getDefault();
212         }
213         // Not pretty, but it works.
214
return ((StyleFormatter) formatter.getPrinter()).getPattern(locale);
215     }
216
217     //-----------------------------------------------------------------------
218
/**
219      * Creates a format that outputs a short date format.
220      * <p>
221      * The format will change as you change the locale of the formatter.
222      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
223      *
224      * @return the formatter
225      */

226     public static DateTimeFormatter shortDate() {
227         return createFormatterForStyleIndex(SHORT, NONE);
228     }
229
230     /**
231      * Creates a format that outputs a short time format.
232      * <p>
233      * The format will change as you change the locale of the formatter.
234      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
235      *
236      * @return the formatter
237      */

238     public static DateTimeFormatter shortTime() {
239         return createFormatterForStyleIndex(NONE, SHORT);
240     }
241
242     /**
243      * Creates a format that outputs a short datetime format.
244      * <p>
245      * The format will change as you change the locale of the formatter.
246      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
247      *
248      * @return the formatter
249      */

250     public static DateTimeFormatter shortDateTime() {
251         return createFormatterForStyleIndex(SHORT, SHORT);
252     }
253
254     //-----------------------------------------------------------------------
255
/**
256      * Creates a format that outputs a medium date format.
257      * <p>
258      * The format will change as you change the locale of the formatter.
259      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
260      *
261      * @return the formatter
262      */

263     public static DateTimeFormatter mediumDate() {
264         return createFormatterForStyleIndex(MEDIUM, NONE);
265     }
266
267     /**
268      * Creates a format that outputs a medium time format.
269      * <p>
270      * The format will change as you change the locale of the formatter.
271      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
272      *
273      * @return the formatter
274      */

275     public static DateTimeFormatter mediumTime() {
276         return createFormatterForStyleIndex(NONE, MEDIUM);
277     }
278
279     /**
280      * Creates a format that outputs a medium datetime format.
281      * <p>
282      * The format will change as you change the locale of the formatter.
283      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
284      *
285      * @return the formatter
286      */

287     public static DateTimeFormatter mediumDateTime() {
288         return createFormatterForStyleIndex(MEDIUM, MEDIUM);
289     }
290
291     //-----------------------------------------------------------------------
292
/**
293      * Creates a format that outputs a long date format.
294      * <p>
295      * The format will change as you change the locale of the formatter.
296      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
297      *
298      * @return the formatter
299      */

300     public static DateTimeFormatter longDate() {
301         return createFormatterForStyleIndex(LONG, NONE);
302     }
303
304     /**
305      * Creates a format that outputs a long time format.
306      * <p>
307      * The format will change as you change the locale of the formatter.
308      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
309      *
310      * @return the formatter
311      */

312     public static DateTimeFormatter longTime() {
313         return createFormatterForStyleIndex(NONE, LONG);
314     }
315
316     /**
317      * Creates a format that outputs a long datetime format.
318      * <p>
319      * The format will change as you change the locale of the formatter.
320      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
321      *
322      * @return the formatter
323      */

324     public static DateTimeFormatter longDateTime() {
325         return createFormatterForStyleIndex(LONG, LONG);
326     }
327
328     //-----------------------------------------------------------------------
329
/**
330      * Creates a format that outputs a full date format.
331      * <p>
332      * The format will change as you change the locale of the formatter.
333      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
334      *
335      * @return the formatter
336      */

337     public static DateTimeFormatter fullDate() {
338         return createFormatterForStyleIndex(FULL, NONE);
339     }
340
341     /**
342      * Creates a format that outputs a full time format.
343      * <p>
344      * The format will change as you change the locale of the formatter.
345      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
346      *
347      * @return the formatter
348      */

349     public static DateTimeFormatter fullTime() {
350         return createFormatterForStyleIndex(NONE, FULL);
351     }
352
353     /**
354      * Creates a format that outputs a full datetime format.
355      * <p>
356      * The format will change as you change the locale of the formatter.
357      * Call {@link DateTimeFormatter#withLocale(Locale)} to switch the locale.
358      *
359      * @return the formatter
360      */

361     public static DateTimeFormatter fullDateTime() {
362         return createFormatterForStyleIndex(FULL, FULL);
363     }
364
365     //-----------------------------------------------------------------------
366
/**
367      * Parses the given pattern and appends the rules to the given
368      * DateTimeFormatterBuilder.
369      *
370      * @param pattern pattern specification
371      * @throws IllegalArgumentException if the pattern is invalid
372      */

373     static void appendPatternTo(DateTimeFormatterBuilder builder, String JavaDoc pattern) {
374         parsePatternTo(builder, pattern);
375     }
376
377     //-----------------------------------------------------------------------
378
/**
379      * Constructor.
380      *
381      * @since 1.1 (previously private)
382      */

383     protected DateTimeFormat() {
384         super();
385     }
386
387     //-----------------------------------------------------------------------
388
/**
389      * Parses the given pattern and appends the rules to the given
390      * DateTimeFormatterBuilder.
391      *
392      * @param pattern pattern specification
393      * @throws IllegalArgumentException if the pattern is invalid
394      * @see #forPattern
395      */

396     private static void parsePatternTo(DateTimeFormatterBuilder builder, String JavaDoc 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 JavaDoc 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': // era designator (text)
413
builder.appendEraText();
414                 break;
415             case 'C': // century of era (number)
416
builder.appendCenturyOfEra(tokenLen, tokenLen);
417                 break;
418             case 'x': // weekyear (number)
419
case 'y': // year (number)
420
case 'Y': // year of era (number)
421
if (tokenLen == 2) {
422                     boolean lenientParse = true;
423
424                     // Peek ahead to next token.
425
if (i + 1 < length) {
426                         indexRef[0]++;
427                         if (isNumericToken(parseToken(pattern, indexRef))) {
428                             // If next token is a number, cannot support
429
// lenient parse, because it will consume digits
430
// that it should not.
431
lenientParse = false;
432                         }
433                         indexRef[0]--;
434                     }
435
436                     // Use pivots which are compatible with SimpleDateFormat.
437
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                     // Try to support long year values.
450
int maxDigits = 9;
451
452                     // Peek ahead to next token.
453
if (i + 1 < length) {
454                         indexRef[0]++;
455                         if (isNumericToken(parseToken(pattern, indexRef))) {
456                             // If next token is a number, cannot support long years.
457
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': // month of year (text and number)
476
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': // day of month (number)
487
builder.appendDayOfMonth(tokenLen);
488                 break;
489             case 'a': // am/pm marker (text)
490
builder.appendHalfdayOfDayText();
491                 break;
492             case 'h': // clockhour of halfday (number, 1..12)
493
builder.appendClockhourOfHalfday(tokenLen);
494                 break;
495             case 'H': // hour of day (number, 0..23)
496
builder.appendHourOfDay(tokenLen);
497                 break;
498             case 'k': // clockhour of day (1..24)
499
builder.appendClockhourOfDay(tokenLen);
500                 break;
501             case 'K': // hour of halfday (0..11)
502
builder.appendHourOfHalfday(tokenLen);
503                 break;
504             case 'm': // minute of hour (number)
505
builder.appendMinuteOfHour(tokenLen);
506                 break;
507             case 's': // second of minute (number)
508
builder.appendSecondOfMinute(tokenLen);
509                 break;
510             case 'S': // fraction of second (number)
511
builder.appendFractionOfSecond(tokenLen, tokenLen);
512                 break;
513             case 'e': // day of week (number)
514
builder.appendDayOfWeek(tokenLen);
515                 break;
516             case 'E': // dayOfWeek (text)
517
if (tokenLen >= 4) {
518                     builder.appendDayOfWeekText();
519                 } else {
520                     builder.appendDayOfWeekShortText();
521                 }
522                 break;
523             case 'D': // day of year (number)
524
builder.appendDayOfYear(tokenLen);
525                 break;
526             case 'w': // week of weekyear (number)
527
builder.appendWeekOfWeekyear(tokenLen);
528                 break;
529             case 'z': // time zone (text)
530
if (tokenLen >= 4) {
531                     builder.appendTimeZoneName();
532                 } else {
533                     builder.appendTimeZoneShortName();
534                 }
535                 break;
536             case 'Z': // time zone offset
537
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 '\'': // literal text
546
String JavaDoc sub = token.substring(1);
547                 if (sub.length() == 1) {
548                     builder.appendLiteral(sub.charAt(0));
549                 } else {
550                     // Create copy of sub since otherwise the temporary quoted
551
// string would still be referenced internally.
552
builder.appendLiteral(new String JavaDoc(sub));
553                 }
554                 break;
555             default:
556                 throw new IllegalArgumentException JavaDoc
557                     ("Illegal pattern component: " + token);
558             }
559         }
560     }
561
562     /**
563      * Parses an individual token.
564      *
565      * @param pattern the pattern string
566      * @param indexRef a single element array, where the input is the start
567      * location and the output is the location after parsing the token
568      * @return the parsed token
569      */

570     private static String JavaDoc parseToken(String JavaDoc pattern, int[] indexRef) {
571         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
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             // Scan a run of the same character, which indicates a time
579
// pattern.
580
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             // This will identify token as text.
593
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                         // '' is treated as escaped '
603
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     /**
623      * Returns true if token should be parsed as a numeric field.
624      *
625      * @param token the token to parse
626      * @return true if numeric field
627      */

628     private static boolean isNumericToken(String JavaDoc token) {
629         int tokenLen = token.length();
630         if (tokenLen > 0) {
631             char c = token.charAt(0);
632             switch (c) {
633             case 'c': // century (number)
634
case 'C': // century of era (number)
635
case 'x': // weekyear (number)
636
case 'y': // year (number)
637
case 'Y': // year of era (number)
638
case 'd': // day of month (number)
639
case 'h': // hour of day (number, 1..12)
640
case 'H': // hour of day (number, 0..23)
641
case 'm': // minute of hour (number)
642
case 's': // second of minute (number)
643
case 'S': // fraction of second (number)
644
case 'e': // day of week (number)
645
case 'D': // day of year (number)
646
case 'F': // day of week in month (number)
647
case 'w': // week of year (number)
648
case 'W': // week of month (number)
649
case 'k': // hour of day (1..24)
650
case 'K': // hour of day (0..11)
651
return true;
652             case 'M': // month of year (text and number)
653
if (tokenLen <= 2) {
654                     return true;
655                 }
656             }
657         }
658             
659         return false;
660     }
661
662     //-----------------------------------------------------------------------
663
/**
664      * Select a format from a custom pattern.
665      *
666      * @param pattern pattern specification
667      * @throws IllegalArgumentException if the pattern is invalid
668      * @see #appendPatternTo
669      */

670     private static DateTimeFormatter createFormatterForPattern(String JavaDoc pattern) {
671         if (pattern == null || pattern.length() == 0) {
672             throw new IllegalArgumentException JavaDoc("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     /**
689      * Select a format from a two character style pattern. The first character
690      * is the date style, and the second character is the time style. Specify a
691      * character of 'S' for short style, 'M' for medium, 'L' for long, and 'F'
692      * for full. A date or time may be ommitted by specifying a style character '-'.
693      *
694      * @param style two characters from the set {"S", "M", "L", "F", "-"}
695      * @throws IllegalArgumentException if the style is invalid
696      */

697     private static DateTimeFormatter createFormatterForStyle(String JavaDoc style) {
698         if (style == null || style.length() != 2) {
699             throw new IllegalArgumentException JavaDoc("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 JavaDoc("Style '--' is invalid");
705         }
706         return createFormatterForStyleIndex(dateStyle, timeStyle);
707     }
708
709     /**
710      * Gets the formatter for the specified style.
711      *
712      * @param dateStyle the date style
713      * @param timeStyle the time style
714      * @return the formatter
715      */

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     /**
738      * Gets the JDK style code from the Joda code.
739      *
740      * @param ch the Joda style code
741      * @return the JDK style code
742      */

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 JavaDoc("Invalid style character: " + ch);
757         }
758     }
759
760     //-----------------------------------------------------------------------
761
static class StyleFormatter
762             implements DateTimePrinter, DateTimeParser {
763
764         private static final Map JavaDoc cCache = new HashMap JavaDoc(); // manual sync
765

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; // guess
779
}
780
781         public void printTo(
782                 StringBuffer JavaDoc buf, long instant, Chronology chrono,
783                 int displayOffset, DateTimeZone displayZone, Locale JavaDoc locale) {
784             DateTimePrinter p = getFormatter(locale).getPrinter();
785             p.printTo(buf, instant, chrono, displayOffset, displayZone, locale);
786         }
787
788         public void printTo(
789                 Writer JavaDoc out, long instant, Chronology chrono,
790                 int displayOffset, DateTimeZone displayZone, Locale JavaDoc locale) throws IOException JavaDoc {
791             DateTimePrinter p = getFormatter(locale).getPrinter();
792             p.printTo(out, instant, chrono, displayOffset, displayZone, locale);
793         }
794
795         public void printTo(StringBuffer JavaDoc buf, ReadablePartial partial, Locale JavaDoc locale) {
796             DateTimePrinter p = getFormatter(locale).getPrinter();
797             p.printTo(buf, partial, locale);
798         }
799
800         public void printTo(Writer JavaDoc out, ReadablePartial partial, Locale JavaDoc locale) throws IOException JavaDoc {
801             DateTimePrinter p = getFormatter(locale).getPrinter();
802             p.printTo(out, partial, locale);
803         }
804
805         public int estimateParsedLength() {
806             return 40; // guess
807
}
808
809         public int parseInto(DateTimeParserBucket bucket, String JavaDoc text, int position) {
810             DateTimeParser p = getFormatter(bucket.getLocale()).getParser();
811             return p.parseInto(bucket, text, position);
812         }
813
814         private DateTimeFormatter getFormatter(Locale JavaDoc locale) {
815             locale = (locale == null ? Locale.getDefault() : locale);
816             String JavaDoc 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 JavaDoc pattern = getPattern(locale);
822                     f = DateTimeFormat.forPattern(pattern);
823                     cCache.put(key, f);
824                 }
825             }
826             return f;
827         }
828
829         String JavaDoc getPattern(Locale JavaDoc locale) {
830             DateFormat JavaDoc 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 JavaDoc == false) {
843                 throw new IllegalArgumentException JavaDoc("No datetime pattern for locale: " + locale);
844             }
845             return ((SimpleDateFormat JavaDoc) f).toPattern();
846         }
847     }
848
849 }
850
Popular Tags