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

How to stabilize flaky DateTimeFormatter#ofLocalizedDateTime test?

给出以下 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))
    }

    /* ... */
}

我正在 运行GNOME 终端 Ubuntu 20.04 上进行以下测试 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"

Java 2021 年 1 月 16 日新闻

Based Ole V.V.'s comment and answer 我发现测试在 shell 中的行为因 Java 版本而异。 Gradle 选择 JAVA_HOME - 因此我需要更新环境变量。请注意,通过 sudo update-alternatives --config java 将符号链接更改为 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 版本。

解决方案:Java/JDK 在 Android Studio

在 IDE 中,我还可以通过 File > Project Structure ... > SDK location 更改项目的 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 或更低版本上产生相同的行为是不成功的。

桌面 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.

Java9 中的默认值等同于CLDR,COMPAT,SPI(CLDR 用于通用语言环境数据存储库)。所以理论上我应该能够通过使用此设置在 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 一致。

或者您可以考虑放弃测试确切的格式。 Locale数据是你程序的输入,不是你程序的一部分,单元测试输入到最后也没有意义,自相矛盾

Link

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