This is an automated email from the ASF dual-hosted git repository. ddekany pushed a commit to branch FREEMARKER-35 in repository https://gitbox.apache.org/repos/asf/freemarker.git
commit 4571e34876d0db0f9da3aad8f9ccf12dd578e34d Author: ddekany <[email protected]> AuthorDate: Wed Dec 15 18:08:58 2021 +0100 [FREEMARKER-35] Removed new settings that deal with the format of Temporal-s that could be treated as date, time, or date-time types. For those, use the same settings as for java.util.Date-s instead. (Will need more work though, as pattern syntax of SimpleDateFormat and DateTimeFormatter somewhat differs.) --- src/main/java/freemarker/core/Configurable.java | 437 ++++----------------- src/main/java/freemarker/core/Environment.java | 4 - .../core/JavaTemplateTemporalFormat.java | 9 +- src/main/java/freemarker/core/PropertySetting.java | 16 +- .../freemarker/core/TemplateConfiguration.java | 49 --- .../java/freemarker/core/_CoreTemporalUtils.java | 25 +- .../freemarker/core/CoercionToTextualTest.java | 1 - .../java/freemarker/core/CoreTemporalUtilTest.java | 4 +- .../freemarker/core/TemporalErrorMessagesTest.java | 17 +- .../java/freemarker/core/TemporalFormatTest.java | 21 +- .../java/freemarker/core/TemporalFormatTest2.java | 4 +- .../test/templatesuite/templates/temporal.ftl | 13 +- 12 files changed, 118 insertions(+), 482 deletions(-) diff --git a/src/main/java/freemarker/core/Configurable.java b/src/main/java/freemarker/core/Configurable.java index c62abfd..385ee87 100644 --- a/src/main/java/freemarker/core/Configurable.java +++ b/src/main/java/freemarker/core/Configurable.java @@ -33,11 +33,13 @@ import java.time.OffsetTime; import java.time.Year; import java.time.YearMonth; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.time.temporal.Temporal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -68,6 +70,7 @@ import freemarker.ext.beans.MemberAccessPolicy; import freemarker.template.AttemptExceptionReporter; import freemarker.template.Configuration; import freemarker.template.DefaultObjectWrapper; +import freemarker.template.DefaultObjectWrapperBuilder; import freemarker.template.ObjectWrapper; import freemarker.template.SimpleObjectWrapper; import freemarker.template.Template; @@ -157,34 +160,6 @@ public class Configurable { /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */ public static final String CUSTOM_TEMPORAL_FORMATS_KEY = CUSTOM_TEMPORAL_FORMATS_KEY_SNAKE_CASE; - public static final String INSTANT_FORMAT_KEY_SNAKE_CASE = "instant_format"; - public static final String INSTANT_FORMAT_KEY_CAMEL_CASE = "instantFormat"; - public static final String INSTANT_FORMAT_KEY = INSTANT_FORMAT_KEY_SNAKE_CASE; - - public static final String LOCAL_DATE_FORMAT_KEY_SNAKE_CASE = "local_date_format"; - public static final String LOCAL_DATE_FORMAT_KEY_CAMEL_CASE = "localDateFormat"; - public static final String LOCAL_DATE_FORMAT_KEY = LOCAL_DATE_FORMAT_KEY_SNAKE_CASE; - - public static final String LOCAL_DATE_TIME_FORMAT_KEY_SNAKE_CASE = "local_date_time_format"; - public static final String LOCAL_DATE_TIME_FORMAT_KEY_CAMEL_CASE = "localDateTimeFormat"; - public static final String LOCAL_DATE_TIME_FORMAT_KEY = LOCAL_DATE_TIME_FORMAT_KEY_SNAKE_CASE; - - public static final String LOCAL_TIME_FORMAT_KEY_SNAKE_CASE = "local_time_format"; - public static final String LOCAL_TIME_FORMAT_KEY_CAMEL_CASE = "localTimeFormat"; - public static final String LOCAL_TIME_FORMAT_KEY = LOCAL_TIME_FORMAT_KEY_SNAKE_CASE; - - public static final String OFFSET_DATE_TIME_FORMAT_KEY_SNAKE_CASE = "offset_date_time_format"; - public static final String OFFSET_DATE_TIME_FORMAT_KEY_CAMEL_CASE = "offsetDateTimeFormat"; - public static final String OFFSET_DATE_TIME_FORMAT_KEY = OFFSET_DATE_TIME_FORMAT_KEY_SNAKE_CASE; - - public static final String OFFSET_TIME_FORMAT_KEY_SNAKE_CASE = "offset_time_format"; - public static final String OFFSET_TIME_FORMAT_KEY_CAMEL_CASE = "offsetTimeFormat"; - public static final String OFFSET_TIME_FORMAT_KEY = OFFSET_TIME_FORMAT_KEY_SNAKE_CASE; - - public static final String ZONED_DATE_TIME_FORMAT_KEY_SNAKE_CASE = "zoned_date_time_format"; - public static final String ZONED_DATE_TIME_FORMAT_KEY_CAMEL_CASE = "zonedDateTimeFormat"; - public static final String ZONED_DATE_TIME_FORMAT_KEY = ZONED_DATE_TIME_FORMAT_KEY_SNAKE_CASE; - public static final String YEAR_FORMAT_KEY_SNAKE_CASE = "year_format"; public static final String YEAR_FORMAT_KEY_CAMEL_CASE = "yearFormat"; public static final String YEAR_FORMAT_KEY = YEAR_FORMAT_KEY_SNAKE_CASE; @@ -366,19 +341,13 @@ public class Configurable { CUSTOM_TEMPORAL_FORMATS_KEY_SNAKE_CASE, DATE_FORMAT_KEY_SNAKE_CASE, DATETIME_FORMAT_KEY_SNAKE_CASE, - INSTANT_FORMAT_KEY_SNAKE_CASE, LAZY_AUTO_IMPORTS_KEY_SNAKE_CASE, LAZY_IMPORTS_KEY_SNAKE_CASE, - LOCAL_DATE_FORMAT_KEY_SNAKE_CASE, - LOCAL_DATE_TIME_FORMAT_KEY_SNAKE_CASE, - LOCAL_TIME_FORMAT_KEY_SNAKE_CASE, LOCALE_KEY_SNAKE_CASE, LOG_TEMPLATE_EXCEPTIONS_KEY_SNAKE_CASE, NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE, NUMBER_FORMAT_KEY_SNAKE_CASE, OBJECT_WRAPPER_KEY_SNAKE_CASE, - OFFSET_DATE_TIME_FORMAT_KEY_SNAKE_CASE, - OFFSET_TIME_FORMAT_KEY_SNAKE_CASE, OUTPUT_ENCODING_KEY_SNAKE_CASE, SHOW_ERROR_TIPS_KEY_SNAKE_CASE, SQL_DATE_AND_TIME_TIME_ZONE_KEY_SNAKE_CASE, @@ -390,8 +359,7 @@ public class Configurable { URL_ESCAPING_CHARSET_KEY_SNAKE_CASE, WRAP_UNCHECKED_EXCEPTIONS_KEY_SNAKE_CASE, YEAR_FORMAT_KEY_SNAKE_CASE, - YEAR_MONTH_FORMAT_KEY_SNAKE_CASE, - ZONED_DATE_TIME_FORMAT_KEY_SNAKE_CASE + YEAR_MONTH_FORMAT_KEY_SNAKE_CASE }; private static final String[] SETTING_NAMES_CAMEL_CASE = new String[] { @@ -409,19 +377,13 @@ public class Configurable { CUSTOM_TEMPORAL_FORMATS_KEY_CAMEL_CASE, DATE_FORMAT_KEY_CAMEL_CASE, DATETIME_FORMAT_KEY_CAMEL_CASE, - INSTANT_FORMAT_KEY_CAMEL_CASE, LAZY_AUTO_IMPORTS_KEY_CAMEL_CASE, LAZY_IMPORTS_KEY_CAMEL_CASE, - LOCAL_DATE_FORMAT_KEY_CAMEL_CASE, - LOCAL_DATE_TIME_FORMAT_KEY_CAMEL_CASE, - LOCAL_TIME_FORMAT_KEY_CAMEL_CASE, LOCALE_KEY_CAMEL_CASE, LOG_TEMPLATE_EXCEPTIONS_KEY_CAMEL_CASE, NEW_BUILTIN_CLASS_RESOLVER_KEY_CAMEL_CASE, NUMBER_FORMAT_KEY_CAMEL_CASE, OBJECT_WRAPPER_KEY_CAMEL_CASE, - OFFSET_DATE_TIME_FORMAT_KEY_CAMEL_CASE, - OFFSET_TIME_FORMAT_KEY_CAMEL_CASE, OUTPUT_ENCODING_KEY_CAMEL_CASE, SHOW_ERROR_TIPS_KEY_CAMEL_CASE, SQL_DATE_AND_TIME_TIME_ZONE_KEY_CAMEL_CASE, @@ -433,8 +395,7 @@ public class Configurable { URL_ESCAPING_CHARSET_KEY_CAMEL_CASE, WRAP_UNCHECKED_EXCEPTIONS_KEY_CAMEL_CASE, YEAR_FORMAT_KEY_CAMEL_CASE, - YEAR_MONTH_FORMAT_KEY_CAMEL_CASE, - ZONED_DATE_TIME_FORMAT_KEY_CAMEL_CASE + YEAR_MONTH_FORMAT_KEY_CAMEL_CASE }; private Configurable parent; @@ -446,13 +407,6 @@ public class Configurable { private String timeFormat; private String dateFormat; private String dateTimeFormat; - private String instantFormat; - private String localDateFormat; - private String localDateTimeFormat; - private String localTimeFormat; - private String offsetDateTimeFormat; - private String offsetTimeFormat; - private String zonedDateTimeFormat; private String yearFormat; private String yearMonthFormat; private TimeZone timeZone; @@ -528,27 +482,6 @@ public class Configurable { dateTimeFormat = ""; properties.setProperty(DATETIME_FORMAT_KEY, dateTimeFormat); - instantFormat = JavaTemplateTemporalFormat.MEDIUM; - properties.setProperty(INSTANT_FORMAT_KEY, instantFormat); - - localDateFormat = JavaTemplateTemporalFormat.MEDIUM; - properties.setProperty(LOCAL_DATE_FORMAT_KEY, localDateFormat); - - localDateTimeFormat = JavaTemplateTemporalFormat.MEDIUM; - properties.setProperty(LOCAL_DATE_TIME_FORMAT_KEY, localDateTimeFormat); - - localTimeFormat = JavaTemplateTemporalFormat.MEDIUM; - properties.setProperty(LOCAL_TIME_FORMAT_KEY, localTimeFormat); - - offsetDateTimeFormat = JavaTemplateTemporalFormat.MEDIUM; - properties.setProperty(OFFSET_DATE_TIME_FORMAT_KEY, offsetDateTimeFormat); - - offsetTimeFormat = JavaTemplateTemporalFormat.LONG; - properties.setProperty(OFFSET_TIME_FORMAT_KEY, offsetTimeFormat); - - zonedDateTimeFormat = JavaTemplateTemporalFormat.MEDIUM; - properties.setProperty(ZONED_DATE_TIME_FORMAT_KEY, zonedDateTimeFormat); - yearFormat = "iso"; properties.setProperty(YEAR_FORMAT_KEY, yearFormat); @@ -1225,6 +1158,25 @@ public class Configurable { * <p>For the possible values see {@link #setDateTimeFormat(String)}. * * <p>Defaults to {@code ""}, which is equivalent to {@code "medium"}. + * + * <p>If temporal support is enabled (see {@link Configuration#setIncompatibleImprovements(Version)} at 2.3.32, and + * {@link DefaultObjectWrapperBuilder#setTemporalSupport(boolean)}) this is also used for these {@link Temporal} + * classes: {@link LocalTime}, {@link OffsetTime}. + * + * <p>Note that to format {@link OffsetTime}-s, the format <em>should show the offset</em>, unless you + * are sure that the {@link #setTimeZone(TimeZone) timeZone} setting will be a time zone that never used daylight + * saving. This is because if the offset is not shown, FreeMarker has to convert the value to the time zone + * specified in the {@link #setTimeZone(TimeZone) timeZone} setting, but we don't know the day, so we can't account + * for daylight saving changes, and thus we can't do zone conversion reliably. To address this, you can do a few + * things (TODO [FREEMARKER-35] Check if these are implemented like shown): + * <ul> + * <li>Use a format style, like {@code "medium"}, or the default {@code ""}. In this case FreeMarker will + * automatically increase the veroboseness (like uses {@code "long"} instead of {@code "medium"}) until the + * offset is shown. This format is also the defaults of FreeMarker, so by default you don't have to anything. + * </li> + * <li>Mark the offset/zone part optional in the format pattern: {@code "HH:mm[X];version=2"}. + * ({@code ";version=2"} is needed for "[" and "]" to be interpreted via {@link DateTimeFormatter}.)</li> + * </ul> */ public void setTimeFormat(String timeFormat) { NullArgumentException.check("timeFormat", timeFormat); @@ -1348,11 +1300,19 @@ public class Configurable { * format. * * <li><p>{@code "short"}, {@code "medium"}, {@code "long"}, or {@code "full"}, which that has locale-dependent - * meaning defined by the Java platform (see in the documentation of {@link java.text.DateFormat}). + * meaning defined by the Java platform (see in the documentation of {@link java.text.DateFormat} in case + * of {@link Date}, and {@link FormatStyle} in case of {@link Temporal}-s). * For date-time values, you can specify the length of the date and time part independently, be separating * them with {@code _}, like {@code "short_medium"}. ({@code "medium"} means * {@code "medium_medium"} for date-time values.) - * + * TODO [FREEMARKER-35] Check if these are implemented + * Note that Java 8 has a bug (JDK-8085887) where formatting {@link LocalDateTime} and {@link LocalTime} + * fails if for the given locale the format contains a time zone field. This was fixed in Java 9. To work this + * issue around, for these classes, FreeMarker will decrease the verbosity if the time part (like "full" to + * "long", "long" to "medium", etc.), until formatting succeeds. Also, when formatting {@link OffsetTime} + * values, FreeMarker might will increase the verboseness to display the offset (see at {@link #setTimeFormat} + * why). + * * <li><p>Anything that starts with {@code "@"} followed by a letter is interpreted as a custom * date/time/dateTime format, but only if either {@link Configuration#getIncompatibleImprovements()} * is at least 2.3.24, or there's any custom formats defined (even if custom number format). The format of @@ -1363,6 +1323,14 @@ public class Configurable { * </ul> * * <p>Defaults to {@code ""}, which is equivalent to {@code "medium_medium"}. + * + * <p>If temporal support is enabled (see {@link Configuration#setIncompatibleImprovements(Version)} at 2.3.32, and + * {@link DefaultObjectWrapperBuilder#setTemporalSupport(boolean)}) this is also used for these {@link Temporal} + * classes: {@link Instance}, {@link LocalDateTime}, {@link OffsetDateTime}, {@link ZonedDateTime}. + * For non-{@code Local} {@link Temporal}-s FreeMarker will detect if the format doesn't show the offset or zone (as + * is typically the case for the {@code "medium"} format), and then before formatting it will convert the value to + * the time zone specified in the {@link #setTimeZone(TimeZone) timeZone} setting of FreeMarker, or when parsing + * a string it will assume that it uses that time zone. */ public void setDateTimeFormat(String dateTimeFormat) { NullArgumentException.check("dateTimeFormat", dateTimeFormat); @@ -1387,264 +1355,6 @@ public class Configurable { } /** - * Sets the format used to convert {@link java.time.Instant}-s to strings, also the format that - * {@code someString?instant} will use to parse strings. - * - * <p>Defaults to {@code "medium"}, which means {@link FormatStyle#MEDIUM}. - * - * @param instantFormat - * See the similar parameter of {@link #setZonedDateTimeFormat(String)}; - * {@code iso}/{@code xs} will show the time offset. - * - * @since 2.3.32 - */ - public void setInstantFormat(String instantFormat) { - this.instantFormat = instantFormat; - } - - /** - * Getter pair of {@link #setInstantFormat(String)}. - * - * @since 2.3.32 - */ - public String getInstantFormat() { - return instantFormat == null ? parent.getInstantFormat() : instantFormat; - } - - /** - * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. - * - * @since 2.3.32 - */ - public boolean isInstantFormatSet() { - return instantFormat != null; - } - - /** - * Sets the format used to convert {@link java.time.LocalDate}-s to strings, also the format that - * {@code someString?local_date} will use to parse strings. - * - * <p>Defaults to {@code "medium"}, which means {@link FormatStyle#MEDIUM}. - * - * @param localDateFormat - * See the similar parameter of {@link #setZonedDateTimeFormat(String)}; - * {@code iso}/{@code xs} will not show the time part. - * - * @since 2.3.32 - */ - public void setLocalDateFormat(String localDateFormat) { - this.localDateFormat = localDateFormat; - } - - /** - * Getter pair of {@link #setLocalDateFormat(String)}. - * - * @since 2.3.32 - */ - public String getLocalDateFormat() { - return localDateFormat == null ? parent.getLocalDateFormat() : localDateFormat; - } - - /** - * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. - * - * @since 2.3.32 - */ - public boolean isLocalDateFormatSet() { - return localDateFormat != null; - } - - /** - * Sets the format used to convert {@link java.time.LocalDateTime}-s to strings, also the format that - * {@code someString?local_date_time} will use to parse strings. - * - * <p>Defaults to {@code "medium"}, which means {@link FormatStyle#MEDIUM}. - * - * @param localDateTimeFormat - * See the similar parameter of {@link #setZonedDateTimeFormat(String)}; - * {@code iso}/{@code xs} will not show an offset. - * - * @since 2.3.32 - */ - public void setLocalDateTimeFormat(String localDateTimeFormat) { - this.localDateTimeFormat = localDateTimeFormat; - } - - /** - * Getter pair of {@link #setLocalDateTimeFormat(String)}. - * - * @since 2.3.32 - */ - public String getLocalDateTimeFormat() { - return localDateTimeFormat == null ? parent.getLocalDateTimeFormat() : localDateTimeFormat; - } - - /** - * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. - * - * @since 2.3.32 - */ - public boolean isLocalDateTimeFormatSet() { - return localDateTimeFormat != null; - } - - /** - * Sets the format used to convert {@link java.time.LocalTime}-s to strings, also the format that - * {@code someString?local_time} will use to parse strings. - * - * <p>Defaults to {@code "medium"}, which means {@link FormatStyle#MEDIUM}. - * - * @param localTimeFormat - * See the similar parameter of {@link #setZonedDateTimeFormat(String)}; - * {@code iso}/{@code xs} will not show the time offset. - * - * @since 2.3.32 - */ - public void setLocalTimeFormat(String localTimeFormat) { - this.localTimeFormat = localTimeFormat; - } - - /** - * Getter pair of {@link #setLocalTimeFormat(String)}. - * - * @since 2.3.32 - */ - public String getLocalTimeFormat() { - return localTimeFormat == null ? parent.getLocalTimeFormat() : localTimeFormat; - } - - /** - * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. - * - * @since 2.3.32 - */ - public boolean isLocalTimeFormatSet() { - return localTimeFormat != null; - } - - /** - * Sets the format used to convert {@link java.time.OffsetDateTime}-s to strings, also the format that - * {@code someString?offset_date_time} will use to parse strings. FreeMarker will detect if the format doesn't - * show the offset (as is typically the case for the {@code "medium"} format), and then it will convert the value to - * the time zone specified in the {@link #setTimeZone(TimeZone) timeZone} setting of FreeMarker. - * - * <p>Defaults to {@code "medium"}, which means {@link FormatStyle#MEDIUM}, which usually doesn't show the time - * offset; see the parameter JavaDoc for more. - * - * @param offsetDateTimeFormat - * See the similar parameter of {@link #setZonedDateTimeFormat(String)}. - * - * @since 2.3.32 - */ - public void setOffsetDateTimeFormat(String offsetDateTimeFormat) { - this.offsetDateTimeFormat = offsetDateTimeFormat; - } - - /** - * Getter pair of {@link #setOffsetDateTimeFormat(String)}. - * @since 2.3.32 - */ - public String getOffsetDateTimeFormat() { - return offsetDateTimeFormat == null ? parent.getOffsetDateTimeFormat() : offsetDateTimeFormat; - } - - /** - * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. - * - * @since 2.3.32 - */ - public boolean isOffsetDateTimeFormatSet() { - return offsetDateTimeFormat != null; - } - - /** - * Sets the format used to convert {@link java.time.OffsetTime}-s to strings, also the format that - * {@code someString?offset_time} will use to parse strings. The format <b>should show the offset</b>, unless you - * are sure that {@link #setTimeZone(TimeZone) timeZone} setting will be a zone that has no daylight saving. - * This is because if the offset is not shown, FreeMarker has to convert the value to the time zone specified in the - * {@link #setTimeZone(TimeZone) timeZone} setting, but we don't know the day, so we can't account for daylight - * saving changes, and thus we can't do zone conversion reliably if a daylight saving is possible. - * - * <p>Defaults to {@code "long"}, which means {@link FormatStyle#LONG}, which usually show the time offset; see the - * parameter JavaDoc for more. - * - * @param offsetTimeFormat - * See the similar parameter of {@link #setZonedDateTimeFormat(String)}, but it <b>must show the offset</b> - * (see earlier why). - * - * @since 2.3.32 - */ - public void setOffsetTimeFormat(String offsetTimeFormat) { - this.offsetTimeFormat = offsetTimeFormat; - } - - /** - * Getter pair of {@link #setOffsetTimeFormat(String)}. - * @since 2.3.32 - */ - public String getOffsetTimeFormat() { - return offsetTimeFormat == null ? parent.getOffsetTimeFormat() : offsetTimeFormat; - } - - /** - * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. - * - * @since 2.3.32 - */ - public boolean isOffsetTimeFormatSet() { - return offsetTimeFormat != null; - } - - /** - * Sets the format used to convert {@link java.time.ZonedDateTime}-s to strings, also the format that - * {@code someString?offset_date_time} will use to parse strings. FreeMarker will detect if the format doesn't - * show the zone or offset (as is typically the case for the {@code "medium"} format), and then it will convert the - * value to the time zone specified in the {@link #setTimeZone(TimeZone) timeZone} setting of FreeMarker. - * - * <p>Defaults to {@code "medium"}, which means {@link FormatStyle#MEDIUM}, which usually doesn't show the time - * zone; see the parameter JavaDoc for more. - * - * @param zonedDateTimeFormat - * One of: - * <ul> - * <li>{@code "iso"}: ISO-8601 format (like {@code 2021-09-29T13:00:05.2}) - * <li>{@code "xs"}: XSD format (same as ISO-8601, but parsing is more restrictive) - * <li>{@code "short"}, {@code "medium"}, {@code "long"}, {@code "full"}, or two of these connected with - * an {@code "_"}: Refers to the {@link FormatStyle} constants. When in a pair, as in - * {@code "medium_long"}, the 1st style refers to the date part, and the 2nd style to the time part. - * Java doesn't specify what these styles actually mean. However, experience with Java 8 shows - * that "short" and "medium" will not show the time zone or time offset (which then triggers the zone - * conversion mentioned earlier), and will show months with numbers, while "long" and "full" will show - * the zone and/or offset, and shows months with their names. (Also "long" and "full" before Java 9 - * fails for {@link LocalDateTime} and {@link LocalTime}, because of bug JDK-8085887.) - * <li>Anything that starts with {@code "@"} followed by a letter is interpreted as a custom temporal - * format ({@link #setCustomTemporalFormats(Map)}). - * </ul> - * - * @since 2.3.32 - */ - public void setZonedDateTimeFormat(String zonedDateTimeFormat) { - this.zonedDateTimeFormat = zonedDateTimeFormat; - } - - /** - * Getter pair of {@link #setZonedDateTimeFormat(String)}. - * @since 2.3.32 - */ - public String getZonedDateTimeFormat() { - return zonedDateTimeFormat == null ? parent.getZonedDateTimeFormat() : zonedDateTimeFormat; - } - - /** - * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. - * - * @since 2.3.32 - */ - public boolean isZonedDateTimeFormatSet() { - return zonedDateTimeFormat != null; - } - - /** * Sets the format used to convert {@link java.time.Year}-s to strings, also the format that * {@code someString?local_time} will use to parse strings. * @@ -1729,26 +1439,24 @@ public class Configurable { */ public String getTemporalFormat(Class<? extends Temporal> temporalClass) { Objects.requireNonNull(temporalClass); - if (temporalClass == Instant.class) { - return getInstantFormat(); + // The temporal classes are final (for now at least), so we can use == operator instead of instanceof. + if (temporalClass == Instant.class + || temporalClass == LocalDateTime.class + || temporalClass == ZonedDateTime.class + || temporalClass == OffsetDateTime.class) { + return getDateTimeFormat(); } else if (temporalClass == LocalDate.class) { - return getLocalDateFormat(); - } else if (temporalClass == LocalDateTime.class) { - return getLocalDateTimeFormat(); - } else if (temporalClass == LocalTime.class) { - return getLocalTimeFormat(); - } else if (temporalClass == OffsetDateTime.class) { - return getOffsetDateTimeFormat(); - } else if (temporalClass == OffsetTime.class) { - return getOffsetTimeFormat(); - } else if (temporalClass == ZonedDateTime.class) { - return getZonedDateTimeFormat(); + return getDateFormat(); + } else if (temporalClass == LocalTime.class || temporalClass == OffsetTime.class) { + return getTimeFormat(); } else if (temporalClass == Year.class) { return getYearFormat(); } else if (temporalClass == YearMonth.class) { return getYearMonthFormat(); } else { - Class<? extends Temporal> normTemporalClass = _CoreTemporalUtils.normalizeSupportedTemporalClass(temporalClass); + // Handle the unlikely situation that in some future Java version we can have subclasses. + Class<? extends Temporal> normTemporalClass = + _CoreTemporalUtils.normalizeSupportedTemporalClass(temporalClass); if (normTemporalClass == temporalClass) { throw new IllegalArgumentException("There's no temporal format setting for this class: " + temporalClass.getName()); @@ -1792,7 +1500,13 @@ public class Configurable { /** * Associates names with formatter factories, which then can be referred by the {@link #setDateTimeFormat(String) * date_format}, {@link #setDateTimeFormat(String) time_format}, and {@link #setDateTimeFormat(String) - * datetime_format} settings with values starting with <code>@<i>name</i></code>. Beware, if you specify any custom + * datetime_format} settings with values starting with <code>@<i>name</i></code>. + * + * <p>It's important that the formats you set here will be only used when formatting {@link Date}-s, not when + * formatting {@link Temporal}-s. For the later, use {@link #setCustomTemporalFormats(Map)}. Ideally, you set the + * same custom formatter names with both methods. + * + * <p>Note that if you specify any custom * formats here, an initial {@code @} followed by a letter will have special meaning in number/date/time/datetime * format strings, even if {@link Configuration#getIncompatibleImprovements() incompatible_improvements} is less * than 2.3.24 (starting with {@link Configuration#getIncompatibleImprovements() incompatible_improvements} 2.3.24 @@ -1801,6 +1515,8 @@ public class Configurable { * @param customDateFormats * Can't be {@code null}. The name must start with an UNICODE letter, and can only contain UNICODE * letters and digits. + * + * @see #setCustomTemporalFormats(Map) * * @since 2.3.24 */ @@ -1871,14 +1587,19 @@ public class Configurable { } /** - * Associates names with formatter factories, which then can be referred by the various temporal format settings - * (like {@link #setLocalDateTimeFormat(String) local_date_time_format}, - * {@link #setLocalDateFormat(String) local_date_format}, {@link #setLocalTimeFormat(String) local_time_format}, - * and so on) a value starting with <code>@<i>name</i></code>. + * Associates names with {@link Temporal} formatter factories, which then can be referred by the + * {@link #setDateTimeFormat(String) date_time_format}, {@link #setDateFormat(String) date_format}, and + * {@link #setTimeFormat(String) time_format} settings, with values starting with <code>@<i>name</i></code>. + * + * <p>It's important that the formats you set here will be only used when formatting {@link Temporal}-s, not when + * formatting {@link Date}-s. For the later, use {@link #setCustomDateFormats(Map)}. Ideally, you set the same + * custom formatter names with both methods. * * @param customTemporalFormats * Can't be {@code null}. The name must start with an UNICODE letter, and can only contain UNICODE * letters and digits. + * + * @see #setCustomDateFormats(Map) * * @since 2.3.32 */ @@ -3236,24 +2957,10 @@ public class Configurable { setDateFormat(value); } else if (DATETIME_FORMAT_KEY_SNAKE_CASE.equals(name) || DATETIME_FORMAT_KEY_CAMEL_CASE.equals(name)) { setDateTimeFormat(value); - } else if (INSTANT_FORMAT_KEY_SNAKE_CASE.equals(name) || INSTANT_FORMAT_KEY_CAMEL_CASE.equals(name)) { - this.instantFormat = value; - } else if (LOCAL_DATE_FORMAT_KEY_SNAKE_CASE.equals(name) || LOCAL_DATE_FORMAT_KEY_CAMEL_CASE.equals(name)) { - this.localDateFormat = value; - } else if (LOCAL_DATE_TIME_FORMAT_KEY_SNAKE_CASE.equals(name) || LOCAL_DATE_TIME_FORMAT_KEY_CAMEL_CASE.equals(name)) { - this.localDateTimeFormat = value; - } else if (LOCAL_TIME_FORMAT_KEY_SNAKE_CASE.equals(name) || LOCAL_TIME_FORMAT_KEY_CAMEL_CASE.equals(name)) { - this.localTimeFormat = value; - } else if (OFFSET_DATE_TIME_FORMAT_KEY_SNAKE_CASE.equals(name) || OFFSET_DATE_TIME_FORMAT_KEY_CAMEL_CASE.equals(name)) { - this.offsetDateTimeFormat = value; - } else if (OFFSET_TIME_FORMAT_KEY_SNAKE_CASE.equals(name) || OFFSET_TIME_FORMAT_KEY_CAMEL_CASE.equals(name)) { - this.offsetTimeFormat = value; } else if (YEAR_FORMAT_KEY_SNAKE_CASE.equals(name) || YEAR_FORMAT_KEY_CAMEL_CASE.equals(name)) { this.yearFormat = value; } else if (YEAR_MONTH_FORMAT_KEY_SNAKE_CASE.equals(name) || YEAR_MONTH_FORMAT_KEY_CAMEL_CASE.equals(name)) { this.yearMonthFormat = value; - } else if (ZONED_DATE_TIME_FORMAT_KEY_SNAKE_CASE.equals(name) || ZONED_DATE_TIME_FORMAT_KEY_CAMEL_CASE.equals(name)) { - this.zonedDateTimeFormat = value; } else if (CUSTOM_DATE_FORMATS_KEY_SNAKE_CASE.equals(name) || CUSTOM_DATE_FORMATS_KEY_CAMEL_CASE.equals(name)) { Map map = (Map) _ObjectBuilderSettingEvaluator.eval( diff --git a/src/main/java/freemarker/core/Environment.java b/src/main/java/freemarker/core/Environment.java index 1208e79..5a80b2a 100644 --- a/src/main/java/freemarker/core/Environment.java +++ b/src/main/java/freemarker/core/Environment.java @@ -2449,10 +2449,6 @@ public final class Environment extends Configurable { throw new UndefinedCustomFormatException( "No custom temporal format was defined with name " + StringUtil.jQuote(name)); } - } else if (formatStringLen == 0) { - // TODO [FREEMARKER-35] This is not right, but for now we mimic what TemporalUtils did - formatParams = formatString; - formatFactory = ToStringTemplateTemporalFormatFactory.INSTANCE; } else { formatParams = formatString; formatFactory = JavaTemplateTemporalFormatFactory.INSTANCE; diff --git a/src/main/java/freemarker/core/JavaTemplateTemporalFormat.java b/src/main/java/freemarker/core/JavaTemplateTemporalFormat.java index dbb03be..f07fbcc 100644 --- a/src/main/java/freemarker/core/JavaTemplateTemporalFormat.java +++ b/src/main/java/freemarker/core/JavaTemplateTemporalFormat.java @@ -61,8 +61,10 @@ class JavaTemplateTemporalFormat extends TemplateTemporalFormat { static final String LONG = "long"; static final String FULL = "full"; private static final String ANY_FORMAT_STYLE = "(" + SHORT + "|" + MEDIUM + "|" + LONG + "|" + FULL + ")"; + // Matches format style patterns like "long_medium", "long", and "" (0-length string). It's a legacy from the + // pre-Temporal code that "" means "medium", and that it's the default of the date/time-related format settings. private static final Pattern FORMAT_STYLE_PATTERN = Pattern.compile( - ANY_FORMAT_STYLE + "(?:_" + ANY_FORMAT_STYLE + ")?"); + "(?:" + ANY_FORMAT_STYLE + "(?:_" + ANY_FORMAT_STYLE + ")?)?"); private final DateTimeFormatter dateTimeFormatter; private final ZoneId zoneId; @@ -80,7 +82,10 @@ class JavaTemplateTemporalFormat extends TemplateTemporalFormat { DateTimeFormatter dateTimeFormatter; if (isFormatStyleString) { - FormatStyle datePartFormatStyle = FormatStyle.valueOf(formatStylePatternMatcher.group(1).toUpperCase(Locale.ROOT)); + String group1 = formatStylePatternMatcher.group(1); + FormatStyle datePartFormatStyle = group1 != null + ? FormatStyle.valueOf(group1.toUpperCase(Locale.ROOT)) + : FormatStyle.MEDIUM; String group2 = formatStylePatternMatcher.group(2); FormatStyle timePartFormatStyle = group2 != null ? FormatStyle.valueOf(group2.toUpperCase(Locale.ROOT)) diff --git a/src/main/java/freemarker/core/PropertySetting.java b/src/main/java/freemarker/core/PropertySetting.java index 2970b84..365662f 100644 --- a/src/main/java/freemarker/core/PropertySetting.java +++ b/src/main/java/freemarker/core/PropertySetting.java @@ -49,21 +49,9 @@ final class PropertySetting extends TemplateElement { Configurable.DATE_FORMAT_KEY_SNAKE_CASE, Configurable.DATETIME_FORMAT_KEY_CAMEL_CASE, Configurable.DATETIME_FORMAT_KEY_SNAKE_CASE, - Configurable.INSTANT_FORMAT_KEY_CAMEL_CASE, - Configurable.INSTANT_FORMAT_KEY_SNAKE_CASE, - Configurable.LOCAL_DATE_FORMAT_KEY_CAMEL_CASE, - Configurable.LOCAL_DATE_TIME_FORMAT_KEY_CAMEL_CASE, - Configurable.LOCAL_TIME_FORMAT_KEY_CAMEL_CASE, - Configurable.LOCAL_DATE_FORMAT_KEY_SNAKE_CASE, - Configurable.LOCAL_DATE_TIME_FORMAT_KEY_SNAKE_CASE, - Configurable.LOCAL_TIME_FORMAT_KEY_SNAKE_CASE, Configurable.LOCALE_KEY, Configurable.NUMBER_FORMAT_KEY_CAMEL_CASE, Configurable.NUMBER_FORMAT_KEY_SNAKE_CASE, - Configurable.OFFSET_DATE_TIME_FORMAT_KEY_CAMEL_CASE, - Configurable.OFFSET_TIME_FORMAT_KEY_CAMEL_CASE, - Configurable.OFFSET_DATE_TIME_FORMAT_KEY_SNAKE_CASE, - Configurable.OFFSET_TIME_FORMAT_KEY_SNAKE_CASE, Configurable.OUTPUT_ENCODING_KEY_CAMEL_CASE, Configurable.OUTPUT_ENCODING_KEY_SNAKE_CASE, Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY_CAMEL_CASE, @@ -77,9 +65,7 @@ final class PropertySetting extends TemplateElement { Configurable.YEAR_FORMAT_KEY_CAMEL_CASE, Configurable.YEAR_MONTH_FORMAT_KEY_CAMEL_CASE, Configurable.YEAR_FORMAT_KEY_SNAKE_CASE, - Configurable.YEAR_MONTH_FORMAT_KEY_SNAKE_CASE, - Configurable.ZONED_DATE_TIME_FORMAT_KEY_CAMEL_CASE, - Configurable.ZONED_DATE_TIME_FORMAT_KEY_SNAKE_CASE + Configurable.YEAR_MONTH_FORMAT_KEY_SNAKE_CASE }; diff --git a/src/main/java/freemarker/core/TemplateConfiguration.java b/src/main/java/freemarker/core/TemplateConfiguration.java index 7e41d7f..f80d728 100644 --- a/src/main/java/freemarker/core/TemplateConfiguration.java +++ b/src/main/java/freemarker/core/TemplateConfiguration.java @@ -193,27 +193,6 @@ public final class TemplateConfiguration extends Configurable implements ParserC if (tc.isDateTimeFormatSet()) { setDateTimeFormat(tc.getDateTimeFormat()); } - if (tc.isInstantFormatSet()) { - setInstantFormat(tc.getInstantFormat()); - } - if (tc.isLocalDateFormatSet()) { - setLocalDateFormat(tc.getLocalDateFormat()); - } - if (tc.isLocalDateTimeFormatSet()) { - setLocalDateTimeFormat(tc.getLocalDateTimeFormat()); - } - if (tc.isLocalTimeFormatSet()) { - setLocalTimeFormat(tc.getLocalTimeFormat()); - } - if (tc.isOffsetDateTimeFormatSet()) { - setOffsetDateTimeFormat(tc.getOffsetDateTimeFormat()); - } - if (tc.isOffsetTimeFormatSet()) { - setOffsetTimeFormat(tc.getOffsetTimeFormat()); - } - if (tc.isZonedDateTimeFormatSet()) { - setZonedDateTimeFormat(tc.getZonedDateTimeFormat()); - } if (tc.isYearFormatSet()) { setYearFormat(tc.getYearFormat()); } @@ -366,27 +345,6 @@ public final class TemplateConfiguration extends Configurable implements ParserC if (isDateTimeFormatSet() && !template.isDateTimeFormatSet()) { template.setDateTimeFormat(getDateTimeFormat()); } - if (isInstantFormatSet() && !template.isInstantFormatSet()) { - template.setInstantFormat(getInstantFormat()); - } - if (isLocalDateFormatSet() && !template.isLocalDateFormatSet()) { - template.setLocalDateFormat(getLocalDateFormat()); - } - if (isLocalTimeFormatSet() && !template.isLocalTimeFormatSet()) { - template.setLocalTimeFormat(getLocalTimeFormat()); - } - if (isLocalDateTimeFormatSet() && !template.isLocalDateTimeFormatSet()) { - template.setLocalDateTimeFormat(getLocalDateTimeFormat()); - } - if (isOffsetTimeFormatSet() && !template.isOffsetTimeFormatSet()) { - template.setOffsetTimeFormat(getOffsetTimeFormat()); - } - if (isOffsetDateTimeFormatSet() && !template.isOffsetDateTimeFormatSet()) { - template.setOffsetDateTimeFormat(getOffsetDateTimeFormat()); - } - if (isZonedDateTimeFormatSet() && !template.isZonedDateTimeFormatSet()) { - template.setZonedDateTimeFormat(getZonedDateTimeFormat()); - } if (isYearFormatSet() && !template.isYearFormatSet()) { template.setYearFormat(getYearFormat()); } @@ -741,13 +699,6 @@ public final class TemplateConfiguration extends Configurable implements ParserC || isCustomNumberFormatsSet() || isDateFormatSet() || isDateTimeFormatSet() - || isInstantFormatSet() - || isLocalDateFormatSet() - || isLocalTimeFormatSet() - || isLocalDateTimeFormatSet() - || isOffsetTimeFormatSet() - || isOffsetDateTimeFormatSet() - || isZonedDateTimeFormatSet() || isYearFormatSet() || isYearMonthFormatSet() || isLazyImportsSet() diff --git a/src/main/java/freemarker/core/_CoreTemporalUtils.java b/src/main/java/freemarker/core/_CoreTemporalUtils.java index f95906c..ca1032b 100644 --- a/src/main/java/freemarker/core/_CoreTemporalUtils.java +++ b/src/main/java/freemarker/core/_CoreTemporalUtils.java @@ -28,12 +28,10 @@ import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.Year; import java.time.YearMonth; -import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.Temporal; import java.util.Arrays; import java.util.List; -import java.util.stream.Stream; import freemarker.template.Configuration; @@ -105,20 +103,15 @@ public class _CoreTemporalUtils { */ public static String temporalClassToFormatSettingName(Class<? extends Temporal> temporalClass) { temporalClass = normalizeSupportedTemporalClass(temporalClass); - if (temporalClass == Instant.class) { - return Configuration.INSTANT_FORMAT_KEY; + if (temporalClass == Instant.class + || temporalClass == LocalDateTime.class + || temporalClass == ZonedDateTime.class + || temporalClass == OffsetDateTime.class) { + return Configuration.DATETIME_FORMAT_KEY; } else if (temporalClass == LocalDate.class) { - return Configuration.LOCAL_DATE_FORMAT_KEY; - } else if (temporalClass == LocalDateTime.class) { - return Configuration.LOCAL_DATE_TIME_FORMAT_KEY; - } else if (temporalClass == LocalTime.class) { - return Configuration.LOCAL_TIME_FORMAT_KEY; - } else if (temporalClass == OffsetDateTime.class) { - return Configuration.OFFSET_DATE_TIME_FORMAT_KEY; - } else if (temporalClass == OffsetTime.class) { - return Configuration.OFFSET_TIME_FORMAT_KEY; - } else if (temporalClass == ZonedDateTime.class) { - return Configuration.ZONED_DATE_TIME_FORMAT_KEY; + return Configuration.DATE_FORMAT_KEY; + } else if (temporalClass == LocalTime.class || temporalClass == OffsetTime.class) { + return Configuration.TIME_FORMAT_KEY; } else if (temporalClass == YearMonth.class) { return Configuration.YEAR_MONTH_FORMAT_KEY; } else if (temporalClass == Year.class) { @@ -127,5 +120,5 @@ public class _CoreTemporalUtils { throw new IllegalArgumentException("Unsupported temporal class: " + temporalClass.getName()); } } - + } diff --git a/src/test/java/freemarker/core/CoercionToTextualTest.java b/src/test/java/freemarker/core/CoercionToTextualTest.java index efa015a..1f121a8 100644 --- a/src/test/java/freemarker/core/CoercionToTextualTest.java +++ b/src/test/java/freemarker/core/CoercionToTextualTest.java @@ -138,7 +138,6 @@ public class CoercionToTextualTest extends TemplateTest { cfg.setCustomTemporalFormats(Collections.singletonMap("HI", HTMLISOTemplateTemporalFormatFactory.INSTANCE)); cfg.setNumberFormat("@G 3"); cfg.setDateTimeFormat("@HI"); - cfg.setInstantFormat("@HI"); cfg.setBooleanFormat("y,n"); cfg.setTimeZone(DateUtil.UTC); diff --git a/src/test/java/freemarker/core/CoreTemporalUtilTest.java b/src/test/java/freemarker/core/CoreTemporalUtilTest.java index 2eab1f5..36fd589 100644 --- a/src/test/java/freemarker/core/CoreTemporalUtilTest.java +++ b/src/test/java/freemarker/core/CoreTemporalUtilTest.java @@ -19,6 +19,7 @@ package freemarker.core; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.time.chrono.ChronoLocalDate; @@ -62,8 +63,9 @@ public class CoreTemporalUtilTest { Set<String> uniqueSettingNames = new HashSet<>(); for (Class<? extends Temporal> supportedTemporalClass : _CoreTemporalUtils.SUPPORTED_TEMPORAL_CLASSES) { - assertTrue(uniqueSettingNames.add(_CoreTemporalUtils.temporalClassToFormatSettingName(supportedTemporalClass))); + uniqueSettingNames.add(_CoreTemporalUtils.temporalClassToFormatSettingName(supportedTemporalClass)); } + assertThat(uniqueSettingNames.size(), equalTo(_CoreTemporalUtils.SUPPORTED_TEMPORAL_CLASSES.size() - 4)); assertTrue(uniqueSettingNames.stream().allMatch(it -> cfg.getSettingNames(false).contains(it))); try { diff --git a/src/test/java/freemarker/core/TemporalErrorMessagesTest.java b/src/test/java/freemarker/core/TemporalErrorMessagesTest.java index 1448d25..201062d 100644 --- a/src/test/java/freemarker/core/TemporalErrorMessagesTest.java +++ b/src/test/java/freemarker/core/TemporalErrorMessagesTest.java @@ -20,6 +20,7 @@ package freemarker.core; import java.time.LocalTime; +import java.time.YearMonth; import org.junit.Test; @@ -42,19 +43,19 @@ public class TemporalErrorMessagesTest extends TemplateTest { @Test public void testDefaultFormatStringBadFormatString() throws TemplateException { - getConfiguration().setSetting("local_time_format", "ABCDEF"); - addToDataModel("t", LocalTime.now()); - assertErrorContains("${t}", "local_time_format", "ABCDEF"); - assertErrorContains("${t?string}", "local_time_format", "ABCDEF"); + getConfiguration().setSetting("year_month_format", "ABCDEF"); + addToDataModel("t", YearMonth.now()); + assertErrorContains("${t}", "year_month", "ABCDEF"); + assertErrorContains("${t?string}", "year_month", "ABCDEF"); } @Test public void testDefaultFormatStringIncompatibleFormatString() throws TemplateException { - getConfiguration().setSetting("local_time_format", "yyyy-HH"); - addToDataModel("t", LocalTime.now()); + getConfiguration().setSetting("year_month_format", "yyyy-mm"); // Deliberately wrong: "mm" is minutes + addToDataModel("t", YearMonth.now()); // TODO [FREEMARKER-35] Should contain "local_time_format" too - assertErrorContains("${t}", "Failed to format temporal value", "yyyy-HH", "YearOfEra"); - assertErrorContains("${t?string}", "Failed to format temporal value", "yyyy-HH", "YearOfEra"); + assertErrorContains("${t}", "Failed to format temporal value", "yyyy-mm", "MinuteOfHour"); + assertErrorContains("${t?string}", "Failed to format temporal value", "yyyy-mm", "MinuteOfHour"); } } diff --git a/src/test/java/freemarker/core/TemporalFormatTest.java b/src/test/java/freemarker/core/TemporalFormatTest.java index 3e0e3ed..83268e0 100644 --- a/src/test/java/freemarker/core/TemporalFormatTest.java +++ b/src/test/java/freemarker/core/TemporalFormatTest.java @@ -62,7 +62,7 @@ public class TemporalFormatTest { "11:00", formatTemporal( conf -> { - conf.setOffsetTimeFormat("HH:mm"); + conf.setTimeFormat("HH:mm"); conf.setTimeZone(zoneWithoutDST); }, offsetTime)); @@ -72,7 +72,7 @@ public class TemporalFormatTest { "11:00", formatTemporal( conf -> { - conf.setOffsetTimeFormat("HH:mm"); + conf.setTimeFormat("HH:mm"); conf.setTimeZone(zoneWithDST); }, offsetTime)); @@ -85,7 +85,7 @@ public class TemporalFormatTest { "10:00+01", formatTemporal( conf -> { - conf.setOffsetTimeFormat("HH:mmX"); + conf.setTimeFormat("HH:mmX"); conf.setTimeZone(zoneWithDST); }, offsetTime)); @@ -94,7 +94,7 @@ public class TemporalFormatTest { "10:00+01", formatTemporal( conf -> { - conf.setOffsetTimeFormat("HH:mmX"); + conf.setTimeFormat("HH:mmX"); conf.setTimeZone(zoneWithoutDST); }, offsetTime)); @@ -128,9 +128,7 @@ public class TemporalFormatTest { + "2021-12-30 10:30, 2021-12-30 08:30, 2021-12-30 15:30", formatTemporal( conf -> { - conf.setLocalDateTimeFormat("yyyy-MM-dd HH:mm"); - conf.setOffsetDateTimeFormat("yyyy-MM-dd HH:mm"); - conf.setZonedDateTimeFormat("yyyy-MM-dd HH:mm"); + conf.setDateTimeFormat("yyyy-MM-dd HH:mm"); conf.setTimeZone(gbZone); }, summerLocalDateTime, summerOffsetDateTime, summerZonedDateTime, @@ -140,9 +138,7 @@ public class TemporalFormatTest { + "2021-12-30 10:30, 2021-12-30 08:30, 2021-12-30 15:30", formatTemporal( conf -> { - conf.setLocalDateTimeFormat("yyyy-MM-dd HH:mm"); - conf.setOffsetDateTimeFormat("yyyy-MM-dd HH:mm"); - conf.setZonedDateTimeFormat("yyyy-MM-dd HH:mm"); + conf.setDateTimeFormat("yyyy-MM-dd HH:mm"); conf.setTimeZone(DateUtil.UTC); }, summerLocalDateTime, summerOffsetDateTime, summerZonedDateTime, @@ -154,8 +150,7 @@ public class TemporalFormatTest { + "2021-12-30 10:30+02, 2021-12-30 10:30-05", formatTemporal( conf -> { - conf.setOffsetDateTimeFormat("yyyy-MM-dd HH:mmX"); - conf.setZonedDateTimeFormat("yyyy-MM-dd HH:mmX"); + conf.setDateTimeFormat("yyyy-MM-dd HH:mmX"); conf.setTimeZone(gbZone); }, summerOffsetDateTime, summerZonedDateTime, @@ -167,7 +162,7 @@ public class TemporalFormatTest { try { formatTemporal( conf -> { - conf.setLocalDateTimeFormat("yyyy-MM-dd HH:mmX"); + conf.setDateTimeFormat("yyyy-MM-dd HH:mmX"); }, LocalDateTime.of(2021, 10, 30, 1, 2)); fail(); diff --git a/src/test/java/freemarker/core/TemporalFormatTest2.java b/src/test/java/freemarker/core/TemporalFormatTest2.java index c519c68..1329579 100644 --- a/src/test/java/freemarker/core/TemporalFormatTest2.java +++ b/src/test/java/freemarker/core/TemporalFormatTest2.java @@ -61,12 +61,12 @@ public class TemporalFormatTest2 extends TemplateTest { "${d?string.@epoch} ${d?string.@epoch} <#setting locale='de_DE'>${d?string.@epoch}", "123456789 123456789 123456789"); - getConfiguration().setOffsetDateTimeFormat("@epoch"); + getConfiguration().setDateTimeFormat("@epoch"); assertOutput( "${d} ${d?string} <#setting locale='de_DE'>${d}", "123456789 123456789 123456789"); - getConfiguration().setOffsetDateTimeFormat("@htmlIso"); + getConfiguration().setDateTimeFormat("@htmlIso"); assertOutput( "${d} ${d?string} <#setting locale='de_DE'>${d}", "1970-01-02<span class='T'>T</span>10:17:36.789Z " diff --git a/src/test/resources/freemarker/test/templatesuite/templates/temporal.ftl b/src/test/resources/freemarker/test/templatesuite/templates/temporal.ftl index cdadba8..db30dcf 100644 --- a/src/test/resources/freemarker/test/templatesuite/templates/temporal.ftl +++ b/src/test/resources/freemarker/test/templatesuite/templates/temporal.ftl @@ -16,6 +16,7 @@ specific language governing permissions and limitations under the License. --> +<@assertEquals expected="Apr 5, 2003 7:07:08 AM" actual=dateTime?string /> <@assertEquals expected="Apr 5, 2003 7:07:08 AM" actual=instant?string /> <@assertEquals expected="Apr 5, 2003 6:07:08 AM" actual=localDateTime?string /> <@assertEquals expected="Apr 5, 2003" actual=localDate?string /> @@ -117,19 +118,19 @@ <#setting locale="en_US"> -<#setting instantFormat="yyyy MMM dd HH:mm:ss"> +<#setting datetimeFormat="yyyy MMM dd HH:mm:ss"> <@assertEquals expected="2003 Apr 05 01:07:08" actual=instant?string /> -<#setting localDateTimeFormat="yyyy MMM dd HH:mm:ss"> +<#setting datetimeFormat="yyyy MMM dd HH:mm:ss"> <@assertEquals expected="2003 Apr 05 06:07:08" actual=localDateTime?string /> -<#setting localDateFormat="yyyy MMM dd"> +<#setting dateFormat="yyyy MMM dd"> <@assertEquals expected="2003 Apr 05" actual=localDate?string /> -<#setting localDateTimeFormat="HH:mm:ss"> +<#setting datetimeFormat="HH:mm:ss"> <@assertEquals expected="6:07:08 AM" actual=localTime?string /> -<#setting offsetDateTimeFormat="yyyy MMM dd HH:mm:ss"> +<#setting datetimeFormat="yyyy MMM dd HH:mm:ss"> <@assertEquals expected="2003 Apr 05 01:07:08" actual=offsetDateTime?string /> <#setting yearFormat="yyyy"> <@assertEquals expected="2003" actual=year?string /> <#setting yearMonthFormat="yyyy MMM"> <@assertEquals expected="2003 Apr" actual=yearMonth?string /> -<#setting zonedDateTimeFormat="yyyy MMM dd HH:mm:ss"> +<#setting datetimeFormat="yyyy MMM dd HH:mm:ss"> <@assertEquals expected="2003 Apr 05 01:07:08" actual=zonedDateTime?string />
