1
1
/*
2
- * Copyright (c) 2012, 2020 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2012, 2021 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
@@ -701,11 +701,20 @@ private DateTimeFormatterBuilder appendValue(NumberPrinterParser pp) {
701
701
*/
702
702
public DateTimeFormatterBuilder appendFraction (
703
703
TemporalField field , int minWidth , int maxWidth , boolean decimalPoint ) {
704
- if (minWidth == maxWidth && decimalPoint == false ) {
705
- // adjacent parsing
706
- appendValue (new FractionPrinterParser (field , minWidth , maxWidth , decimalPoint ));
704
+ if (field == NANO_OF_SECOND ) {
705
+ if (minWidth == maxWidth && decimalPoint == false ) {
706
+ // adjacent parsing
707
+ appendValue (new NanosPrinterParser (minWidth , maxWidth , decimalPoint ));
708
+ } else {
709
+ appendInternal (new NanosPrinterParser (minWidth , maxWidth , decimalPoint ));
710
+ }
707
711
} else {
708
- appendInternal (new FractionPrinterParser (field , minWidth , maxWidth , decimalPoint ));
712
+ if (minWidth == maxWidth && decimalPoint == false ) {
713
+ // adjacent parsing
714
+ appendValue (new FractionPrinterParser (field , minWidth , maxWidth , decimalPoint ));
715
+ } else {
716
+ appendInternal (new FractionPrinterParser (field , minWidth , maxWidth , decimalPoint ));
717
+ }
709
718
}
710
719
return this ;
711
720
}
@@ -2758,6 +2767,24 @@ NumberPrinterParser withSubsequentWidth(int subsequentWidth) {
2758
2767
return new NumberPrinterParser (field , minWidth , maxWidth , signStyle , this .subsequentWidth + subsequentWidth );
2759
2768
}
2760
2769
2770
+ /*
2771
+ * Copied from Long.stringSize
2772
+ */
2773
+ private static int stringSize (long x ) {
2774
+ int d = 1 ;
2775
+ if (x >= 0 ) {
2776
+ d = 0 ;
2777
+ x = -x ;
2778
+ }
2779
+ long p = -10 ;
2780
+ for (int i = 1 ; i < 19 ; i ++) {
2781
+ if (x > p )
2782
+ return i + d ;
2783
+ p = 10 * p ;
2784
+ }
2785
+ return 19 + d ;
2786
+ }
2787
+
2761
2788
@ Override
2762
2789
public boolean format (DateTimePrintContext context , StringBuilder buf ) {
2763
2790
Long valueLong = context .getValue (field );
@@ -2766,18 +2793,21 @@ public boolean format(DateTimePrintContext context, StringBuilder buf) {
2766
2793
}
2767
2794
long value = getValue (context , valueLong );
2768
2795
DecimalStyle decimalStyle = context .getDecimalStyle ();
2769
- String str = (value == Long .MIN_VALUE ? "9223372036854775808" : Long .toString (Math .abs (value )));
2770
- if (str .length () > maxWidth ) {
2796
+ int size = stringSize (value );
2797
+ if (value < 0 ) {
2798
+ size --;
2799
+ }
2800
+
2801
+ if (size > maxWidth ) {
2771
2802
throw new DateTimeException ("Field " + field +
2772
2803
" cannot be printed as the value " + value +
2773
2804
" exceeds the maximum print width of " + maxWidth );
2774
2805
}
2775
- str = decimalStyle .convertNumberToI18N (str );
2776
2806
2777
2807
if (value >= 0 ) {
2778
2808
switch (signStyle ) {
2779
2809
case EXCEEDS_PAD :
2780
- if (minWidth < 19 && value >= EXCEED_POINTS [ minWidth ] ) {
2810
+ if (minWidth < 19 && size > minWidth ) {
2781
2811
buf .append (decimalStyle .getPositiveSign ());
2782
2812
}
2783
2813
break ;
@@ -2793,10 +2823,16 @@ public boolean format(DateTimePrintContext context, StringBuilder buf) {
2793
2823
" cannot be negative according to the SignStyle" );
2794
2824
}
2795
2825
}
2796
- for (int i = 0 ; i < minWidth - str .length (); i ++) {
2797
- buf .append (decimalStyle .getZeroDigit ());
2826
+ char zeroDigit = decimalStyle .getZeroDigit ();
2827
+ for (int i = 0 ; i < minWidth - size ; i ++) {
2828
+ buf .append (zeroDigit );
2829
+ }
2830
+ if (zeroDigit == '0' && value != Long .MIN_VALUE ) {
2831
+ buf .append (Math .abs (value ));
2832
+ } else {
2833
+ String str = value == Long .MIN_VALUE ? "9223372036854775808" : Long .toString (Math .abs (value ));
2834
+ buf .append (decimalStyle .convertNumberToI18N (str ));
2798
2835
}
2799
- buf .append (str );
2800
2836
return true ;
2801
2837
}
2802
2838
@@ -3111,12 +3147,216 @@ public String toString() {
3111
3147
}
3112
3148
}
3113
3149
3150
+ //-----------------------------------------------------------------------
3151
+ /**
3152
+ * Prints and parses a NANO_OF_SECOND field with optional padding.
3153
+ */
3154
+ static final class NanosPrinterParser extends NumberPrinterParser {
3155
+ private final boolean decimalPoint ;
3156
+
3157
+ /**
3158
+ * Constructor.
3159
+ *
3160
+ * @param minWidth the minimum width to output, from 0 to 9
3161
+ * @param maxWidth the maximum width to output, from 0 to 9
3162
+ * @param decimalPoint whether to output the localized decimal point symbol
3163
+ */
3164
+ NanosPrinterParser (int minWidth , int maxWidth , boolean decimalPoint ) {
3165
+ this (minWidth , maxWidth , decimalPoint , 0 );
3166
+ if (minWidth < 0 || minWidth > 9 ) {
3167
+ throw new IllegalArgumentException ("Minimum width must be from 0 to 9 inclusive but was " + minWidth );
3168
+ }
3169
+ if (maxWidth < 1 || maxWidth > 9 ) {
3170
+ throw new IllegalArgumentException ("Maximum width must be from 1 to 9 inclusive but was " + maxWidth );
3171
+ }
3172
+ if (maxWidth < minWidth ) {
3173
+ throw new IllegalArgumentException ("Maximum width must exceed or equal the minimum width but " +
3174
+ maxWidth + " < " + minWidth );
3175
+ }
3176
+ }
3177
+
3178
+ /**
3179
+ * Constructor.
3180
+ *
3181
+ * @param minWidth the minimum width to output, from 0 to 9
3182
+ * @param maxWidth the maximum width to output, from 0 to 9
3183
+ * @param decimalPoint whether to output the localized decimal point symbol
3184
+ * @param subsequentWidth the subsequentWidth for this instance
3185
+ */
3186
+ NanosPrinterParser (int minWidth , int maxWidth , boolean decimalPoint , int subsequentWidth ) {
3187
+ super (NANO_OF_SECOND , minWidth , maxWidth , SignStyle .NOT_NEGATIVE , subsequentWidth );
3188
+ this .decimalPoint = decimalPoint ;
3189
+ }
3190
+
3191
+ /**
3192
+ * Returns a new instance with fixed width flag set.
3193
+ *
3194
+ * @return a new updated printer-parser, not null
3195
+ */
3196
+ @ Override
3197
+ NanosPrinterParser withFixedWidth () {
3198
+ if (subsequentWidth == -1 ) {
3199
+ return this ;
3200
+ }
3201
+ return new NanosPrinterParser (minWidth , maxWidth , decimalPoint , -1 );
3202
+ }
3203
+
3204
+ /**
3205
+ * Returns a new instance with an updated subsequent width.
3206
+ *
3207
+ * @param subsequentWidth the width of subsequent non-negative numbers, 0 or greater
3208
+ * @return a new updated printer-parser, not null
3209
+ */
3210
+ @ Override
3211
+ NanosPrinterParser withSubsequentWidth (int subsequentWidth ) {
3212
+ return new NanosPrinterParser (minWidth , maxWidth , decimalPoint , this .subsequentWidth + subsequentWidth );
3213
+ }
3214
+
3215
+ /**
3216
+ * For NanosPrinterParser, the width is fixed if context is strict,
3217
+ * minWidth equal to maxWidth and decimalpoint is absent.
3218
+ * @param context the context
3219
+ * @return if the field is fixed width
3220
+ * @see #appendFraction(java.time.temporal.TemporalField, int, int, boolean)
3221
+ */
3222
+ @ Override
3223
+ boolean isFixedWidth (DateTimeParseContext context ) {
3224
+ if (context .isStrict () && minWidth == maxWidth && decimalPoint == false ) {
3225
+ return true ;
3226
+ }
3227
+ return false ;
3228
+ }
3229
+
3230
+ // Simplified variant of Integer.stringSize that assumes positive values
3231
+ private static int stringSize (int x ) {
3232
+ int p = 10 ;
3233
+ for (int i = 1 ; i < 10 ; i ++) {
3234
+ if (x < p )
3235
+ return i ;
3236
+ p = 10 * p ;
3237
+ }
3238
+ return 10 ;
3239
+ }
3240
+
3241
+ private static final int [] TENS = new int [] {
3242
+ 1 ,
3243
+ 10 ,
3244
+ 100 ,
3245
+ 1000 ,
3246
+ 10000 ,
3247
+ 100000 ,
3248
+ 1000000 ,
3249
+ 10000000 ,
3250
+ 100000000
3251
+ };
3252
+
3253
+ @ Override
3254
+ public boolean format (DateTimePrintContext context , StringBuilder buf ) {
3255
+ Long value = context .getValue (field );
3256
+ if (value == null ) {
3257
+ return false ;
3258
+ }
3259
+ int val = field .range ().checkValidIntValue (value , field );
3260
+ DecimalStyle decimalStyle = context .getDecimalStyle ();
3261
+ int stringSize = stringSize (val );
3262
+ char zero = decimalStyle .getZeroDigit ();
3263
+ if (val == 0 || stringSize < 10 - maxWidth ) {
3264
+ // 0 or would round down to 0
3265
+ // to get output identical to FractionPrinterParser use minWidth if the
3266
+ // value is zero, maxWidth otherwise
3267
+ int width = val == 0 ? minWidth : maxWidth ;
3268
+ if (width > 0 ) {
3269
+ if (decimalPoint ) {
3270
+ buf .append (decimalStyle .getDecimalSeparator ());
3271
+ }
3272
+ for (int i = 0 ; i < width ; i ++) {
3273
+ buf .append (zero );
3274
+ }
3275
+ }
3276
+ } else {
3277
+ if (decimalPoint ) {
3278
+ buf .append (decimalStyle .getDecimalSeparator ());
3279
+ }
3280
+ // add leading zeros
3281
+ for (int i = 9 - stringSize ; i > 0 ; i --) {
3282
+ buf .append (zero );
3283
+ }
3284
+ // truncate unwanted digits
3285
+ if (maxWidth < 9 ) {
3286
+ val /= TENS [9 - maxWidth ];
3287
+ }
3288
+ // truncate zeros
3289
+ for (int i = maxWidth ; i > minWidth ; i --) {
3290
+ if ((val % 10 ) != 0 ) {
3291
+ break ;
3292
+ }
3293
+ val /= 10 ;
3294
+ }
3295
+ if (zero == '0' ) {
3296
+ buf .append (val );
3297
+ } else {
3298
+ buf .append (decimalStyle .convertNumberToI18N (Integer .toString (val )));
3299
+ }
3300
+ }
3301
+ return true ;
3302
+ }
3303
+
3304
+ @ Override
3305
+ public int parse (DateTimeParseContext context , CharSequence text , int position ) {
3306
+ int effectiveMin = (context .isStrict () || isFixedWidth (context ) ? minWidth : 0 );
3307
+ int effectiveMax = (context .isStrict () || isFixedWidth (context ) ? maxWidth : 9 );
3308
+ int length = text .length ();
3309
+ if (position == length ) {
3310
+ // valid if whole field is optional, invalid if minimum width
3311
+ return (effectiveMin > 0 ? ~position : position );
3312
+ }
3313
+ if (decimalPoint ) {
3314
+ if (text .charAt (position ) != context .getDecimalStyle ().getDecimalSeparator ()) {
3315
+ // valid if whole field is optional, invalid if minimum width
3316
+ return (effectiveMin > 0 ? ~position : position );
3317
+ }
3318
+ position ++;
3319
+ }
3320
+ int minEndPos = position + effectiveMin ;
3321
+ if (minEndPos > length ) {
3322
+ return ~position ; // need at least min width digits
3323
+ }
3324
+ int maxEndPos = Math .min (position + effectiveMax , length );
3325
+ int total = 0 ; // can use int because we are only parsing up to 9 digits
3326
+ int pos = position ;
3327
+ while (pos < maxEndPos ) {
3328
+ char ch = text .charAt (pos );
3329
+ int digit = context .getDecimalStyle ().convertToDigit (ch );
3330
+ if (digit < 0 ) {
3331
+ if (pos < minEndPos ) {
3332
+ return ~position ; // need at least min width digits
3333
+ }
3334
+ break ;
3335
+ }
3336
+ pos ++;
3337
+ total = total * 10 + digit ;
3338
+ }
3339
+ for (int i = 9 - (pos - position ); i > 0 ; i --) {
3340
+ total *= 10 ;
3341
+ }
3342
+ return context .setParsedField (field , total , position , pos );
3343
+ }
3344
+
3345
+ @ Override
3346
+ public String toString () {
3347
+ String decimal = (decimalPoint ? ",DecimalPoint" : "" );
3348
+ return "Fraction(" + field + "," + minWidth + "," + maxWidth + decimal + ")" ;
3349
+ }
3350
+ }
3351
+
3114
3352
//-----------------------------------------------------------------------
3115
3353
/**
3116
3354
* Prints and parses a numeric date-time field with optional padding.
3117
3355
*/
3118
3356
static final class FractionPrinterParser extends NumberPrinterParser {
3119
3357
private final boolean decimalPoint ;
3358
+ private final BigDecimal minBD ;
3359
+ private final BigDecimal rangeBD ;
3120
3360
3121
3361
/**
3122
3362
* Constructor.
@@ -3156,6 +3396,9 @@ static final class FractionPrinterParser extends NumberPrinterParser {
3156
3396
FractionPrinterParser (TemporalField field , int minWidth , int maxWidth , boolean decimalPoint , int subsequentWidth ) {
3157
3397
super (field , minWidth , maxWidth , SignStyle .NOT_NEGATIVE , subsequentWidth );
3158
3398
this .decimalPoint = decimalPoint ;
3399
+ ValueRange range = field .range ();
3400
+ this .minBD = BigDecimal .valueOf (range .getMinimum ());
3401
+ this .rangeBD = BigDecimal .valueOf (range .getMaximum ()).subtract (minBD ).add (BigDecimal .ONE );
3159
3402
}
3160
3403
3161
3404
/**
@@ -3217,12 +3460,12 @@ public boolean format(DateTimePrintContext context, StringBuilder buf) {
3217
3460
} else {
3218
3461
int outputScale = Math .min (Math .max (fraction .scale (), minWidth ), maxWidth );
3219
3462
fraction = fraction .setScale (outputScale , RoundingMode .FLOOR );
3220
- String str = fraction .toPlainString ().substring (2 );
3221
- str = decimalStyle .convertNumberToI18N (str );
3222
3463
if (decimalPoint ) {
3223
3464
buf .append (decimalStyle .getDecimalSeparator ());
3224
3465
}
3225
- buf .append (str );
3466
+ String str = fraction .toPlainString ();
3467
+ str = decimalStyle .convertNumberToI18N (str );
3468
+ buf .append (str , 2 , str .length ());
3226
3469
}
3227
3470
return true ;
3228
3471
}
@@ -3284,10 +3527,7 @@ public int parse(DateTimeParseContext context, CharSequence text, int position)
3284
3527
* @throws DateTimeException if the value cannot be converted to a fraction
3285
3528
*/
3286
3529
private BigDecimal convertToFraction (long value ) {
3287
- ValueRange range = field .range ();
3288
- range .checkValidValue (value , field );
3289
- BigDecimal minBD = BigDecimal .valueOf (range .getMinimum ());
3290
- BigDecimal rangeBD = BigDecimal .valueOf (range .getMaximum ()).subtract (minBD ).add (BigDecimal .ONE );
3530
+ field .range ().checkValidValue (value , field );
3291
3531
BigDecimal valueBD = BigDecimal .valueOf (value ).subtract (minBD );
3292
3532
BigDecimal fraction = valueBD .divide (rangeBD , 9 , RoundingMode .FLOOR );
3293
3533
// stripTrailingZeros bug
@@ -3311,9 +3551,6 @@ private BigDecimal convertToFraction(long value) {
3311
3551
* @throws DateTimeException if the value cannot be converted
3312
3552
*/
3313
3553
private long convertFromFraction (BigDecimal fraction ) {
3314
- ValueRange range = field .range ();
3315
- BigDecimal minBD = BigDecimal .valueOf (range .getMinimum ());
3316
- BigDecimal rangeBD = BigDecimal .valueOf (range .getMaximum ()).subtract (minBD ).add (BigDecimal .ONE );
3317
3554
BigDecimal valueBD = fraction .multiply (rangeBD ).setScale (0 , RoundingMode .FLOOR ).add (minBD );
3318
3555
return valueBD .longValueExact ();
3319
3556
}
0 commit comments