diff --git a/Date-and-Time-Formatting-with-JDK-20-and-higher.md b/Date-and-Time-Formatting-with-JDK-20-and-higher.md index a19dc80..b048aca 100644 --- a/Date-and-Time-Formatting-with-JDK-20-and-higher.md +++ b/Date-and-Time-Formatting-with-JDK-20-and-higher.md @@ -1,3 +1,77 @@ -This is a placeholder for forthcoming documentation regarding and date and time parsing and formatting issues with JDK 20+ +## Overview -See https://github.com/spring-projects/spring-framework/issues/33151 \ No newline at end of file +This Wiki page discusses date/time formatting and parsing issues that developers may encounter when running their applications on JDK 20+. + +Specifically, this document provides background information on changes in the JDK as well as guidance on how to address formatting and parsing issues encountered in Spring applications. + +Before you read any further, we highly recommend that you first read [JEP 252: Use CLDR Locale Data by Default](https://openjdk.org/jeps/252). + +Although this document primarily focuses on issues related to time formats for the US English locale (for example, `3:30 PM`), numerous other use cases may potentially be affected by locale-sensitive date/time formats provided by the [Unicode Common Locale Data Repository (CLDR) project](https://cldr.unicode.org/). + +## Background + +JDK 20 adopted Unicode [CLDR 42](https://cldr.unicode.org/downloads/cldr-42) which includes [CLDR-14032](https://unicode-org.atlassian.net/browse/CLDR-14032) that changed the space character that precedes the `period` (AM or PM) in formatted date/time text from a standard space (`" "`) to a narrow non-breaking space (NNBSP: `"\u202F"`). Consequently, applications that rely on date/time parsing and formatting may encounter incompatible changes in behavior when using Spring on JDK 20 or higher – for example, web applications that make use of Spring Framework's `@DateTimeFormat` support. + +On JDK 20, 21, and 22, applications can use the `-Djava.locale.providers=COMPAT` command-line argument for the `java` compiler in order to force the use of legacy locale data which uses a standard space for the space character that precedes the `period` in formatted date/time text. + +Note, however, that the aforementioned `COMPAT` mode has been removed in JDK 23. + +It is also worth pointing out that string represenations of date/time formats can no longer be reliably encoded with ISO-8859-1 (latin-1) encoding. The reason is that characters such as a narrow non-breaking space (`"\u202F"`) can only be properly represented with UTF encoding. + +Consequently, developers and frameworks must find a way to either avoid or deal with locale-sensitive date/time formats provided by both current and future versions of the Unicode CLDR. + +## Recommendations + +The Spring team recommends the use of ISO standardized formats for both parsing and formatting of date/time values whenever possible. For example, consider using a predefined `iso` pattern in Spring's `@DateTimeFormat` annotation (such as `ISO.DATE_TIME`) or one of the `ISO_*` constants defined in `java.time.format.DateTimeFormatter` (such as `ISO_DATE_TIME`) for programmatic handling of JSR-310 `java.time` value types. + +If using an ISO standardized format is not an option for your use case, consider one of the _lenient_ approaches outlined below. + +The Spring team also recommends the use of UTF encoding whenever possible – for example, `UTF-8`. + +### Lenient `@DateTimeFormat` Configuration + +TODO + +### Lenient Parsing in Tests and Application Code + +TODO + +### Lenient `SimpleDateFormat` and `DateTimeFormatter` Configuration + +In JDK 23, the Java team introduced support for lenient parsing of space characters in `SimpleDateFormat` as well as `DateTimeFormatter`. + +`SimpleDateFormat` is lenient by default; however, `DateTimeFormatter` instances are not lenient by default, and factory methods like `DateTimeFormatter.ofLocalizedTime(...)` do not create lenient formatters. + +To create a lenient `DateTimeFormatter`, one must forgo the use of the static factory methods in `DateTimeFormatter` and instead make use of the `DateTimeFormatterBuilder`. The following example shows how to create a static factory method for a lenient `DateTimeFormatter` that is comparable to what `DateTimeFormatter.ofLocalizedDateTime(FormatStyle, FormatStyle)` produces. + +```java +pubic static DateTimeFormatter createLenientDateTimeFormatter( + FormatStyle dateStyle, FormatStyle timeStyle) { + + return new DateTimeFormatterBuilder() + .parseLenient() + .appendLocalized(dateStyle, timeStyle) + .toFormatter() + .withChronology(IsoChronology.INSTANCE); +} +``` + +## Resources + +- https://openjdk.org/jeps/252 +- https://jdk.java.net/20/release-notes#JDK-8284840 +- https://cldr.unicode.org/downloads/cldr-42 +- https://unicode-org.atlassian.net/browse/CLDR-14032 +- https://bugs.openjdk.org/browse/JDK-8223587 +- https://bugs.openjdk.org/browse/JDK-8284840 +- https://bugs.openjdk.org/browse/JDK-8297316 +- https://bugs.openjdk.org/browse/JDK-8304925 +- https://bugs.openjdk.org/browse/JDK-8324665 + +## Related Spring Issues + +- https://github.com/spring-projects/spring-framework/issues/30185 +- https://github.com/spring-projects/spring-framework/issues/33144 +- https://github.com/spring-projects/spring-framework/issues/30649 +- https://github.com/spring-projects/spring-framework/issues/33151 +- https://github.com/spring-projects/spring-boot/issues/42430