DateTimeFormattingTests.java 26.7 KB
Newer Older
1
/*
2
 * Copyright 2002-2021 the original author or authors.
3 4 5 6 7
 *
 * 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
 *
S
Spring Operator 已提交
8
 *      https://www.apache.org/licenses/LICENSE-2.0
9 10 11 12 13 14 15 16 17 18
 *
 * 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.standard;

19
import java.time.Duration;
20 21 22 23
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
24
import java.time.Month;
25
import java.time.MonthDay;
26
import java.time.Period;
27
import java.time.Year;
28
import java.time.YearMonth;
29 30
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
31
import java.time.format.DateTimeParseException;
32 33
import java.time.format.FormatStyle;
import java.util.ArrayList;
34
import java.util.Date;
35
import java.util.GregorianCalendar;
36 37
import java.util.List;
import java.util.Locale;
38
import java.util.TimeZone;
39

40 41
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
42
import org.junit.jupiter.api.Nested;
43
import org.junit.jupiter.api.Test;
44 45
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
46 47

import org.springframework.beans.MutablePropertyValues;
48
import org.springframework.beans.TypeMismatchException;
49
import org.springframework.context.i18n.LocaleContextHolder;
50
import org.springframework.core.convert.ConversionFailedException;
51 52 53 54
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.format.support.FormattingConversionService;
55
import org.springframework.validation.BindingResult;
56
import org.springframework.validation.DataBinder;
57
import org.springframework.validation.FieldError;
58

59
import static org.assertj.core.api.Assertions.assertThat;
60 61 62 63 64

/**
 * @author Keith Donald
 * @author Juergen Hoeller
 * @author Phillip Webb
65
 * @author Sam Brannen
66
 */
67
class DateTimeFormattingTests {
68

69
	private final FormattingConversionService conversionService = new FormattingConversionService();
70 71 72

	private DataBinder binder;

73

74
	@BeforeEach
75
	void setup() {
76
		DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
77
		setup(registrar);
78 79
	}

80
	private void setup(DateTimeFormatterRegistrar registrar) {
81 82 83 84 85 86 87 88 89 90 91 92 93 94
		DefaultConversionService.addDefaultConverters(conversionService);
		registrar.registerFormatters(conversionService);

		DateTimeBean bean = new DateTimeBean();
		bean.getChildren().add(new DateTimeBean());
		binder = new DataBinder(bean);
		binder.setConversionService(conversionService);

		LocaleContextHolder.setLocale(Locale.US);
		DateTimeContext context = new DateTimeContext();
		context.setTimeZone(ZoneId.of("-05:00"));
		DateTimeContextHolder.setDateTimeContext(context);
	}

95
	@AfterEach
96
	void cleanup() {
97 98 99 100
		LocaleContextHolder.setLocale(null);
		DateTimeContextHolder.setDateTimeContext(null);
	}

101

102
	@Test
103
	void testBindLocalDate() {
104 105 106
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("localDate", "10/31/09");
		binder.bind(propertyValues);
107 108
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("10/31/09");
109 110 111
	}

	@Test
112
	void testBindLocalDateWithSpecificStyle() {
113 114
		DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
		registrar.setDateStyle(FormatStyle.LONG);
115
		setup(registrar);
116 117 118
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("localDate", "October 31, 2009");
		binder.bind(propertyValues);
119 120
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("October 31, 2009");
121 122 123
	}

	@Test
124
	void testBindLocalDateWithSpecificFormatter() {
125 126
		DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
		registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
127
		setup(registrar);
128 129 130
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("localDate", "20091031");
		binder.bind(propertyValues);
131 132
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("20091031");
133 134 135
	}

	@Test
136
	void testBindLocalDateArray() {
137
		MutablePropertyValues propertyValues = new MutablePropertyValues();
J
Juergen Hoeller 已提交
138
		propertyValues.add("localDate", new String[] {"10/31/09"});
139
		binder.bind(propertyValues);
140
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
141 142 143
	}

	@Test
144
	void testBindLocalDateAnnotated() {
145
		MutablePropertyValues propertyValues = new MutablePropertyValues();
146
		propertyValues.add("styleLocalDate", "Oct 31, 2009");
147
		binder.bind(propertyValues);
148
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
149
		assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct 31, 2009");
150 151 152
	}

