如何稳定片状的 DateTimeFormatter#ofLocalizedDateTime 测试?

How to stabilize flaky DateTimeFormatter#ofLocalizedDateTime test?

提问人:JJD 提问时间:1/15/2021 最后编辑:JJD 更新时间:1/16/2021 访问量:298

问:

给定以下基于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 终端 shell 中的 Ubuntu 20.04 上运行以下测试():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:预期:“1/22/19 1:00 AM” 实际:“1/22/19,1:
00 AM”

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

根据 Ole V.V. 的评论和回答,我发现测试在 shell 中的行为因 Java 版本而异。Gradle 拾取 - 因此我需要更新环境变量。请注意,将符号链接更改为可执行文件 via 不起作用。JAVA_HOMEjavasudo update-alternatives --config 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中,我还可以通过以下方式更改项目的:JDK locationFile > Project Structure ... > SDK location

JDK location

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

相关

Kotlin 日期格式化 日期比较 3tenbp datetimeformatter

评论

0赞 jensgram 1/15/2021
绝对确定这两个 JVM 运行相同的语言环境吗?不确定短格式的实际定义位置,但日期和时间之间的逗号似乎表示运行时区域设置的差异。actual
0赞 JJD 1/15/2021
我以为我通过调用来控制执行测试时的区域设置 - 因此底层系统设置到哪个区域设置并不重要。Locale.setDefault()
0赞 jensgram 1/15/2021
事实上。不过,检查两个运行时应该很有趣。DateFormatter#dateShortTimeShortFormatter
1赞 Anonymous 1/16/2021
您在 Ubuntu 上使用哪个 Java 版本?
1赞 JJD 1/16/2021
Android Studio 2020.3.1 Canary 4 指向嵌入式 JRE 11.0.8。在shell中,我使用OpenJDK 1.8.0_275。

答:

3赞 Anonymous 1/16/2021 #1

区域设置数据

日期和时间格式位于区域设置数据中。因此,您的 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);

输出为:

19-1-22 上午1:00

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

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

Desktop Java 最多从四个来源获取其语言环境数据:

区域设置数据有四个不同的来源,由 以下关键字:

  • CLDR表示 Unicode CLDR 项目提供的区域设置数据。
  • HOST表示当前用户对基础操作系统设置的自定义。它仅适用于用户的 默认区域设置,可自定义的设置可能因 操作系统。但是,主要是日期、时间、数字和 支持货币格式。
  • SPI表示由已安装的服务提供程序接口 (SPI) 提供程序实现的区分区域设置的服务。
  • COMPAT(以前称为 )表示与 JDK 9 之前的版本兼容的区域设置数据。 仍可用作 该值,但首选。JREJRECOMPAT

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

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

但不是:

19-1-22 上午1:00

这里没有逗号。它符合您的预期,并且似乎已经进入了您的 Ubuntu。我所知道的解释是 CLDR 也有版本,因此我收集到 Java 8 捆绑的 CLDR 版本与 Java 9 中的版本不同。

我不是 Android 开发人员。我不知道 Android 从哪里获取其区域设置数据,或者您是否可以控制它。您可能想去搜索选项。

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

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

链接

默认情况下,从 JDK 9 中的国际化增强功能中启用 CLDR 语言环境数据

评论

1赞 JJD 1/16/2021
谢谢你的解释。我发现测试确实成功/失败取决于 Java 版本。看看我在上面的帖子中添加了什么。
0赞 JJD 1/16/2021
我在上面解释了如何让测试在Android Studio中通过。再次非常感谢。