blob: baa3344efe513cc9a95e414f2a7a14feddb1d6f7 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package libcore.java.text;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.TimeZone;
public class DateFormatSymbolsTest extends junit.framework.TestCase {
/**
* The list of time zone ids formatted as "UTC".
*/
private static final String[] UTC_ZONE_IDS = new String[] {
"Etc/UCT", "Etc/UTC", "Etc/Universal", "Etc/Zulu", "UCT", "UTC", "Universal", "Zulu"
};
private void assertLocaleIsEquivalentToRoot(Locale locale) {
DateFormatSymbols dfs = DateFormatSymbols.getInstance(locale);
assertEquals(DateFormatSymbols.getInstance(Locale.ROOT), dfs);
}
/** http://b/3056586 */
public void test_getInstance_unknown_locale() throws Exception {
// TODO: we fail this test. on Android, the root locale uses GMT offsets as names.
// see the invalid locale test below. on the RI, the root locale uses English names.
assertLocaleIsEquivalentToRoot(new Locale("xx", "XX"));
}
public void test_getInstance_invalid_locale() throws Exception {
assertLocaleIsEquivalentToRoot(new Locale("not exist language", "not exist country"));
}
public void testSerialization() throws Exception {
// Set the default locale. The default locale used to determine what strings were used by
// the DateFormatSymbols after deserialization. See http://b/16502916
Locale.setDefault(Locale.US);
// The Polish language needs stand-alone month and weekday names.
Locale pl = new Locale("pl");
DateFormatSymbols originalDfs = new DateFormatSymbols(pl);
// Serialize...
ByteArrayOutputStream out = new ByteArrayOutputStream();
new ObjectOutputStream(out).writeObject(originalDfs);
byte[] bytes = out.toByteArray();
// Deserialize...
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
DateFormatSymbols deserializedDfs = (DateFormatSymbols) in.readObject();
assertEquals(-1, in.read());
// The two objects be equal.
assertEquals(originalDfs, deserializedDfs);
// The original differentiates between regular month names and stand-alone month names...
assertEquals("stycznia", formatDate(pl, "MMMM", originalDfs));
assertEquals("stycze\u0144", formatDate(pl, "LLLL", originalDfs));
// And so does the deserialized version.
assertEquals("stycznia", formatDate(pl, "MMMM", deserializedDfs));
assertEquals("stycze\u0144", formatDate(pl, "LLLL", deserializedDfs));
}
private String formatDate(Locale l, String fmt, DateFormatSymbols dfs) {
SimpleDateFormat sdf = new SimpleDateFormat(fmt, l);
sdf.setDateFormatSymbols(dfs);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.format(new Date(0));
}
public void test_getZoneStrings_cloning() throws Exception {
// Check that corrupting our array doesn't affect other callers.
// Kill a row.
{
String[][] originalZoneStrings = DateFormatSymbols.getInstance(Locale.US).getZoneStrings();
assertNotNull(originalZoneStrings[0]);
originalZoneStrings[0] = null;
String[][] currentZoneStrings = DateFormatSymbols.getInstance(Locale.US).getZoneStrings();
assertNotNull(currentZoneStrings[0]);
}
// Kill an element.
{
String[][] originalZoneStrings = DateFormatSymbols.getInstance(Locale.US).getZoneStrings();
assertNotNull(originalZoneStrings[0][0]);
originalZoneStrings[0][0] = null;
String[][] currentZoneStrings = DateFormatSymbols.getInstance(Locale.US).getZoneStrings();
assertNotNull(currentZoneStrings[0][0]);
}
}
public void test_getZoneStrings_UTC() throws Exception {
assertUtc(Locale.US);
assertUtc(Locale.FRANCE);
assertUtc(Locale.CHINA);
}
private static void assertUtc(Locale locale) {
HashSet<String> utcZones = new HashSet<>(Arrays.asList(UTC_ZONE_IDS));
for (String[] row : DateFormatSymbols.getInstance(locale).getZoneStrings()) {
if (utcZones.contains(row[0])) {
// Element 0 is the Olson id.
// The long name (1) can be anything providing it isn't GMT+00:00.
assertFalse(Arrays.toString(row), row[1].startsWith("GMT"));
// The short name (2) is always "UTC".
assertEquals(Arrays.toString(row), "UTC", row[2]);
// The _DST variants (3) and (4) do not apply for UTC so ICU won't provide strings.
}
}
}
// http://b/8128460
// If icu4c doesn't actually have a name, we arrange to return null from native code rather
// that use icu4c's probably-out-of-date time zone transition data.
// getZoneStrings has to paper over this.
public void test_getZoneStrings_no_nulls() throws Exception {
String[][] array = DateFormatSymbols.getInstance(Locale.US).getZoneStrings();
int failCount = 0;
for (String[] row : array) {
for (String element : row) {
if (element == null) {
System.err.println(Arrays.toString(row));
++failCount;
}
}
}
assertEquals(0, failCount);
}
// http://b/7955614
public void test_getZoneStrings_Apia() {
String[][] array = DateFormatSymbols.getInstance(Locale.US).getZoneStrings();
for (int i = 0; i < array.length; ++i) {
String[] row = array[i];
// Pacific/Apia is somewhat arbitrary; we just want a zone we have to generate
// "GMT" strings for the short names.
if (row[0].equals("Pacific/Apia")) {
TimeZone apiaTz = TimeZone.getTimeZone("Pacific/Apia");
assertEquals("Apia Standard Time", row[1]);
assertEquals(formattedStandardTimeOffset(apiaTz), row[2]);
assertEquals("Apia Daylight Time", row[3]);
assertEquals(formattedDstOffset(apiaTz), row[4]);
}
}
}
private static String formattedStandardTimeOffset(TimeZone tz) {
return formattedOffset(tz.getRawOffset());
}
private static String formattedDstOffset(TimeZone tz) {
return formattedOffset(tz.getRawOffset() + tz.getDSTSavings());
}
private static String formattedOffset(int offset) {
String pattern = "GMT%+d:%02d";
int millisInHour = 60 * 60 * 1_000;
int hours = offset / millisInHour;
int minutes = (offset - hours * millisInHour) / 1_000 / 60;
return String.format(pattern, hours, minutes);
}
public void test_setZoneStrings_checks_dimensions() throws Exception {
DateFormatSymbols dfs = DateFormatSymbols.getInstance();
String[][] zoneStrings = dfs.getZoneStrings();
zoneStrings[0] = new String[] { "id_only " };
try {
dfs.setZoneStrings(zoneStrings);
fail("No IllegalArgumentException when setting incorrect zoneStrings");
} catch (IllegalArgumentException e) {
// expected
}
}
public void test_zoneStrings_are_lazy() throws Exception {
DateFormatSymbols dfs = DateFormatSymbols.getInstance();
assertFalse("Newly created DFS should have no zoneStrings", hasZoneStringsFieldValue(dfs));
dfs.hashCode();
assertFalse("hashCode() should not need zoneStrings", hasZoneStringsFieldValue(dfs));
DateFormatSymbols otherDfs = DateFormatSymbols.getInstance();
dfs.equals(otherDfs);
assertFalse("equals() should usually not need zoneStrings", hasZoneStringsFieldValue(dfs));
otherDfs.getZoneStrings();
assertTrue("getZoneStrings() needs zoneStrings", hasZoneStringsFieldValue(otherDfs));
otherDfs.setZoneStrings(otherDfs.getZoneStrings());
dfs.equals(otherDfs);
assertTrue("equals() needs zoneStrings when other object has user-provided values",
hasZoneStringsFieldValue(dfs));
}
/**
* Return {@code true} iff {@code dfs} has a non-null {@code zoneStrings}. This introspection is
* necessary, because as a lazy field it having a value should not otherwise be observable.
*/
private static boolean hasZoneStringsFieldValue(DateFormatSymbols dfs) throws Exception {
Field field = DateFormatSymbols.class.getDeclaredField("zoneStrings");
field.setAccessible(true);
return field.get(dfs) != null;
}
}