Unicode ICU 而不是 java.time.DateTimeFormatter 应该用于国际日期和时间
Unicode ICU and not java.time.DateTimeFormatter should be used for international dates and times
请就 SonarQube 报告的问题提出小问题。
一段非常简单的代码:
Instant.now().atZone(ZoneId.systemDefault()).toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"))
我被标记了一些对我来说很奇怪的东西:
i18n-java:V1009
Unicode ICU and not java.time.DateTimeFormatter should be used for international dates and times
Java's DateTimeFormatter is quite error prone prior to Java version 9. Version 9 and greater with Unicode CLDR data is much improved, but still has errors for key locales. For ideal results, use Unicode ICU DateTimePatternGenerator.
Noncompliant Code Example
Locale userPreferredLocale = Locale.forLanguageTag("zh-Hans");
...
DateTimeFormatter mediumFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(userPreferredLocale);
Compliant Solution
import com.ibm.icu.text.DateTimePatternGenerator;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.ULocale;
...
ULocale userPreferredLocale = ULocale.forLanguageTag("zh-Hans");
...
DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(userPreferredLocale);
//pattern for com.ibm.icu.text.DateFormat.FULL="MMM DD,YYYY,h:mm A"
SimpleDateFormat sdf = new SimpleDateFormat(dtpg.getBestPattern("MMM dd, YYYY, h:mm A"), uloc);
我正在使用 Java 11,并希望避免导入此 IBM 包。
我不太确定当前的问题是什么(到目前为止代码工作正常)。
但我确实对解决这个问题很感兴趣。请问有什么“好方法”可以实现吗?
我会理解以下评论:
“禁用SonarQube,禁用这个规则,不要太在意那些误报,不要太看重分析工具等”
但我很感兴趣如何解决这个“Unicode ICU 而不是 java.time.DateTimeFormatter 应该用于国际日期和时间”。
谢谢
tl;博士
别担心。只需检查由 DateTimeFormatter
生成的典型日期时间格式的文本是否满足您支持的任何语言环境中用户的期望。如果是这样,就不需要涉及 IBM 的 ICU 库了。
让 java.time 自动本地化表示日期时间值的文本。
ZoneId ZoneId = ZoneId.of( "America/Montreal" ) ;
Locale locale = Locale.CANADA_FRENCH ;
ZonedDateTime zdt = ZonedDateTime.now( zoneId ) ;
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale ) ;
String output = zdt.format( formatter ) ;
详情
仅供参考,声纳消息指的是 Unicode CLDR became the default locale resource in Java implementations based on OpenJDK for Java 9 and later. See JEP 252: Use CLDR Locale Data by Default. Java 10 brought further support.
你说:
I am quite unsure what is the current problem (code working fine so far).
我相信这不是代码工作的问题。
问题是本地化是否符合语言环境。
日期时间值的格式因人类语言、文化和亚文化而异。我们谈论的是翻译的单词,以及标点符号、缩写、元素顺序、大写等规则。跟踪所有这些本地化信息涉及大量数据。最重要的是,他们改变了。文化变了,学术界的理解也变了。
Java8 及更早版本的 OpenJDK 默认使用的此类语言环境数据的实现相对有限且浅薄,没有涵盖许多子文化。
相比之下,由 Unicode Consortium 管理的 CLDR 庞大 且详细,涵盖了许多亚文化。早期版本的 OpenJDK 包含 CLDR 的副本。但直到 Java 9 才成为默认的语言环境资源,首先在那里进行查找。
也许 Sonar 消息说 java.time.DateTimeFormatter
在特定亚文化的细微差别方面存在一些问题。但我还没有听说过。如果您担心,可以查看 OpenJDK 问题跟踪器。
➥ 我不会担心的。如果您知道您的应用程序将仅用于少数特定区域设置,请测试这些区域设置。从每个地区招募一组用户。查看您的应用程序的典型输出是否符合他们的期望。如果他们满意,写一些单元测试,收工。
请记住,正如我所说,文化规范会随着时间而改变。 Unicode 联盟跟踪这些变化,根据需要发布新版本的 CLDR。当您更新 Java 实施时,您可能会获得 CLDR 的更新版本。很可能某天在某些语言环境中生成表示日期时间的文本时可能会得到不同的输出。如果您可能关心这些变化,请编写我上面提到的那些单元测试。
仅供参考,Sonar 消息中提到的库是 International Components for Unicode (ICU) built by Taligent and IBM, now housed at the Unicode Consortium: http://site.icu-project.org/
的 Java 实现
Basil Bourque 已经写下了明智的答案。我谦虚地补充一点。
SonarQube是对是错
- 您得到的消息是正确,即
DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss")
不会产生一种让跨多种语言和文化的用户满意的格式。不知道这是否是您的目标。
- 有关建议
dtpg.getBestPattern("MMM dd, YYYY, h:mm A")
的消息错误,因为代码会因 java.lang.IllegalArgumentException: Illegal pattern character 'A'
或其他一些异常而崩溃。
消息指的不合规代码产生:
2020-12-31
在 Java 8
2020年12月31日
在 Java 11
所以,在我不知道 Basil Bourque 的回答的情况下,在我看来,SonarQube 规则是一个过时的规则,它对 Java 8 有效,但你应该忽略 Java 11.
我建议
正确的方法很大程度上取决于您设置日期和时间的格式。
如果要显示给用户,有两个选项:
- 快捷方式是使用类似于 SonarQube 消息中提到的不符合 Java 8 的代码,因为很多程序员从中获得了很好的经验,并且它似乎在以后的 Java 个版本。
- 最佳解决方案是询问您的用户他们喜欢哪种格式,然后给他们。
如果与其他系统交换:
- 最好教育其他系统的开发人员接受 ISO 8601 格式。然后只需使用
Instant.toString()
或 OffsetDateTime.toString)
因为这些方法会产生 ISO 8601.
- 如果其他系统需要您现有代码提供的格式,您别无选择:坚持使用您已有的格式化程序。
如果用于存储:更喜欢存储数据时间对象而不是字符串。例如,使用数据库引擎的 timestamp with time zone
或 datetime
数据类型 and/or 让 Hibernate 5 保存一个 Instant
或其他日期时间对象。
代码分析器
TL;DR:代码分析器当然可以帮助我们发现代码中的错误和其他不足之处。他们当然也有一些误报。
我确实信任代码分析器。我对 SonarQube 没有任何经验。我是一个快乐的 SpotBugs 用户。当我确信代码符合我的要求时,我还选择性地告诉 SpotBugs 忽略我编写的某些方法中的某些规则。我非常确信我会对 SonarQube 做同样的事情。
请就 SonarQube 报告的问题提出小问题。
一段非常简单的代码:
Instant.now().atZone(ZoneId.systemDefault()).toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"))
我被标记了一些对我来说很奇怪的东西: i18n-java:V1009
Unicode ICU and not java.time.DateTimeFormatter should be used for international dates and times
Java's DateTimeFormatter is quite error prone prior to Java version 9. Version 9 and greater with Unicode CLDR data is much improved, but still has errors for key locales. For ideal results, use Unicode ICU DateTimePatternGenerator.
Noncompliant Code Example
Locale userPreferredLocale = Locale.forLanguageTag("zh-Hans");
...
DateTimeFormatter mediumFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(userPreferredLocale);
Compliant Solution
import com.ibm.icu.text.DateTimePatternGenerator;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.ULocale;
...
ULocale userPreferredLocale = ULocale.forLanguageTag("zh-Hans");
...
DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(userPreferredLocale);
//pattern for com.ibm.icu.text.DateFormat.FULL="MMM DD,YYYY,h:mm A"
SimpleDateFormat sdf = new SimpleDateFormat(dtpg.getBestPattern("MMM dd, YYYY, h:mm A"), uloc);
我正在使用 Java 11,并希望避免导入此 IBM 包。 我不太确定当前的问题是什么(到目前为止代码工作正常)。
但我确实对解决这个问题很感兴趣。请问有什么“好方法”可以实现吗?
我会理解以下评论: “禁用SonarQube,禁用这个规则,不要太在意那些误报,不要太看重分析工具等”
但我很感兴趣如何解决这个“Unicode ICU 而不是 java.time.DateTimeFormatter 应该用于国际日期和时间”。
谢谢
tl;博士
别担心。只需检查由 DateTimeFormatter
生成的典型日期时间格式的文本是否满足您支持的任何语言环境中用户的期望。如果是这样,就不需要涉及 IBM 的 ICU 库了。
让 java.time 自动本地化表示日期时间值的文本。
ZoneId ZoneId = ZoneId.of( "America/Montreal" ) ;
Locale locale = Locale.CANADA_FRENCH ;
ZonedDateTime zdt = ZonedDateTime.now( zoneId ) ;
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale ) ;
String output = zdt.format( formatter ) ;
详情
仅供参考,声纳消息指的是 Unicode CLDR became the default locale resource in Java implementations based on OpenJDK for Java 9 and later. See JEP 252: Use CLDR Locale Data by Default. Java 10 brought further support.
你说:
I am quite unsure what is the current problem (code working fine so far).
我相信这不是代码工作的问题。
问题是本地化是否符合语言环境。
日期时间值的格式因人类语言、文化和亚文化而异。我们谈论的是翻译的单词,以及标点符号、缩写、元素顺序、大写等规则。跟踪所有这些本地化信息涉及大量数据。最重要的是,他们改变了。文化变了,学术界的理解也变了。
Java8 及更早版本的 OpenJDK 默认使用的此类语言环境数据的实现相对有限且浅薄,没有涵盖许多子文化。
相比之下,由 Unicode Consortium 管理的 CLDR 庞大 且详细,涵盖了许多亚文化。早期版本的 OpenJDK 包含 CLDR 的副本。但直到 Java 9 才成为默认的语言环境资源,首先在那里进行查找。
也许 Sonar 消息说 java.time.DateTimeFormatter
在特定亚文化的细微差别方面存在一些问题。但我还没有听说过。如果您担心,可以查看 OpenJDK 问题跟踪器。
➥ 我不会担心的。如果您知道您的应用程序将仅用于少数特定区域设置,请测试这些区域设置。从每个地区招募一组用户。查看您的应用程序的典型输出是否符合他们的期望。如果他们满意,写一些单元测试,收工。
请记住,正如我所说,文化规范会随着时间而改变。 Unicode 联盟跟踪这些变化,根据需要发布新版本的 CLDR。当您更新 Java 实施时,您可能会获得 CLDR 的更新版本。很可能某天在某些语言环境中生成表示日期时间的文本时可能会得到不同的输出。如果您可能关心这些变化,请编写我上面提到的那些单元测试。
仅供参考,Sonar 消息中提到的库是 International Components for Unicode (ICU) built by Taligent and IBM, now housed at the Unicode Consortium: http://site.icu-project.org/
的 Java 实现Basil Bourque 已经写下了明智的答案。我谦虚地补充一点。
SonarQube是对是错
- 您得到的消息是正确,即
DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss")
不会产生一种让跨多种语言和文化的用户满意的格式。不知道这是否是您的目标。 - 有关建议
dtpg.getBestPattern("MMM dd, YYYY, h:mm A")
的消息错误,因为代码会因java.lang.IllegalArgumentException: Illegal pattern character 'A'
或其他一些异常而崩溃。
消息指的不合规代码产生:
2020-12-31
在 Java 82020年12月31日
在 Java 11
所以,在我不知道 Basil Bourque 的回答的情况下,在我看来,SonarQube 规则是一个过时的规则,它对 Java 8 有效,但你应该忽略 Java 11.
我建议
正确的方法很大程度上取决于您设置日期和时间的格式。
如果要显示给用户,有两个选项:
- 快捷方式是使用类似于 SonarQube 消息中提到的不符合 Java 8 的代码,因为很多程序员从中获得了很好的经验,并且它似乎在以后的 Java 个版本。
- 最佳解决方案是询问您的用户他们喜欢哪种格式,然后给他们。
如果与其他系统交换:
- 最好教育其他系统的开发人员接受 ISO 8601 格式。然后只需使用
Instant.toString()
或OffsetDateTime.toString)
因为这些方法会产生 ISO 8601. - 如果其他系统需要您现有代码提供的格式,您别无选择:坚持使用您已有的格式化程序。
- 最好教育其他系统的开发人员接受 ISO 8601 格式。然后只需使用
如果用于存储:更喜欢存储数据时间对象而不是字符串。例如,使用数据库引擎的
timestamp with time zone
或datetime
数据类型 and/or 让 Hibernate 5 保存一个Instant
或其他日期时间对象。
代码分析器
TL;DR:代码分析器当然可以帮助我们发现代码中的错误和其他不足之处。他们当然也有一些误报。
我确实信任代码分析器。我对 SonarQube 没有任何经验。我是一个快乐的 SpotBugs 用户。当我确信代码符合我的要求时,我还选择性地告诉 SpotBugs 忽略我编写的某些方法中的某些规则。我非常确信我会对 SonarQube 做同样的事情。