	@Test
153
	void testBindLocalDateAnnotatedWithError() {
154
		MutablePropertyValues propertyValues = new MutablePropertyValues();
155
		propertyValues.add("styleLocalDate", "Oct -31, 2009");
156
		binder.bind(propertyValues);
157 158
		assertThat(binder.getBindingResult().getFieldErrorCount("styleLocalDate")).isEqualTo(1);
		assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct -31, 2009");
159 160 161
	}

	@Test
162
	void testBindNestedLocalDateAnnotated() {
163
		MutablePropertyValues propertyValues = new MutablePropertyValues();
164
		propertyValues.add("children[0].styleLocalDate", "Oct 31, 2009");
165
		binder.bind(propertyValues);
166
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
167
		assertThat(binder.getBindingResult().getFieldValue("children[0].styleLocalDate")).isEqualTo("Oct 31, 2009");
168 169 170
	}

	@Test
171
	void testBindLocalDateAnnotatedWithDirectFieldAccess() {
172 173
		binder.initDirectFieldAccess();
		MutablePropertyValues propertyValues = new MutablePropertyValues();
174
		propertyValues.add("styleLocalDate", "Oct 31, 2009");
175
		binder.bind(propertyValues);
176
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
177
		assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct 31, 2009");
178 179 180
	}

	@Test
181
	void testBindLocalDateAnnotatedWithDirectFieldAccessAndError() {
182 183
		binder.initDirectFieldAccess();
		MutablePropertyValues propertyValues = new MutablePropertyValues();
184
		propertyValues.add("styleLocalDate", "Oct -31, 2009");
185
		binder.bind(propertyValues);
186 187
		assertThat(binder.getBindingResult().getFieldErrorCount("styleLocalDate")).isEqualTo(1);
		assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct -31, 2009");
188 189
	}

190
	@Test
191
	void testBindLocalDateFromJavaUtilCalendar() {
192 193 194
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("localDate", new GregorianCalendar(2009, 9, 31, 0, 0));
		binder.bind(propertyValues);
195 196
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("10/31/09");
197 198
	}

199
	@Test
200
	void testBindLocalTime() {
201 202 203
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("localTime", "12:00 PM");
		binder.bind(propertyValues);
204 205
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00 PM");
206 207 208
	}

	@Test
209
	void testBindLocalTimeWithSpecificStyle() {
210 211
		DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
		registrar.setTimeStyle(FormatStyle.MEDIUM);
212
		setup(registrar);
213 214 215
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("localTime", "12:00:00 PM");
		binder.bind(propertyValues);
216 217
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00:00 PM");
218 219 220
	}

	@Test
221
	void testBindLocalTimeWithSpecificFormatter() {
222 223
		DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
		registrar.setTimeFormatter(DateTimeFormatter.ofPattern("HHmmss"));
224
		setup(registrar);
225 226 227
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("localTime", "130000");
		binder.bind(propertyValues);
228 229
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("130000");
230 231 232
	}

