You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2021/12/15 17:16:43 UTC
[freemarker] 01/02: [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.)
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 <dd...@apache.org>
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 />