From f4dfe947020139cd37185d83e4f583169fa8b396 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 18 Oct 2022 12:49:14 +0200 Subject: [PATCH] Fallback to ISO-based default java.time type parsing Closes gh-26985 --- .../standard/TemporalAccessorParser.java | 51 +++++++++++++++++-- .../standard/DateTimeFormattingTests.java | 29 +++++++++++ 2 files changed, 76 insertions(+), 4 deletions(-) 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 ef9cd0a8c053..eec58f9eac3c 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -77,7 +77,8 @@ public TemporalAccessorParser(Class temporalAccessor } TemporalAccessorParser(Class temporalAccessorType, DateTimeFormatter formatter, - @Nullable String[] fallbackPatterns, @Nullable Object source) { + @Nullable String[] fallbackPatterns, @Nullable Object source) { + this.temporalAccessorType = temporalAccessorType; this.formatter = formatter; this.fallbackPatterns = fallbackPatterns; @@ -104,10 +105,19 @@ public TemporalAccessor parse(String text, Locale locale) throws ParseException } } } + else { + // Fallback to ISO-based default java.time type parsing + try { + return defaultParse(text); + } + catch (DateTimeParseException ignoredException) { + // Ignore fallback parsing exception like above + } + } if (this.source != null) { throw new DateTimeParseException( - String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source), - text, ex.getErrorIndex(), ex); + String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source), + text, ex.getErrorIndex(), ex); } // else rethrow original exception throw ex; @@ -148,4 +158,37 @@ else if (MonthDay.class == this.temporalAccessorType) { } } + private TemporalAccessor defaultParse(String text) throws DateTimeParseException { + if (Instant.class == this.temporalAccessorType) { + return Instant.parse(text); + } + else if (LocalDate.class == this.temporalAccessorType) { + return LocalDate.parse(text); + } + else if (LocalTime.class == this.temporalAccessorType) { + return LocalTime.parse(text); + } + else if (LocalDateTime.class == this.temporalAccessorType) { + return LocalDateTime.parse(text); + } + else if (ZonedDateTime.class == this.temporalAccessorType) { + return ZonedDateTime.parse(text); + } + else if (OffsetDateTime.class == this.temporalAccessorType) { + return OffsetDateTime.parse(text); + } + else if (OffsetTime.class == this.temporalAccessorType) { + return OffsetTime.parse(text); + } + else if (YearMonth.class == this.temporalAccessorType) { + return YearMonth.parse(text); + } + else if (MonthDay.class == this.temporalAccessorType) { + return MonthDay.parse(text); + } + else { + throw new IllegalStateException("Unsupported TemporalAccessor type: " + this.temporalAccessorType); + } + } + } 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 4eeba3514647..657bd164e3a3 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 @@ -110,6 +110,15 @@ void testBindLocalDate() { assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("10/31/09"); } + @Test + void testBindLocalDateWithISO() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("localDate", "2009-10-31"); + binder.bind(propertyValues); + assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0); + assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("10/31/09"); + } + @Test void testBindLocalDateWithSpecificStyle() { DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); @@ -207,6 +216,15 @@ void testBindLocalTime() { assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00 PM"); } + @Test + void testBindLocalTimeWithISO() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("localTime", "12:00:00"); + binder.bind(propertyValues); + assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0); + assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00 PM"); + } + @Test void testBindLocalTimeWithSpecificStyle() { DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); @@ -260,6 +278,17 @@ void testBindLocalDateTime() { assertThat(value.endsWith("12:00 PM")).isTrue(); } + @Test + void testBindLocalDateTimeWithISO() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("localDateTime", "2009-10-31T12:00:00"); + binder.bind(propertyValues); + assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0); + String value = binder.getBindingResult().getFieldValue("localDateTime").toString(); + assertThat(value.startsWith("10/31/09")).isTrue(); + assertThat(value.endsWith("12:00 PM")).isTrue(); + } + @Test void testBindLocalDateTimeAnnotated() { MutablePropertyValues propertyValues = new MutablePropertyValues();