	@Test
233
	void testBindLocalTimeAnnotated() {
234
		MutablePropertyValues propertyValues = new MutablePropertyValues();
235
		propertyValues.add("styleLocalTime", "12:00:00 PM");
236
		binder.bind(propertyValues);
237
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
238
		assertThat(binder.getBindingResult().getFieldValue("styleLocalTime")).isEqualTo("12:00:00 PM");
239 240
	}

241
	@Test
242
	void testBindLocalTimeFromJavaUtilCalendar() {
243 244 245
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("localTime", new GregorianCalendar(1970, 0, 0, 12, 0));
		binder.bind(propertyValues);
246 247
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00 PM");
248 249
	}

250
	@Test
251
	void testBindLocalDateTime() {
252
		MutablePropertyValues propertyValues = new MutablePropertyValues();
253
		propertyValues.add("localDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
254
		binder.bind(propertyValues);
255
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
256
		String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
257 258
		assertThat(value.startsWith("10/31/09")).isTrue();
		assertThat(value.endsWith("12:00 PM")).isTrue();
259 260 261
	}

	@Test
262
	void testBindLocalDateTimeAnnotated() {
263
		MutablePropertyValues propertyValues = new MutablePropertyValues();
264
		propertyValues.add("styleLocalDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
265
		binder.bind(propertyValues);
266
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
267
		String value = binder.getBindingResult().getFieldValue("styleLocalDateTime").toString();
268 269
		assertThat(value.startsWith("Oct 31, 2009")).isTrue();
		assertThat(value.endsWith("12:00:00 PM")).isTrue();
270 271
	}

272
	@Test
273
	void testBindLocalDateTimeFromJavaUtilCalendar() {
274 275 276
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("localDateTime", new GregorianCalendar(2009, 9, 31, 12, 0));
		binder.bind(propertyValues);
277
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
278
		String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
279 280
		assertThat(value.startsWith("10/31/09")).isTrue();
		assertThat(value.endsWith("12:00 PM")).isTrue();
281 282
	}

283
	@Test
284
	void testBindDateTimeWithSpecificStyle() {
285 286
		DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
		registrar.setDateTimeStyle(FormatStyle.MEDIUM);
287
		setup(registrar);
288
		MutablePropertyValues propertyValues = new MutablePropertyValues();
289
		propertyValues.add("localDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
290
		binder.bind(propertyValues);
291
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
292
		String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
293 294
		assertThat(value.startsWith("Oct 31, 2009")).isTrue();
		assertThat(value.endsWith("12:00:00 PM")).isTrue();
295 296 297
	}

	@Test
298
	void testBindPatternLocalDateTime() {
299
		MutablePropertyValues propertyValues = new MutablePropertyValues();
300
		propertyValues.add("patternLocalDateTime", "10/31/09 12:00 PM");
301
		binder.bind(propertyValues);
302
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
303
		assertThat(binder.getBindingResult().getFieldValue("patternLocalDateTime")).isEqualTo("10/31/09 12:00 PM");
304 305
	}

306
	@Test
307
	void testBindDateTimeOverflow() {
308
		MutablePropertyValues propertyValues = new MutablePropertyValues();
309
		propertyValues.add("patternLocalDateTime", "02/29/09 12:00 PM");
310
		binder.bind(propertyValues);
311
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(1);
312 313
	}

314
	@Test
315
	void testBindISODate() {
316
		MutablePropertyValues propertyValues = new MutablePropertyValues();
317
		propertyValues.add("isoLocalDate", "2009-10-31");
318
		binder.bind(propertyValues);
319
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
320
		assertThat(binder.getBindingResult().getFieldValue("isoLocalDate")).isEqualTo("2009-10-31");
321 322 323
	}

	@Test
324
	void testBindISOTime() {
325
		MutablePropertyValues propertyValues = new MutablePropertyValues();
326
		propertyValues.add("isoLocalTime", "12:00:00");
327
		binder.bind(propertyValues);
328
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
329
		assertThat(binder.getBindingResult().getFieldValue("isoLocalTime")).isEqualTo("12:00:00");
330 331 332
	}

	@Test
333
	void testBindISOTimeWithZone() {
334
		MutablePropertyValues propertyValues = new MutablePropertyValues();
335
		propertyValues.add("isoLocalTime", "12:00:00.000-05:00");
336
		binder.bind(propertyValues);
337
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
338
		assertThat(binder.getBindingResult().getFieldValue("isoLocalTime")).isEqualTo("12:00:00");
339 340 341
	}

	@Test
342
	void testBindISODateTime() {
343
		MutablePropertyValues propertyValues = new MutablePropertyValues();
344
		propertyValues.add("isoLocalDateTime", "2009-10-31T12:00:00");
345
		binder.bind(propertyValues);
346
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
347
		assertThat(binder.getBindingResult().getFieldValue("isoLocalDateTime")).isEqualTo("2009-10-31T12:00:00");
348 349 350
	}

	@Test
351
	void testBindISODateTimeWithZone() {
352
		MutablePropertyValues propertyValues = new MutablePropertyValues();
353
		propertyValues.add("isoLocalDateTime", "2009-10-31T12:00:00.000Z");
354
		binder.bind(propertyValues);
355
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
356
		assertThat(binder.getBindingResult().getFieldValue("isoLocalDateTime")).isEqualTo("2009-10-31T12:00:00");
357 358 359
	}

	@Test
360
	void testBindInstant() {
361 362 363
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("instant", "2009-10-31T12:00:00.000Z");
		binder.bind(propertyValues);
364 365
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("instant").toString().startsWith("2009-10-31T12:00")).isTrue();
366 367
	}

368
	@Test
369
	@SuppressWarnings("deprecation")
370
	void testBindInstantFromJavaUtilDate() {
371 372 373 374 375 376
		TimeZone defaultZone = TimeZone.getDefault();
		TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
		try {
			MutablePropertyValues propertyValues = new MutablePropertyValues();
			propertyValues.add("instant", new Date(109, 9, 31, 12, 0));
			binder.bind(propertyValues);
377 378
			assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
			assertThat(binder.getBindingResult().getFieldValue("instant").toString().startsWith("2009-10-31")).isTrue();
379 380 381 382
		}
		finally {
			TimeZone.setDefault(defaultZone);
		}
383 384
	}

385
	@Test
386
	void testBindPeriod() {
387 388 389
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("period", "P6Y3M1D");
		binder.bind(propertyValues);
390 391
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("period").toString().equals("P6Y3M1D")).isTrue();
392 393 394
	}

	@Test
395
	void testBindDuration() {
396 397 398
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("duration", "PT8H6M12.345S");
		binder.bind(propertyValues);
399 400
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("duration").toString().equals("PT8H6M12.345S")).isTrue();
401 402
	}

403
	@Test
404
	void testBindYear() {
405 406 407
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("year", "2007");
		binder.bind(propertyValues);
408 409
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("year").toString().equals("2007")).isTrue();
410 411 412
	}

	@Test
