kotlin - 如何稳定不稳定的 DateTimeFormatter#ofLocalizedDateTime 测试?

标签 kotlin date-formatting date-comparison threetenbp datetimeformatter

鉴于以下ThreeTenBp基于DateFormatter:

class DateFormatter private constructor() {

    private val dateShortTimeShortFormatter = 
        org.threeten.bp.format.DateTimeFormatter.ofLocalizedDateTime(
            FormatStyle.SHORT, FormatStyle.SHORT)

    fun getFormattedDateTimeShort(time: Long): String {
        return dateShortTimeShortFormatter.withZone(ZoneId.systemDefault())
                                          .format(Instant.ofEpochMilli(time))
    }

    /* ... */
}

我正在 Ubuntu 20.04 上的 GNOME 终端 shell (LANG=en_US.UTF-8) 中运行以下测试:

class DateFormatterTest {

    private val systemTimezone = TimeZone.getDefault()
    private val systemLocale = Locale.getDefault()

    @Before
    fun resetTimeZone() {
        TimeZone.setDefault(TimeZone.getTimeZone("GMT+1"))
    }
    
    @After
    fun resetSystemDefaults() {
        Locale.setDefault(systemLocale)
        TimeZone.setDefault(systemTimezone)
    }


    @Test
    fun getFormattedDateTimeShort() {
        Locale.setDefault(Locale.US)
        assertThat(DateFormatter.newInstance().getFormattedDateTimeShort(1548115200000L))
                                              .isEqualTo("1/22/19 1:00 AM")    
    }

}

成功了。

当我在 Android Studio 4.2 Beta 3 或 Android Studio 2020.3.1 Canary 4 中运行它时,它失败并出现以下错误:

org.junit.ComparisonFailure:
Expected :"1/22/19 1:00 AM"
Actual :"1/22/19, 1:00 AM"

截至 2021 年 1 月 16 日的 Java 新闻

根据 Ole V.V. 的评论和回答,我发现测试在 shell 中的行为有所不同,具体取决于 Java 版本。 Gradle 选择 JAVA_HOME - 因此我需要更新环境变量。请注意,通过 sudo update-alternatives --config java 将符号链接(symbolic link)更改为 java 可执行文件没有任何效果。

export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64

-> Test succeeds

export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64

-> Test fails

export JAVA_HOME=/usr/lib/jvm/java-14-openjdk-amd64

-> Process 'Gradle Test Executor 1' finished with non-zero exit value 134

Java/JRE 9 不适用于此 Ubuntu 版本。

解决方案:Android Studio 中的 Java/JDK

在 IDE 中,我还可以通过 File > Project Structure ... > SDK location 更改项目的 JDK 位置:

JDK location

一旦我从预配置的 JDK 11(在 IDE 安装文件夹内)更改为 JDK 8 - 那么测试成功!

相关

最佳答案

区域设置数据

日期和时间格式位于区域设置数据中。因此,您的 Android Java 和 Ubuntu 上的 Java 中具有不同的区域设置数据。 Java 可以从不同的来源获取其语言环境数据,并且您可以在某种程度上控制哪些来源。

我在 ThreeTen Backport 和 Java 9 上运行了这个(请原谅我的 Java):

    Locale.setDefault(Locale.US);
    System.setProperty("user.timezone", "GMT+01:00");
    
    DateTimeFormatter dateShortTimeShortFormatter
            = org.threeten.bp.format.DateTimeFormatter.ofLocalizedDateTime(
                    FormatStyle.SHORT, FormatStyle.SHORT);
    
    String text = dateShortTimeShortFormatter.withZone(ZoneId.systemDefault())
            .format(Instant.ofEpochMilli(1548115200000L));
    
    System.out.println(text);

输出为:

1/22/19, 1:00 AM

日期和时间之间有一个逗号,与您在 Android Studio 中获得的内容一致。

但恐怕成功故事就到此为止了。我在想,如果您接受逗号,您就可以在两种环境中生成它,并且您的测试就会通过。但我没有成功尝试在 Java 8 或更低版本上产生相同的行为。

Desktop Java 从最多四个来源获取其区域设置数据:

There are four distinct sources for locale data, identified by the following keywords:

  • CLDR represents the locale data provided by the Unicode CLDR project.
  • HOST represents the current user's customization of the underlying operating system's settings. It works only with the user's default locale, and the customizable settings may vary depending on the operating system. However, primarily date, time, number, and currency formats are supported.
  • SPI represents the locale-sensitive services implemented by the installed Service Provider Interface (SPI) providers.
  • COMPAT (formerly called JRE) represents the locale data that is compatible with releases prior to JDK 9. JRE can still be used as the value, but COMPAT is preferred.

Java 9 中的默认值相当于 CLDR,COMPAT,SPI(CLDR 代表 Common Locale Data Repository)。因此理论上我应该能够通过使用此设置在 Java 8 中获取相同的语言环境数据:

    System.setProperty("java.locale.providers", "CLDR,COMPAT,SPI");

但是没有:

1/22/19 1:00 AM

这里没有逗号。它与您的预期一致,并且似乎已经在您的 Ubuntu 上实现了。我知道的解释是 CLDR 也有版本,因此我推测与 Java 8 捆绑的 CLDR 版本与 Java 9 中的版本不同。

我不是 Android 开发人员。我不知道 Android 从哪里获取区域设置数据,也不知道你是否可以控制它。您可能想要寻找选项。

当然,如果您可以将 Ubuntu 上的 Java 升级到 Java 9 或更高版本,那似乎确实会提供与 Android Studio 一致的行为。

或者您可以考虑放弃测试确切的格式。语言环境数据是输入到程序中的,而不是程序的一部分,单元测试输入最终没有意义,这是一个术语上的矛盾。

链接

CLDR Locale Data Enabled by Default来自JDK 9 中的国际化增强

关于kotlin - 如何稳定不稳定的 DateTimeFormatter#ofLocalizedDateTime 测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65732319/

相关文章:

php - 从 "Y-m-d H:i:s"格式的日期数组中获取最近的日期

mysql - where 子句中的 SQL 日期比较未按预期工作

java - 我应该如何正确比较两个日期?

kotlin - Kotlin中Java8时间对比

kotlin - 所有生产者协程完成后如何关闭 channel ?

Java 自定义日期参数输入

R - 日期格式检查

kotlin - bool 值的使用?在 if 表达式中

android - async{} 内部流

cocoa - 将 NSDate 格式化为年、月、日、时、分、秒的特定样式