42
42
import org .junit .jupiter .api .BeforeEach ;
43
43
import org .junit .jupiter .api .Nested ;
44
44
import org .junit .jupiter .api .Test ;
45
+ import org .junit .jupiter .api .condition .EnabledForJreRange ;
45
46
import org .junit .jupiter .params .ParameterizedTest ;
46
47
import org .junit .jupiter .params .provider .ValueSource ;
47
48
58
59
import org .springframework .validation .FieldError ;
59
60
60
61
import static org .assertj .core .api .Assertions .assertThat ;
62
+ import static org .junit .jupiter .api .condition .JRE .JAVA_19 ;
63
+ import static org .junit .jupiter .api .condition .JRE .JAVA_20 ;
61
64
62
65
/**
63
66
* @author Keith Donald
68
71
*/
69
72
class DateTimeFormattingTests {
70
73
74
+ // JDK <= 19 requires a standard space before "AM/PM".
75
+ // JDK >= 20 requires a NNBSP before "AM/PM".
76
+ // \u202F is a narrow non-breaking space (NNBSP).
77
+ private static final String TIME_SEPARATOR = (Runtime .version ().feature () < 20 ? " " : "\u202F " );
78
+
79
+
71
80
private final FormattingConversionService conversionService = new FormattingConversionService ();
72
81
73
82
private DataBinder binder ;
@@ -210,10 +219,11 @@ void testBindLocalDateFromJavaUtilCalendar() {
210
219
@ Test
211
220
void testBindLocalTime () {
212
221
MutablePropertyValues propertyValues = new MutablePropertyValues ();
213
- propertyValues .add ("localTime" , "12:00 PM" );
222
+ propertyValues .add ("localTime" , "12:00%sPM" . formatted ( TIME_SEPARATOR ) );
214
223
binder .bind (propertyValues );
215
224
assertThat (binder .getBindingResult ().getErrorCount ()).isZero ();
216
- assertThat (binder .getBindingResult ().getFieldValue ("localTime" )).isEqualTo ("12:00 PM" );
225
+ // \p{Zs} matches any Unicode space character
226
+ assertThat (binder .getBindingResult ().getFieldValue ("localTime" )).asString ().matches ("12:00\\ p{Zs}PM" );
217
227
}
218
228
219
229
@ Test
@@ -222,7 +232,8 @@ void testBindLocalTimeWithISO() {
222
232
propertyValues .add ("localTime" , "12:00:00" );
223
233
binder .bind (propertyValues );
224
234
assertThat (binder .getBindingResult ().getErrorCount ()).isZero ();
225
- assertThat (binder .getBindingResult ().getFieldValue ("localTime" )).isEqualTo ("12:00 PM" );
235
+ // \p{Zs} matches any Unicode space character
236
+ assertThat (binder .getBindingResult ().getFieldValue ("localTime" )).asString ().matches ("12:00\\ p{Zs}PM" );
226
237
}
227
238
228
239
@ Test
@@ -231,10 +242,11 @@ void testBindLocalTimeWithSpecificStyle() {
231
242
registrar .setTimeStyle (FormatStyle .MEDIUM );
232
243
setup (registrar );
233
244
MutablePropertyValues propertyValues = new MutablePropertyValues ();
234
- propertyValues .add ("localTime" , "12:00:00 PM" );
245
+ propertyValues .add ("localTime" , "12:00:00%sPM" . formatted ( TIME_SEPARATOR ) );
235
246
binder .bind (propertyValues );
236
247
assertThat (binder .getBindingResult ().getErrorCount ()).isZero ();
237
- assertThat (binder .getBindingResult ().getFieldValue ("localTime" )).isEqualTo ("12:00:00 PM" );
248
+ // \p{Zs} matches any Unicode space character
249
+ assertThat (binder .getBindingResult ().getFieldValue ("localTime" )).asString ().matches ("12:00:00\\ p{Zs}PM" );
238
250
}
239
251
240
252
@ Test
@@ -252,10 +264,11 @@ void testBindLocalTimeWithSpecificFormatter() {
252
264
@ Test
253
265
void testBindLocalTimeAnnotated () {
254
266
MutablePropertyValues propertyValues = new MutablePropertyValues ();
255
- propertyValues .add ("styleLocalTime" , "12:00:00 PM" );
267
+ propertyValues .add ("styleLocalTime" , "12:00:00%sPM" . formatted ( TIME_SEPARATOR ) );
256
268
binder .bind (propertyValues );
257
269
assertThat (binder .getBindingResult ().getErrorCount ()).isZero ();
258
- assertThat (binder .getBindingResult ().getFieldValue ("styleLocalTime" )).isEqualTo ("12:00:00 PM" );
270
+ // \p{Zs} matches any Unicode space character
271
+ assertThat (binder .getBindingResult ().getFieldValue ("styleLocalTime" )).asString ().matches ("12:00:00\\ p{Zs}PM" );
259
272
}
260
273
261
274
@ Test
@@ -264,7 +277,8 @@ void testBindLocalTimeFromJavaUtilCalendar() {
264
277
propertyValues .add ("localTime" , new GregorianCalendar (1970 , 0 , 0 , 12 , 0 ));
265
278
binder .bind (propertyValues );
266
279
assertThat (binder .getBindingResult ().getErrorCount ()).isZero ();
267
- assertThat (binder .getBindingResult ().getFieldValue ("localTime" )).isEqualTo ("12:00 PM" );
280
+ // \p{Zs} matches any Unicode space character
281
+ assertThat (binder .getBindingResult ().getFieldValue ("localTime" )).asString ().matches ("12:00\\ p{Zs}PM" );
268
282
}
269
283
270
284
@ Test
@@ -274,7 +288,8 @@ void testBindLocalDateTime() {
274
288
binder .bind (propertyValues );
275
289
assertThat (binder .getBindingResult ().getErrorCount ()).isZero ();
276
290
String value = binder .getBindingResult ().getFieldValue ("localDateTime" ).toString ();
277
- assertThat (value ).startsWith ("10/31/09" ).endsWith ("12:00 PM" );
291
+ // \p{Zs} matches any Unicode space character
292
+ assertThat (value ).startsWith ("10/31/09" ).matches (".+?12:00\\ p{Zs}PM" );
278
293
}
279
294
280
295
@ Test
@@ -284,7 +299,8 @@ void testBindLocalDateTimeWithISO() {
284
299
binder .bind (propertyValues );
285
300
assertThat (binder .getBindingResult ().getErrorCount ()).isZero ();
286
301
String value = binder .getBindingResult ().getFieldValue ("localDateTime" ).toString ();
287
- assertThat (value ).startsWith ("10/31/09" ).endsWith ("12:00 PM" );
302
+ // \p{Zs} matches any Unicode space character
303
+ assertThat (value ).startsWith ("10/31/09" ).matches (".+?12:00\\ p{Zs}PM" );
288
304
}
289
305
290
306
@ Test
@@ -294,7 +310,8 @@ void testBindLocalDateTimeAnnotated() {
294
310
binder .bind (propertyValues );
295
311
assertThat (binder .getBindingResult ().getErrorCount ()).isZero ();
296
312
String value = binder .getBindingResult ().getFieldValue ("styleLocalDateTime" ).toString ();
297
- assertThat (value ).startsWith ("Oct 31, 2009" ).endsWith ("12:00:00 PM" );
313
+ // \p{Zs} matches any Unicode space character
314
+ assertThat (value ).startsWith ("Oct 31, 2009" ).matches (".+?12:00:00\\ p{Zs}PM" );
298
315
}
299
316
300
317
@ Test
@@ -304,7 +321,8 @@ void testBindLocalDateTimeFromJavaUtilCalendar() {
304
321
binder .bind (propertyValues );
305
322
assertThat (binder .getBindingResult ().getErrorCount ()).isZero ();
306
323
String value = binder .getBindingResult ().getFieldValue ("localDateTime" ).toString ();
307
- assertThat (value ).startsWith ("10/31/09" ).endsWith ("12:00 PM" );
324
+ // \p{Zs} matches any Unicode space character
325
+ assertThat (value ).startsWith ("10/31/09" ).matches (".+?12:00\\ p{Zs}PM" );
308
326
}
309
327
310
328
@ Test
@@ -317,7 +335,8 @@ void testBindDateTimeWithSpecificStyle() {
317
335
binder .bind (propertyValues );
318
336
assertThat (binder .getBindingResult ().getErrorCount ()).isZero ();
319
337
String value = binder .getBindingResult ().getFieldValue ("localDateTime" ).toString ();
320
- assertThat (value ).startsWith ("Oct 31, 2009" ).endsWith ("12:00:00 PM" );
338
+ // \p{Zs} matches any Unicode space character
339
+ assertThat (value ).startsWith ("Oct 31, 2009" ).matches (".+?12:00:00\\ p{Zs}PM" );
321
340
}
322
341
323
342
@ Test
@@ -558,18 +577,32 @@ void patternLocalDate(String propertyValue) {
558
577
assertThat (bindingResult .getFieldValue (propertyName )).isEqualTo ("2021-03-02" );
559
578
}
560
579
580
+ @ EnabledForJreRange (max = JAVA_19 )
561
581
@ ParameterizedTest (name = "input date: {0}" )
562
- // @ValueSource(strings = {"12:00:00\u202FPM", "12:00:00", "12:00"})
582
+ // JDK <= 19 requires a standard space before the "PM".
563
583
@ ValueSource (strings = {"12:00:00 PM" , "12:00:00" , "12:00" })
564
- void styleLocalTime (String propertyValue ) {
584
+ void styleLocalTime_PreJDK20 (String propertyValue ) {
585
+ styleLocalTime (propertyValue );
586
+ }
587
+
588
+ @ EnabledForJreRange (min = JAVA_20 )
589
+ @ ParameterizedTest (name = "input date: {0}" )
590
+ // JDK >= 20 requires a NNBSP before the "PM".
591
+ // \u202F is a narrow non-breaking space (NNBSP).
592
+ @ ValueSource (strings = {"12:00:00\u202F PM" , "12:00:00" , "12:00" })
593
+ void styleLocalTime_PostJDK20 (String propertyValue ) {
594
+ styleLocalTime (propertyValue );
595
+ }
596
+
597
+ private void styleLocalTime (String propertyValue ) {
565
598
String propertyName = "styleLocalTimeWithFallbackPatterns" ;
566
599
MutablePropertyValues propertyValues = new MutablePropertyValues ();
567
600
propertyValues .add (propertyName , propertyValue );
568
601
binder .bind (propertyValues );
569
602
BindingResult bindingResult = binder .getBindingResult ();
570
603
assertThat (bindingResult .getErrorCount ()).isZero ();
571
- // assertThat(bindingResult.getFieldValue(propertyName)).asString().matches("12:00:00\\SPM");
572
- assertThat (bindingResult .getFieldValue (propertyName )).isEqualTo ( "12:00:00 PM" );
604
+ // \p{Zs} matches any Unicode space character
605
+ assertThat (bindingResult .getFieldValue (propertyName )).asString (). matches ( "12:00:00\\ p{Zs} PM" );
573
606
}
574
607
575
608
@ ParameterizedTest (name = "input date: {0}" )
0 commit comments