413
	void testBindMonth() {
414 415 416
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("month", "JULY");
		binder.bind(propertyValues);
417 418
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("month").toString().equals("JULY")).isTrue();
419 420 421
	}

	@Test
422
	void testBindMonthInAnyCase() {
423 424 425
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("month", "July");
		binder.bind(propertyValues);
426 427
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("month").toString().equals("JULY")).isTrue();
428 429
	}

430
	@Test
431
	void testBindYearMonth() {
432 433 434
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("yearMonth", "2007-12");
		binder.bind(propertyValues);
435 436
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("yearMonth").toString().equals("2007-12")).isTrue();
437 438 439
	}

	@Test
440
	void testBindMonthDay() {
441 442 443
		MutablePropertyValues propertyValues = new MutablePropertyValues();
		propertyValues.add("monthDay", "--12-03");
		binder.bind(propertyValues);
444 445
		assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
		assertThat(binder.getBindingResult().getFieldValue("monthDay").toString().equals("--12-03")).isTrue();
446 447
	}

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
	@Nested
	class FallbackPatternTests {

		@ParameterizedTest(name = "input date: {0}")
		@ValueSource(strings = {"2021-03-02", "2021.03.02", "20210302", "3/2/21"})
		void styleLocalDate(String propertyValue) {
			String propertyName = "styleLocalDateWithFallbackPatterns";
			MutablePropertyValues propertyValues = new MutablePropertyValues();
			propertyValues.add(propertyName, propertyValue);
			binder.bind(propertyValues);
			BindingResult bindingResult = binder.getBindingResult();
			assertThat(bindingResult.getErrorCount()).isEqualTo(0);
			assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("3/2/21");
		}

		@ParameterizedTest(name = "input date: {0}")
		@ValueSource(strings = {"2021-03-02", "2021.03.02", "20210302", "3/2/21"})
		void patternLocalDate(String propertyValue) {
			String propertyName = "patternLocalDateWithFallbackPatterns";
			MutablePropertyValues propertyValues = new MutablePropertyValues();
			propertyValues.add(propertyName, propertyValue);
			binder.bind(propertyValues);
			BindingResult bindingResult = binder.getBindingResult();
			assertThat(bindingResult.getErrorCount()).isEqualTo(0);
			assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02");
		}

		@ParameterizedTest(name = "input date: {0}")
		@ValueSource(strings = {"12:00:00 PM", "12:00:00", "12:00"})
		void styleLocalTime(String propertyValue) {
			String propertyName = "styleLocalTimeWithFallbackPatterns";
			MutablePropertyValues propertyValues = new MutablePropertyValues();
			propertyValues.add(propertyName, propertyValue);
			binder.bind(propertyValues);
			BindingResult bindingResult = binder.getBindingResult();
			assertThat(bindingResult.getErrorCount()).isEqualTo(0);
			assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("12:00:00 PM");
		}

		@ParameterizedTest(name = "input date: {0}")
		@ValueSource(strings = {"2021-03-02T12:00:00", "2021-03-02 12:00:00", "3/2/21 12:00"})
		void isoLocalDateTime(String propertyValue) {
			String propertyName = "isoLocalDateTimeWithFallbackPatterns";
			MutablePropertyValues propertyValues = new MutablePropertyValues();
			propertyValues.add(propertyName, propertyValue);
			binder.bind(propertyValues);
			BindingResult bindingResult = binder.getBindingResult();
			assertThat(bindingResult.getErrorCount()).isEqualTo(0);
			assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02T12:00:00");
		}

		@Test
		void patternLocalDateWithUnsupportedPattern() {
			String propertyValue = "210302";
			String propertyName = "patternLocalDateWithFallbackPatterns";
			MutablePropertyValues propertyValues = new MutablePropertyValues();
			propertyValues.add(propertyName, propertyValue);
			binder.bind(propertyValues);
			BindingResult bindingResult = binder.getBindingResult();
			assertThat(bindingResult.getErrorCount()).isEqualTo(1);
			FieldError fieldError = bindingResult.getFieldError(propertyName);
			assertThat(fieldError.unwrap(TypeMismatchException.class))
				.hasMessageContaining("for property 'patternLocalDateWithFallbackPatterns'")
				.hasCauseInstanceOf(ConversionFailedException.class).getCause()
					.hasMessageContaining("for value '210302'")
					.hasCauseInstanceOf(IllegalArgumentException.class).getCause()
						.hasMessageContaining("Parse attempt failed for value [210302]")
						.hasCauseInstanceOf(DateTimeParseException.class).getCause()
							// Unable to parse date time value "210302" using configuration from
							// @org.springframework.format.annotation.DateTimeFormat(
							// pattern=yyyy-MM-dd, style=SS, iso=NONE, fallbackPatterns=[M/d/yy, yyyyMMdd, yyyy.MM.dd])
							.hasMessageContainingAll(
								"Unable to parse date time value \"210302\" using configuration from",
								"@org.springframework.format.annotation.DateTimeFormat",
								"yyyy-MM-dd", "M/d/yy", "yyyyMMdd", "yyyy.MM.dd");
		}
}

