< prev index next >

src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java

Print this page


   1 /*
   2  * Copyright (c) 2012, 2013, 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


 649             active.valueParserIndex = appendInternal(pp);
 650         }
 651         return this;
 652     }
 653 
 654     //-----------------------------------------------------------------------
 655     /**
 656      * Appends the fractional value of a date-time field to the formatter.
 657      * <p>
 658      * The fractional value of the field will be output including the
 659      * preceding decimal point. The preceding value is not output.
 660      * For example, the second-of-minute value of 15 would be output as {@code .25}.
 661      * <p>
 662      * The width of the printed fraction can be controlled. Setting the
 663      * minimum width to zero will cause no output to be generated.
 664      * The printed fraction will have the minimum width necessary between
 665      * the minimum and maximum widths - trailing zeroes are omitted.
 666      * No rounding occurs due to the maximum width - digits are simply dropped.
 667      * <p>
 668      * When parsing in strict mode, the number of parsed digits must be between
 669      * the minimum and maximum width. When parsing in lenient mode, the minimum
 670      * width is considered to be zero and the maximum is nine.



 671      * <p>
 672      * If the value cannot be obtained then an exception will be thrown.
 673      * If the value is negative an exception will be thrown.
 674      * If the field does not have a fixed set of valid values then an
 675      * exception will be thrown.
 676      * If the field value in the date-time to be printed is invalid it
 677      * cannot be printed and an exception will be thrown.
 678      *
 679      * @param field  the field to append, not null
 680      * @param minWidth  the minimum width of the field excluding the decimal point, from 0 to 9
 681      * @param maxWidth  the maximum width of the field excluding the decimal point, from 1 to 9
 682      * @param decimalPoint  whether to output the localized decimal point symbol
 683      * @return this, for chaining, not null
 684      * @throws IllegalArgumentException if the field has a variable set of valid values or
 685      *  either width is invalid
 686      */
 687     public DateTimeFormatterBuilder appendFraction(
 688             TemporalField field, int minWidth, int maxWidth, boolean decimalPoint) {




 689         appendInternal(new FractionPrinterParser(field, minWidth, maxWidth, decimalPoint));

 690         return this;
 691     }
 692 
 693     //-----------------------------------------------------------------------
 694     /**
 695      * Appends the text of a date-time field to the formatter using the full
 696      * text style.
 697      * <p>
 698      * The text of the field will be output during a format.
 699      * The value must be within the valid range of the field.
 700      * If the value cannot be obtained then an exception will be thrown.
 701      * If the field has no textual representation, then the numeric value will be used.
 702      * <p>
 703      * The value will be printed as per the normal format of an integer value.
 704      * Only negative numbers will be signed. No padding will be added.
 705      *
 706      * @param field  the field to append, not null
 707      * @return this, for chaining, not null
 708      */
 709     public DateTimeFormatterBuilder appendText(TemporalField field) {


2902          */
2903         @Override
2904         boolean isFixedWidth(DateTimeParseContext context) {
2905            if (context.isStrict() == false) {
2906                return false;
2907            }
2908            return super.isFixedWidth(context);
2909         }
2910 
2911         @Override
2912         public String toString() {
2913             return "ReducedValue(" + field + "," + minWidth + "," + maxWidth +
2914                     "," + Objects.requireNonNullElse(baseDate, baseValue) + ")";
2915         }
2916     }
2917 
2918     //-----------------------------------------------------------------------
2919     /**
2920      * Prints and parses a numeric date-time field with optional padding.
2921      */
2922     static final class FractionPrinterParser implements DateTimePrinterParser {
2923         private final TemporalField field;
2924         private final int minWidth;
2925         private final int maxWidth;
2926         private final boolean decimalPoint;
2927 
2928         /**
2929          * Constructor.
2930          *
2931          * @param field  the field to output, not null
2932          * @param minWidth  the minimum width to output, from 0 to 9
2933          * @param maxWidth  the maximum width to output, from 0 to 9
2934          * @param decimalPoint  whether to output the localized decimal point symbol
2935          */
2936         FractionPrinterParser(TemporalField field, int minWidth, int maxWidth, boolean decimalPoint) {

2937             Objects.requireNonNull(field, "field");
2938             if (field.range().isFixed() == false) {
2939                 throw new IllegalArgumentException("Field must have a fixed set of values: " + field);
2940             }
2941             if (minWidth < 0 || minWidth > 9) {
2942                 throw new IllegalArgumentException("Minimum width must be from 0 to 9 inclusive but was " + minWidth);
2943             }
2944             if (maxWidth < 1 || maxWidth > 9) {
2945                 throw new IllegalArgumentException("Maximum width must be from 1 to 9 inclusive but was " + maxWidth);
2946             }
2947             if (maxWidth < minWidth) {
2948                 throw new IllegalArgumentException("Maximum width must exceed or equal the minimum width but " +
2949                         maxWidth + " < " + minWidth);
2950             }
2951             this.field = field;
2952             this.minWidth = minWidth;
2953             this.maxWidth = maxWidth;










2954             this.decimalPoint = decimalPoint;
2955         }
2956 






































2957         @Override
2958         public boolean format(DateTimePrintContext context, StringBuilder buf) {
2959             Long value = context.getValue(field);
2960             if (value == null) {
2961                 return false;
2962             }
2963             DecimalStyle decimalStyle = context.getDecimalStyle();
2964             BigDecimal fraction = convertToFraction(value);
2965             if (fraction.scale() == 0) {  // scale is zero if value is zero
2966                 if (minWidth > 0) {
2967                     if (decimalPoint) {
2968                         buf.append(decimalStyle.getDecimalSeparator());
2969                     }
2970                     for (int i = 0; i < minWidth; i++) {
2971                         buf.append(decimalStyle.getZeroDigit());
2972                     }
2973                 }
2974             } else {
2975                 int outputScale = Math.min(Math.max(fraction.scale(), minWidth), maxWidth);
2976                 fraction = fraction.setScale(outputScale, RoundingMode.FLOOR);
2977                 String str = fraction.toPlainString().substring(2);
2978                 str = decimalStyle.convertNumberToI18N(str);
2979                 if (decimalPoint) {
2980                     buf.append(decimalStyle.getDecimalSeparator());
2981                 }
2982                 buf.append(str);
2983             }
2984             return true;
2985         }
2986 
2987         @Override
2988         public int parse(DateTimeParseContext context, CharSequence text, int position) {
2989             int effectiveMin = (context.isStrict() ? minWidth : 0);
2990             int effectiveMax = (context.isStrict() ? maxWidth : 9);
2991             int length = text.length();
2992             if (position == length) {
2993                 // valid if whole field is optional, invalid if minimum width
2994                 return (effectiveMin > 0 ? ~position : position);
2995             }
2996             if (decimalPoint) {
2997                 if (text.charAt(position) != context.getDecimalStyle().getDecimalSeparator()) {
2998                     // valid if whole field is optional, invalid if minimum width
2999                     return (effectiveMin > 0 ? ~position : position);
3000                 }
3001                 position++;
3002             }
3003             int minEndPos = position + effectiveMin;
3004             if (minEndPos > length) {
3005                 return ~position;  // need at least min width digits
3006             }
3007             int maxEndPos = Math.min(position + effectiveMax, length);
3008             int total = 0;  // can use int because we are only parsing up to 9 digits
3009             int pos = position;
3010             while (pos < maxEndPos) {


   1 /*
   2  * Copyright (c) 2012, 2016, 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


 649             active.valueParserIndex = appendInternal(pp);
 650         }
 651         return this;
 652     }
 653 
 654     //-----------------------------------------------------------------------
 655     /**
 656      * Appends the fractional value of a date-time field to the formatter.
 657      * <p>
 658      * The fractional value of the field will be output including the
 659      * preceding decimal point. The preceding value is not output.
 660      * For example, the second-of-minute value of 15 would be output as {@code .25}.
 661      * <p>
 662      * The width of the printed fraction can be controlled. Setting the
 663      * minimum width to zero will cause no output to be generated.
 664      * The printed fraction will have the minimum width necessary between
 665      * the minimum and maximum widths - trailing zeroes are omitted.
 666      * No rounding occurs due to the maximum width - digits are simply dropped.
 667      * <p>
 668      * When parsing in strict mode, the number of parsed digits must be between
 669      * the minimum and maximum width. In strict mode, if the minimum and maximum widths
 670      * are equal and there is no decimal point then the parser will
 671      * participate in adjacent value parsing, see
 672      * {@code appendValue(java.time.temporal.TemporalField, int)}. When parsing in lenient mode,
 673      * the minimum width is considered to be zero and the maximum is nine.
 674      * <p>
 675      * If the value cannot be obtained then an exception will be thrown.
 676      * If the value is negative an exception will be thrown.
 677      * If the field does not have a fixed set of valid values then an
 678      * exception will be thrown.
 679      * If the field value in the date-time to be printed is invalid it
 680      * cannot be printed and an exception will be thrown.
 681      *
 682      * @param field  the field to append, not null
 683      * @param minWidth  the minimum width of the field excluding the decimal point, from 0 to 9
 684      * @param maxWidth  the maximum width of the field excluding the decimal point, from 1 to 9
 685      * @param decimalPoint  whether to output the localized decimal point symbol
 686      * @return this, for chaining, not null
 687      * @throws IllegalArgumentException if the field has a variable set of valid values or
 688      *  either width is invalid
 689      */
 690     public DateTimeFormatterBuilder appendFraction(
 691             TemporalField field, int minWidth, int maxWidth, boolean decimalPoint) {
 692         if (minWidth == maxWidth && decimalPoint == false) {
 693             // adjacent parsing
 694             appendValue(new FractionPrinterParser(field, minWidth, maxWidth, decimalPoint));
 695         } else {
 696             appendInternal(new FractionPrinterParser(field, minWidth, maxWidth, decimalPoint));
 697         }
 698         return this;
 699     }
 700 
 701     //-----------------------------------------------------------------------
 702     /**
 703      * Appends the text of a date-time field to the formatter using the full
 704      * text style.
 705      * <p>
 706      * The text of the field will be output during a format.
 707      * The value must be within the valid range of the field.
 708      * If the value cannot be obtained then an exception will be thrown.
 709      * If the field has no textual representation, then the numeric value will be used.
 710      * <p>
 711      * The value will be printed as per the normal format of an integer value.
 712      * Only negative numbers will be signed. No padding will be added.
 713      *
 714      * @param field  the field to append, not null
 715      * @return this, for chaining, not null
 716      */
 717     public DateTimeFormatterBuilder appendText(TemporalField field) {


2910          */
2911         @Override
2912         boolean isFixedWidth(DateTimeParseContext context) {
2913            if (context.isStrict() == false) {
2914                return false;
2915            }
2916            return super.isFixedWidth(context);
2917         }
2918 
2919         @Override
2920         public String toString() {
2921             return "ReducedValue(" + field + "," + minWidth + "," + maxWidth +
2922                     "," + Objects.requireNonNullElse(baseDate, baseValue) + ")";
2923         }
2924     }
2925 
2926     //-----------------------------------------------------------------------
2927     /**
2928      * Prints and parses a numeric date-time field with optional padding.
2929      */
2930     static final class FractionPrinterParser extends NumberPrinterParser {



2931        private final boolean decimalPoint;
2932 
2933         /**
2934          * Constructor.
2935          *
2936          * @param field  the field to output, not null
2937          * @param minWidth  the minimum width to output, from 0 to 9
2938          * @param maxWidth  the maximum width to output, from 0 to 9
2939          * @param decimalPoint  whether to output the localized decimal point symbol
2940          */
2941         FractionPrinterParser(TemporalField field, int minWidth, int maxWidth, boolean decimalPoint) {
2942             this(field, minWidth, maxWidth, decimalPoint, 0);
2943             Objects.requireNonNull(field, "field");
2944             if (field.range().isFixed() == false) {
2945                 throw new IllegalArgumentException("Field must have a fixed set of values: " + field);
2946             }
2947             if (minWidth < 0 || minWidth > 9) {
2948                 throw new IllegalArgumentException("Minimum width must be from 0 to 9 inclusive but was " + minWidth);
2949             }
2950             if (maxWidth < 1 || maxWidth > 9) {
2951                 throw new IllegalArgumentException("Maximum width must be from 1 to 9 inclusive but was " + maxWidth);
2952             }
2953             if (maxWidth < minWidth) {
2954                 throw new IllegalArgumentException("Maximum width must exceed or equal the minimum width but " +
2955                         maxWidth + " < " + minWidth);
2956             }
2957         }
2958 
2959         /**
2960          * Constructor.
2961          *
2962          * @param field  the field to output, not null
2963          * @param minWidth  the minimum width to output, from 0 to 9
2964          * @param maxWidth  the maximum width to output, from 0 to 9
2965          * @param decimalPoint  whether to output the localized decimal point symbol
2966          * @param subsequentWidth the subsequentWidth for this instance
2967          */
2968         FractionPrinterParser(TemporalField field, int minWidth, int maxWidth, boolean decimalPoint, int subsequentWidth) {
2969             super(field, minWidth, maxWidth, SignStyle.NOT_NEGATIVE, subsequentWidth);
2970             this.decimalPoint = decimalPoint;
2971         }
2972 
2973         /**
2974          * Returns a new instance with fixed width flag set.
2975          *
2976          * @return a new updated printer-parser, not null
2977          */
2978         @Override
2979         FractionPrinterParser withFixedWidth() {
2980             if (subsequentWidth == -1) {
2981                 return this;
2982             }
2983             return new FractionPrinterParser(field, minWidth, maxWidth, decimalPoint, -1);
2984         }
2985 
2986         /**
2987          * Returns a new instance with an updated subsequent width.
2988          *
2989          * @param subsequentWidth  the width of subsequent non-negative numbers, 0 or greater
2990          * @return a new updated printer-parser, not null
2991          */
2992         @Override
2993         FractionPrinterParser withSubsequentWidth(int subsequentWidth) {
2994             return new FractionPrinterParser(field, minWidth, maxWidth, decimalPoint, this.subsequentWidth + subsequentWidth);
2995         }
2996 
2997         /**
2998          *
2999          * @param context the context
3000          * @return if the field is fixed width
3001          * @see DateTimeFormatterBuilder#appendValueFraction(java.time.temporal.TemporalField, int, int, boolean)
3002          */
3003         @Override
3004         boolean isFixedWidth(DateTimeParseContext context) {
3005             if (context.isStrict() && minWidth == maxWidth && decimalPoint == false) {
3006                 return true;
3007             }
3008             return false;
3009         }
3010 
3011         @Override
3012         public boolean format(DateTimePrintContext context, StringBuilder buf) {
3013             Long value = context.getValue(field);
3014             if (value == null) {
3015                 return false;
3016             }
3017             DecimalStyle decimalStyle = context.getDecimalStyle();
3018             BigDecimal fraction = convertToFraction(value);
3019             if (fraction.scale() == 0) {  // scale is zero if value is zero
3020                 if (minWidth > 0) {
3021                     if (decimalPoint) {
3022                         buf.append(decimalStyle.getDecimalSeparator());
3023                     }
3024                     for (int i = 0; i < minWidth; i++) {
3025                         buf.append(decimalStyle.getZeroDigit());
3026                     }
3027                 }
3028             } else {
3029                 int outputScale = Math.min(Math.max(fraction.scale(), minWidth), maxWidth);
3030                 fraction = fraction.setScale(outputScale, RoundingMode.FLOOR);
3031                 String str = fraction.toPlainString().substring(2);
3032                 str = decimalStyle.convertNumberToI18N(str);
3033                 if (decimalPoint) {
3034                     buf.append(decimalStyle.getDecimalSeparator());
3035                 }
3036                 buf.append(str);
3037             }
3038             return true;
3039         }
3040 
3041         @Override
3042         public int parse(DateTimeParseContext context, CharSequence text, int position) {
3043             int effectiveMin = (context.isStrict() || isFixedWidth(context) ? minWidth : 0);
3044             int effectiveMax = (context.isStrict() || isFixedWidth(context) ? maxWidth : 9);
3045             int length = text.length();
3046             if (position == length) {
3047                 // valid if whole field is optional, invalid if minimum width
3048                 return (effectiveMin > 0 ? ~position : position);
3049             }
3050             if (decimalPoint) {
3051                 if (text.charAt(position) != context.getDecimalStyle().getDecimalSeparator()) {
3052                     // valid if whole field is optional, invalid if minimum width
3053                     return (effectiveMin > 0 ? ~position : position);
3054                 }
3055                 position++;
3056             }
3057             int minEndPos = position + effectiveMin;
3058             if (minEndPos > length) {
3059                 return ~position;  // need at least min width digits
3060             }
3061             int maxEndPos = Math.min(position + effectiveMax, length);
3062             int total = 0;  // can use int because we are only parsing up to 9 digits
3063             int pos = position;
3064             while (pos < maxEndPos) {


< prev index next >