代码之家  ›  专栏  ›  技术社区  ›  PatPanda

Unicode而不是java。时间DateTimeFormatter应用于国际日期和时间

  •  0
  • PatPanda  · 技术社区  · 4 年前

    关于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);
    

    我使用的是Java11,希望避免导入这个IBM软件包。 我很不确定当前的问题是什么(到目前为止代码运行良好)。

    但我确实对解决这个问题很感兴趣。请问有没有一个好的方法来实现它?

    我会理解这样的评论: 禁用SonarQube,禁用这个规则,不要太关注那些假阳性,不要太重视分析工具等等

    但我对如何解决这个“Unicode ICU而不是java.time.DateTimeFormatter应该用于国际日期和时间”很感兴趣。

    非常感谢。

    0 回复  |  直到 4 年前
        1
  •  3
  •   Basil Bourque    4 年前

    tl;博士

    别担心。只需检查一下你的典型日期时间格式文本是否由 DateTimeFormatter 在您支持的任何地区都能满足用户的期望。如果是这样,就不需要让IBM的ICU图书馆参与进来。

    允许 JAVA时间 自动本地化表示日期时间值的文本。

    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 ) ;
    

    细节

    仅供参考,声纳信息指的是 CLDR 成为基于的Java实现中的默认语言环境资源 OpenJDK 适用于Java9及更高版本。看见 JEP 252: Use CLDR Locale Data by Default .Java 10 further support .

    你说:

    我很不确定当前的问题是什么(到目前为止代码运行良好)。

    我相信这不是代码工作的问题。

    问题是本地化是否适用于区域设置。

    日期时间值的格式因人类语言、文化和亚文化的不同而有很大差异。我们讨论的是翻译的单词,以及标点符号、缩写、元素顺序、大小写等规则。跟踪所有这些本地化信息需要大量数据。最重要的是,他们改变了。文化在变化,学术界也在理解变化。

    OpenJDK for Java 8及更早版本中默认使用的此类语言环境数据的实现相对有限且肤浅,没有覆盖许多子文化。

    相比之下,Unicode联盟管理的CLDR是 巨大的 详细,涵盖了许多亚文化。早期版本的OpenJDK包含CLDR的副本。但只有在Java9中,它才成为默认的语言环境资源,首先在那里进行查找。

    也许声纳信息是这样说的 java.time.DateTimeFormatter 在特定的亚文化差异方面存在一些问题。但我没有听说过。如果您担心的话,可以查看OpenJDK问题跟踪程序。

    我不会担心的。如果你知道你的应用程序将只用于少数特定的地区,请测试这些地区。从每个地区招募一组用户。查看应用程序的典型输出是否符合他们的期望。如果他们满意,写一些单元测试,今天到此为止。

    请记住,正如我所说,文化规范会随着时间的推移而改变。Unicode联盟跟踪这些变化,根据需要发布新版本的CLDR。当您更新Java实现时,可能会得到CLDR的更新版本。很可能在某一天,在某个语言环境中,当生成表示日期时间的文本时,您可能会得到不同的输出。如果您可能关心这些变化,请编写我上面提到的单元测试。

    仅供参考,Sonar消息中提到的库是 International Components for Unicode (ICU) 建造人 Taligent IBM ,现位于Unicode联盟: http://site.icu-project.org/

        2
  •  3
  •   Anonymous    4 年前

    巴兹尔·波尔克已经写下了明智的答案。我谦恭地补充一点。

    这是对的,也是错的。

    • 你得到的信息是 对的 那个 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 关于Java8
    • 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 数据库引擎的数据类型和/或让Hibernate 5保存 Instant 或其他日期时间对象。

    代码分析器

    TL;DR:代码分析器当然可以帮助我们发现代码中的错误和其他不足之处。他们当然也有一些误报。

    我确实信任代码分析器。我没有任何声纳Qube的经验。我是一个快乐的SpotBugs用户。当我确信代码符合我的要求时,我还会有选择地告诉SpotBugs忽略我编写的某些方法中的一些规则。我非常确信我也会用SonarQube做同样的事情。