Skip to content

Commit 89338c9

Browse files
committed
Stop using using legacy locale data for Date/Time formatting tests
Commit 84714fb introduced usage of the -Djava.locale.providers=COMPAT command-line argument for javac in order to allow our JDK 20 builds to pass by using legacy locale data. That was done to ensure that Date/Time formats using AM/PM produced a standard space (" ") before the "AM" or "PM" instead of a narrow non-breaking space (NNBSP "\u202F"), which was introduced in Java 20 due to adoption of Unicode Common Locale Data Repository (CLDR-14032). This commit removes usage of the -Djava.locale.providers=COMPAT command-line argument and updates all affected tests to: - Use an NNBSP before "AM" or "PM" in input text when running on Java 20 or higher. - Leniently match against any Unicode space character in formatted values containing "AM" or "PM". See https://jdk.java.net/20/release-notes#JDK-8284840 See https://unicode-org.atlassian.net/browse/CLDR-14032 See gh-30185 Closes gh-33144
1 parent 51641ec commit 89338c9

File tree

4 files changed

+56
-23
lines changed

4 files changed

+56
-23
lines changed

buildSrc/src/main/java/org/springframework/build/TestConventions.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -63,8 +63,7 @@ private void configureTests(Project project, Test test) {
6363
test.systemProperty("testGroups", project.getProperties().get("testGroups"));
6464
}
6565
test.jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED",
66-
"--add-opens=java.base/java.util=ALL-UNNAMED",
67-
"-Djava.locale.providers=COMPAT");
66+
"--add-opens=java.base/java.util=ALL-UNNAMED");
6867
}
6968

