Given the following ThreeTenBp based 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))
}
/* ... */
}
I am running the following test on Ubuntu 20.04 in the GNOME Terminal 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")
}
}
It succeeds.
When I run it in Android Studio 4.2 Beta 3 or Android Studio 2020.3.1 Canary 4 it fails with the following error:
org.junit.ComparisonFailure:
Expected :"1/22/19 1:00 AM"
Actual :"1/22/19, 1:00 AM"
Based Ole V.V.'s comment and answer I figured out that the test behaves different in the shell depending on the Java version. Gradle picks up JAVA_HOME
- therefore I need to update the environment variable. Please note that changing the symlink to the java
executable via sudo update-alternatives --config java
has no effect.
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 is not available for this Ubuntu version.
In the IDE I can also change the JDK location
for the project via File > Project Structure ... > SDK location
:
Once I change from the pre-configured JDK 11 (within the installation folder of the IDE) to JDK 8 - then the test succeeds!
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 calledJRE
) represents the locale data that is compatible with releases prior to JDK 9.JRE
can still be used as the value, butCOMPAT
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.
CLDR Locale Data Enabled by Default from Internationalization Enhancements in JDK 9