From b6495573e9dc5470df268b63f8e7a93f38406cd2 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Fri, 21 Nov 2025 20:42:48 +0800 Subject: [PATCH 1/9] add benchmark --- .../time/format/DateTimeFormatterParse.java | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 test/micro/org/openjdk/bench/java/time/format/DateTimeFormatterParse.java diff --git a/test/micro/org/openjdk/bench/java/time/format/DateTimeFormatterParse.java b/test/micro/org/openjdk/bench/java/time/format/DateTimeFormatterParse.java new file mode 100644 index 0000000000000..cdf6f47083576 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/time/format/DateTimeFormatterParse.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.time.format; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; + +import java.util.Locale; +import java.util.Random; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +@State(Scope.Thread) +public class DateTimeFormatterParse { + static final DateTimeFormatter formatterLocalTime = DateTimeFormatter.ofPattern("HH:mm:ss"); + static final DateTimeFormatter formatterLocalTimeWithNano = DateTimeFormatter.ofPattern("HH:mm:ss.SSS"); + static final DateTimeFormatter formatterLocalDate = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + static final DateTimeFormatter formatterLocalDateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + static final DateTimeFormatter formatterLocalDateTimeWithNano = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"); + static final DateTimeFormatter formatterOffsetDateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSZZZZZ"); + static final DateTimeFormatter formatterZonedDateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSZZZZZ'['VV']'"); + + static final String STR_LOCALTIME = "21:11:48"; + static final String STR_LOCALTIME_WITH_NANO = "21:11:48.456"; + static final String STR_LOCALDATE = "2024-08-01"; + static final String STR_LOCALDATETIME = "2024-08-01T21:11:48"; + static final String STR_LOCALDATETIME_WITH_NANO = "2024-08-01T21:11:48.456"; + static final String STR_INSTANT = "2024-08-12T03:25:54.980339Z"; + static final String STR_OFFSETDATETIME = "2024-08-12T11:50:46.731509+08:00"; + static final String STR_ZONEDDATETIME = "2024-08-12T11:50:46.731509+08:00[Asia/Shanghai]"; + + @Benchmark + public LocalTime parseLocalTime() { + return LocalTime.parse(STR_LOCALTIME, formatterLocalTime); + } + + @Benchmark + public LocalTime parseLocalTimeWithNano() { + return LocalTime.parse(STR_LOCALTIME_WITH_NANO, formatterLocalTimeWithNano); + } + + @Benchmark + public LocalDate parseLocalDate() { + return LocalDate.parse(STR_LOCALDATE, formatterLocalDate); + } + + @Benchmark + public LocalDateTime parseLocalDateTime() { + return LocalDateTime.parse(STR_LOCALDATETIME, formatterLocalDateTime); + } + + @Benchmark + public LocalDateTime parseLocalDateTimeWithNano() { + return LocalDateTime.parse(STR_LOCALDATETIME_WITH_NANO, formatterLocalDateTimeWithNano); + } + + @Benchmark + public OffsetDateTime parseOffsetDateTime() { + return OffsetDateTime.parse(STR_OFFSETDATETIME, formatterOffsetDateTime); + } + + @Benchmark + public ZonedDateTime parseZonedDateTime() { + return ZonedDateTime.parse(STR_ZONEDDATETIME, formatterZonedDateTime); + } + + @Benchmark + public Instant parseInstant() { + return Instant.parse(STR_INSTANT); + } +} \ No newline at end of file From d8742d7514abfe0e36f105fa7310fdb1755ae546 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Mon, 24 Nov 2025 11:43:14 +0800 Subject: [PATCH 2/9] use EnumMap --- .../java/time/format/DateTimeFormatter.java | 7 +++++++ .../time/format/DateTimeFormatterBuilder.java | 17 +++++++++++++++++ .../time/format/DateTimeParseContext.java | 2 +- .../classes/java/time/format/Parsed.java | 19 ++++++++++++++++--- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java index 16d7193c5561f..3b9b84b56832e 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java @@ -544,6 +544,12 @@ public final class DateTimeFormatter { */ private final ZoneId zone; + /** + * Flag indicating whether this formatter only uses ChronoField instances. + * This is used to optimize the storage of parsed field values in the Parsed class. + */ + final boolean onlyChronoField; + //----------------------------------------------------------------------- /** * Creates a formatter using the specified pattern. @@ -1486,6 +1492,7 @@ public static final TemporalQuery parsedLeapSecond() { this.resolverStyle = Objects.requireNonNull(resolverStyle, "resolverStyle"); this.chrono = chrono; this.zone = zone; + this.onlyChronoField = printerParser.onlyChronoField(); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 4708094effbde..3aa98b01a8a47 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -2580,6 +2580,23 @@ public int parse(DateTimeParseContext context, CharSequence text, int position) } } + /** + * Checks whether this composite printer parser only uses ChronoField instances. + * This is used to optimize the storage of parsed field values in the Parsed class. + * + * @return true if all printer parsers in this composite only use ChronoField instances, + * false otherwise + */ + boolean onlyChronoField() { + for (DateTimePrinterParser pp : printerParsers) { + if ((pp instanceof NumberPrinterParser npp && !(npp.field instanceof ChronoField)) + || (pp instanceof CompositePrinterParser cpp && !cpp.onlyChronoField())) { + return false; + } + } + return true; + } + @Override public String toString() { StringBuilder buf = new StringBuilder(); diff --git a/src/java.base/share/classes/java/time/format/DateTimeParseContext.java b/src/java.base/share/classes/java/time/format/DateTimeParseContext.java index 0a4c7e825a3b6..13ed730cb7a73 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeParseContext.java +++ b/src/java.base/share/classes/java/time/format/DateTimeParseContext.java @@ -120,7 +120,7 @@ final class DateTimeParseContext { DateTimeParseContext(DateTimeFormatter formatter) { super(); this.formatter = formatter; - parsed.add(new Parsed()); + parsed.add(new Parsed(formatter.onlyChronoField)); } /** diff --git a/src/java.base/share/classes/java/time/format/Parsed.java b/src/java.base/share/classes/java/time/format/Parsed.java index 1ec956dfa2cd3..9305605307977 100644 --- a/src/java.base/share/classes/java/time/format/Parsed.java +++ b/src/java.base/share/classes/java/time/format/Parsed.java @@ -98,6 +98,7 @@ import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalQuery; import java.time.temporal.UnsupportedTemporalTypeException; +import java.util.EnumMap; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -125,10 +126,21 @@ final class Parsed implements TemporalAccessor { // some fields are accessed using package scope from DateTimeParseContext + final boolean onlyChronoField; + @SuppressWarnings("unchecked") + private Map createFieldValuesMap() { + if (onlyChronoField) { + return new HashMap<>(); + } else { + // Create the EnumMap with raw types and cast it appropriately + // This is safe because ChronoField implements TemporalField + return (Map) (Map) new EnumMap<>(ChronoField.class); + } + } /** * The parsed fields. */ - final Map fieldValues = new HashMap<>(); + final Map fieldValues = createFieldValuesMap(); /** * The parsed zone. */ @@ -169,7 +181,8 @@ final class Parsed implements TemporalAccessor { /** * Creates an instance. */ - Parsed() { + Parsed(boolean onlyChronoField) { + this.onlyChronoField = onlyChronoField; } /** @@ -177,7 +190,7 @@ final class Parsed implements TemporalAccessor { */ Parsed copy() { // only copy fields used in parsing stage - Parsed cloned = new Parsed(); + Parsed cloned = new Parsed(onlyChronoField); cloned.fieldValues.putAll(this.fieldValues); cloned.zone = this.zone; cloned.zoneNameType = this.zoneNameType; From 5a050fe9750a067d20a6b72efa1ec2c74cebf93a Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 25 Nov 2025 09:18:30 +0800 Subject: [PATCH 3/9] bug fix, form @liach --- .../time/format/DateTimeFormatterBuilder.java | 1 + .../classes/java/time/format/Parsed.java | 21 +++++++------------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 3aa98b01a8a47..e330b8cbee01d 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -2590,6 +2590,7 @@ public int parse(DateTimeParseContext context, CharSequence text, int position) boolean onlyChronoField() { for (DateTimePrinterParser pp : printerParsers) { if ((pp instanceof NumberPrinterParser npp && !(npp.field instanceof ChronoField)) + || (pp instanceof TextPrinterParser tpp && !(tpp.field instanceof ChronoField)) || (pp instanceof CompositePrinterParser cpp && !cpp.onlyChronoField())) { return false; } diff --git a/src/java.base/share/classes/java/time/format/Parsed.java b/src/java.base/share/classes/java/time/format/Parsed.java index 9305605307977..46623357b9e50 100644 --- a/src/java.base/share/classes/java/time/format/Parsed.java +++ b/src/java.base/share/classes/java/time/format/Parsed.java @@ -126,21 +126,10 @@ final class Parsed implements TemporalAccessor { // some fields are accessed using package scope from DateTimeParseContext - final boolean onlyChronoField; - @SuppressWarnings("unchecked") - private Map createFieldValuesMap() { - if (onlyChronoField) { - return new HashMap<>(); - } else { - // Create the EnumMap with raw types and cast it appropriately - // This is safe because ChronoField implements TemporalField - return (Map) (Map) new EnumMap<>(ChronoField.class); - } - } /** * The parsed fields. */ - final Map fieldValues = createFieldValuesMap(); + final Map fieldValues; /** * The parsed zone. */ @@ -181,8 +170,12 @@ private Map createFieldValuesMap() { /** * Creates an instance. */ + @SuppressWarnings("unchecked") Parsed(boolean onlyChronoField) { - this.onlyChronoField = onlyChronoField; + // Create the EnumMap with raw types and cast it appropriately + // This is safe because ChronoField implements TemporalField + fieldValues = onlyChronoField ? (Map) (Map) new EnumMap<>(ChronoField.class) + : new HashMap<>(); } /** @@ -190,7 +183,7 @@ private Map createFieldValuesMap() { */ Parsed copy() { // only copy fields used in parsing stage - Parsed cloned = new Parsed(onlyChronoField); + Parsed cloned = new Parsed(fieldValues instanceof EnumMap); cloned.fieldValues.putAll(this.fieldValues); cloned.zone = this.zone; cloned.zoneNameType = this.zoneNameType; From 7137d9ec285170ea9aa7428f4d2c1e5031c5e37f Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 25 Nov 2025 14:37:41 +0800 Subject: [PATCH 4/9] copyright --- .../share/classes/java/time/format/DateTimeParseContext.java | 2 +- src/java.base/share/classes/java/time/format/Parsed.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/time/format/DateTimeParseContext.java b/src/java.base/share/classes/java/time/format/DateTimeParseContext.java index 13ed730cb7a73..fff139dc9f38f 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeParseContext.java +++ b/src/java.base/share/classes/java/time/format/DateTimeParseContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/java.base/share/classes/java/time/format/Parsed.java b/src/java.base/share/classes/java/time/format/Parsed.java index 46623357b9e50..12623367ca08d 100644 --- a/src/java.base/share/classes/java/time/format/Parsed.java +++ b/src/java.base/share/classes/java/time/format/Parsed.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it From 9a0ad61a89876400645d3b0bb73807c8b5f588eb Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Mon, 1 Dec 2025 08:09:51 +0800 Subject: [PATCH 5/9] from @liach --- .../java/time/format/DateTimeFormatter.java | 21 +++++----- .../time/format/DateTimeFormatterBuilder.java | 39 ++++++++++--------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java index 3b9b84b56832e..1adb49ff37cef 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java @@ -1480,11 +1480,12 @@ public static final TemporalQuery parsedLeapSecond() { * @param resolverFields the fields to use during resolving, null for all fields * @param chrono the chronology to use, null for no override * @param zone the zone to use, null for no override + * @param onlyChronoField flag indicating whether this formatter only uses ChronoField instances */ DateTimeFormatter(CompositePrinterParser printerParser, Locale locale, DecimalStyle decimalStyle, ResolverStyle resolverStyle, Set resolverFields, - Chronology chrono, ZoneId zone) { + Chronology chrono, ZoneId zone, boolean onlyChronoField) { this.printerParser = Objects.requireNonNull(printerParser, "printerParser"); this.resolverFields = resolverFields; this.locale = Objects.requireNonNull(locale, "locale"); @@ -1492,7 +1493,7 @@ public static final TemporalQuery parsedLeapSecond() { this.resolverStyle = Objects.requireNonNull(resolverStyle, "resolverStyle"); this.chrono = chrono; this.zone = zone; - this.onlyChronoField = printerParser.onlyChronoField(); + this.onlyChronoField = onlyChronoField; } //----------------------------------------------------------------------- @@ -1530,7 +1531,7 @@ public DateTimeFormatter withLocale(Locale locale) { if (this.locale.equals(locale)) { return this; } - return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone, onlyChronoField); } /** @@ -1576,7 +1577,7 @@ public DateTimeFormatter localizedBy(Locale locale) { Objects.equals(z, zone)) { return this; } else { - return new DateTimeFormatter(printerParser, locale, ds, resolverStyle, resolverFields, c, z); + return new DateTimeFormatter(printerParser, locale, ds, resolverStyle, resolverFields, c, z, onlyChronoField); } } @@ -1602,7 +1603,7 @@ public DateTimeFormatter withDecimalStyle(DecimalStyle decimalStyle) { if (this.decimalStyle.equals(decimalStyle)) { return this; } - return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone, onlyChronoField); } //----------------------------------------------------------------------- @@ -1656,7 +1657,7 @@ public DateTimeFormatter withChronology(Chronology chrono) { if (Objects.equals(this.chrono, chrono)) { return this; } - return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone, onlyChronoField); } //----------------------------------------------------------------------- @@ -1713,7 +1714,7 @@ public DateTimeFormatter withZone(ZoneId zone) { if (Objects.equals(this.zone, zone)) { return this; } - return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone, onlyChronoField); } //----------------------------------------------------------------------- @@ -1755,7 +1756,7 @@ public DateTimeFormatter withResolverStyle(ResolverStyle resolverStyle) { if (Objects.equals(this.resolverStyle, resolverStyle)) { return this; } - return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone, onlyChronoField); } //----------------------------------------------------------------------- @@ -1821,7 +1822,7 @@ public DateTimeFormatter withResolverFields(TemporalField... resolverFields) { if (Objects.equals(this.resolverFields, fields)) { return this; } - return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, fields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, fields, chrono, zone, onlyChronoField); } /** @@ -1870,7 +1871,7 @@ public DateTimeFormatter withResolverFields(Set resolverFields) { if (resolverFields != null) { resolverFields = Collections.unmodifiableSet(new HashSet<>(resolverFields)); } - return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone, onlyChronoField); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index e330b8cbee01d..8366706a7db14 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -200,6 +200,12 @@ public final class DateTimeFormatterBuilder { */ private int valueParserIndex = -1; + /** + * Flag indicating whether this builder only uses ChronoField instances. + * This is used to optimize the storage of parsed field values in the Parsed class. + */ + private boolean onlyChronoField = true; + /** * Gets the formatting pattern for date and time styles for a locale and chronology. * The locale and chronology are used to lookup the locale specific format @@ -2371,6 +2377,19 @@ private int appendInternal(DateTimePrinterParser pp) { active.padNextWidth = 0; active.padNextChar = 0; } + + // Update the onlyChronoField flag if the printer/parser uses non-ChronoField instances + TemporalField field = null; + if (pp instanceof NumberPrinterParser npp) { + field = npp.field; + } else if (pp instanceof TextPrinterParser tpp) { + field = tpp.field; + } else if (pp instanceof DefaultValueParser dvp) { + field = dvp.field; + } + if (field != null && !(field instanceof ChronoField)) { + active.onlyChronoField = false; + } active.printerParsers.add(pp); active.valueParserIndex = -1; return active.printerParsers.size() - 1; @@ -2443,7 +2462,7 @@ private DateTimeFormatter toFormatter(Locale locale, ResolverStyle resolverStyle } CompositePrinterParser pp = new CompositePrinterParser(printerParsers, false); return new DateTimeFormatter(pp, locale, DecimalStyle.STANDARD, - resolverStyle, null, chrono, null); + resolverStyle, null, chrono, null, onlyChronoField); } //----------------------------------------------------------------------- @@ -2580,24 +2599,6 @@ public int parse(DateTimeParseContext context, CharSequence text, int position) } } - /** - * Checks whether this composite printer parser only uses ChronoField instances. - * This is used to optimize the storage of parsed field values in the Parsed class. - * - * @return true if all printer parsers in this composite only use ChronoField instances, - * false otherwise - */ - boolean onlyChronoField() { - for (DateTimePrinterParser pp : printerParsers) { - if ((pp instanceof NumberPrinterParser npp && !(npp.field instanceof ChronoField)) - || (pp instanceof TextPrinterParser tpp && !(tpp.field instanceof ChronoField)) - || (pp instanceof CompositePrinterParser cpp && !cpp.onlyChronoField())) { - return false; - } - } - return true; - } - @Override public String toString() { StringBuilder buf = new StringBuilder(); From 073e2b8dca338db8d0410ea40f0fb93fc8669532 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Mon, 1 Dec 2025 09:52:07 +0800 Subject: [PATCH 6/9] bug fix --- .../time/format/DateTimeFormatterBuilder.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 8366706a7db14..3828a5d809c12 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -709,6 +709,7 @@ private DateTimeFormatterBuilder appendValue(NumberPrinterParser pp) { } // Replace the modified parser with the updated one active.printerParsers.set(activeValueParser, basePP); + checkField(basePP); } else { // The new Parser becomes the active parser active.valueParserIndex = appendInternal(pp); @@ -2379,20 +2380,31 @@ private int appendInternal(DateTimePrinterParser pp) { } // Update the onlyChronoField flag if the printer/parser uses non-ChronoField instances - TemporalField field = null; + checkField(pp); + active.printerParsers.add(pp); + active.valueParserIndex = -1; + return active.printerParsers.size() - 1; + } + + /** + * Update the onlyChronoField flag if the printer/parser uses non-ChronoField instances + * @param pp the printer-parser + */ + private void checkField(DateTimePrinterParser pp) { + TemporalField field; if (pp instanceof NumberPrinterParser npp) { field = npp.field; } else if (pp instanceof TextPrinterParser tpp) { field = tpp.field; } else if (pp instanceof DefaultValueParser dvp) { field = dvp.field; + } else { + return; } - if (field != null && !(field instanceof ChronoField)) { + + if (!(field instanceof ChronoField)) { active.onlyChronoField = false; } - active.printerParsers.add(pp); - active.valueParserIndex = -1; - return active.printerParsers.size() - 1; } //----------------------------------------------------------------------- From b2b19e1368ef9b295876e6b79f8bfb163f3b3f1c Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 3 Dec 2025 09:39:15 +0800 Subject: [PATCH 7/9] resolverFields, from @liach --- .../share/classes/java/time/format/DateTimeFormatter.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java index 1adb49ff37cef..be4d63ee5aa2b 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java @@ -1493,6 +1493,14 @@ public static final TemporalQuery parsedLeapSecond() { this.resolverStyle = Objects.requireNonNull(resolverStyle, "resolverStyle"); this.chrono = chrono; this.zone = zone; + if (resolverFields != null) { + for (TemporalField resolverField : resolverFields) { + if (!(resolverField instanceof ChronoField)) { + onlyChronoField = false; + break; + } + } + } this.onlyChronoField = onlyChronoField; } From 90dca37798c8b831b0ba7b021ed33514c0081b6d Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 9 Dec 2025 14:09:32 +0800 Subject: [PATCH 8/9] Revert "resolverFields, from @liach" This reverts commit b2b19e1368ef9b295876e6b79f8bfb163f3b3f1c. --- .../share/classes/java/time/format/DateTimeFormatter.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java index be4d63ee5aa2b..1adb49ff37cef 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java @@ -1493,14 +1493,6 @@ public static final TemporalQuery parsedLeapSecond() { this.resolverStyle = Objects.requireNonNull(resolverStyle, "resolverStyle"); this.chrono = chrono; this.zone = zone; - if (resolverFields != null) { - for (TemporalField resolverField : resolverFields) { - if (!(resolverField instanceof ChronoField)) { - onlyChronoField = false; - break; - } - } - } this.onlyChronoField = onlyChronoField; } From 9a13e51e492e3809681a7cad7317f53ca329b0c4 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 9 Dec 2025 17:57:28 +0800 Subject: [PATCH 9/9] remove redundant checkField --- .../share/classes/java/time/format/DateTimeFormatterBuilder.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 3828a5d809c12..8f345d62abb27 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -709,7 +709,6 @@ private DateTimeFormatterBuilder appendValue(NumberPrinterParser pp) { } // Replace the modified parser with the updated one active.printerParsers.set(activeValueParser, basePP); - checkField(basePP); } else { // The new Parser becomes the active parser active.valueParserIndex = appendInternal(pp);