提问人:JJD 提问时间:1/15/2021 最后编辑:JJD 更新时间:1/16/2021 访问量:298
如何稳定片状的 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 终端 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_HOME
java
sudo 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 location
File > Project Structure ... > SDK 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);
输出为:
19-1-22 上午1:00
这在日期和时间之间有一个逗号,与您在 Android Studio 中获得的内容一致。
但恐怕成功的故事到此为止。我在想,如果你接受逗号,你就可以在两种环境中生成它,你的测试就会通过。但是我试图在 Java 8 或更低版本上产生相同的行为没有成功。
Desktop Java 最多从四个来源获取其语言环境数据:
区域设置数据有四个不同的来源,由 以下关键字:
CLDR
表示 Unicode CLDR 项目提供的区域设置数据。HOST
表示当前用户对基础操作系统设置的自定义。它仅适用于用户的 默认区域设置,可自定义的设置可能因 操作系统。但是,主要是日期、时间、数字和 支持货币格式。SPI
表示由已安装的服务提供程序接口 (SPI) 提供程序实现的区分区域设置的服务。COMPAT
(以前称为 )表示与 JDK 9 之前的版本兼容的区域设置数据。 仍可用作 该值,但首选。JRE
JRE
COMPAT
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 语言环境数据
评论
actual
Locale.setDefault()
DateFormatter#dateShortTimeShortFormatter