src/share/classes/java/util/Formatter.java

Print this page




  33 import java.io.FileNotFoundException;
  34 import java.io.Flushable;
  35 import java.io.OutputStream;
  36 import java.io.OutputStreamWriter;
  37 import java.io.PrintStream;
  38 import java.io.UnsupportedEncodingException;
  39 import java.math.BigDecimal;
  40 import java.math.BigInteger;
  41 import java.math.MathContext;
  42 import java.math.RoundingMode;
  43 import java.nio.charset.Charset;
  44 import java.nio.charset.IllegalCharsetNameException;
  45 import java.nio.charset.UnsupportedCharsetException;
  46 import java.text.DateFormatSymbols;
  47 import java.text.DecimalFormat;
  48 import java.text.DecimalFormatSymbols;
  49 import java.text.NumberFormat;
  50 import java.util.regex.Matcher;
  51 import java.util.regex.Pattern;
  52 















  53 import sun.misc.DoubleConsts;
  54 import sun.misc.FormattedFloatingDecimal;
  55 
  56 /**
  57  * An interpreter for printf-style format strings.  This class provides support
  58  * for layout justification and alignment, common formats for numeric, string,
  59  * and date/time data, and locale-specific output.  Common Java types such as
  60  * {@code byte}, {@link java.math.BigDecimal BigDecimal}, and {@link Calendar}
  61  * are supported.  Limited formatting customization for arbitrary user types is
  62  * provided through the {@link Formattable} interface.
  63  *
  64  * <p> Formatters are not necessarily safe for multithreaded access.  Thread
  65  * safety is optional and is the responsibility of users of methods in this
  66  * class.
  67  *
  68  * <p> Formatted printing for the Java language is heavily inspired by C's
  69  * {@code printf}.  Although the format strings are similar to C, some
  70  * customizations have been made to accommodate the Java language and exploit
  71  * some of its features.  Also, Java formatting is more strict than C's; for
  72  * example, if a conversion is incompatible with a flag, an exception will be


 237  * Byte}, {@code short}, and {@link Short}. This conversion may also be
 238  * applied to the types {@code int} and {@link Integer} when {@link
 239  * Character#isValidCodePoint} returns {@code true}
 240  *
 241  * <li> <b>Numeric</b>
 242  *
 243  * <ol>
 244  *
 245  * <li> <b>Integral</b> - may be applied to Java integral types: {@code byte},
 246  * {@link Byte}, {@code short}, {@link Short}, {@code int} and {@link
 247  * Integer}, {@code long}, {@link Long}, and {@link java.math.BigInteger
 248  * BigInteger}
 249  *
 250  * <li><b>Floating Point</b> - may be applied to Java floating-point types:
 251  * {@code float}, {@link Float}, {@code double}, {@link Double}, and {@link
 252  * java.math.BigDecimal BigDecimal}
 253  *
 254  * </ol>
 255  *
 256  * <li> <b>Date/Time</b> - may be applied to Java types which are capable of
 257  * encoding a date or time: {@code long}, {@link Long}, {@link Calendar}, and
 258  * {@link Date}.
 259  *
 260  * <li> <b>Percent</b> - produces a literal {@code '%'}
 261  * (<tt>'&#92;u0025'</tt>)
 262  *
 263  * <li> <b>Line Separator</b> - produces the platform-specific line separator
 264  *
 265  * </ol>
 266  *
 267  * <p> The following table summarizes the supported conversions.  Conversions
 268  * denoted by an upper-case character (i.e. {@code 'B'}, {@code 'H'},
 269  * {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'}, {@code 'G'},
 270  * {@code 'A'}, and {@code 'T'}) are the same as those for the corresponding
 271  * lower-case conversion characters except that the result is converted to
 272  * upper case according to the rules of the prevailing {@link java.util.Locale
 273  * Locale}.  The result is equivalent to the following invocation of {@link
 274  * String#toUpperCase()}
 275  *
 276  * <pre>
 277  *    out.toUpperCase() </pre>
 278  *


1471  *     BigDecimal#toString()}.
1472  *
1473  * </table>
1474  *
1475  * <p> All <a href="#intFlags">flags</a> defined for Byte, Short, Integer, and
1476  * Long apply.
1477  *
1478  * <p> If the {@code '#'} flag is given, then the decimal separator will
1479  * always be present.
1480  *
1481  * <p> The <a href="#floatdFlags">default behavior</a> when no flags are
1482  * given is the same as for Float and Double.
1483  *
1484  * <p> The specification of <a href="#floatDWidth">width</a> and <a
1485  * href="#floatDPrec">precision</a> is the same as defined for Float and
1486  * Double.
1487  *
1488  * <h4><a name="ddt">Date/Time</a></h4>
1489  *
1490  * <p> This conversion may be applied to {@code long}, {@link Long}, {@link
1491  * Calendar}, and {@link Date}.
1492  *
1493  * <table cellpadding=5 summary="DTConv">
1494  *
1495  * <tr><td valign="top"> {@code 't'}
1496  *     <td valign="top"> <tt>'&#92;u0074'</tt>
1497  *     <td> Prefix for date and time conversion characters.
1498  * <tr><td valign="top"> {@code 'T'}
1499  *     <td valign="top"> <tt>'&#92;u0054'</tt>
1500  *     <td> The upper-case variant of {@code 't'}.
1501  *
1502  * </table>
1503  *
1504  * <p> The following date and time conversion character suffixes are defined
1505  * for the {@code 't'} and {@code 'T'} conversions.  The types are similar to
1506  * but not completely identical to those defined by GNU {@code date} and
1507  * POSIX {@code strftime(3c)}.  Additional conversion types are provided to
1508  * access Java-specific functionality (e.g. {@code 'L'} for milliseconds
1509  * within the second).
1510  *
1511  * <p> The following conversion characters are used for formatting times:


2765                 print("null");
2766                 return;
2767             }
2768             Calendar cal = null;
2769 
2770             // Instead of Calendar.setLenient(true), perhaps we should
2771             // wrap the IllegalArgumentException that might be thrown?
2772             if (arg instanceof Long) {
2773                 // Note that the following method uses an instance of the
2774                 // default time zone (TimeZone.getDefaultRef().
2775                 cal = Calendar.getInstance(l == null ? Locale.US : l);
2776                 cal.setTimeInMillis((Long)arg);
2777             } else if (arg instanceof Date) {
2778                 // Note that the following method uses an instance of the
2779                 // default time zone (TimeZone.getDefaultRef().
2780                 cal = Calendar.getInstance(l == null ? Locale.US : l);
2781                 cal.setTime((Date)arg);
2782             } else if (arg instanceof Calendar) {
2783                 cal = (Calendar) ((Calendar)arg).clone();
2784                 cal.setLenient(true);



2785             } else {
2786                 failConversion(c, arg);
2787             }
2788             // Use the provided locale so that invocations of
2789             // localizedMagnitude() use optimizations for null.
2790             print(cal, c, l);
2791         }
2792 
2793         private void printCharacter(Object arg) throws IOException {
2794             if (arg == null) {
2795                 print("null");
2796                 return;
2797             }
2798             String s = null;
2799             if (arg instanceof Character) {
2800                 s = ((Character)arg).toString();
2801             } else if (arg instanceof Byte) {
2802                 byte i = ((Byte)arg).byteValue();
2803                 if (Character.isValidCodePoint(i))
2804                     s = new String(Character.toChars(i));


4015             case DateTime.DATE:            { // 'D' (mm/dd/yy)
4016                 char sep = '/';
4017                 print(sb, t, DateTime.MONTH, l).append(sep);
4018                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4019                 print(sb, t, DateTime.YEAR_2, l);
4020                 break;
4021             }
4022             case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4023                 char sep = '-';
4024                 print(sb, t, DateTime.YEAR_4, l).append(sep);
4025                 print(sb, t, DateTime.MONTH, l).append(sep);
4026                 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4027                 break;
4028             }
4029             default:
4030                 assert false;
4031             }
4032             return sb;
4033         }
4034 












































































































































































































































4035         // -- Methods to support throwing exceptions --
4036 
4037         private void failMismatch(Flags f, char c) {
4038             String fs = f.toString();
4039             throw new FormatFlagsConversionMismatchException(fs, c);
4040         }
4041 
4042         private void failConversion(char c, Object arg) {
4043             throw new IllegalFormatConversionException(c, arg.getClass());
4044         }
4045 
4046         private char getZero(Locale l) {
4047             if ((l != null) &&  !l.equals(locale())) {
4048                 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4049                 return dfs.getZeroDigit();
4050             }
4051             return zero;
4052         }
4053 
4054         private StringBuilder




  33 import java.io.FileNotFoundException;
  34 import java.io.Flushable;
  35 import java.io.OutputStream;
  36 import java.io.OutputStreamWriter;
  37 import java.io.PrintStream;
  38 import java.io.UnsupportedEncodingException;
  39 import java.math.BigDecimal;
  40 import java.math.BigInteger;
  41 import java.math.MathContext;
  42 import java.math.RoundingMode;
  43 import java.nio.charset.Charset;
  44 import java.nio.charset.IllegalCharsetNameException;
  45 import java.nio.charset.UnsupportedCharsetException;
  46 import java.text.DateFormatSymbols;
  47 import java.text.DecimalFormat;
  48 import java.text.DecimalFormatSymbols;
  49 import java.text.NumberFormat;
  50 import java.util.regex.Matcher;
  51 import java.util.regex.Pattern;
  52 
  53 import java.time.Clock;
  54 import java.time.DateTimeException;
  55 import java.time.Instant;
  56 import java.time.ZoneId;
  57 import java.time.ZoneOffset;
  58 import java.time.temporal.ChronoField;
  59 import java.time.temporal.TemporalAccessor;
  60 import java.time.temporal.Queries;
  61 import java.time.temporal.OffsetDate;
  62 import java.time.temporal.OffsetDateTime;
  63 import java.time.temporal.OffsetTime;
  64 import java.time.temporal.ChronoZonedDateTime;
  65 import java.time.format.TextStyle;
  66 import java.time.zone.ZoneRules;
  67 
  68 import sun.misc.DoubleConsts;
  69 import sun.misc.FormattedFloatingDecimal;
  70 
  71 /**
  72  * An interpreter for printf-style format strings.  This class provides support
  73  * for layout justification and alignment, common formats for numeric, string,
  74  * and date/time data, and locale-specific output.  Common Java types such as
  75  * {@code byte}, {@link java.math.BigDecimal BigDecimal}, and {@link Calendar}
  76  * are supported.  Limited formatting customization for arbitrary user types is
  77  * provided through the {@link Formattable} interface.
  78  *
  79  * <p> Formatters are not necessarily safe for multithreaded access.  Thread
  80  * safety is optional and is the responsibility of users of methods in this
  81  * class.
  82  *
  83  * <p> Formatted printing for the Java language is heavily inspired by C's
  84  * {@code printf}.  Although the format strings are similar to C, some
  85  * customizations have been made to accommodate the Java language and exploit
  86  * some of its features.  Also, Java formatting is more strict than C's; for
  87  * example, if a conversion is incompatible with a flag, an exception will be


 252  * Byte}, {@code short}, and {@link Short}. This conversion may also be
 253  * applied to the types {@code int} and {@link Integer} when {@link
 254  * Character#isValidCodePoint} returns {@code true}
 255  *
 256  * <li> <b>Numeric</b>
 257  *
 258  * <ol>
 259  *
 260  * <li> <b>Integral</b> - may be applied to Java integral types: {@code byte},
 261  * {@link Byte}, {@code short}, {@link Short}, {@code int} and {@link
 262  * Integer}, {@code long}, {@link Long}, and {@link java.math.BigInteger
 263  * BigInteger}
 264  *
 265  * <li><b>Floating Point</b> - may be applied to Java floating-point types:
 266  * {@code float}, {@link Float}, {@code double}, {@link Double}, and {@link
 267  * java.math.BigDecimal BigDecimal}
 268  *
 269  * </ol>
 270  *
 271  * <li> <b>Date/Time</b> - may be applied to Java types which are capable of
 272  * encoding a date or time: {@code long}, {@link Long}, {@link Calendar},
 273  * {@link Date} and {@link TemporalAccessor TemporalAccessor}
 274  *
 275  * <li> <b>Percent</b> - produces a literal {@code '%'}
 276  * (<tt>'&#92;u0025'</tt>)
 277  *
 278  * <li> <b>Line Separator</b> - produces the platform-specific line separator
 279  *
 280  * </ol>
 281  *
 282  * <p> The following table summarizes the supported conversions.  Conversions
 283  * denoted by an upper-case character (i.e. {@code 'B'}, {@code 'H'},
 284  * {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'}, {@code 'G'},
 285  * {@code 'A'}, and {@code 'T'}) are the same as those for the corresponding
 286  * lower-case conversion characters except that the result is converted to
 287  * upper case according to the rules of the prevailing {@link java.util.Locale
 288  * Locale}.  The result is equivalent to the following invocation of {@link
 289  * String#toUpperCase()}
 290  *
 291  * <pre>
 292  *    out.toUpperCase() </pre>
 293  *


1486  *     BigDecimal#toString()}.
1487  *
1488  * </table>
1489  *
1490  * <p> All <a href="#intFlags">flags</a> defined for Byte, Short, Integer, and
1491  * Long apply.
1492  *
1493  * <p> If the {@code '#'} flag is given, then the decimal separator will
1494  * always be present.
1495  *
1496  * <p> The <a href="#floatdFlags">default behavior</a> when no flags are
1497  * given is the same as for Float and Double.
1498  *
1499  * <p> The specification of <a href="#floatDWidth">width</a> and <a
1500  * href="#floatDPrec">precision</a> is the same as defined for Float and
1501  * Double.
1502  *
1503  * <h4><a name="ddt">Date/Time</a></h4>
1504  *
1505  * <p> This conversion may be applied to {@code long}, {@link Long}, {@link
1506  * Calendar}, {@link Date} and {@link TemporalAccessor TemporalAccessor}
1507  *
1508  * <table cellpadding=5 summary="DTConv">
1509  *
1510  * <tr><td valign="top"> {@code 't'}
1511  *     <td valign="top"> <tt>'&#92;u0074'</tt>
1512  *     <td> Prefix for date and time conversion characters.
1513  * <tr><td valign="top"> {@code 'T'}
1514  *     <td valign="top"> <tt>'&#92;u0054'</tt>
1515  *     <td> The upper-case variant of {@code 't'}.
1516  *
1517  * </table>
1518  *
1519  * <p> The following date and time conversion character suffixes are defined
1520  * for the {@code 't'} and {@code 'T'} conversions.  The types are similar to
1521  * but not completely identical to those defined by GNU {@code date} and
1522  * POSIX {@code strftime(3c)}.  Additional conversion types are provided to
1523  * access Java-specific functionality (e.g. {@code 'L'} for milliseconds
1524  * within the second).
1525  *
1526  * <p> The following conversion characters are used for formatting times:


2780                 print("null");
2781                 return;
2782             }
2783             Calendar cal = null;
2784 
2785             // Instead of Calendar.setLenient(true), perhaps we should
2786             // wrap the IllegalArgumentException that might be thrown?
2787             if (arg instanceof Long) {
2788                 // Note that the following method uses an instance of the
2789                 // default time zone (TimeZone.getDefaultRef().
2790                 cal = Calendar.getInstance(l == null ? Locale.US : l);
2791                 cal.setTimeInMillis((Long)arg);
2792             } else if (arg instanceof Date) {
2793                 // Note that the following method uses an instance of the
2794                 // default time zone (TimeZone.getDefaultRef().
2795                 cal = Calendar.getInstance(l == null ? Locale.US : l);
2796                 cal.setTime((Date)arg);
2797             } else if (arg instanceof Calendar) {
2798                 cal = (Calendar) ((Calendar)arg).clone();
2799                 cal.setLenient(true);
2800             } else if (arg instanceof TemporalAccessor) {
2801                 print((TemporalAccessor)arg, c, l);
2802                 return;
2803             } else {
2804                 failConversion(c, arg);
2805             }
2806             // Use the provided locale so that invocations of
2807             // localizedMagnitude() use optimizations for null.
2808             print(cal, c, l);
2809         }
2810 
2811         private void printCharacter(Object arg) throws IOException {
2812             if (arg == null) {
2813                 print("null");
2814                 return;
2815             }
2816             String s = null;
2817             if (arg instanceof Character) {
2818                 s = ((Character)arg).toString();
2819             } else if (arg instanceof Byte) {
2820                 byte i = ((Byte)arg).byteValue();
2821                 if (Character.isValidCodePoint(i))
2822                     s = new String(Character.toChars(i));


4033             case DateTime.DATE:            { // 'D' (mm/dd/yy)
4034                 char sep = '/';
4035                 print(sb, t, DateTime.MONTH, l).append(sep);
4036                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4037                 print(sb, t, DateTime.YEAR_2, l);
4038                 break;
4039             }
4040             case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4041                 char sep = '-';
4042                 print(sb, t, DateTime.YEAR_4, l).append(sep);
4043                 print(sb, t, DateTime.MONTH, l).append(sep);
4044                 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4045                 break;
4046             }
4047             default:
4048                 assert false;
4049             }
4050             return sb;
4051         }
4052 
4053         private void print(TemporalAccessor t, char c, Locale l)  throws IOException {
4054             StringBuilder sb = new StringBuilder();
4055             print(sb, t, c, l);
4056             // justify based on width
4057             String s = justify(sb.toString());
4058             if (f.contains(Flags.UPPERCASE))
4059                 s = s.toUpperCase();
4060             a.append(s);
4061         }
4062 
4063         private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4064                                  Locale l) throws IOException {
4065             if (sb == null)
4066                 sb = new StringBuilder();
4067             try {
4068                 switch (c) {
4069                 case DateTime.HOUR_OF_DAY_0: {  // 'H' (00 - 23)
4070                     int i = t.get(ChronoField.HOUR_OF_DAY);
4071                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4072                     break;
4073                 }
4074                 case DateTime.HOUR_OF_DAY: {   // 'k' (0 - 23) -- like H
4075                     int i = t.get(ChronoField.HOUR_OF_DAY);
4076                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4077                     break;
4078                 }
4079                 case DateTime.HOUR_0:      {  // 'I' (01 - 12)
4080                     int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);
4081                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4082                     break;
4083                 }
4084                 case DateTime.HOUR:        { // 'l' (1 - 12) -- like I
4085                     int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);
4086                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4087                     break;
4088                 }
4089                 case DateTime.MINUTE:      { // 'M' (00 - 59)
4090                     int i = t.get(ChronoField.MINUTE_OF_HOUR);
4091                     Flags flags = Flags.ZERO_PAD;
4092                     sb.append(localizedMagnitude(null, i, flags, 2, l));
4093                     break;
4094                 }
4095                 case DateTime.NANOSECOND:  { // 'N' (000000000 - 999999999)
4096                     int i = t.get(ChronoField.MILLI_OF_SECOND) * 1000000;
4097                     Flags flags = Flags.ZERO_PAD;
4098                     sb.append(localizedMagnitude(null, i, flags, 9, l));
4099                     break;
4100                 }
4101                 case DateTime.MILLISECOND: { // 'L' (000 - 999)
4102                     int i = t.get(ChronoField.MILLI_OF_SECOND);
4103                     Flags flags = Flags.ZERO_PAD;
4104                     sb.append(localizedMagnitude(null, i, flags, 3, l));
4105                     break;
4106                 }
4107                 case DateTime.MILLISECOND_SINCE_EPOCH: { // 'Q' (0 - 99...?)
4108                     long i = t.getLong(ChronoField.INSTANT_SECONDS) * 1000L +
4109                              t.getLong(ChronoField.MILLI_OF_SECOND);
4110                     Flags flags = Flags.NONE;
4111                     sb.append(localizedMagnitude(null, i, flags, width, l));
4112                     break;
4113                 }
4114                 case DateTime.AM_PM:       { // 'p' (am or pm)
4115                     // Calendar.AM = 0, Calendar.PM = 1, LocaleElements defines upper
4116                     String[] ampm = { "AM", "PM" };
4117                     if (l != null && l != Locale.US) {
4118                         DateFormatSymbols dfs = DateFormatSymbols.getInstance(l);
4119                         ampm = dfs.getAmPmStrings();
4120                     }
4121                     String s = ampm[t.get(ChronoField.AMPM_OF_DAY)];
4122                     sb.append(s.toLowerCase(l != null ? l : Locale.US));
4123                     break;
4124                 }
4125                 case DateTime.SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?)
4126                     long i = t.getLong(ChronoField.INSTANT_SECONDS);
4127                     Flags flags = Flags.NONE;
4128                     sb.append(localizedMagnitude(null, i, flags, width, l));
4129                     break;
4130                 }
4131                 case DateTime.SECOND:      { // 'S' (00 - 60 - leap second)
4132                     int i = t.get(ChronoField.SECOND_OF_MINUTE);
4133                     Flags flags = Flags.ZERO_PAD;
4134                     sb.append(localizedMagnitude(null, i, flags, 2, l));
4135                     break;
4136                 }
4137                 case DateTime.ZONE_NUMERIC: { // 'z' ({-|+}####) - ls minus?
4138                     int i = t.get(ChronoField.OFFSET_SECONDS);
4139                     boolean neg = i < 0;
4140                     sb.append(neg ? '-' : '+');
4141                     if (neg)
4142                         i = -i;
4143                     int min = i / 60;
4144                     // combine minute and hour into a single integer
4145                     int offset = (min / 60) * 100 + (min % 60);
4146                     Flags flags = Flags.ZERO_PAD;
4147                     sb.append(localizedMagnitude(null, offset, flags, 4, l));
4148                     break;
4149                 }
4150                 case DateTime.ZONE:        { // 'Z' (symbol)
4151                     ZoneId zid = t.query(Queries.zone());
4152                     if (zid == null) {
4153                         throw new IllegalFormatConversionException(c, t.getClass());
4154                     }
4155                     if (!(zid instanceof ZoneOffset) &&
4156                         t.isSupported(ChronoField.INSTANT_SECONDS)) {
4157                         Instant instant = Instant.from(t);
4158                         sb.append(TimeZone.getTimeZone(zid.getId())
4159                                           .getDisplayName(zid.getRules().isDaylightSavings(instant),
4160                                                           TimeZone.SHORT,
4161                                                           (l == null) ? Locale.US : l));
4162                         break;
4163                     }
4164                     sb.append(zid.getId());
4165                     break;
4166                 }
4167                 // Date
4168                 case DateTime.NAME_OF_DAY_ABBREV:     // 'a'
4169                 case DateTime.NAME_OF_DAY:          { // 'A'
4170                     int i = t.get(ChronoField.DAY_OF_WEEK) % 7 + 1;
4171                     Locale lt = ((l == null) ? Locale.US : l);
4172                     DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
4173                     if (c == DateTime.NAME_OF_DAY)
4174                         sb.append(dfs.getWeekdays()[i]);
4175                     else
4176                         sb.append(dfs.getShortWeekdays()[i]);
4177                     break;
4178                 }
4179                 case DateTime.NAME_OF_MONTH_ABBREV:   // 'b'
4180                 case DateTime.NAME_OF_MONTH_ABBREV_X: // 'h' -- same b
4181                 case DateTime.NAME_OF_MONTH:        { // 'B'
4182                     int i = t.get(ChronoField.MONTH_OF_YEAR) - 1;
4183                     Locale lt = ((l == null) ? Locale.US : l);
4184                     DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
4185                     if (c == DateTime.NAME_OF_MONTH)
4186                         sb.append(dfs.getMonths()[i]);
4187                     else
4188                         sb.append(dfs.getShortMonths()[i]);
4189                     break;
4190                 }
4191                 case DateTime.CENTURY:                // 'C' (00 - 99)
4192                 case DateTime.YEAR_2:                 // 'y' (00 - 99)
4193                 case DateTime.YEAR_4:               { // 'Y' (0000 - 9999)
4194                     int i = t.get(ChronoField.YEAR);
4195                     int size = 2;
4196                     switch (c) {
4197                     case DateTime.CENTURY:
4198                         i /= 100;
4199                         break;
4200                     case DateTime.YEAR_2:
4201                         i %= 100;
4202                         break;
4203                     case DateTime.YEAR_4:
4204                         size = 4;
4205                         break;
4206                     }
4207                     Flags flags = Flags.ZERO_PAD;
4208                     sb.append(localizedMagnitude(null, i, flags, size, l));
4209                     break;
4210                 }
4211                 case DateTime.DAY_OF_MONTH_0:         // 'd' (01 - 31)
4212                 case DateTime.DAY_OF_MONTH:         { // 'e' (1 - 31) -- like d
4213                     int i = t.get(ChronoField.DAY_OF_MONTH);
4214                     Flags flags = (c == DateTime.DAY_OF_MONTH_0
4215                                    ? Flags.ZERO_PAD
4216                                    : Flags.NONE);
4217                     sb.append(localizedMagnitude(null, i, flags, 2, l));
4218                     break;
4219                 }
4220                 case DateTime.DAY_OF_YEAR:          { // 'j' (001 - 366)
4221                     int i = t.get(ChronoField.DAY_OF_YEAR);
4222                     Flags flags = Flags.ZERO_PAD;
4223                     sb.append(localizedMagnitude(null, i, flags, 3, l));
4224                     break;
4225                 }
4226                 case DateTime.MONTH:                { // 'm' (01 - 12)
4227                     int i = t.get(ChronoField.MONTH_OF_YEAR);
4228                     Flags flags = Flags.ZERO_PAD;
4229                     sb.append(localizedMagnitude(null, i, flags, 2, l));
4230                     break;
4231                 }
4232 
4233                 // Composites
4234                 case DateTime.TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4235                 case DateTime.TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
4236                     char sep = ':';
4237                     print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4238                     print(sb, t, DateTime.MINUTE, l);
4239                     if (c == DateTime.TIME) {
4240                         sb.append(sep);
4241                         print(sb, t, DateTime.SECOND, l);
4242                     }
4243                     break;
4244                 }
4245                 case DateTime.TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
4246                     char sep = ':';
4247                     print(sb, t, DateTime.HOUR_0, l).append(sep);
4248                     print(sb, t, DateTime.MINUTE, l).append(sep);
4249                     print(sb, t, DateTime.SECOND, l).append(' ');
4250                     // this may be in wrong place for some locales
4251                     StringBuilder tsb = new StringBuilder();
4252                     print(tsb, t, DateTime.AM_PM, l);
4253                     sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
4254                     break;
4255                 }
4256                 case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4257                     char sep = ' ';
4258                     print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4259                     print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4260                     print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4261                     print(sb, t, DateTime.TIME, l).append(sep);
4262                     print(sb, t, DateTime.ZONE, l).append(sep);
4263                     print(sb, t, DateTime.YEAR_4, l);
4264                     break;
4265                 }
4266                 case DateTime.DATE:            { // 'D' (mm/dd/yy)
4267                     char sep = '/';
4268                     print(sb, t, DateTime.MONTH, l).append(sep);
4269                     print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4270                     print(sb, t, DateTime.YEAR_2, l);
4271                     break;
4272                 }
4273                 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4274                     char sep = '-';
4275                     print(sb, t, DateTime.YEAR_4, l).append(sep);
4276                     print(sb, t, DateTime.MONTH, l).append(sep);
4277                     print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4278                     break;
4279                 }
4280                 default:
4281                     assert false;
4282                 }
4283             } catch (DateTimeException x) {
4284                 throw new IllegalFormatConversionException(c, t.getClass());
4285             }
4286             return sb;
4287         }
4288 
4289         // -- Methods to support throwing exceptions --
4290 
4291         private void failMismatch(Flags f, char c) {
4292             String fs = f.toString();
4293             throw new FormatFlagsConversionMismatchException(fs, c);
4294         }
4295 
4296         private void failConversion(char c, Object arg) {
4297             throw new IllegalFormatConversionException(c, arg.getClass());
4298         }
4299 
4300         private char getZero(Locale l) {
4301             if ((l != null) &&  !l.equals(locale())) {
4302                 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4303                 return dfs.getZeroDigit();
4304             }
4305             return zero;
4306         }
4307 
4308         private StringBuilder