< prev index next >

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

Print this page


   1 /*
   2  * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


 267  * encoding a date or time: {@code long}, {@link Long}, {@link Calendar},
 268  * {@link Date} and {@link TemporalAccessor TemporalAccessor}
 269  *
 270  * <li> <b>Percent</b> - produces a literal {@code '%'}
 271  * (<code>'\u0025'</code>)
 272  *
 273  * <li> <b>Line Separator</b> - produces the platform-specific line separator
 274  *
 275  * </ol>
 276  *
 277  * <p> For category <i>General</i>, <i>Character</i>, <i>Numberic</i>,
 278  * <i>Integral</i> and <i>Date/Time</i> conversion, unless otherwise specified,
 279  * if the argument <i>arg</i> is {@code null}, then the result is "{@code null}".
 280  *
 281  * <p> The following table summarizes the supported conversions.  Conversions
 282  * denoted by an upper-case character (i.e. {@code 'B'}, {@code 'H'},
 283  * {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'}, {@code 'G'},
 284  * {@code 'A'}, and {@code 'T'}) are the same as those for the corresponding
 285  * lower-case conversion characters except that the result is converted to
 286  * upper case according to the rules of the prevailing {@link java.util.Locale
 287  * Locale}.  The result is equivalent to the following invocation of {@link
 288  * String#toUpperCase(Locale)}


 289  *
 290  * <pre>
 291  *    out.toUpperCase(Locale.getDefault(Locale.Category.FORMAT)) </pre>
 292  *
 293  * <table class="striped">
 294  * <caption style="display:none">genConv</caption>
 295  * <thead>
 296  * <tr><th scope="col" style="vertical-align:bottom"> Conversion
 297  *     <th scope="col" style="vertical-align:bottom"> Argument Category
 298  *     <th scope="col" style="vertical-align:bottom"> Description
 299  * </thead>
 300  * <tbody>
 301  * <tr><th scope="row" style="vertical-align:top"> {@code 'b'}, {@code 'B'}
 302  *     <td style="vertical-align:top"> general
 303  *     <td> If the argument <i>arg</i> is {@code null}, then the result is
 304  *     "{@code false}".  If <i>arg</i> is a {@code boolean} or {@link
 305  *     Boolean}, then the result is the string returned by {@link
 306  *     String#valueOf(boolean) String.valueOf(arg)}.  Otherwise, the result is
 307  *     "true".
 308  *
 309  * <tr><th scope="row" style="vertical-align:top"> {@code 'h'}, {@code 'H'}
 310  *     <td style="vertical-align:top"> general
 311  *     <td> The result is obtained by invoking


 692  * respectively will be thrown.
 693  *
 694  * <p> If a format specifier contains a conversion character that is not
 695  * applicable to the corresponding argument, then an {@link
 696  * IllegalFormatConversionException} will be thrown.
 697  *
 698  * <p> All specified exceptions may be thrown by any of the {@code format}
 699  * methods of {@code Formatter} as well as by any {@code format} convenience
 700  * methods such as {@link String#format(String,Object...) String.format} and
 701  * {@link java.io.PrintStream#printf(String,Object...) PrintStream.printf}.
 702  *
 703  * <p> For category <i>General</i>, <i>Character</i>, <i>Numberic</i>,
 704  * <i>Integral</i> and <i>Date/Time</i> conversion, unless otherwise specified,
 705  * if the argument <i>arg</i> is {@code null}, then the result is "{@code null}".
 706  *
 707  * <p> Conversions denoted by an upper-case character (i.e. {@code 'B'},
 708  * {@code 'H'}, {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'},
 709  * {@code 'G'}, {@code 'A'}, and {@code 'T'}) are the same as those for the
 710  * corresponding lower-case conversion characters except that the result is
 711  * converted to upper case according to the rules of the prevailing {@link
 712  * java.util.Locale Locale}.  The result is equivalent to the following
 713  * invocation of {@link String#toUpperCase(Locale)}
 714  *
 715  * <pre>
 716  *    out.toUpperCase(Locale.getDefault(Locale.Category.FORMAT)) </pre>
 717  *
 718  * <h4><a id="dgen">General</a></h4>
 719  *
 720  * <p> The following general conversions may be applied to any argument type:
 721  *
 722  * <table class="striped">
 723  * <caption style="display:none">dgConv</caption>
 724  * <tbody>
 725  *
 726  * <tr><th scope="row" style="vertical-align:top"> {@code 'b'}
 727  *     <td style="vertical-align:top"> <code>'\u0062'</code>
 728  *     <td> Produces either "{@code true}" or "{@code false}" as returned by
 729  *     {@link Boolean#toString(boolean)}.
 730  *
 731  *     <p> If the argument is {@code null}, then the result is
 732  *     "{@code false}".  If the argument is a {@code boolean} or {@link
 733  *     Boolean}, then the result is the string returned by {@link
 734  *     String#valueOf(boolean) String.valueOf()}.  Otherwise, the result is
 735  *     "{@code true}".
 736  *


2880 
2881         public void print(Object arg, Locale l) throws IOException {
2882             if (dt) {
2883                 printDateTime(arg, l);
2884                 return;
2885             }
2886             switch(c) {
2887             case Conversion.DECIMAL_INTEGER:
2888             case Conversion.OCTAL_INTEGER:
2889             case Conversion.HEXADECIMAL_INTEGER:
2890                 printInteger(arg, l);
2891                 break;
2892             case Conversion.SCIENTIFIC:
2893             case Conversion.GENERAL:
2894             case Conversion.DECIMAL_FLOAT:
2895             case Conversion.HEXADECIMAL_FLOAT:
2896                 printFloat(arg, l);
2897                 break;
2898             case Conversion.CHARACTER:
2899             case Conversion.CHARACTER_UPPER:
2900                 printCharacter(arg);
2901                 break;
2902             case Conversion.BOOLEAN:
2903                 printBoolean(arg);
2904                 break;
2905             case Conversion.STRING:
2906                 printString(arg, l);
2907                 break;
2908             case Conversion.HASHCODE:
2909                 printHashCode(arg);
2910                 break;
2911             case Conversion.LINE_SEPARATOR:
2912                 a.append(System.lineSeparator());
2913                 break;
2914             case Conversion.PERCENT_SIGN:
2915                 a.append('%');
2916                 break;
2917             default:
2918                 assert false;
2919             }
2920         }
2921 
2922         private void printInteger(Object arg, Locale l) throws IOException {
2923             if (arg == null)
2924                 print("null");
2925             else if (arg instanceof Byte)
2926                 print(((Byte)arg).byteValue(), l);
2927             else if (arg instanceof Short)
2928                 print(((Short)arg).shortValue(), l);
2929             else if (arg instanceof Integer)
2930                 print(((Integer)arg).intValue(), l);
2931             else if (arg instanceof Long)
2932                 print(((Long)arg).longValue(), l);
2933             else if (arg instanceof BigInteger)
2934                 print(((BigInteger)arg), l);
2935             else
2936                 failConversion(c, arg);
2937         }
2938 
2939         private void printFloat(Object arg, Locale l) throws IOException {
2940             if (arg == null)
2941                 print("null");
2942             else if (arg instanceof Float)
2943                 print(((Float)arg).floatValue(), l);
2944             else if (arg instanceof Double)
2945                 print(((Double)arg).doubleValue(), l);
2946             else if (arg instanceof BigDecimal)
2947                 print(((BigDecimal)arg), l);
2948             else
2949                 failConversion(c, arg);
2950         }
2951 
2952         private void printDateTime(Object arg, Locale l) throws IOException {
2953             if (arg == null) {
2954                 print("null");
2955                 return;
2956             }
2957             Calendar cal = null;
2958 
2959             // Instead of Calendar.setLenient(true), perhaps we should
2960             // wrap the IllegalArgumentException that might be thrown?
2961             if (arg instanceof Long) {
2962                 // Note that the following method uses an instance of the
2963                 // default time zone (TimeZone.getDefaultRef().
2964                 cal = Calendar.getInstance(l == null ? Locale.US : l);
2965                 cal.setTimeInMillis((Long)arg);
2966             } else if (arg instanceof Date) {
2967                 // Note that the following method uses an instance of the
2968                 // default time zone (TimeZone.getDefaultRef().
2969                 cal = Calendar.getInstance(l == null ? Locale.US : l);
2970                 cal.setTime((Date)arg);
2971             } else if (arg instanceof Calendar) {
2972                 cal = (Calendar) ((Calendar) arg).clone();
2973                 cal.setLenient(true);
2974             } else if (arg instanceof TemporalAccessor) {
2975                 print((TemporalAccessor) arg, c, l);
2976                 return;
2977             } else {
2978                 failConversion(c, arg);
2979             }
2980             // Use the provided locale so that invocations of
2981             // localizedMagnitude() use optimizations for null.
2982             print(cal, c, l);
2983         }
2984 
2985         private void printCharacter(Object arg) throws IOException {
2986             if (arg == null) {
2987                 print("null");
2988                 return;
2989             }
2990             String s = null;
2991             if (arg instanceof Character) {
2992                 s = ((Character)arg).toString();
2993             } else if (arg instanceof Byte) {
2994                 byte i = ((Byte)arg).byteValue();
2995                 if (Character.isValidCodePoint(i))
2996                     s = new String(Character.toChars(i));
2997                 else
2998                     throw new IllegalFormatCodePointException(i);
2999             } else if (arg instanceof Short) {
3000                 short i = ((Short)arg).shortValue();
3001                 if (Character.isValidCodePoint(i))
3002                     s = new String(Character.toChars(i));
3003                 else
3004                     throw new IllegalFormatCodePointException(i);
3005             } else if (arg instanceof Integer) {
3006                 int i = ((Integer)arg).intValue();
3007                 if (Character.isValidCodePoint(i))
3008                     s = new String(Character.toChars(i));
3009                 else
3010                     throw new IllegalFormatCodePointException(i);
3011             } else {
3012                 failConversion(c, arg);
3013             }
3014             print(s);
3015         }
3016 
3017         private void printString(Object arg, Locale l) throws IOException {
3018             if (arg instanceof Formattable) {
3019                 Formatter fmt = Formatter.this;
3020                 if (fmt.locale() != l)
3021                     fmt = new Formatter(fmt.out(), l);
3022                 ((Formattable)arg).formatTo(fmt, f.valueOf(), width, precision);
3023             } else {
3024                 if (f.contains(Flags.ALTERNATE))
3025                     failMismatch(Flags.ALTERNATE, 's');
3026                 if (arg == null)
3027                     print("null");
3028                 else
3029                     print(arg.toString());
3030             }
3031         }
3032 
3033         private void printBoolean(Object arg) throws IOException {
3034             String s;
3035             if (arg != null)
3036                 s = ((arg instanceof Boolean)
3037                      ? ((Boolean)arg).toString()
3038                      : Boolean.toString(true));
3039             else
3040                 s = Boolean.toString(false);
3041             print(s);
3042         }
3043 
3044         private void printHashCode(Object arg) throws IOException {
3045             String s = (arg == null
3046                         ? "null"
3047                         : Integer.toHexString(arg.hashCode()));
3048             print(s);
3049         }
3050 
3051         private void print(String s) throws IOException {
3052             if (precision != -1 && precision < s.length())
3053                 s = s.substring(0, precision);
3054             if (f.contains(Flags.UPPERCASE))
3055                 s = s.toUpperCase(Locale.getDefault(Locale.Category.FORMAT));
3056             appendJustified(a, s);
3057         }
3058 





3059         private Appendable appendJustified(Appendable a, CharSequence cs) throws IOException {
3060              if (width == -1) {
3061                  return a.append(cs);
3062              }
3063              boolean padRight = f.contains(Flags.LEFT_JUSTIFY);
3064              int sp = width - cs.length();
3065              if (padRight) {
3066                  a.append(cs);
3067              }
3068              for (int i = 0; i < sp; i++) {
3069                  a.append(' ');
3070              }
3071              if (!padRight) {
3072                  a.append(cs);
3073              }
3074              return a;
3075         }
3076 
3077         public String toString() {
3078             StringBuilder sb = new StringBuilder("%");


3259                     sb.append('0');
3260                 if (f.contains(Flags.ZERO_PAD)) {
3261                     trailingZeros(sb, width - len);
3262                 }
3263                 sb.append(s);
3264             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3265                 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3266                               Flags.PLUS);
3267                 String s = Long.toHexString(value);
3268                 int len = (f.contains(Flags.ALTERNATE)
3269                            ? s.length() + 2
3270                            : s.length());
3271 
3272                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3273                 if (f.contains(Flags.ALTERNATE))
3274                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3275                 if (f.contains(Flags.ZERO_PAD)) {
3276                     trailingZeros(sb, width - len);
3277                 }
3278                 if (f.contains(Flags.UPPERCASE))
3279                     s = s.toUpperCase(Locale.getDefault(Locale.Category.FORMAT));
3280                 sb.append(s);
3281             }
3282 
3283             // justify based on width
3284             appendJustified(a, sb);
3285         }
3286 
3287         // neg := val < 0
3288         private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3289             if (!neg) {
3290                 if (f.contains(Flags.PLUS)) {
3291                     sb.append('+');
3292                 } else if (f.contains(Flags.LEADING_SPACE)) {
3293                     sb.append(' ');
3294                 }
3295             } else {
3296                 if (f.contains(Flags.PARENTHESES))
3297                     sb.append('(');
3298                 else
3299                     sb.append('-');


3334                 if (f.contains(Flags.ZERO_PAD)) {
3335                     trailingZeros(sb, width - len);
3336                 }
3337                 sb.append(s);
3338             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3339                 String s = v.toString(16);
3340 
3341                 int len = s.length() + sb.length();
3342                 if (neg && f.contains(Flags.PARENTHESES))
3343                     len++;
3344 
3345                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3346                 if (f.contains(Flags.ALTERNATE)) {
3347                     len += 2;
3348                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3349                 }
3350                 if (f.contains(Flags.ZERO_PAD)) {
3351                     trailingZeros(sb, width - len);
3352                 }
3353                 if (f.contains(Flags.UPPERCASE))
3354                     s = s.toUpperCase(Locale.getDefault(Locale.Category.FORMAT));
3355                 sb.append(s);
3356             }
3357 
3358             // trailing sign indicator
3359             trailingSign(sb, (value.signum() == -1));
3360 
3361             // justify based on width
3362             appendJustified(a, sb);
3363         }
3364 
3365         private void print(float value, Locale l) throws IOException {
3366             print((double) value, l);
3367         }
3368 
3369         private void print(double value, Locale l) throws IOException {
3370             StringBuilder sb = new StringBuilder();
3371             boolean neg = Double.compare(value, 0.0) == -1;
3372 
3373             if (!Double.isNaN(value)) {
3374                 double v = Math.abs(value);


3933         private int adjustWidth(int width, Flags f, boolean neg) {
3934             int newW = width;
3935             if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3936                 newW--;
3937             return newW;
3938         }
3939 
3940         // Add trailing zeros
3941         private void trailingZeros(StringBuilder sb, int nzeros) {
3942             for (int i = 0; i < nzeros; i++) {
3943                 sb.append('0');
3944             }
3945         }
3946 
3947         private void print(Calendar t, char c, Locale l)  throws IOException {
3948             StringBuilder sb = new StringBuilder();
3949             print(sb, t, c, l);
3950 
3951             // justify based on width
3952             if (f.contains(Flags.UPPERCASE)) {
3953                 appendJustified(a, sb.toString().toUpperCase(Locale.getDefault(Locale.Category.FORMAT)));
3954             } else {
3955                 appendJustified(a, sb);
3956             }
3957         }
3958 
3959         private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3960                 throws IOException {
3961             if (sb == null)
3962                 sb = new StringBuilder();
3963             switch (c) {
3964             case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3965             case DateTime.HOUR_0:        // 'I' (01 - 12)
3966             case DateTime.HOUR_OF_DAY:   // 'k' (0 - 23) -- like H
3967             case DateTime.HOUR:        { // 'l' (1 - 12) -- like I
3968                 int i = t.get(Calendar.HOUR_OF_DAY);
3969                 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3970                     i = (i == 0 || i == 12 ? 12 : i % 12);
3971                 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3972                                || c == DateTime.HOUR_0
3973                                ? Flags.ZERO_PAD


4115             case DateTime.TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4116             case DateTime.TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
4117                 char sep = ':';
4118                 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4119                 print(sb, t, DateTime.MINUTE, l);
4120                 if (c == DateTime.TIME) {
4121                     sb.append(sep);
4122                     print(sb, t, DateTime.SECOND, l);
4123                 }
4124                 break;
4125             }
4126             case DateTime.TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
4127                 char sep = ':';
4128                 print(sb, t, DateTime.HOUR_0, l).append(sep);
4129                 print(sb, t, DateTime.MINUTE, l).append(sep);
4130                 print(sb, t, DateTime.SECOND, l).append(' ');
4131                 // this may be in wrong place for some locales
4132                 StringBuilder tsb = new StringBuilder();
4133                 print(tsb, t, DateTime.AM_PM, l);
4134 
4135                 sb.append(tsb.toString().toUpperCase(Objects.requireNonNullElse(l,
4136                                                Locale.getDefault(Locale.Category.FORMAT))));
4137                 break;
4138             }
4139             case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4140                 char sep = ' ';
4141                 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4142                 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4143                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4144                 print(sb, t, DateTime.TIME, l).append(sep);
4145                 print(sb, t, DateTime.ZONE, l).append(sep);
4146                 print(sb, t, DateTime.YEAR_4, l);
4147                 break;
4148             }
4149             case DateTime.DATE:            { // 'D' (mm/dd/yy)
4150                 char sep = '/';
4151                 print(sb, t, DateTime.MONTH, l).append(sep);
4152                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4153                 print(sb, t, DateTime.YEAR_2, l);
4154                 break;
4155             }
4156             case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4157                 char sep = '-';
4158                 print(sb, t, DateTime.YEAR_4, l).append(sep);
4159                 print(sb, t, DateTime.MONTH, l).append(sep);
4160                 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4161                 break;
4162             }
4163             default:
4164                 assert false;
4165             }
4166             return sb;
4167         }
4168 
4169         private void print(TemporalAccessor t, char c, Locale l)  throws IOException {
4170             StringBuilder sb = new StringBuilder();
4171             print(sb, t, c, l);
4172             // justify based on width
4173             if (f.contains(Flags.UPPERCASE)) {
4174                 appendJustified(a, sb.toString().toUpperCase(Locale.getDefault(Locale.Category.FORMAT)));
4175             } else {
4176                 appendJustified(a, sb);
4177             }
4178         }
4179 
4180         private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4181                                  Locale l) throws IOException {
4182             if (sb == null)
4183                 sb = new StringBuilder();
4184             try {
4185                 switch (c) {
4186                 case DateTime.HOUR_OF_DAY_0: {  // 'H' (00 - 23)
4187                     int i = t.get(ChronoField.HOUR_OF_DAY);
4188                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4189                     break;
4190                 }
4191                 case DateTime.HOUR_OF_DAY: {   // 'k' (0 - 23) -- like H
4192                     int i = t.get(ChronoField.HOUR_OF_DAY);
4193                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4194                     break;


4356                 // Composites
4357                 case DateTime.TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4358                 case DateTime.TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
4359                     char sep = ':';
4360                     print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4361                     print(sb, t, DateTime.MINUTE, l);
4362                     if (c == DateTime.TIME) {
4363                         sb.append(sep);
4364                         print(sb, t, DateTime.SECOND, l);
4365                     }
4366                     break;
4367                 }
4368                 case DateTime.TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
4369                     char sep = ':';
4370                     print(sb, t, DateTime.HOUR_0, l).append(sep);
4371                     print(sb, t, DateTime.MINUTE, l).append(sep);
4372                     print(sb, t, DateTime.SECOND, l).append(' ');
4373                     // this may be in wrong place for some locales
4374                     StringBuilder tsb = new StringBuilder();
4375                     print(tsb, t, DateTime.AM_PM, l);
4376                     sb.append(tsb.toString().toUpperCase(Objects.requireNonNullElse(l,
4377                                         Locale.getDefault(Locale.Category.FORMAT))));
4378                     break;
4379                 }
4380                 case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4381                     char sep = ' ';
4382                     print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4383                     print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4384                     print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4385                     print(sb, t, DateTime.TIME, l).append(sep);
4386                     print(sb, t, DateTime.ZONE, l).append(sep);
4387                     print(sb, t, DateTime.YEAR_4, l);
4388                     break;
4389                 }
4390                 case DateTime.DATE:            { // 'D' (mm/dd/yy)
4391                     char sep = '/';
4392                     print(sb, t, DateTime.MONTH, l).append(sep);
4393                     print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4394                     print(sb, t, DateTime.YEAR_2, l);
4395                     break;
4396                 }
4397                 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)


   1 /*
   2  * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


 267  * encoding a date or time: {@code long}, {@link Long}, {@link Calendar},
 268  * {@link Date} and {@link TemporalAccessor TemporalAccessor}
 269  *
 270  * <li> <b>Percent</b> - produces a literal {@code '%'}
 271  * (<code>'\u0025'</code>)
 272  *
 273  * <li> <b>Line Separator</b> - produces the platform-specific line separator
 274  *
 275  * </ol>
 276  *
 277  * <p> For category <i>General</i>, <i>Character</i>, <i>Numberic</i>,
 278  * <i>Integral</i> and <i>Date/Time</i> conversion, unless otherwise specified,
 279  * if the argument <i>arg</i> is {@code null}, then the result is "{@code null}".
 280  *
 281  * <p> The following table summarizes the supported conversions.  Conversions
 282  * denoted by an upper-case character (i.e. {@code 'B'}, {@code 'H'},
 283  * {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'}, {@code 'G'},
 284  * {@code 'A'}, and {@code 'T'}) are the same as those for the corresponding
 285  * lower-case conversion characters except that the result is converted to
 286  * upper case according to the rules of the prevailing {@link java.util.Locale
 287  * Locale}. If there is no explicit locale specified, either at the
 288  * construction of the instance or as a parameter to its method
 289  * invocation, then the {@link java.util.Locale.Category#FORMAT default locale}
 290  * is used.
 291  *


 292  *
 293  * <table class="striped">
 294  * <caption style="display:none">genConv</caption>
 295  * <thead>
 296  * <tr><th scope="col" style="vertical-align:bottom"> Conversion
 297  *     <th scope="col" style="vertical-align:bottom"> Argument Category
 298  *     <th scope="col" style="vertical-align:bottom"> Description
 299  * </thead>
 300  * <tbody>
 301  * <tr><th scope="row" style="vertical-align:top"> {@code 'b'}, {@code 'B'}
 302  *     <td style="vertical-align:top"> general
 303  *     <td> If the argument <i>arg</i> is {@code null}, then the result is
 304  *     "{@code false}".  If <i>arg</i> is a {@code boolean} or {@link
 305  *     Boolean}, then the result is the string returned by {@link
 306  *     String#valueOf(boolean) String.valueOf(arg)}.  Otherwise, the result is
 307  *     "true".
 308  *
 309  * <tr><th scope="row" style="vertical-align:top"> {@code 'h'}, {@code 'H'}
 310  *     <td style="vertical-align:top"> general
 311  *     <td> The result is obtained by invoking


 692  * respectively will be thrown.
 693  *
 694  * <p> If a format specifier contains a conversion character that is not
 695  * applicable to the corresponding argument, then an {@link
 696  * IllegalFormatConversionException} will be thrown.
 697  *
 698  * <p> All specified exceptions may be thrown by any of the {@code format}
 699  * methods of {@code Formatter} as well as by any {@code format} convenience
 700  * methods such as {@link String#format(String,Object...) String.format} and
 701  * {@link java.io.PrintStream#printf(String,Object...) PrintStream.printf}.
 702  *
 703  * <p> For category <i>General</i>, <i>Character</i>, <i>Numberic</i>,
 704  * <i>Integral</i> and <i>Date/Time</i> conversion, unless otherwise specified,
 705  * if the argument <i>arg</i> is {@code null}, then the result is "{@code null}".
 706  *
 707  * <p> Conversions denoted by an upper-case character (i.e. {@code 'B'},
 708  * {@code 'H'}, {@code 'S'}, {@code 'C'}, {@code 'X'}, {@code 'E'},
 709  * {@code 'G'}, {@code 'A'}, and {@code 'T'}) are the same as those for the
 710  * corresponding lower-case conversion characters except that the result is
 711  * converted to upper case according to the rules of the prevailing {@link
 712  * java.util.Locale Locale}. If there is no explicit locale specified,
 713  * either at the construction of the instance or as a parameter to its method
 714  * invocation, then the {@link java.util.Locale.Category#FORMAT default locale}
 715  * is used.

 716  *
 717  * <h4><a id="dgen">General</a></h4>
 718  *
 719  * <p> The following general conversions may be applied to any argument type:
 720  *
 721  * <table class="striped">
 722  * <caption style="display:none">dgConv</caption>
 723  * <tbody>
 724  *
 725  * <tr><th scope="row" style="vertical-align:top"> {@code 'b'}
 726  *     <td style="vertical-align:top"> <code>'\u0062'</code>
 727  *     <td> Produces either "{@code true}" or "{@code false}" as returned by
 728  *     {@link Boolean#toString(boolean)}.
 729  *
 730  *     <p> If the argument is {@code null}, then the result is
 731  *     "{@code false}".  If the argument is a {@code boolean} or {@link
 732  *     Boolean}, then the result is the string returned by {@link
 733  *     String#valueOf(boolean) String.valueOf()}.  Otherwise, the result is
 734  *     "{@code true}".
 735  *


2879 
2880         public void print(Object arg, Locale l) throws IOException {
2881             if (dt) {
2882                 printDateTime(arg, l);
2883                 return;
2884             }
2885             switch(c) {
2886             case Conversion.DECIMAL_INTEGER:
2887             case Conversion.OCTAL_INTEGER:
2888             case Conversion.HEXADECIMAL_INTEGER:
2889                 printInteger(arg, l);
2890                 break;
2891             case Conversion.SCIENTIFIC:
2892             case Conversion.GENERAL:
2893             case Conversion.DECIMAL_FLOAT:
2894             case Conversion.HEXADECIMAL_FLOAT:
2895                 printFloat(arg, l);
2896                 break;
2897             case Conversion.CHARACTER:
2898             case Conversion.CHARACTER_UPPER:
2899                 printCharacter(arg, l);
2900                 break;
2901             case Conversion.BOOLEAN:
2902                 printBoolean(arg, l);
2903                 break;
2904             case Conversion.STRING:
2905                 printString(arg, l);
2906                 break;
2907             case Conversion.HASHCODE:
2908                 printHashCode(arg, l);
2909                 break;
2910             case Conversion.LINE_SEPARATOR:
2911                 a.append(System.lineSeparator());
2912                 break;
2913             case Conversion.PERCENT_SIGN:
2914                 a.append('%');
2915                 break;
2916             default:
2917                 assert false;
2918             }
2919         }
2920 
2921         private void printInteger(Object arg, Locale l) throws IOException {
2922             if (arg == null)
2923                 print("null", l);
2924             else if (arg instanceof Byte)
2925                 print(((Byte)arg).byteValue(), l);
2926             else if (arg instanceof Short)
2927                 print(((Short)arg).shortValue(), l);
2928             else if (arg instanceof Integer)
2929                 print(((Integer)arg).intValue(), l);
2930             else if (arg instanceof Long)
2931                 print(((Long)arg).longValue(), l);
2932             else if (arg instanceof BigInteger)
2933                 print(((BigInteger)arg), l);
2934             else
2935                 failConversion(c, arg);
2936         }
2937 
2938         private void printFloat(Object arg, Locale l) throws IOException {
2939             if (arg == null)
2940                 print("null", l);
2941             else if (arg instanceof Float)
2942                 print(((Float)arg).floatValue(), l);
2943             else if (arg instanceof Double)
2944                 print(((Double)arg).doubleValue(), l);
2945             else if (arg instanceof BigDecimal)
2946                 print(((BigDecimal)arg), l);
2947             else
2948                 failConversion(c, arg);
2949         }
2950 
2951         private void printDateTime(Object arg, Locale l) throws IOException {
2952             if (arg == null) {
2953                 print("null", l);
2954                 return;
2955             }
2956             Calendar cal = null;
2957 
2958             // Instead of Calendar.setLenient(true), perhaps we should
2959             // wrap the IllegalArgumentException that might be thrown?
2960             if (arg instanceof Long) {
2961                 // Note that the following method uses an instance of the
2962                 // default time zone (TimeZone.getDefaultRef().
2963                 cal = Calendar.getInstance(l == null ? Locale.US : l);
2964                 cal.setTimeInMillis((Long)arg);
2965             } else if (arg instanceof Date) {
2966                 // Note that the following method uses an instance of the
2967                 // default time zone (TimeZone.getDefaultRef().
2968                 cal = Calendar.getInstance(l == null ? Locale.US : l);
2969                 cal.setTime((Date)arg);
2970             } else if (arg instanceof Calendar) {
2971                 cal = (Calendar) ((Calendar) arg).clone();
2972                 cal.setLenient(true);
2973             } else if (arg instanceof TemporalAccessor) {
2974                 print((TemporalAccessor) arg, c, l);
2975                 return;
2976             } else {
2977                 failConversion(c, arg);
2978             }
2979             // Use the provided locale so that invocations of
2980             // localizedMagnitude() use optimizations for null.
2981             print(cal, c, l);
2982         }
2983 
2984         private void printCharacter(Object arg, Locale l) throws IOException {
2985             if (arg == null) {
2986                 print("null", l);
2987                 return;
2988             }
2989             String s = null;
2990             if (arg instanceof Character) {
2991                 s = ((Character)arg).toString();
2992             } else if (arg instanceof Byte) {
2993                 byte i = ((Byte)arg).byteValue();
2994                 if (Character.isValidCodePoint(i))
2995                     s = new String(Character.toChars(i));
2996                 else
2997                     throw new IllegalFormatCodePointException(i);
2998             } else if (arg instanceof Short) {
2999                 short i = ((Short)arg).shortValue();
3000                 if (Character.isValidCodePoint(i))
3001                     s = new String(Character.toChars(i));
3002                 else
3003                     throw new IllegalFormatCodePointException(i);
3004             } else if (arg instanceof Integer) {
3005                 int i = ((Integer)arg).intValue();
3006                 if (Character.isValidCodePoint(i))
3007                     s = new String(Character.toChars(i));
3008                 else
3009                     throw new IllegalFormatCodePointException(i);
3010             } else {
3011                 failConversion(c, arg);
3012             }
3013             print(s, l);
3014         }
3015 
3016         private void printString(Object arg, Locale l) throws IOException {
3017             if (arg instanceof Formattable) {
3018                 Formatter fmt = Formatter.this;
3019                 if (fmt.locale() != l)
3020                     fmt = new Formatter(fmt.out(), l);
3021                 ((Formattable)arg).formatTo(fmt, f.valueOf(), width, precision);
3022             } else {
3023                 if (f.contains(Flags.ALTERNATE))
3024                     failMismatch(Flags.ALTERNATE, 's');
3025                 if (arg == null)
3026                     print("null", l);
3027                 else
3028                     print(arg.toString(), l);
3029             }
3030         }
3031 
3032         private void printBoolean(Object arg, Locale l) throws IOException {
3033             String s;
3034             if (arg != null)
3035                 s = ((arg instanceof Boolean)
3036                      ? ((Boolean)arg).toString()
3037                      : Boolean.toString(true));
3038             else
3039                 s = Boolean.toString(false);
3040             print(s, l);
3041         }
3042 
3043         private void printHashCode(Object arg, Locale l) throws IOException {
3044             String s = (arg == null
3045                         ? "null"
3046                         : Integer.toHexString(arg.hashCode()));
3047             print(s, l);
3048         }
3049 
3050         private void print(String s, Locale l) throws IOException {
3051             if (precision != -1 && precision < s.length())
3052                 s = s.substring(0, precision);
3053             if (f.contains(Flags.UPPERCASE))
3054                 s = toUpperCaseWithLocale(s, l);
3055             appendJustified(a, s);
3056         }
3057 
3058         private String toUpperCaseWithLocale(String s, Locale l) {
3059             return s.toUpperCase(Objects.requireNonNullElse(l,
3060                     Locale.getDefault(Locale.Category.FORMAT)));
3061         }
3062 
3063         private Appendable appendJustified(Appendable a, CharSequence cs) throws IOException {
3064              if (width == -1) {
3065                  return a.append(cs);
3066              }
3067              boolean padRight = f.contains(Flags.LEFT_JUSTIFY);
3068              int sp = width - cs.length();
3069              if (padRight) {
3070                  a.append(cs);
3071              }
3072              for (int i = 0; i < sp; i++) {
3073                  a.append(' ');
3074              }
3075              if (!padRight) {
3076                  a.append(cs);
3077              }
3078              return a;
3079         }
3080 
3081         public String toString() {
3082             StringBuilder sb = new StringBuilder("%");


3263                     sb.append('0');
3264                 if (f.contains(Flags.ZERO_PAD)) {
3265                     trailingZeros(sb, width - len);
3266                 }
3267                 sb.append(s);
3268             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3269                 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3270                               Flags.PLUS);
3271                 String s = Long.toHexString(value);
3272                 int len = (f.contains(Flags.ALTERNATE)
3273                            ? s.length() + 2
3274                            : s.length());
3275 
3276                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3277                 if (f.contains(Flags.ALTERNATE))
3278                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3279                 if (f.contains(Flags.ZERO_PAD)) {
3280                     trailingZeros(sb, width - len);
3281                 }
3282                 if (f.contains(Flags.UPPERCASE))
3283                     s = toUpperCaseWithLocale(s, l);
3284                 sb.append(s);
3285             }
3286 
3287             // justify based on width
3288             appendJustified(a, sb);
3289         }
3290 
3291         // neg := val < 0
3292         private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3293             if (!neg) {
3294                 if (f.contains(Flags.PLUS)) {
3295                     sb.append('+');
3296                 } else if (f.contains(Flags.LEADING_SPACE)) {
3297                     sb.append(' ');
3298                 }
3299             } else {
3300                 if (f.contains(Flags.PARENTHESES))
3301                     sb.append('(');
3302                 else
3303                     sb.append('-');


3338                 if (f.contains(Flags.ZERO_PAD)) {
3339                     trailingZeros(sb, width - len);
3340                 }
3341                 sb.append(s);
3342             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3343                 String s = v.toString(16);
3344 
3345                 int len = s.length() + sb.length();
3346                 if (neg && f.contains(Flags.PARENTHESES))
3347                     len++;
3348 
3349                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3350                 if (f.contains(Flags.ALTERNATE)) {
3351                     len += 2;
3352                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3353                 }
3354                 if (f.contains(Flags.ZERO_PAD)) {
3355                     trailingZeros(sb, width - len);
3356                 }
3357                 if (f.contains(Flags.UPPERCASE))
3358                     s = toUpperCaseWithLocale(s, l);
3359                 sb.append(s);
3360             }
3361 
3362             // trailing sign indicator
3363             trailingSign(sb, (value.signum() == -1));
3364 
3365             // justify based on width
3366             appendJustified(a, sb);
3367         }
3368 
3369         private void print(float value, Locale l) throws IOException {
3370             print((double) value, l);
3371         }
3372 
3373         private void print(double value, Locale l) throws IOException {
3374             StringBuilder sb = new StringBuilder();
3375             boolean neg = Double.compare(value, 0.0) == -1;
3376 
3377             if (!Double.isNaN(value)) {
3378                 double v = Math.abs(value);


3937         private int adjustWidth(int width, Flags f, boolean neg) {
3938             int newW = width;
3939             if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3940                 newW--;
3941             return newW;
3942         }
3943 
3944         // Add trailing zeros
3945         private void trailingZeros(StringBuilder sb, int nzeros) {
3946             for (int i = 0; i < nzeros; i++) {
3947                 sb.append('0');
3948             }
3949         }
3950 
3951         private void print(Calendar t, char c, Locale l)  throws IOException {
3952             StringBuilder sb = new StringBuilder();
3953             print(sb, t, c, l);
3954 
3955             // justify based on width
3956             if (f.contains(Flags.UPPERCASE)) {
3957                 appendJustified(a, toUpperCaseWithLocale(sb.toString(), l));
3958             } else {
3959                 appendJustified(a, sb);
3960             }
3961         }
3962 
3963         private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3964                 throws IOException {
3965             if (sb == null)
3966                 sb = new StringBuilder();
3967             switch (c) {
3968             case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3969             case DateTime.HOUR_0:        // 'I' (01 - 12)
3970             case DateTime.HOUR_OF_DAY:   // 'k' (0 - 23) -- like H
3971             case DateTime.HOUR:        { // 'l' (1 - 12) -- like I
3972                 int i = t.get(Calendar.HOUR_OF_DAY);
3973                 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3974                     i = (i == 0 || i == 12 ? 12 : i % 12);
3975                 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3976                                || c == DateTime.HOUR_0
3977                                ? Flags.ZERO_PAD


4119             case DateTime.TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4120             case DateTime.TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
4121                 char sep = ':';
4122                 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4123                 print(sb, t, DateTime.MINUTE, l);
4124                 if (c == DateTime.TIME) {
4125                     sb.append(sep);
4126                     print(sb, t, DateTime.SECOND, l);
4127                 }
4128                 break;
4129             }
4130             case DateTime.TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
4131                 char sep = ':';
4132                 print(sb, t, DateTime.HOUR_0, l).append(sep);
4133                 print(sb, t, DateTime.MINUTE, l).append(sep);
4134                 print(sb, t, DateTime.SECOND, l).append(' ');
4135                 // this may be in wrong place for some locales
4136                 StringBuilder tsb = new StringBuilder();
4137                 print(tsb, t, DateTime.AM_PM, l);
4138 
4139                 sb.append(toUpperCaseWithLocale(tsb.toString(), l));

4140                 break;
4141             }
4142             case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4143                 char sep = ' ';
4144                 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4145                 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4146                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4147                 print(sb, t, DateTime.TIME, l).append(sep);
4148                 print(sb, t, DateTime.ZONE, l).append(sep);
4149                 print(sb, t, DateTime.YEAR_4, l);
4150                 break;
4151             }
4152             case DateTime.DATE:            { // 'D' (mm/dd/yy)
4153                 char sep = '/';
4154                 print(sb, t, DateTime.MONTH, l).append(sep);
4155                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4156                 print(sb, t, DateTime.YEAR_2, l);
4157                 break;
4158             }
4159             case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4160                 char sep = '-';
4161                 print(sb, t, DateTime.YEAR_4, l).append(sep);
4162                 print(sb, t, DateTime.MONTH, l).append(sep);
4163                 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4164                 break;
4165             }
4166             default:
4167                 assert false;
4168             }
4169             return sb;
4170         }
4171 
4172         private void print(TemporalAccessor t, char c, Locale l)  throws IOException {
4173             StringBuilder sb = new StringBuilder();
4174             print(sb, t, c, l);
4175             // justify based on width
4176             if (f.contains(Flags.UPPERCASE)) {
4177                 appendJustified(a, toUpperCaseWithLocale(sb.toString(), l));
4178             } else {
4179                 appendJustified(a, sb);
4180             }
4181         }
4182 
4183         private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4184                                  Locale l) throws IOException {
4185             if (sb == null)
4186                 sb = new StringBuilder();
4187             try {
4188                 switch (c) {
4189                 case DateTime.HOUR_OF_DAY_0: {  // 'H' (00 - 23)
4190                     int i = t.get(ChronoField.HOUR_OF_DAY);
4191                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4192                     break;
4193                 }
4194                 case DateTime.HOUR_OF_DAY: {   // 'k' (0 - 23) -- like H
4195                     int i = t.get(ChronoField.HOUR_OF_DAY);
4196                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4197                     break;


4359                 // Composites
4360                 case DateTime.TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4361                 case DateTime.TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
4362                     char sep = ':';
4363                     print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4364                     print(sb, t, DateTime.MINUTE, l);
4365                     if (c == DateTime.TIME) {
4366                         sb.append(sep);
4367                         print(sb, t, DateTime.SECOND, l);
4368                     }
4369                     break;
4370                 }
4371                 case DateTime.TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
4372                     char sep = ':';
4373                     print(sb, t, DateTime.HOUR_0, l).append(sep);
4374                     print(sb, t, DateTime.MINUTE, l).append(sep);
4375                     print(sb, t, DateTime.SECOND, l).append(' ');
4376                     // this may be in wrong place for some locales
4377                     StringBuilder tsb = new StringBuilder();
4378                     print(tsb, t, DateTime.AM_PM, l);
4379                     sb.append(toUpperCaseWithLocale(tsb.toString(), l));

4380                     break;
4381                 }
4382                 case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4383                     char sep = ' ';
4384                     print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4385                     print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4386                     print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4387                     print(sb, t, DateTime.TIME, l).append(sep);
4388                     print(sb, t, DateTime.ZONE, l).append(sep);
4389                     print(sb, t, DateTime.YEAR_4, l);
4390                     break;
4391                 }
4392                 case DateTime.DATE:            { // 'D' (mm/dd/yy)
4393                     char sep = '/';
4394                     print(sb, t, DateTime.MONTH, l).append(sep);
4395                     print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4396                     print(sb, t, DateTime.YEAR_2, l);
4397                     break;
4398                 }
4399                 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)


< prev index next >