2

给定以下基于ThreeTenBpDateFormatter

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 ( LANG=en_US.UTF-8) 中的 Ubuntu 20.04 上运行以下测试:

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 VV 的评论和回答,我发现测试在 shell 中的行为因 Java 版本而异。Gradle 拿起JAVA_HOME- 因此我需要更新环境变量。请注意,将符号链接更改为java可执行文件 viasudo 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位置

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

有关的

4

1 回答 1

3

Locale data

Date and time formats are in the locale data. So you have got different locale data in your Android Java and the Java on your Ubuntu. Java can get its locale data from different sources, and you can to some extent control which.

I ran this on ThreeTen Backport and Java 9 (pardon my 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);

Output was:

1/22/19, 1:00 AM

This has a comma between date and time and agrees with what you got in Android Studio.

But I’m afraid that the success story ends here. I was thinking that if you would accept the comma, you could produce it in both environments, and your tests would pass. But I was unsuccessful trying to produce the same behaviour on Java 8 or lower.

Desktop Java gets its locale data from up to four sources:

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.

The default in Java 9 is equivalent to CLDR,COMPAT,SPI (CLDR is for Common Locale Data Repository). So theoretically I should be able to get the same locale data in Java 8 by using this setting:

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

But no:

1/22/19 1:00 AM

There’s no comma here. It agrees with what you expected and seem to have got on your Ubuntu. The explanation that I know is that CLDR also comes in versions, so I gather that the CLDR version bundled with Java 8 differs from the one in Java 9.

I am no Android developer. I don’t know from where Android gets its locale data or whether you can control that. You may want to go searching for options.

Of course if you can upgrade Java on your Ubuntu to Java 9 or later, that does seem to give you behaviour consistent with Android Studio.

Or you may consider renouncing on testing the exact format. Locale data are input to your program, not part of your program, and unit testing input doesn’t make sense in the end, it’s a contradiction in terms.

Link

CLDR Locale Data Enabled by Default from Internationalization Enhancements in JDK 9

于 2021-01-15T20:45:58.487 回答