Skip to content

Commit 7b1fcfc

Browse files
committed
Consistently strict parsing of date overflows (using java.time's strict resolution style)
Issue: SPR-13567
1 parent 028a690 commit 7b1fcfc

File tree

5 files changed

+41
-6
lines changed

5 files changed

+41
-6
lines changed

spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -75,6 +75,12 @@
7575
* <p>Defaults to empty String, indicating no custom pattern String has been specified.
7676
* Set this attribute when you wish to format your field in accordance with a custom
7777
* date time pattern not represented by a style or ISO format.
78+
* <p>Note: This pattern follows the original {@link java.text.SimpleDateFormat} style,
79+
* as also supported by Joda-Time, with strict parsing semantics towards overflows
80+
* (e.g. rejecting a Feb 29 value for a non-leap-year). As a consequence, 'yy'
81+
* characters indicate a year in the traditional style, not a "year-of-era" as in the
82+
* {@link java.time.format.DateTimeFormatter} specification (i.e. 'yy' turns into 'uu'
83+
* when going through that {@code DateTimeFormatter} with strict resolution mode).
7884
*/
7985
String pattern() default "";
8086

spring-context/src/main/java/org/springframework/format/datetime/standard/DateTimeFormatterFactory.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import java.time.format.DateTimeFormatter;
2020
import java.time.format.FormatStyle;
21+
import java.time.format.ResolverStyle;
2122
import java.util.TimeZone;
2223

2324
import org.springframework.format.annotation.DateTimeFormat.ISO;
@@ -174,7 +175,11 @@ public DateTimeFormatter createDateTimeFormatter() {
174175
public DateTimeFormatter createDateTimeFormatter(DateTimeFormatter fallbackFormatter) {
175176
DateTimeFormatter dateTimeFormatter = null;
176177
if (StringUtils.hasLength(this.pattern)) {
177-
dateTimeFormatter = DateTimeFormatter.ofPattern(this.pattern);
178+
// Using strict parsing to align with Joda-Time and standard DateFormat behavior:
179+
// otherwise, an overflow like e.g. Feb 29 for a non-leap-year wouldn't get rejected.
180+
// However, with strict parsing, a year digit needs to be specified as 'u'...
181+
String patternToUse = this.pattern.replace("yy", "uu");
182+
dateTimeFormatter = DateTimeFormatter.ofPattern(patternToUse).withResolverStyle(ResolverStyle.STRICT);
178183
}
179184
else if (this.iso != null && this.iso != ISO.NONE) {
180185
switch (this.iso) {

spring-context/src/test/java/org/springframework/format/datetime/DateFormattingTests.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -148,6 +148,14 @@ public void testBindDateAnnotatedPattern() {
148148
assertEquals("10/31/09 1:05", binder.getBindingResult().getFieldValue("dateAnnotatedPattern"));
149149
}
150150

151+
@Test
152+
public void testBindDateTimeOverflow() {
153+
MutablePropertyValues propertyValues = new MutablePropertyValues();
154+
propertyValues.add("dateAnnotatedPattern", "02/29/09 12:00 PM");
155+
binder.bind(propertyValues);
156+
assertEquals(1, binder.getBindingResult().getErrorCount());
157+
}
158+
151159
@Test
152160
public void testBindISODate() {
153161
MutablePropertyValues propertyValues = new MutablePropertyValues();

spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -317,6 +317,14 @@ public void testBindDateTimeAnnotatedPattern() {
317317
assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("dateTimeAnnotatedPattern"));
318318
}
319319

320+
@Test
321+
public void testBindDateTimeOverflow() {
322+
MutablePropertyValues propertyValues = new MutablePropertyValues();
323+
propertyValues.add("dateTimeAnnotatedPattern", "02/29/09 12:00 PM");
324+
binder.bind(propertyValues);
325+
assertEquals(1, binder.getBindingResult().getErrorCount());
326+
}
327+
320328
@Test
321329
public void testBindDateTimeAnnotatedDefault() {
322330
MutablePropertyValues propertyValues = new MutablePropertyValues();

spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -292,6 +292,14 @@ public void testBindDateTimeAnnotatedPattern() {
292292
assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("dateTimeAnnotatedPattern"));
293293
}
294294

295+
@Test
296+
public void testBindDateTimeOverflow() {
297+
MutablePropertyValues propertyValues = new MutablePropertyValues();
298+
propertyValues.add("dateTimeAnnotatedPattern", "02/29/09 12:00 PM");
299+
binder.bind(propertyValues);
300+
assertEquals(1, binder.getBindingResult().getErrorCount());
301+
}
302+
295303
@Test
296304
public void testBindISODate() {
297305
MutablePropertyValues propertyValues = new MutablePropertyValues();

0 commit comments

Comments
 (0)