am 1ff54651: Merge "Change the DateFormatSymbols serialized form"
* commit '1ff5465107e518d45cadeebbe70d23e2a8f8c56e':
Change the DateFormatSymbols serialized form
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
index 70e41a2..1a6a25e 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
@@ -16,8 +16,6 @@
*/
package org.apache.harmony.tests.java.text;
-import java.io.File;
-import java.net.URL;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
@@ -25,7 +23,6 @@
import java.text.DateFormatSymbols;
import java.util.Arrays;
import java.util.Locale;
-import java.util.ServiceConfigurationError;
public class DateFormatSymbolsTest extends junit.framework.TestCase {
@@ -402,13 +399,6 @@
dfs = new DateFormatSymbols(new Locale("en", "us"));
}
- /**
- * Tears down the fixture, for example, close a network connection. This
- * method is called after a test is executed.
- */
- protected void tearDown() {
- }
-
// Test serialization mechanism of DateFormatSymbols
public void test_serialization() throws Exception {
DateFormatSymbols symbols = new DateFormatSymbols(Locale.FRANCE);
@@ -426,7 +416,6 @@
DateFormatSymbols symbolsD = (DateFormatSymbols) objectIStream
.readObject();
- // The associated currency will not persist
String[][] zoneStringsD = symbolsD.getZoneStrings();
assertNotNull(zoneStringsD);
assertEquals(symbols, symbolsD);
diff --git a/luni/src/main/java/java/text/DateFormatSymbols.java b/luni/src/main/java/java/text/DateFormatSymbols.java
index 54a1a3f..56c1e65 100644
--- a/luni/src/main/java/java/text/DateFormatSymbols.java
+++ b/luni/src/main/java/java/text/DateFormatSymbols.java
@@ -63,15 +63,13 @@
// Localized display names.
String[][] zoneStrings;
- // Has the user called setZoneStrings?
- transient boolean customZoneStrings;
- /**
- * Locale, necessary to lazily load time zone strings. We force the time
- * zone names to load upon serialization, so this will never be needed
- * post deserialization.
+ /*
+ * Locale, necessary to lazily load time zone strings. Added to the serialized form for
+ * Android's L release. May be null if the object was deserialized using data from older
+ * releases. See b/16502916.
*/
- transient final Locale locale;
+ final Locale locale;
/**
* Gets zone strings, initializing them if necessary. Does not create
@@ -153,11 +151,12 @@
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
- // NOTE: We don't serialize the locale we were created with, so we can't
- // get back the localeData object we want. This is broken for callers that
- // access this field directly (i.e, SimpleDateFormat). We should ideally
- // have serialized the locale we were created with. See b/16502916.
- this.localeData = LocaleData.get(Locale.getDefault());
+ Locale locale = this.locale;
+ if (locale == null) {
+ // Before the L release Android did not serialize the locale. Handle its absence.
+ locale = Locale.getDefault();
+ }
+ this.localeData = LocaleData.get(locale);
}
private void writeObject(ObjectOutputStream oos) throws IOException {
@@ -220,7 +219,6 @@
// 'zoneStrings' is so large, we just print a representative value.
return getClass().getName() +
"[amPmStrings=" + Arrays.toString(ampms) +
- ",customZoneStrings=" + customZoneStrings +
",eras=" + Arrays.toString(eras) +
",localPatternChars=" + localPatternChars +
",months=" + Arrays.toString(months) +
@@ -488,6 +486,25 @@
}
}
this.zoneStrings = clone2dStringArray(zoneStrings);
- this.customZoneStrings = true;
+ }
+
+ /**
+ * Returns the display name of the timezone specified. Returns null if no name was found in the
+ * zone strings.
+ *
+ * @param daylight whether to return the daylight savings or the standard name
+ * @param style one of the {@link TimeZone} styles such as {@link TimeZone#SHORT}
+ *
+ * @hide used internally
+ */
+ String getTimeZoneDisplayName(TimeZone tz, boolean daylight, int style) {
+ if (style != TimeZone.SHORT && style != TimeZone.LONG) {
+ throw new IllegalArgumentException("Bad style: " + style);
+ }
+
+ // If custom zone strings have been set with setZoneStrings() we use those. Otherwise we
+ // use the ones from LocaleData.
+ String[][] zoneStrings = internalZoneStrings();
+ return TimeZoneNames.getDisplayName(zoneStrings, tz.getID(), daylight, style);
}
}
diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java
index 259bfe0..c1a8ee9 100644
--- a/luni/src/main/java/java/text/SimpleDateFormat.java
+++ b/luni/src/main/java/java/text/SimpleDateFormat.java
@@ -740,15 +740,9 @@
TimeZone tz = calendar.getTimeZone();
boolean daylight = (calendar.get(Calendar.DST_OFFSET) != 0);
int style = count < 4 ? TimeZone.SHORT : TimeZone.LONG;
- if (!formatData.customZoneStrings) {
- buffer.append(tz.getDisplayName(daylight, style, formatData.locale));
- return;
- }
- // We can't call TimeZone.getDisplayName() because it would not use
- // the custom DateFormatSymbols of this SimpleDateFormat.
- String custom = TimeZoneNames.getDisplayName(formatData.zoneStrings, tz.getID(), daylight, style);
- if (custom != null) {
- buffer.append(custom);
+ String zoneString = formatData.getTimeZoneDisplayName(tz, daylight, style);
+ if (zoneString != null) {
+ buffer.append(zoneString);
return;
}
}
@@ -759,21 +753,11 @@
// See http://www.unicode.org/reports/tr35/#Date_Format_Patterns for the different counts.
// @param generalTimeZone "GMT-08:00" rather than "-0800".
private void appendNumericTimeZone(StringBuffer buffer, int count, boolean generalTimeZone) {
- int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
- char sign = '+';
- if (offset < 0) {
- sign = '-';
- offset = -offset;
- }
- if (generalTimeZone || count == 4) {
- buffer.append("GMT");
- }
- buffer.append(sign);
- appendNumber(buffer, 2, offset / 3600000);
- if (generalTimeZone || count >= 4) {
- buffer.append(':');
- }
- appendNumber(buffer, 2, (offset % 3600000) / 60000);
+ int offsetMillis = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
+ boolean includeGmt = generalTimeZone || count == 4;
+ boolean includeMinuteSeparator = generalTimeZone || count >= 4;
+ buffer.append(TimeZone.createGmtOffsetString(includeGmt, includeMinuteSeparator,
+ offsetMillis));
}
private void appendMilliseconds(StringBuffer buffer, int count, int value) {
diff --git a/luni/src/main/java/java/util/TimeZone.java b/luni/src/main/java/java/util/TimeZone.java
index e4c68c5..854a4a6 100644
--- a/luni/src/main/java/java/util/TimeZone.java
+++ b/luni/src/main/java/java/util/TimeZone.java
@@ -63,7 +63,7 @@
*
* @see Calendar
* @see GregorianCalendar
- * @see SimpleDateFormat
+ * @see java.text.SimpleDateFormat
*/
public abstract class TimeZone implements Serializable, Cloneable {
private static final long serialVersionUID = 3581463369166924961L;
@@ -206,27 +206,48 @@
// upgrade to icu4c 50 and rewrite the underlying native code. See also the
// "element[j] != null" check in SimpleDateFormat.parseTimeZone, and the extra work in
// DateFormatSymbols.getZoneStrings.
-
- int offset = getRawOffset();
+ int offsetMillis = getRawOffset();
if (daylightTime) {
- offset += getDSTSavings();
+ offsetMillis += getDSTSavings();
}
- offset /= 60000;
+ return createGmtOffsetString(true /* includeGmt */, true /* includeMinuteSeparator */,
+ offsetMillis);
+ }
+
+ /**
+ * Returns a string representation of an offset from UTC.
+ *
+ * <p>The format is "[GMT](+|-)HH[:]MM". The output is not localized.
+ *
+ * @param includeGmt true to include "GMT", false to exclude
+ * @param includeMinuteSeparator true to include the separator between hours and minutes, false
+ * to exclude.
+ * @param offsetMillis the offset from UTC
+ *
+ * @hide used internally by SimpleDateFormat
+ */
+ public static String createGmtOffsetString(boolean includeGmt,
+ boolean includeMinuteSeparator, int offsetMillis) {
+ int offsetMinutes = offsetMillis / 60000;
char sign = '+';
- if (offset < 0) {
+ if (offsetMinutes < 0) {
sign = '-';
- offset = -offset;
+ offsetMinutes = -offsetMinutes;
}
StringBuilder builder = new StringBuilder(9);
- builder.append("GMT");
+ if (includeGmt) {
+ builder.append("GMT");
+ }
builder.append(sign);
- appendNumber(builder, 2, offset / 60);
- builder.append(':');
- appendNumber(builder, 2, offset % 60);
+ appendNumber(builder, 2, offsetMinutes / 60);
+ if (includeMinuteSeparator) {
+ builder.append(':');
+ }
+ appendNumber(builder, 2, offsetMinutes % 60);
return builder.toString();
}
- private void appendNumber(StringBuilder builder, int count, int value) {
+ private static void appendNumber(StringBuilder builder, int count, int value) {
String string = Integer.toString(value);
for (int i = 0; i < count - string.length(); i++) {
builder.append('0');
diff --git a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
index 9ab6f70..057cd17 100644
--- a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
+++ b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
@@ -46,8 +46,8 @@
}
public void testSerialization() throws Exception {
- // Set the default locale. The default locale determines what strings are used by the
- // DateFormatSymbols after deserialization.
+ // 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.
@@ -64,17 +64,16 @@
DateFormatSymbols deserializedDfs = (DateFormatSymbols) in.readObject();
assertEquals(-1, in.read());
- // The two objects should claim to be equal, even though they aren't really.
+ // 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));
- // But the deserialized object is screwed because the RI's serialized form doesn't
- // contain the locale or the necessary strings. Don't serialize DateFormatSymbols, folks!
+ // And so does the deserialized version.
assertEquals("stycznia", formatDate(pl, "MMMM", deserializedDfs));
- assertEquals("January", formatDate(pl, "LLLL", deserializedDfs));
+ assertEquals("stycze\u0144", formatDate(pl, "LLLL", deserializedDfs));
}
private String formatDate(Locale l, String fmt, DateFormatSymbols dfs) {