diff --git a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java index b043acf6fb24..7fb2c5548250 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java @@ -25,8 +25,10 @@ import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.joda.time.LocalTime; +import org.joda.time.MonthDay; import org.joda.time.ReadableInstant; import org.joda.time.ReadablePartial; +import org.joda.time.YearMonth; import org.joda.time.format.DateTimeFormatter; import org.springframework.context.support.EmbeddedValueResolutionSupport; @@ -42,6 +44,7 @@ * * @author Keith Donald * @author Juergen Hoeller + * @author Kazuki Shimizu * @since 3.0 * @see DateTimeFormat */ @@ -65,6 +68,8 @@ public class JodaDateTimeFormatAnnotationFormatterFactory extends EmbeddedValueR fieldTypes.add(Date.class); fieldTypes.add(Calendar.class); fieldTypes.add(Long.class); + fieldTypes.add(YearMonth.class); + fieldTypes.add(MonthDay.class); FIELD_TYPES = Collections.unmodifiableSet(fieldTypes); } @@ -101,6 +106,12 @@ else if (LocalTime.class == fieldType) { else if (LocalDateTime.class == fieldType) { return new LocalDateTimeParser(getFormatter(annotation, fieldType)); } + else if (YearMonth.class == fieldType) { + return new YearMonthParser(getFormatter(annotation, fieldType)); + } + else if (MonthDay.class == fieldType) { + return new MonthDayParser(getFormatter(annotation, fieldType)); + } else { return new DateTimeParser(getFormatter(annotation, fieldType)); } diff --git a/spring-context/src/main/java/org/springframework/format/datetime/joda/MonthDayParser.java b/spring-context/src/main/java/org/springframework/format/datetime/joda/MonthDayParser.java new file mode 100644 index 000000000000..1c86ffaad45e --- /dev/null +++ b/spring-context/src/main/java/org/springframework/format/datetime/joda/MonthDayParser.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.format.datetime.joda; + +import org.joda.time.MonthDay; +import org.joda.time.format.DateTimeFormatter; +import org.springframework.format.Parser; + +import java.text.ParseException; +import java.util.Locale; + +/** + * Parses Joda {@link MonthDay} instances using a + * {@link DateTimeFormatter}. + * + * @author Kazuki Shimizu + * @since 4.3.4 + */ +public final class MonthDayParser implements Parser { + + private final DateTimeFormatter formatter; + + /** + * Create a new DateTimeParser. + * @param formatter the Joda DateTimeFormatter instance + */ + public MonthDayParser(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + @Override + public MonthDay parse(String text, Locale locale) throws ParseException { + return MonthDay.parse(text, JodaTimeContextHolder.getFormatter(this.formatter, locale)); + } + +} diff --git a/spring-context/src/main/java/org/springframework/format/datetime/joda/YearMonthParser.java b/spring-context/src/main/java/org/springframework/format/datetime/joda/YearMonthParser.java new file mode 100644 index 000000000000..059c3028fbf6 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/format/datetime/joda/YearMonthParser.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.format.datetime.joda; + +import org.joda.time.YearMonth; +import org.joda.time.format.DateTimeFormatter; +import org.springframework.format.Parser; + +import java.text.ParseException; +import java.util.Locale; + +/** + * Parses Joda {@link YearMonth} instances using a + * {@link DateTimeFormatter}. + * + * @author Kazuki Shimizu + * @since 4.3.4 + */ +public final class YearMonthParser implements Parser { + + private final DateTimeFormatter formatter; + + /** + * Create a new DateTimeParser. + * @param formatter the Joda DateTimeFormatter instance + */ + public YearMonthParser(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + @Override + public YearMonth parse(String text, Locale locale) throws ParseException { + return YearMonth.parse(text, JodaTimeContextHolder.getFormatter(this.formatter, locale)); + } + +} diff --git a/spring-context/src/main/java/org/springframework/format/datetime/standard/Jsr310DateTimeFormatAnnotationFormatterFactory.java b/spring-context/src/main/java/org/springframework/format/datetime/standard/Jsr310DateTimeFormatAnnotationFormatterFactory.java index 932c52887f45..03405a59f9e0 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/standard/Jsr310DateTimeFormatAnnotationFormatterFactory.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/standard/Jsr310DateTimeFormatAnnotationFormatterFactory.java @@ -21,6 +21,8 @@ import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.time.MonthDay; +import java.time.YearMonth; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; @@ -39,6 +41,7 @@ * JSR-310 java.time package in JDK 8. * * @author Juergen Hoeller + * @author Kazuki Shimizu * @since 4.0 * @see org.springframework.format.annotation.DateTimeFormat */ @@ -56,6 +59,8 @@ public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValu fieldTypes.add(ZonedDateTime.class); fieldTypes.add(OffsetDateTime.class); fieldTypes.add(OffsetTime.class); + fieldTypes.add(YearMonth.class); + fieldTypes.add(MonthDay.class); FIELD_TYPES = Collections.unmodifiableSet(fieldTypes); } diff --git a/spring-context/src/main/java/org/springframework/format/datetime/standard/TemporalAccessorParser.java b/spring-context/src/main/java/org/springframework/format/datetime/standard/TemporalAccessorParser.java index e78584e646cf..38b429e6518d 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/standard/TemporalAccessorParser.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/standard/TemporalAccessorParser.java @@ -20,8 +20,10 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.MonthDay; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.time.YearMonth; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; @@ -34,6 +36,7 @@ * using a {@link java.time.format.DateTimeFormatter}) (the contextual one, if available). * * @author Juergen Hoeller + * @author Kazuki Shimizu * @since 4.0 * @see DateTimeContextHolder#getFormatter * @see java.time.LocalDate#parse(CharSequence, java.time.format.DateTimeFormatter) @@ -42,6 +45,8 @@ * @see java.time.ZonedDateTime#parse(CharSequence, java.time.format.DateTimeFormatter) * @see java.time.OffsetDateTime#parse(CharSequence, java.time.format.DateTimeFormatter) * @see java.time.OffsetTime#parse(CharSequence, java.time.format.DateTimeFormatter) + * @see java.time.YearMonth#parse(CharSequence, java.time.format.DateTimeFormatter) + * @see java.time.MonthDay#parse(CharSequence, java.time.format.DateTimeFormatter) */ public final class TemporalAccessorParser implements Parser { @@ -83,6 +88,12 @@ else if (OffsetDateTime.class == this.temporalAccessorType) { else if (OffsetTime.class == this.temporalAccessorType) { return OffsetTime.parse(text, formatterToUse); } + else if (YearMonth.class == this.temporalAccessorType) { + return YearMonth.parse(text, formatterToUse); + } + else if (MonthDay.class == this.temporalAccessorType) { + return MonthDay.parse(text, formatterToUse); + } else { throw new IllegalStateException("Unsupported TemporalAccessor type: " + this.temporalAccessorType); } diff --git a/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java b/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java index 3ac3e45f1b9f..b15044d6d099 100644 --- a/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java +++ b/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java @@ -53,6 +53,7 @@ * @author Keith Donald * @author Juergen Hoeller * @author Phillip Webb + * @author Kazuki Shimizu */ public class JodaTimeFormattingTests { @@ -495,6 +496,16 @@ public void testBindYearMonth() { assertTrue(binder.getBindingResult().getFieldValue("yearMonth").toString().equals("2007-12")); } + @Test + public void testBindYearMonthAnnotatedPattern() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("yearMonthAnnotatedPattern", "12/2007"); + binder.bind(propertyValues); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertTrue(binder.getBindingResult().getFieldValue("yearMonthAnnotatedPattern").toString().equals("12/2007")); + assertEquals(YearMonth.parse("2007-12"), binder.getBindingResult().getRawFieldValue("yearMonthAnnotatedPattern")); + } + @Test public void testBindMonthDay() { MutablePropertyValues propertyValues = new MutablePropertyValues(); @@ -504,6 +515,15 @@ public void testBindMonthDay() { assertTrue(binder.getBindingResult().getFieldValue("monthDay").toString().equals("--12-03")); } + @Test + public void testBindMonthDayAnnotatedPattern() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("monthDayAnnotatedPattern", "1/3"); + binder.bind(propertyValues); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertTrue(binder.getBindingResult().getFieldValue("monthDayAnnotatedPattern").toString().equals("1/3")); + assertEquals(MonthDay.parse("--01-03"), binder.getBindingResult().getRawFieldValue("monthDayAnnotatedPattern")); + } @SuppressWarnings("unused") private static class JodaTimeBean { @@ -568,8 +588,14 @@ private static class JodaTimeBean { private YearMonth yearMonth; + @DateTimeFormat(pattern = "MM/yyyy") + private YearMonth yearMonthAnnotatedPattern; + private MonthDay monthDay; + @DateTimeFormat(pattern = "M/d") + private MonthDay monthDayAnnotatedPattern; + private final List children = new ArrayList<>(); public LocalDate getLocalDate() { @@ -757,6 +783,14 @@ public void setYearMonth(YearMonth yearMonth) { this.yearMonth = yearMonth; } + public YearMonth getYearMonthAnnotatedPattern() { + return yearMonthAnnotatedPattern; + } + + public void setYearMonthAnnotatedPattern(YearMonth yearMonthAnnotatedPattern) { + this.yearMonthAnnotatedPattern = yearMonthAnnotatedPattern; + } + public MonthDay getMonthDay() { return monthDay; } @@ -765,6 +799,14 @@ public void setMonthDay(MonthDay monthDay) { this.monthDay = monthDay; } + public MonthDay getMonthDayAnnotatedPattern() { + return monthDayAnnotatedPattern; + } + + public void setMonthDayAnnotatedPattern(MonthDay monthDayAnnotatedPattern) { + this.monthDayAnnotatedPattern = monthDayAnnotatedPattern; + } + public List getChildren() { return children; } diff --git a/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java b/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java index 6b78ce0d712c..cb9dd9df817d 100644 --- a/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java +++ b/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java @@ -51,6 +51,7 @@ * @author Keith Donald * @author Juergen Hoeller * @author Phillip Webb + * @author Kazuki Shimizu */ public class DateTimeFormattingTests { @@ -372,6 +373,16 @@ public void testBindYearMonth() { assertTrue(binder.getBindingResult().getFieldValue("yearMonth").toString().equals("2007-12")); } + @Test + public void testBindYearMonthAnnotatedPattern() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("yearMonthAnnotatedPattern", "12/2007"); + binder.bind(propertyValues); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertTrue(binder.getBindingResult().getFieldValue("yearMonthAnnotatedPattern").toString().equals("12/2007")); + assertEquals(YearMonth.parse("2007-12"), binder.getBindingResult().getRawFieldValue("yearMonthAnnotatedPattern")); + } + @Test public void testBindMonthDay() { MutablePropertyValues propertyValues = new MutablePropertyValues(); @@ -381,6 +392,16 @@ public void testBindMonthDay() { assertTrue(binder.getBindingResult().getFieldValue("monthDay").toString().equals("--12-03")); } + @Test + public void testBindMonthDayAnnotatedPattern() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("monthDayAnnotatedPattern", "1/3"); + binder.bind(propertyValues); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertTrue(binder.getBindingResult().getFieldValue("monthDayAnnotatedPattern").toString().equals("1/3")); + assertEquals(MonthDay.parse("--01-03"), binder.getBindingResult().getRawFieldValue("monthDayAnnotatedPattern")); + } + public static class DateTimeBean { @@ -419,6 +440,12 @@ public static class DateTimeBean { private YearMonth yearMonth; + @DateTimeFormat(pattern="MM/uuuu") + private YearMonth yearMonthAnnotatedPattern; + + @DateTimeFormat(pattern="M/d") + private MonthDay monthDayAnnotatedPattern; + private MonthDay monthDay; private final List children = new ArrayList<>(); @@ -535,6 +562,14 @@ public void setYearMonth(YearMonth yearMonth) { this.yearMonth = yearMonth; } + public YearMonth getYearMonthAnnotatedPattern() { + return yearMonthAnnotatedPattern; + } + + public void setYearMonthAnnotatedPattern(YearMonth yearMonthAnnotatedPattern) { + this.yearMonthAnnotatedPattern = yearMonthAnnotatedPattern; + } + public MonthDay getMonthDay() { return monthDay; } @@ -543,6 +578,14 @@ public void setMonthDay(MonthDay monthDay) { this.monthDay = monthDay; } + public MonthDay getMonthDayAnnotatedPattern() { + return monthDayAnnotatedPattern; + } + + public void setMonthDayAnnotatedPattern(MonthDay monthDayAnnotatedPattern) { + this.monthDayAnnotatedPattern = monthDayAnnotatedPattern; + } + public List getChildren() { return children; }