diff --git a/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java b/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java index 0b3b351405..54168efb5f 100644 --- a/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java +++ b/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java @@ -26,17 +26,20 @@ import java.lang.annotation.Target; * Declares that a field or method parameter should be formatted as a date or time. * *

Supports formatting by style pattern, ISO date time pattern, or custom format pattern string. - * Can be applied to {@code java.util.Date}, {@code java.util.Calendar}, {@code Long} (for - * millisecond timestamps) as well as JSR-310 java.time and Joda-Time value types. + * Can be applied to {@link java.util.Date}, {@link java.util.Calendar}, {@link Long} (for + * millisecond timestamps) as well as JSR-310 {@code java.time} and Joda-Time value types. * - *

For style-based formatting, set the {@link #style} attribute to be the style pattern code. + *

For style-based formatting, set the {@link #style} attribute to the desired style pattern code. * The first character of the code is the date style, and the second character is the time style. * Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full. - * A date or time may be omitted by specifying the style character '-'. + * The date or time may be omitted by specifying the style character '-' — for example, + * 'M-' specifies a medium format for the date with no time. * - *

For ISO-based formatting, set the {@link #iso} attribute to be the desired {@link ISO} format, - * such as {@link ISO#DATE}. For custom formatting, set the {@link #pattern} attribute to be the - * DateTime pattern, such as {@code "yyyy/MM/dd hh:mm:ss a"}. + *

For ISO-based formatting, set the {@link #iso} attribute to the desired {@link ISO} format, + * such as {@link ISO#DATE}. + * + *

For custom formatting, set the {@link #pattern} attribute to a date time pattern, such as + * {@code "yyyy/MM/dd hh:mm:ss a"}. * *

Each attribute is mutually exclusive, so only set one attribute per annotation instance * (the one most convenient for your formatting needs). @@ -48,8 +51,19 @@ import java.lang.annotation.Target; * with a style code of 'SS' (short date, short time). * * + *

Time Zones

+ *

Whenever the {@link #style} or {@link #pattern} attribute is used, the + * {@linkplain java.util.TimeZone#getDefault() default time zone} of the JVM will + * be used when formatting {@link java.util.Date} values. Whenever the {@link #iso} + * attribute is used when formatting {@link java.util.Date} values, {@code UTC} + * will be used as the time zone. The same time zone will be applied to any + * {@linkplain #fallbackPatterns fallback patterns} as well. In order to enforce + * consistent use of {@code UTC} as the time zone, you can bootstrap the JVM with + * {@code -Duser.timezone=UTC}. + * * @author Keith Donald * @author Juergen Hoeller + * @author Sam Brannen * @since 3.0 * @see java.time.format.DateTimeFormatter * @see org.joda.time.format.DateTimeFormat @@ -60,34 +74,59 @@ import java.lang.annotation.Target; public @interface DateTimeFormat { /** - * The style pattern to use to format the field. - *

Defaults to 'SS' for short date time. Set this attribute when you wish to format - * your field in accordance with a common style other than the default style. + * The style pattern to use to format the field or method parameter. + *

Defaults to 'SS' for short date, short time. Set this attribute when you + * wish to format your field or method parameter in accordance with a common + * style other than the default style. + * @see #fallbackPatterns */ String style() default "SS"; /** - * The ISO pattern to use to format the field. - *

The possible ISO patterns are defined in the {@link ISO} enum. + * The ISO pattern to use to format the field or method parameter. + *

Supported ISO patterns are defined in the {@link ISO} enum. *

Defaults to {@link ISO#NONE}, indicating this attribute should be ignored. - * Set this attribute when you wish to format your field in accordance with an ISO format. + * Set this attribute when you wish to format your field or method parameter + * in accordance with an ISO format. + * @see #fallbackPatterns */ ISO iso() default ISO.NONE; /** - * The custom pattern to use to format the field. - *

Defaults to empty String, indicating no custom pattern String has been specified. - * Set this attribute when you wish to format your field in accordance with a custom - * date time pattern not represented by a style or ISO format. + * The custom pattern to use to format the field or method parameter. + *

Defaults to empty String, indicating no custom pattern String has been + * specified. Set this attribute when you wish to format your field or method + * parameter in accordance with a custom date time pattern not represented by + * a style or ISO format. *

Note: This pattern follows the original {@link java.text.SimpleDateFormat} style, * as also supported by Joda-Time, with strict parsing semantics towards overflows * (e.g. rejecting a Feb 29 value for a non-leap-year). As a consequence, 'yy' * characters indicate a year in the traditional style, not a "year-of-era" as in the * {@link java.time.format.DateTimeFormatter} specification (i.e. 'yy' turns into 'uu' - * when going through that {@code DateTimeFormatter} with strict resolution mode). + * when going through a {@code DateTimeFormatter} with strict resolution mode). + * @see #fallbackPatterns */ String pattern() default ""; + /** + * The set of custom patterns to use as a fallback in case parsing fails for + * the primary {@link #pattern}, {@link #iso}, or {@link #style} attribute. + *

For example, if you wish to use the ISO date format for parsing and + * printing but allow for lenient parsing of user input for various date + * formats, you could configure something similar to the following. + *

+	 * {@literal @}DateTimeFormat(iso = ISO.DATE, fallbackPatterns = { "M/d/yy", "dd.MM.yyyy" })
+	 * 
+ *

Fallback patterns are only used for parsing. They are not used for + * printing the value as a String. The primary {@link #pattern}, {@link #iso}, + * or {@link #style} attribute is always used for printing. For details on + * which time zone is used for fallback patterns, see the + * {@linkplain DateTimeFormat class-level documentation}. + *

Fallback patterns are not supported for Joda-Time value types. + * @since 5.3.5 + */ + String[] fallbackPatterns() default {}; + /** * Common ISO date time format patterns. @@ -95,20 +134,20 @@ public @interface DateTimeFormat { enum ISO { /** - * The most common ISO Date Format {@code yyyy-MM-dd}, - * e.g. "2000-10-31". + * The most common ISO Date Format {@code yyyy-MM-dd} — for example, + * "2000-10-31". */ DATE, /** - * The most common ISO Time Format {@code HH:mm:ss.SSSXXX}, - * e.g. "01:30:00.000-05:00". + * The most common ISO Time Format {@code HH:mm:ss.SSSXXX} — for example, + * "01:30:00.000-05:00". */ TIME, /** - * The most common ISO DateTime Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSXXX}, - * e.g. "2000-10-31T01:30:00.000-05:00". + * The most common ISO Date Time Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSXXX} + * — for example, "2000-10-31T01:30:00.000-05:00". */ DATE_TIME, diff --git a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java index bef0684837..d69e9c15e5 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,17 +27,21 @@ import java.util.Map; import java.util.TimeZone; import org.springframework.format.Formatter; +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.lang.Nullable; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** * A formatter for {@link java.util.Date} types. - * Allows the configuration of an explicit date pattern and locale. + *

Supports the configuration of an explicit date time pattern, timezone, + * locale, and fallback date time patterns for lenient parsing. * * @author Keith Donald * @author Juergen Hoeller * @author Phillip Webb + * @author Sam Brannen * @since 3.0 * @see SimpleDateFormat */ @@ -56,9 +60,15 @@ public class DateFormatter implements Formatter { } + @Nullable + private Object source; + @Nullable private String pattern; + @Nullable + private String[] fallbackPatterns; + private int style = DateFormat.DEFAULT; @Nullable @@ -74,19 +84,33 @@ public class DateFormatter implements Formatter { /** - * Create a new default DateFormatter. + * Create a new default {@code DateFormatter}. */ public DateFormatter() { } /** - * Create a new DateFormatter for the given date pattern. + * Create a new {@code DateFormatter} for the given date time pattern. */ public DateFormatter(String pattern) { this.pattern = pattern; } + /** + * Set the source of the configuration for this {@code DateFormatter} — + * for example, an instance of the {@link DateTimeFormat @DateTimeFormat} + * annotation if such an annotation was used to configure this {@code DateFormatter}. + *

The supplied source object will only be used for descriptive purposes + * by invoking its {@code toString()} method — for example, when + * generating an exception message to provide further context. + * @param source the source of the configuration + * @since 5.3.5 + */ + public void setSource(Object source) { + this.source = source; + } + /** * Set the pattern to use to format date values. *

If not specified, DateFormat's default style will be used. @@ -96,7 +120,19 @@ public class DateFormatter implements Formatter { } /** - * Set the ISO format used for this date. + * Set additional patterns to use as a fallback in case parsing fails for the + * configured {@linkplain #setPattern pattern}, {@linkplain #setIso ISO format}, + * {@linkplain #setStyle style}, or {@linkplain #setStylePattern style pattern}. + * @param fallbackPatterns the fallback parsing patterns + * @since 5.3.5 + * @see DateTimeFormat#fallbackPatterns() + */ + public void setFallbackPatterns(String... fallbackPatterns) { + this.fallbackPatterns = fallbackPatterns; + } + + /** + * Set the ISO format to use to format date values. * @param iso the {@link ISO} format * @since 3.2 */ @@ -105,7 +141,7 @@ public class DateFormatter implements Formatter { } /** - * Set the style to use to format date values. + * Set the {@link DateFormat} style to use to format date values. *

If not specified, DateFormat's default style will be used. * @see DateFormat#DEFAULT * @see DateFormat#SHORT @@ -118,8 +154,10 @@ public class DateFormatter implements Formatter { } /** - * Set the two character to use to format date values. The first character used for - * the date style, the second is for the time style. Supported characters are + * Set the two characters to use to format date values. + *

The first character is used for the date style; the second is used for + * the time style. + *

Supported characters: *