7069
private void configureTestRetryPlugin(Project project, Test test) {

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ void createDateTimeFormatterWithFallback() {
7878
void createDateTimeFormatterInOrderOfPropertyPriority() {
7979
factory.setStylePattern("SS");
8080
String value = applyLocale(factory.createDateTimeFormatter()).format(dateTime);
81-
assertThat(value).startsWith("10/21/09");
82-
assertThat(value).endsWith("12:10 PM");
81+
// \p{Zs} matches any Unicode space character
82+
assertThat(value).startsWith("10/21/09").matches(".+?12:10\\p{Zs}PM");
8383

8484
factory.setIso(ISO.DATE);
8585
assertThat(applyLocale(factory.createDateTimeFormatter()).format(dateTime)).isEqualTo("2009-10-21");

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

+50-17
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.junit.jupiter.api.BeforeEach;
4343
import org.junit.jupiter.api.Nested;
4444
import org.junit.jupiter.api.Test;
45+
import org.junit.jupiter.api.condition.EnabledForJreRange;
4546
import org.junit.jupiter.params.ParameterizedTest;
4647
import org.junit.jupiter.params.provider.ValueSource;
4748

@@ -58,6 +59,8 @@
5859
import org.springframework.validation.FieldError;
5960

6061
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;
6164

6265
/**
6366
* @author Keith Donald
@@ -68,6 +71,12 @@
6871
*/
6972
class DateTimeFormattingTests {
7073

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+
7180
private final FormattingConversionService conversionService = new FormattingConversionService();
7281

7382
private DataBinder binder;
@@ -210,10 +219,11 @@ void testBindLocalDateFromJavaUtilCalendar() {
210219
@Test
211220
void testBindLocalTime() {
212221
MutablePropertyValues propertyValues = new MutablePropertyValues();
213-
propertyValues.add("localTime", "12:00 PM");
222+
propertyValues.add("localTime", "12:00%sPM".formatted(TIME_SEPARATOR));
214223
binder.bind(propertyValues);
215224
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");
217227
}
218228

219229
@Test
@@ -222,7 +232,8 @@ void testBindLocalTimeWithISO() {
222232
propertyValues.add("localTime", "12:00:00");
223233
binder.bind(propertyValues);
224234
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");
226237
}
227238

228239
@Test
@@ -231,10 +242,11 @@ void testBindLocalTimeWithSpecificStyle() {
231242
registrar.setTimeStyle(FormatStyle.MEDIUM);
232243
setup(registrar);
233244
MutablePropertyValues propertyValues = new MutablePropertyValues();
234-
propertyValues.add("localTime", "12:00:00 PM");
245+
propertyValues.add("localTime", "12:00:00%sPM".formatted(TIME_SEPARATOR));
235246
binder.bind(propertyValues);
236247
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");
238250
}
239251

240252
@Test
@@ -252,10 +264,11 @@ void testBindLocalTimeWithSpecificFormatter() {
252264
@Test
253265
void testBindLocalTimeAnnotated() {
254266
MutablePropertyValues propertyValues = new MutablePropertyValues();
255-
propertyValues.add("styleLocalTime", "12:00:00 PM");
267+
propertyValues.add("styleLocalTime", "12:00:00%sPM".formatted(TIME_SEPARATOR));
256268
binder.bind(propertyValues);
257269
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");
259272
}
260273

261274
@Test
@@ -264,7 +277,8 @@ void testBindLocalTimeFromJavaUtilCalendar() {
264277
propertyValues.add("localTime", new GregorianCalendar(1970, 0, 0, 12, 0));
265278
binder.bind(propertyValues);
266279
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");
268282
}
269283

270284
@Test
@@ -274,7 +288,8 @@ void testBindLocalDateTime() {
274288
binder.bind(propertyValues);
275289
assertThat(binder.getBindingResult().getErrorCount()).isZero();
276290
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");
278293
}
279294

280295
@Test
@@ -284,7 +299,8 @@ void testBindLocalDateTimeWithISO() {
284299
binder.bind(propertyValues);
285300
assertThat(binder.getBindingResult().getErrorCount()).isZero();
286301
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");
288304
}
289305

290306
@Test
@@ -294,7 +310,8 @@ void testBindLocalDateTimeAnnotated() {
294310
binder.bind(propertyValues);
295311
assertThat(binder.getBindingResult().getErrorCount()).isZero();
296312
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");
298315
}
299316

300317
@Test
@@ -304,7 +321,8 @@ void testBindLocalDateTimeFromJavaUtilCalendar() {
304321
binder.bind(propertyValues);
305322
assertThat(binder.getBindingResult().getErrorCount()).isZero();
306323
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");
308326
}
309327

310328
@Test
@@ -317,7 +335,8 @@ void testBindDateTimeWithSpecificStyle() {
317335
binder.bind(propertyValues);
318336
assertThat(binder.getBindingResult().getErrorCount()).isZero();
319337
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");
321340
}
322341

323342
@Test
@@ -558,18 +577,32 @@ void patternLocalDate(String propertyValue) {
558577
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02");
559578
}
560579

580+
@EnabledForJreRange(max = JAVA_19)
561581
@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".
563583
@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\u202FPM", "12:00:00", "12:00"})
593+
void styleLocalTime_PostJDK20(String propertyValue) {
594+
styleLocalTime(propertyValue);
595+
}
596+
597+
private void styleLocalTime(String propertyValue) {
565598
String propertyName = "styleLocalTimeWithFallbackPatterns";
566599
MutablePropertyValues propertyValues = new MutablePropertyValues();
567600
propertyValues.add(propertyName, propertyValue);
568601
binder.bind(propertyValues);
569602
BindingResult bindingResult = binder.getBindingResult();
570603
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");
573606
}
574607

575608
@ParameterizedTest(name = "input date: {0}")

spring-web/src/test/java/org/springframework/http/converter/json/GsonFactoryBeanTests.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,10 @@ void customizeDateFormatNone() {
128128
cal.set(Calendar.DATE, 1);
129129
Date date = cal.getTime();
130130
bean.setDate(date);
131+
// \p{Zs} matches any Unicode space character
131132
assertThat(gson.toJson(bean))
132133
.startsWith("{\"date\":\"Jan 1, 2014")
133-
.endsWith("12:00:00 AM\"}");
134+
.matches(".+?12:00:00\\p{Zs}AM\"}");
134135
}
135136

136137
@Test

0 commit comments

Comments
 (0)