526 527 528 529 530

	public static class DateTimeBean {

		private LocalDate localDate;

531
		@DateTimeFormat(style = "M-")
532 533 534 535 536 537 538
		private LocalDate styleLocalDate;

		@DateTimeFormat(style = "S-", fallbackPatterns = { "yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd" })
		private LocalDate styleLocalDateWithFallbackPatterns;

		@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = { "M/d/yy", "yyyyMMdd", "yyyy.MM.dd" })
		private LocalDate patternLocalDateWithFallbackPatterns;
539 540 541

		private LocalTime localTime;

542
		@DateTimeFormat(style = "-M")
543 544 545 546
		private LocalTime styleLocalTime;

		@DateTimeFormat(style = "-M", fallbackPatterns = { "HH:mm:ss", "HH:mm"})
		private LocalTime styleLocalTimeWithFallbackPatterns;
547 548 549

		private LocalDateTime localDateTime;

550
		@DateTimeFormat(style = "MM")
551
		private LocalDateTime styleLocalDateTime;
552

553
		@DateTimeFormat(pattern = "M/d/yy h:mm a")
554
		private LocalDateTime patternLocalDateTime;
555

556
		@DateTimeFormat(iso = ISO.DATE)
557
		private LocalDate isoLocalDate;
558

559
		@DateTimeFormat(iso = ISO.TIME)
560
		private LocalTime isoLocalTime;
561

562
		@DateTimeFormat(iso = ISO.DATE_TIME)
