804 @Override
805 public Iterator<Entry<String, Long>> getTextIterator(TemporalField field, TextStyle style, Locale locale) {
806 return store.getTextIterator(style);
807 }
808 };
809 appendInternal(new TextPrinterParser(field, TextStyle.FULL, provider));
810 return this;
811 }
812
813 //-----------------------------------------------------------------------
814 /**
815 * Appends an instant using ISO-8601 to the formatter, formatting fractional
816 * digits in groups of three.
817 * <p>
818 * Instants have a fixed output format.
819 * They are converted to a date-time with a zone-offset of UTC and formatted
820 * using the standard ISO-8601 format.
821 * With this method, formatting nano-of-second outputs zero, three, six
822 * or nine digits as necessary.
823 * The localized decimal style is not used.
824 * <p>
825 * The instant is obtained using {@link ChronoField#INSTANT_SECONDS INSTANT_SECONDS}
826 * and optionally {@code NANO_OF_SECOND}. The value of {@code INSTANT_SECONDS}
827 * may be outside the maximum range of {@code LocalDateTime}.
828 * <p>
829 * The {@linkplain ResolverStyle resolver style} has no effect on instant parsing.
830 * The end-of-day time of '24:00' is handled as midnight at the start of the following day.
831 * The leap-second time of '23:59:59' is handled to some degree, see
832 * {@link DateTimeFormatter#parsedLeapSecond()} for full details.
833 * <p>
834 * An alternative to this method is to format/parse the instant as a single
835 * epoch-seconds value. That is achieved using {@code appendValue(INSTANT_SECONDS)}.
836 *
837 * @return this, for chaining, not null
838 */
839 public DateTimeFormatterBuilder appendInstant() {
840 appendInternal(new InstantPrinterParser(-2));
841 return this;
842 }
843
3445 buf.append((char) (digit + '0'));
3446 inNano = inNano - (digit * div);
3447 div = div / 10;
3448 }
3449 }
3450 buf.append('Z');
3451 return true;
3452 }
3453
3454 @Override
3455 public int parse(DateTimeParseContext context, CharSequence text, int position) {
3456 // new context to avoid overwriting fields like year/month/day
3457 int minDigits = (fractionalDigits < 0 ? 0 : fractionalDigits);
3458 int maxDigits = (fractionalDigits < 0 ? 9 : fractionalDigits);
3459 CompositePrinterParser parser = new DateTimeFormatterBuilder()
3460 .append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('T')
3461 .appendValue(HOUR_OF_DAY, 2).appendLiteral(':')
3462 .appendValue(MINUTE_OF_HOUR, 2).appendLiteral(':')
3463 .appendValue(SECOND_OF_MINUTE, 2)
3464 .appendFraction(NANO_OF_SECOND, minDigits, maxDigits, true)
3465 .appendLiteral('Z')
3466 .toFormatter().toPrinterParser(false);
3467 DateTimeParseContext newContext = context.copy();
3468 int pos = parser.parse(newContext, text, position);
3469 if (pos < 0) {
3470 return pos;
3471 }
3472 // parser restricts most fields to 2 digits, so definitely int
3473 // correctly parsed nano is also guaranteed to be valid
3474 long yearParsed = newContext.getParsed(YEAR);
3475 int month = newContext.getParsed(MONTH_OF_YEAR).intValue();
3476 int day = newContext.getParsed(DAY_OF_MONTH).intValue();
3477 int hour = newContext.getParsed(HOUR_OF_DAY).intValue();
3478 int min = newContext.getParsed(MINUTE_OF_HOUR).intValue();
3479 Long secVal = newContext.getParsed(SECOND_OF_MINUTE);
3480 Long nanoVal = newContext.getParsed(NANO_OF_SECOND);
3481 int sec = (secVal != null ? secVal.intValue() : 0);
3482 int nano = (nanoVal != null ? nanoVal.intValue() : 0);
3483 int days = 0;
3484 if (hour == 24 && min == 0 && sec == 0 && nano == 0) {
3485 hour = 0;
3486 days = 1;
3487 } else if (hour == 23 && min == 59 && sec == 60) {
3488 context.setParsedLeapSecond();
3489 sec = 59;
3490 }
3491 int year = (int) yearParsed % 10_000;
3492 long instantSecs;
3493 try {
3494 LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, min, sec, 0).plusDays(days);
3495 instantSecs = ldt.toEpochSecond(ZoneOffset.UTC);
3496 instantSecs += Math.multiplyExact(yearParsed / 10_000L, SECONDS_PER_10000_YEARS);
3497 } catch (RuntimeException ex) {
3498 return ~position;
3499 }
3500 int successPos = pos;
3501 successPos = context.setParsedField(INSTANT_SECONDS, instantSecs, position, successPos);
3502 return context.setParsedField(NANO_OF_SECOND, nano, position, successPos);
3503 }
3504
3505 @Override
3506 public String toString() {
3507 return "Instant()";
3508 }
3509 }
3510
3511 //-----------------------------------------------------------------------
3512 /**
3513 * Prints or parses an offset ID.
3514 */
3515 static final class OffsetIdPrinterParser implements DateTimePrinterParser {
|
804 @Override
805 public Iterator<Entry<String, Long>> getTextIterator(TemporalField field, TextStyle style, Locale locale) {
806 return store.getTextIterator(style);
807 }
808 };
809 appendInternal(new TextPrinterParser(field, TextStyle.FULL, provider));
810 return this;
811 }
812
813 //-----------------------------------------------------------------------
814 /**
815 * Appends an instant using ISO-8601 to the formatter, formatting fractional
816 * digits in groups of three.
817 * <p>
818 * Instants have a fixed output format.
819 * They are converted to a date-time with a zone-offset of UTC and formatted
820 * using the standard ISO-8601 format.
821 * With this method, formatting nano-of-second outputs zero, three, six
822 * or nine digits as necessary.
823 * The localized decimal style is not used.
824 * During parsing, any offset if available is accepted.
825 * <p>
826 * The instant is obtained using {@link ChronoField#INSTANT_SECONDS INSTANT_SECONDS}
827 * and optionally {@code NANO_OF_SECOND}. The value of {@code INSTANT_SECONDS}
828 * may be outside the maximum range of {@code LocalDateTime}.
829 * <p>
830 * The {@linkplain ResolverStyle resolver style} has no effect on instant parsing.
831 * The end-of-day time of '24:00' is handled as midnight at the start of the following day.
832 * The leap-second time of '23:59:59' is handled to some degree, see
833 * {@link DateTimeFormatter#parsedLeapSecond()} for full details.
834 * <p>
835 * An alternative to this method is to format/parse the instant as a single
836 * epoch-seconds value. That is achieved using {@code appendValue(INSTANT_SECONDS)}.
837 *
838 * @return this, for chaining, not null
839 */
840 public DateTimeFormatterBuilder appendInstant() {
841 appendInternal(new InstantPrinterParser(-2));
842 return this;
843 }
844
3446 buf.append((char) (digit + '0'));
3447 inNano = inNano - (digit * div);
3448 div = div / 10;
3449 }
3450 }
3451 buf.append('Z');
3452 return true;
3453 }
3454
3455 @Override
3456 public int parse(DateTimeParseContext context, CharSequence text, int position) {
3457 // new context to avoid overwriting fields like year/month/day
3458 int minDigits = (fractionalDigits < 0 ? 0 : fractionalDigits);
3459 int maxDigits = (fractionalDigits < 0 ? 9 : fractionalDigits);
3460 CompositePrinterParser parser = new DateTimeFormatterBuilder()
3461 .append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('T')
3462 .appendValue(HOUR_OF_DAY, 2).appendLiteral(':')
3463 .appendValue(MINUTE_OF_HOUR, 2).appendLiteral(':')
3464 .appendValue(SECOND_OF_MINUTE, 2)
3465 .appendFraction(NANO_OF_SECOND, minDigits, maxDigits, true)
3466 .appendOffsetId()
3467 .toFormatter().toPrinterParser(false);
3468 DateTimeParseContext newContext = context.copy();
3469 int pos = parser.parse(newContext, text, position);
3470 if (pos < 0) {
3471 return pos;
3472 }
3473 // parser restricts most fields to 2 digits, so definitely int
3474 // correctly parsed nano is also guaranteed to be valid
3475 long yearParsed = newContext.getParsed(YEAR);
3476 int month = newContext.getParsed(MONTH_OF_YEAR).intValue();
3477 int day = newContext.getParsed(DAY_OF_MONTH).intValue();
3478 int hour = newContext.getParsed(HOUR_OF_DAY).intValue();
3479 int min = newContext.getParsed(MINUTE_OF_HOUR).intValue();
3480 Long secVal = newContext.getParsed(SECOND_OF_MINUTE);
3481 Long nanoVal = newContext.getParsed(NANO_OF_SECOND);
3482 int sec = (secVal != null ? secVal.intValue() : 0);
3483 int nano = (nanoVal != null ? nanoVal.intValue() : 0);
3484 int offset = newContext.getParsed(OFFSET_SECONDS).intValue();
3485 int days = 0;
3486 if (hour == 24 && min == 0 && sec == 0 && nano == 0) {
3487 hour = 0;
3488 days = 1;
3489 } else if (hour == 23 && min == 59 && sec == 60) {
3490 context.setParsedLeapSecond();
3491 sec = 59;
3492 }
3493 int year = (int) yearParsed % 10_000;
3494 long instantSecs;
3495 try {
3496 LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, min, sec, 0).plusDays(days);
3497 instantSecs = ldt.toEpochSecond(ZoneOffset.ofTotalSeconds(offset));
3498 instantSecs += Math.multiplyExact(yearParsed / 10_000L, SECONDS_PER_10000_YEARS);
3499 } catch (RuntimeException ex) {
3500 return ~position;
3501 }
3502 int successPos = pos;
3503 successPos = context.setParsedField(INSTANT_SECONDS, instantSecs, position, successPos);
3504 return context.setParsedField(NANO_OF_SECOND, nano, position, successPos);
3505 }
3506
3507 @Override
3508 public String toString() {
3509 return "Instant()";
3510 }
3511 }
3512
3513 //-----------------------------------------------------------------------
3514 /**
3515 * Prints or parses an offset ID.
3516 */
3517 static final class OffsetIdPrinterParser implements DateTimePrinterParser {
|