diff --git a/make/ToolsJdk.gmk b/make/ToolsJdk.gmk index 629cadbf83a06..bb5b65eb6ff00 100644 --- a/make/ToolsJdk.gmk +++ b/make/ToolsJdk.gmk @@ -110,6 +110,9 @@ TOOL_OSX_TOBIN = $(JAVA_SMALL) -Djava.awt.headless=true -cp $(BUILDTOOLS_OUTPUTD TOOL_CLDRCONVERTER = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ build.tools.cldrconverter.CLDRConverter +TOOL_DTPFACTORYGENERATOR = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ + build.tools.dtpfactorygenerator.DateTimePrinterParserFactoryGenerator + TOOL_INTPOLY = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ build.tools.intpoly.FieldGen diff --git a/make/jdk/src/classes/build/tools/dtpfactorygenerator/DateTimePrinterParserFactoryGenerator.java b/make/jdk/src/classes/build/tools/dtpfactorygenerator/DateTimePrinterParserFactoryGenerator.java new file mode 100644 index 0000000000000..aef2c1cdda122 --- /dev/null +++ b/make/jdk/src/classes/build/tools/dtpfactorygenerator/DateTimePrinterParserFactoryGenerator.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 build.tools.dtpfactorygenerator; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +/** + * A standalone generator to create DateTimePrinterParserFactory.java from template. + * This separates the datetime printer/parser factory generation from the CLDR converter. + */ +public class DateTimePrinterParserFactoryGenerator { + + private static String templateFile; + private static String destinationDir = "build/gensrc"; + + public static void main(String[] args) throws Exception { + if (args.length != 0) { + String currentArg = null; + try { + for (int i = 0; i < args.length; i++) { + currentArg = args[i]; + switch (currentArg) { + case "-dtpftempfile": + templateFile = args[++i]; + break; + case "-o": + destinationDir = args[++i]; + break; + case "-help": + usage(); + System.exit(0); + break; + default: + throw new RuntimeException(); + } + } + } catch (RuntimeException e) { + System.err.println("Error: unknown or incomplete arg(s): " + currentArg); + usage(); + System.exit(1); + } + } + + if (templateFile == null) { + System.err.println("Error: -dtpftempfile is required"); + usage(); + System.exit(1); + } + + // Generate java.time.format.DateTimePrinterParserFactory.java + generateDateTimePrinterParserFactory(); + } + + private static void usage() { + System.err.println("Usage: java DateTimePrinterParserFactoryGenerator [options]"); + System.err.println("\t-help output this usage message and exit"); + System.err.println("\t-dtpftempfile template file for java.time.format.DateTimePrinterParserFactory.java"); + System.err.println("\t-o dir output directory (default: ./build/gensrc)"); + } + + private static void generateDateTimePrinterParserFactory() throws Exception { + Files.createDirectories(Paths.get(destinationDir, "java", "time", "format")); + Files.write(Paths.get(destinationDir, "java", "time", "format", "DateTimePrinterParserFactory.java"), + Files.lines(Paths.get(templateFile)) + .flatMap(l -> { + if (l.startsWith("%%%%CASES-FORMAT:")) { + return generateDateTimePrinterCases(l, "%%%%CASES-FORMAT:", false); // formatter cases + } else if (l.startsWith("%%%%CASES-PARSE:")) { + return generateDateTimePrinterCases(l, "%%%%CASES-PARSE:", true); // parser cases + } else { + return Stream.of(l); + } + }) + .collect(Collectors.toList()), + StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } + + private static Stream generateDateTimePrinterCases(String line, String prefix, boolean isParse) { + // Parse the range, defaulting to 1-16 if no range is specified + int start = 1; + int end = 16; + + String rangePart = line.substring(prefix.length(), line.length() - 4); // Remove trailing%%%% + String[] parts = rangePart.split("-"); + if (parts.length == 2) { + try { + start = Integer.parseInt(parts[0]); + end = Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + // Use defaults if parsing fails + } + } + + return IntStream.rangeClosed(start, end) + .mapToObj(i -> { + if (isParse) { + // Generate parser cases + var sb = new StringBuilder(" case ").append(i).append(" -> (context, text, position)\n"); + if (i == 1) { + // Special case for 1 - direct method reference used instead + // This shouldn't happen since case 1 is handled separately in the template + sb.append(" -> printerParsers[0].parse(context, text, position);"); + } else { + // For i >= 2, build the sequence + sb.append(" -> (position = printerParsers[0].parse(context, text, position)) < 0\n"); + for (int j = 1; j < i - 1; j++) { + sb.append(" ? position : (position = printerParsers[").append(j).append("].parse(context, text, position)) < 0\n"); + } + // The last parser in the chain doesn't check for failure, just returns its result + if (i > 1) { + sb.append(" ? position : printerParsers[").append(i - 1).append("].parse(context, text, position);"); + } + } + return sb.toString(); + } else { + // Generate formatter cases (original behavior) + var sb = new StringBuilder(" case ").append(i).append(" -> (context, buf, optional)\n") + .append(" -> "); + for (int j = 0; j < i; j++) { + sb.append("printerParsers[").append(j).append("].format(context, buf, optional)"); + if (j < i - 1) { + sb.append("\n && "); + } + } + return sb.append(";").toString(); + } + }); + } +} \ No newline at end of file diff --git a/make/modules/java.base/Gensrc.gmk b/make/modules/java.base/Gensrc.gmk index 79db438934e2d..03128b8d4eba7 100644 --- a/make/modules/java.base/Gensrc.gmk +++ b/make/modules/java.base/Gensrc.gmk @@ -44,8 +44,10 @@ include gensrc/GensrcVarHandles.gmk CLDR_DATA_DIR := $(TOPDIR)/make/data/cldr/common GENSRC_DIR := $(SUPPORT_OUTPUTDIR)/gensrc/java.base CLDR_GEN_DONE := $(GENSRC_DIR)/_cldr-gensrc.marker +DTPFACTORY_GEN_DONE := $(GENSRC_DIR)/_dtpfactory-gensrc.marker TZ_DATA_DIR := $(MODULE_SRC)/share/data/tzdata ZONENAME_TEMPLATE := $(MODULE_SRC)/share/classes/java/time/format/ZoneName.java.template +DTPFACTORY_TEMPLATE := $(MODULE_SRC)/share/classes/java/time/format/DateTimePrinterParserFactory.java.template # The `-utf8` option is used even for US English, as some names # may contain non-ASCII characters, such as “Türkiye”. @@ -68,7 +70,20 @@ $(CLDR_GEN_DONE): $(wildcard $(CLDR_DATA_DIR)/dtd/*.dtd) \ -utf8) $(TOUCH) $@ -TARGETS += $(CLDR_GEN_DONE) +# Generate DateTimePrinterParserFactory.java from template +$(DTPFACTORY_GEN_DONE): $(DTPFACTORY_TEMPLATE) $(BUILD_TOOLS_JDK) + $(call MakeDir, $(GENSRC_DIR)) + $(call LogInfo, Processing DateTimePrinterParserFactory template for java.base) + $(call ExecuteWithLog, $@, \ + $(TOOL_DTPFACTORYGENERATOR) \ + -dtpftempfile $(DTPFACTORY_TEMPLATE) \ + -o $(GENSRC_DIR)) + $(TOUCH) $@ + +# Ensure both CLDR and DateTimePrinterParserFactory generation complete before continuing +$(CLDR_GEN_DONE) $(DTPFACTORY_GEN_DONE): + +TARGETS += $(CLDR_GEN_DONE) $(DTPFACTORY_GEN_DONE) ################################################################################ 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..c8ced16052ffa 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -2447,34 +2447,7 @@ private DateTimeFormatter toFormatter(Locale locale, ResolverStyle resolverStyle } //----------------------------------------------------------------------- - /** - * Strategy for formatting/parsing date-time information. - *

- * The printer may format any part, or the whole, of the input date-time object. - * Typically, a complete format is constructed from a number of smaller - * units, each outputting a single field. - *

- * The parser may parse any piece of text from the input, storing the result - * in the context. Typically, each individual parser will just parse one - * field, such as the day-of-month, storing the value in the context. - * Once the parse is complete, the caller will then resolve the parsed values - * to create the desired object, such as a {@code LocalDate}. - *

- * The parse position will be updated during the parse. Parsing will start at - * the specified index and the return value specifies the new parse position - * for the next parser. If an error occurs, the returned index will be negative - * and will have the error position encoded using the complement operator. - * - * @implSpec - * This interface must be implemented with care to ensure other classes operate correctly. - * All implementations that can be instantiated must be final, immutable and thread-safe. - *

- * The context is not a thread-safe object and a new instance will be created - * for each format that occurs. The context must not be stored in an instance - * variable or shared with any other threads. - */ - interface DateTimePrinterParser { - + interface DateTimePrinter { /** * Prints the date-time object to the buffer. *

@@ -2494,6 +2467,9 @@ interface DateTimePrinterParser { * @throws DateTimeException if the date-time cannot be printed successfully */ boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional); + } + + interface DateTimeParser { /** * Parses text into date-time information. @@ -2512,6 +2488,35 @@ interface DateTimePrinterParser { int parse(DateTimeParseContext context, CharSequence text, int position); } + /** + * Strategy for formatting/parsing date-time information. + *

+ * The printer may format any part, or the whole, of the input date-time object. + * Typically, a complete format is constructed from a number of smaller + * units, each outputting a single field. + *

+ * The parser may parse any piece of text from the input, storing the result + * in the context. Typically, each individual parser will just parse one + * field, such as the day-of-month, storing the value in the context. + * Once the parse is complete, the caller will then resolve the parsed values + * to create the desired object, such as a {@code LocalDate}. + *

+ * The parse position will be updated during the parse. Parsing will start at + * the specified index and the return value specifies the new parse position + * for the next parser. If an error occurs, the returned index will be negative + * and will have the error position encoded using the complement operator. + * + * @implSpec + * This interface must be implemented with care to ensure other classes operate correctly. + * All implementations that can be instantiated must be final, immutable and thread-safe. + *

+ * The context is not a thread-safe object and a new instance will be created + * for each format that occurs. The context must not be stored in an instance + * variable or shared with any other threads. + */ + interface DateTimePrinterParser extends DateTimePrinter, DateTimeParser { + } + //----------------------------------------------------------------------- /** * Composite printer and parser. @@ -2519,6 +2524,8 @@ interface DateTimePrinterParser { static final class CompositePrinterParser implements DateTimePrinterParser { private final DateTimePrinterParser[] printerParsers; private final boolean optional; + private final DateTimePrinter formatter; + private final DateTimeParser parser; private CompositePrinterParser(List printerParsers, boolean optional) { this(printerParsers.toArray(new DateTimePrinterParser[0]), optional); @@ -2527,6 +2534,8 @@ private CompositePrinterParser(List printerParsers, boole private CompositePrinterParser(DateTimePrinterParser[] printerParsers, boolean optional) { this.printerParsers = printerParsers; this.optional = optional; + this.formatter = DateTimePrinterParserFactory.createFormatter(printerParsers); + this.parser = DateTimePrinterParserFactory.createParser(printerParsers, optional); } /** @@ -2546,38 +2555,16 @@ public CompositePrinterParser withOptional(boolean optional) { public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { int length = buf.length(); boolean effectiveOptional = optional | this.optional; - for (DateTimePrinterParser pp : printerParsers) { - if (!pp.format(context, buf, effectiveOptional)) { - buf.setLength(length); // reset buffer - return true; - } + if (!formatter.format(context, buf, effectiveOptional)) { + buf.setLength(length); // reset buffer + return true; } return true; } @Override public int parse(DateTimeParseContext context, CharSequence text, int position) { - if (optional) { - context.startOptional(); - int pos = position; - for (DateTimePrinterParser pp : printerParsers) { - pos = pp.parse(context, text, pos); - if (pos < 0) { - context.endOptional(false); - return position; // return original position - } - } - context.endOptional(true); - return pos; - } else { - for (DateTimePrinterParser pp : printerParsers) { - position = pp.parse(context, text, position); - if (position < 0) { - break; - } - } - return position; - } + return parser.parse(context, text, position); } @Override diff --git a/src/java.base/share/classes/java/time/format/DateTimePrinterParserFactory.java.template b/src/java.base/share/classes/java/time/format/DateTimePrinterParserFactory.java.template new file mode 100644 index 0000000000000..9e666c1c4eb29 --- /dev/null +++ b/src/java.base/share/classes/java/time/format/DateTimePrinterParserFactory.java.template @@ -0,0 +1,117 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 java.time.format; + +import static java.time.format.DateTimeFormatterBuilder.DateTimeParser; +import static java.time.format.DateTimeFormatterBuilder.DateTimePrinter; +import static java.time.format.DateTimeFormatterBuilder.DateTimePrinterParser; + +final class DateTimePrinterParserFactory { + private DateTimePrinterParserFactory() { + } + + /** + * Creates a DateTimePrinter based on the number of printer parsers. + * This method efficiently creates specialized formatters for different numbers of printer parsers, + * optimizing performance by avoiding loops for small numbers of parsers. + * The manual expansion of cases (rather than using a loop) is designed to allow the TypeProfile optimizer to be effective. + * + * @param printerParsers an array of DateTimePrinterParser instances to format + * @return a DateTimePrinter that formats according to the provided printer parsers + */ + static DateTimePrinter createFormatter(DateTimePrinterParser[] printerParsers) { + int length = printerParsers.length; + return switch (length) { + case 1 -> printerParsers[0]::format; + case 2 -> (context, buf, optional) + -> printerParsers[0].format(context, buf, optional) + && printerParsers[1].format(context, buf, optional); + case 3 -> (context, buf, optional) + -> printerParsers[0].format(context, buf, optional) + && printerParsers[1].format(context, buf, optional) + && printerParsers[2].format(context, buf, optional); +%%%%CASES-FORMAT:4-16%%%% + default -> (context, buf, optional) -> { + for (var pp : printerParsers) { + if (!pp.format(context, buf, optional)) { + return false; + } + } + return true; + }; + }; + } + + /** + * Creates a DateTimeParser based on the number of printer parsers. + * This method efficiently creates specialized parsers for different numbers of printer parsers, + * optimizing performance by avoiding loops for small numbers of parsers. + * The manual expansion of cases (rather than using a loop) is designed to allow the TypeProfile optimizer to be effective. + * For optional parsing, the method wraps the parser with optional handling logic that starts and ends an optional context. + * + * @param printerParsers an array of DateTimePrinterParser instances to parse + * @param optional flag indicating whether the parsing is optional + * @return a DateTimeParser that parses according to the provided printer parsers + */ + static DateTimeParser createParser(DateTimePrinterParser[] printerParsers, boolean optional) { + int len = printerParsers.length; + DateTimeParser parser = switch (len) { + case 1 -> printerParsers[0]::parse; + case 2 -> (context, text, position) + -> (position = printerParsers[0].parse(context, text, position)) < 0 + ? position : printerParsers[1].parse(context, text, position); + case 3 -> (context, text, position) + -> (position = printerParsers[0].parse(context, text, position)) < 0 + ? position : (position = printerParsers[1].parse(context, text, position)) < 0 + ? position : printerParsers[2].parse(context, text, position); +%%%%CASES-PARSE:4-16%%%% + default -> (context, text, position) -> { + for (var pp : printerParsers) { + position = pp.parse(context, text, position); + if (position < 0) { + break; + } + } + return position; + }; + }; + if (!optional) { + return parser; + } + // Wrap the parser with optional handling logic + return (context, text, position) -> { + context.startOptional(); + int pos = position; + pos = parser.parse(context, text, pos); + if (pos < 0) { + context.endOptional(false); + return position; // return original position + } + context.endOptional(true); + return pos; + }; + } +} \ No newline at end of file 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