563 564 565 566
		private LocalDateTime isoLocalDateTime;

		@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = { "yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
		private LocalDateTime isoLocalDateTimeWithFallbackPatterns;
567 568 569

		private Instant instant;

570 571 572 573
		private Period period;

		private Duration duration;

574 575 576 577
		private Year year;

		private Month month;

578 579 580 581
		private YearMonth yearMonth;

		private MonthDay monthDay;

582
		private final List<DateTimeBean> children = new ArrayList<>();
583

584

585
		public LocalDate getLocalDate() {
586
			return this.localDate;
587 588 589 590 591 592
		}

		public void setLocalDate(LocalDate localDate) {
			this.localDate = localDate;
		}

593 594
		public LocalDate getStyleLocalDate() {
			return this.styleLocalDate;
595 596
		}

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
		public void setStyleLocalDate(LocalDate styleLocalDate) {
			this.styleLocalDate = styleLocalDate;
		}

		public LocalDate getStyleLocalDateWithFallbackPatterns() {
			return this.styleLocalDateWithFallbackPatterns;
		}

		public void setStyleLocalDateWithFallbackPatterns(LocalDate styleLocalDateWithFallbackPatterns) {
			this.styleLocalDateWithFallbackPatterns = styleLocalDateWithFallbackPatterns;
		}
		public LocalDate getPatternLocalDateWithFallbackPatterns() {
			return this.patternLocalDateWithFallbackPatterns;
		}

		public void setPatternLocalDateWithFallbackPatterns(LocalDate patternLocalDateWithFallbackPatterns) {
			this.patternLocalDateWithFallbackPatterns = patternLocalDateWithFallbackPatterns;
614 615 616
		}

		public LocalTime getLocalTime() {
617
			return this.localTime;
618 619 620 621 622 623
		}

		public void setLocalTime(LocalTime localTime) {
			this.localTime = localTime;
		}

624 625 626 627 628 629 630 631 632 633
		public LocalTime getStyleLocalTime() {
			return this.styleLocalTime;
		}

		public void setStyleLocalTime(LocalTime styleLocalTime) {
			this.styleLocalTime = styleLocalTime;
		}

		public LocalTime getStyleLocalTimeWithFallbackPatterns() {
			return this.styleLocalTimeWithFallbackPatterns;
634 635
		}

636 637
		public void setStyleLocalTimeWithFallbackPatterns(LocalTime styleLocalTimeWithFallbackPatterns) {
			this.styleLocalTimeWithFallbackPatterns = styleLocalTimeWithFallbackPatterns;
638 639 640
		}

		public LocalDateTime getLocalDateTime() {
641
			return this.localDateTime;
642 643 644 645 646 647
		}

		public void setLocalDateTime(LocalDateTime localDateTime) {
			this.localDateTime = localDateTime;
		}

648 649 650 651 652 653 654 655 656 657
		public LocalDateTime getStyleLocalDateTime() {
			return this.styleLocalDateTime;
		}

		public void setStyleLocalDateTime(LocalDateTime styleLocalDateTime) {
			this.styleLocalDateTime = styleLocalDateTime;
		}

		public LocalDateTime getPatternLocalDateTime() {
			return this.patternLocalDateTime;
658 659
		}

660 661
		public void setPatternLocalDateTime(LocalDateTime patternLocalDateTime) {
			this.patternLocalDateTime = patternLocalDateTime;
662 663
		}

664 665
		public LocalDate getIsoLocalDate() {
			return this.isoLocalDate;
666 667
		}

668 669
		public void setIsoLocalDate(LocalDate isoLocalDate) {
			this.isoLocalDate = isoLocalDate;
670 671
		}

672 673
		public LocalTime getIsoLocalTime() {
			return this.isoLocalTime;
674 675
		}

676 677
		public void setIsoLocalTime(LocalTime isoLocalTime) {
			this.isoLocalTime = isoLocalTime;
678 679
		}

680 681
		public LocalDateTime getIsoLocalDateTime() {
			return this.isoLocalDateTime;
682 683
		}

684 685
		public void setIsoLocalDateTime(LocalDateTime isoLocalDateTime) {
			this.isoLocalDateTime = isoLocalDateTime;
686 687
		}

688 689
		public LocalDateTime getIsoLocalDateTimeWithFallbackPatterns() {
			return this.isoLocalDateTimeWithFallbackPatterns;
690 691
		}

692 693
		public void setIsoLocalDateTimeWithFallbackPatterns(LocalDateTime isoLocalDateTimeWithFallbackPatterns) {
			this.isoLocalDateTimeWithFallbackPatterns = isoLocalDateTimeWithFallbackPatterns;
694 695 696
		}

		public Instant getInstant() {
697
			return this.instant;
698 699 700 701 702 703
		}

		public void setInstant(Instant instant) {
			this.instant = instant;
		}

704
		public Period getPeriod() {
705
			return this.period;
706 707 708 709 710 711 712
		}

		public void setPeriod(Period period) {
			this.period = period;
		}

		public Duration getDuration() {
713
			return this.duration;
714 715 716 717 718 719
		}

		public void setDuration(Duration duration) {
			this.duration = duration;
		}

720
		public Year getYear() {
721
			return this.year;
722 723 724 725 726 727 728
		}

		public void setYear(Year year) {
			this.year = year;
		}

		public Month getMonth() {
729
			return this.month;
730 731 732 733 734 735
		}

		public void setMonth(Month month) {
			this.month = month;
		}

736
		public YearMonth getYearMonth() {
737
			return this.yearMonth;
738 739 740 741 742 743 744
		}

		public void setYearMonth(YearMonth yearMonth) {
			this.yearMonth = yearMonth;
		}

		public MonthDay getMonthDay() {
745
			return this.monthDay;
746 747 748 749 750 751
		}

		public void setMonthDay(MonthDay monthDay) {
			this.monthDay = monthDay;
		}

752
		public List<DateTimeBean> getChildren() {
753
			return this.children;
754 755 756 757
		}
	}

}