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 26192b8e178..16d7193c556 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java @@ -1904,11 +1904,11 @@ public final class DateTimeFormatter { try { DateTimePrintContext context = new DateTimePrintContext(temporal, this); if (appendable instanceof StringBuilder) { - printerParser.format(context, (StringBuilder) appendable); + printerParser.format(context, (StringBuilder) appendable, false); } else { // buffer output to avoid writing to appendable in case of error StringBuilder buf = new StringBuilder(32); - printerParser.format(context, buf); + printerParser.format(context, buf, false); appendable.append(buf); } } catch (IOException ex) { 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 9f5b82775b9..4708094effb 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -1,5 +1,6 @@ /* * 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 @@ -2484,10 +2485,15 @@ public final class DateTimeFormatterBuilder { * * @param context the context to format using, not null * @param buf the buffer to append to, not null + * @param optional whether the enclosing formatter is optional. + * If true and this formatter is nested in an optional formatter + * and the data is not available, then no error is returned and + * nothing is appended to the buffer. If false and the data is not available + * then an exception is thrown or false is returned as appropriate. * @return false if unable to query the value from the date-time, true otherwise * @throws DateTimeException if the date-time cannot be printed successfully */ - boolean format(DateTimePrintContext context, StringBuilder buf); + boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional); /** * Parses text into date-time information. @@ -2537,21 +2543,13 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { int length = buf.length(); - if (optional) { - context.startOptional(); - } - try { - for (DateTimePrinterParser pp : printerParsers) { - if (pp.format(context, buf) == false) { - buf.setLength(length); // reset buffer - return true; - } - } - } finally { - if (optional) { - context.endOptional(); + boolean effectiveOptional = optional | this.optional; + for (DateTimePrinterParser pp : printerParsers) { + if (!pp.format(context, buf, effectiveOptional)) { + buf.setLength(length); // reset buffer + return true; } } return true; @@ -2620,9 +2618,9 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { int preLen = buf.length(); - if (printerParser.format(context, buf) == false) { + if (printerParser.format(context, buf, optional) == false) { return false; } int len = buf.length() - preLen; @@ -2689,7 +2687,7 @@ public final class DateTimeFormatterBuilder { LENIENT; @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { return true; // nothing to do here } @@ -2731,7 +2729,7 @@ public final class DateTimeFormatterBuilder { this.value = value; } - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { return true; } @@ -2757,7 +2755,7 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { buf.append(literal); return true; } @@ -2807,7 +2805,7 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { buf.append(literal); return true; } @@ -2919,8 +2917,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long valueLong = context.getValue(field); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long valueLong = context.getValue(field, optional); if (valueLong == null) { return false; } @@ -3374,8 +3372,8 @@ public final class DateTimeFormatterBuilder { }; @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long value = context.getValue(field); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long value = context.getValue(field, optional); if (value == null) { return false; } @@ -3563,8 +3561,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long value = context.getValue(field); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long value = context.getValue(field, optional); if (value == null) { return false; } @@ -3711,8 +3709,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long value = context.getValue(field); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long value = context.getValue(field, optional); if (value == null) { return false; } @@ -3724,7 +3722,7 @@ public final class DateTimeFormatterBuilder { text = provider.getText(chrono, field, value, textStyle, context.getLocale()); } if (text == null) { - return numberPrinterParser().format(context, buf); + return numberPrinterParser().format(context, buf, optional); } buf.append(text); return true; @@ -3806,9 +3804,9 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { // use INSTANT_SECONDS, thus this code is not bound by Instant.MAX - Long inSecs = context.getValue(INSTANT_SECONDS); + Long inSecs = context.getValue(INSTANT_SECONDS, optional); Long inNanos = null; if (context.getTemporal().isSupported(NANO_OF_SECOND)) { inNanos = context.getTemporal().getLong(NANO_OF_SECOND); @@ -3992,8 +3990,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long offsetSecs = context.getValue(OFFSET_SECONDS); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long offsetSecs = context.getValue(OFFSET_SECONDS, optional); if (offsetSecs == null) { return false; } @@ -4291,8 +4289,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long offsetSecs = context.getValue(OFFSET_SECONDS); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long offsetSecs = context.getValue(OFFSET_SECONDS, optional); if (offsetSecs == null) { return false; } @@ -4505,8 +4503,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - ZoneId zone = context.getValue(TemporalQueries.zoneId()); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + ZoneId zone = context.getValue(TemporalQueries.zoneId(), optional); if (zone == null) { return false; } @@ -4627,8 +4625,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - ZoneId zone = context.getValue(query); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + ZoneId zone = context.getValue(query, optional); if (zone == null) { return false; } @@ -5052,8 +5050,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Chronology chrono = context.getValue(TemporalQueries.chronology()); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Chronology chrono = context.getValue(TemporalQueries.chronology(), optional); if (chrono == null) { return false; } @@ -5149,9 +5147,9 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { Chronology chrono = Chronology.from(context.getTemporal()); - return formatter(context.getLocale(), chrono).toPrinterParser(false).format(context, buf); + return formatter(context.getLocale(), chrono).toPrinterParser(false).format(context, buf, optional); } @Override @@ -5260,8 +5258,8 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - return printerParser(context.getLocale()).format(context, buf); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + return printerParser(context.getLocale()).format(context, buf, optional); } @Override @@ -5364,12 +5362,12 @@ public final class DateTimeFormatterBuilder { } @Override - public boolean format(DateTimePrintContext context, StringBuilder buf) { - Long hod = context.getValue(HOUR_OF_DAY); + public boolean format(DateTimePrintContext context, StringBuilder buf, boolean optional) { + Long hod = context.getValue(HOUR_OF_DAY, optional); if (hod == null) { return false; } - Long moh = context.getValue(MINUTE_OF_HOUR); + Long moh = context.getValue(MINUTE_OF_HOUR, optional); long value = Math.floorMod(hod, 24) * 60 + (moh != null ? Math.floorMod(moh, 60) : 0); Locale locale = context.getLocale(); LocaleStore store = findDayPeriodStore(locale); diff --git a/src/java.base/share/classes/java/time/format/DateTimePrintContext.java b/src/java.base/share/classes/java/time/format/DateTimePrintContext.java index d755ba3ee78..051796b6a9c 100644 --- a/src/java.base/share/classes/java/time/format/DateTimePrintContext.java +++ b/src/java.base/share/classes/java/time/format/DateTimePrintContext.java @@ -1,5 +1,6 @@ /* * 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 @@ -86,11 +87,6 @@ import java.util.Objects; *

* This class provides a single wrapper to items used in the format. * - * @implSpec - * This class is a mutable context intended for use from a single thread. - * Usage of the class is thread-safe within standard printing as the framework creates - * a new instance of the class for each format and printing is single-threaded. - * * @since 1.8 */ final class DateTimePrintContext { @@ -103,10 +99,6 @@ final class DateTimePrintContext { * The formatter, not null. */ private final DateTimeFormatter formatter; - /** - * Whether the current formatter is optional. - */ - private int optional; /** * Creates a new instance of the context. @@ -115,7 +107,6 @@ final class DateTimePrintContext { * @param formatter the formatter controlling the format, not null */ DateTimePrintContext(TemporalAccessor temporal, DateTimeFormatter formatter) { - super(); this.temporal = adjust(temporal, formatter); this.formatter = formatter; } @@ -348,30 +339,17 @@ final class DateTimePrintContext { } //----------------------------------------------------------------------- - /** - * Starts the printing of an optional segment of the input. - */ - void startOptional() { - this.optional++; - } - - /** - * Ends the printing of an optional segment of the input. - */ - void endOptional() { - this.optional--; - } - /** * Gets a value using a query. * * @param query the query to use, not null + * @param optional whether the query is optional, true if the query may be missing * @return the result, null if not found and optional is true * @throws DateTimeException if the type is not available and the section is not optional */ - R getValue(TemporalQuery query) { + R getValue(TemporalQuery query, boolean optional) { R result = temporal.query(query); - if (result == null && optional == 0) { + if (result == null && !optional) { throw new DateTimeException("Unable to extract " + query + " from temporal " + temporal); } @@ -384,11 +362,12 @@ final class DateTimePrintContext { * This will return the value for the specified field. * * @param field the field to find, not null + * @param optional whether the field is optional, true if the field may be missing * @return the value, null if not found and optional is true * @throws DateTimeException if the field is not available and the section is not optional */ - Long getValue(TemporalField field) { - if (optional > 0 && !temporal.isSupported(field)) { + Long getValue(TemporalField field, boolean optional) { + if (optional && !temporal.isSupported(field)) { return null; } return temporal.getLong(field);