Locale-awareness for date parsing and formatting:
- always format in en_US for best interchange - always parse in en_US, system locale and ISO-8601
This commit is contained in:
parent
4fc0577933
commit
6e81cfdbb4
@ -44,6 +44,7 @@ import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TimeZone;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -287,29 +288,40 @@ final class DefaultTypeAdapters {
|
||||
}
|
||||
|
||||
static class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
|
||||
private final DateFormat format;
|
||||
private final DateFormat enUsFormat;
|
||||
private final DateFormat localFormat;
|
||||
private final DateFormat iso8601Format;
|
||||
|
||||
DefaultDateTypeAdapter() {
|
||||
this.format = DateFormat.getDateTimeInstance();
|
||||
this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US),
|
||||
DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));
|
||||
}
|
||||
|
||||
DefaultDateTypeAdapter(final String datePattern) {
|
||||
this.format = new SimpleDateFormat(datePattern);
|
||||
DefaultDateTypeAdapter(String datePattern) {
|
||||
this(new SimpleDateFormat(datePattern, Locale.US), new SimpleDateFormat(datePattern));
|
||||
}
|
||||
|
||||
DefaultDateTypeAdapter(final int style) {
|
||||
this.format = DateFormat.getDateInstance(style);
|
||||
DefaultDateTypeAdapter(int style) {
|
||||
this(DateFormat.getDateInstance(style, Locale.US), DateFormat.getDateInstance(style));
|
||||
}
|
||||
|
||||
public DefaultDateTypeAdapter(final int dateStyle, final int timeStyle) {
|
||||
this.format = DateFormat.getDateTimeInstance(dateStyle, timeStyle);
|
||||
public DefaultDateTypeAdapter(int dateStyle, int timeStyle) {
|
||||
this(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US),
|
||||
DateFormat.getDateTimeInstance(dateStyle, timeStyle));
|
||||
}
|
||||
|
||||
public DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) {
|
||||
this.enUsFormat = enUsFormat;
|
||||
this.localFormat = localFormat;
|
||||
this.iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||
this.iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
// These methods need to be synchronized since JDK DateFormat classes are not thread-safe
|
||||
// See issue 162
|
||||
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
synchronized (format) {
|
||||
String dateFormatAsString = format.format(src);
|
||||
synchronized (localFormat) {
|
||||
String dateFormatAsString = enUsFormat.format(src);
|
||||
return new JsonPrimitive(dateFormatAsString);
|
||||
}
|
||||
}
|
||||
@ -319,12 +331,20 @@ final class DefaultTypeAdapters {
|
||||
if (!(json instanceof JsonPrimitive)) {
|
||||
throw new JsonParseException("The date should be a string value");
|
||||
}
|
||||
try {
|
||||
synchronized (format) {
|
||||
return format.parse(json.getAsString());
|
||||
synchronized (localFormat) {
|
||||
try {
|
||||
return localFormat.parse(json.getAsString());
|
||||
} catch (ParseException ignored) {
|
||||
}
|
||||
try {
|
||||
return enUsFormat.parse(json.getAsString());
|
||||
} catch (ParseException ignored) {
|
||||
}
|
||||
try {
|
||||
return iso8601Format.parse(json.getAsString());
|
||||
} catch (ParseException e) {
|
||||
throw new JsonSyntaxException(json.getAsString(), e);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,7 +352,7 @@ final class DefaultTypeAdapters {
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(DefaultDateTypeAdapter.class.getSimpleName());
|
||||
sb.append('(').append(format.getClass().getSimpleName()).append(')');
|
||||
sb.append('(').append(localFormat.getClass().getSimpleName()).append(')');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@ -454,7 +474,7 @@ final class DefaultTypeAdapters {
|
||||
throw new JsonParseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public JsonElement serialize(InetAddress src, Type typeOfSrc,
|
||||
JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.getHostAddress());
|
||||
|
@ -17,12 +17,12 @@
|
||||
package com.google.gson;
|
||||
|
||||
import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* A simple unit test for the {@link DefaultDateTypeAdapter} class.
|
||||
@ -31,6 +31,100 @@ import java.util.Date;
|
||||
*/
|
||||
public class DefaultDateTypeAdapterTest extends TestCase {
|
||||
|
||||
public void testFormattingInEnUs() {
|
||||
testFormattingAlwaysEmitsUsLocale(Locale.US);
|
||||
}
|
||||
|
||||
public void testFormattingInFr() {
|
||||
testFormattingAlwaysEmitsUsLocale(Locale.FRANCE);
|
||||
}
|
||||
|
||||
private void testFormattingAlwaysEmitsUsLocale(Locale locale) {
|
||||
TimeZone defaultTimeZone = TimeZone.getDefault();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
Locale defaultLocale = Locale.getDefault();
|
||||
Locale.setDefault(locale);
|
||||
try {
|
||||
assertFormatted("Jan 1, 1970 12:00:00 AM", new DefaultDateTypeAdapter());
|
||||
assertFormatted("1/1/70", new DefaultDateTypeAdapter(DateFormat.SHORT));
|
||||
assertFormatted("Jan 1, 1970", new DefaultDateTypeAdapter(DateFormat.MEDIUM));
|
||||
assertFormatted("January 1, 1970", new DefaultDateTypeAdapter(DateFormat.LONG));
|
||||
assertFormatted("1/1/70 12:00 AM",
|
||||
new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT));
|
||||
assertFormatted("Jan 1, 1970 12:00:00 AM",
|
||||
new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM));
|
||||
assertFormatted("January 1, 1970 12:00:00 AM UTC",
|
||||
new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG));
|
||||
assertFormatted("Thursday, January 1, 1970 12:00:00 AM UTC",
|
||||
new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL));
|
||||
} finally {
|
||||
TimeZone.setDefault(defaultTimeZone);
|
||||
Locale.setDefault(defaultLocale);
|
||||
}
|
||||
}
|
||||
|
||||
public void testParsingDatesFormattedWithSystemLocale() {
|
||||
TimeZone defaultTimeZone = TimeZone.getDefault();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
Locale defaultLocale = Locale.getDefault();
|
||||
Locale.setDefault(Locale.FRANCE);
|
||||
try {
|
||||
assertParsed("1 janv. 1970 00:00:00", new DefaultDateTypeAdapter());
|
||||
assertParsed("01/01/70", new DefaultDateTypeAdapter(DateFormat.SHORT));
|
||||
assertParsed("1 janv. 1970", new DefaultDateTypeAdapter(DateFormat.MEDIUM));
|
||||
assertParsed("1 janvier 1970", new DefaultDateTypeAdapter(DateFormat.LONG));
|
||||
assertParsed("01/01/70 00:00",
|
||||
new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT));
|
||||
assertParsed("1 janv. 1970 00:00:00",
|
||||
new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM));
|
||||
assertParsed("1 janvier 1970 00:00:00 UTC",
|
||||
new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG));
|
||||
assertParsed("jeudi 1 janvier 1970 00 h 00 UTC",
|
||||
new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL));
|
||||
} finally {
|
||||
TimeZone.setDefault(defaultTimeZone);
|
||||
Locale.setDefault(defaultLocale);
|
||||
}
|
||||
}
|
||||
|
||||
public void testParsingDatesFormattedWithUsLocale() {
|
||||
TimeZone defaultTimeZone = TimeZone.getDefault();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
Locale defaultLocale = Locale.getDefault();
|
||||
Locale.setDefault(Locale.US);
|
||||
try {
|
||||
assertParsed("Jan 1, 1970 0:00:00 AM", new DefaultDateTypeAdapter());
|
||||
assertParsed("1/1/70", new DefaultDateTypeAdapter(DateFormat.SHORT));
|
||||
assertParsed("Jan 1, 1970", new DefaultDateTypeAdapter(DateFormat.MEDIUM));
|
||||
assertParsed("January 1, 1970", new DefaultDateTypeAdapter(DateFormat.LONG));
|
||||
assertParsed("1/1/70 0:00 AM",
|
||||
new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT));
|
||||
assertParsed("Jan 1, 1970 0:00:00 AM",
|
||||
new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM));
|
||||
assertParsed("January 1, 1970 0:00:00 AM UTC",
|
||||
new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG));
|
||||
assertParsed("Thursday, January 1, 1970 0:00:00 AM UTC",
|
||||
new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL));
|
||||
} finally {
|
||||
TimeZone.setDefault(defaultTimeZone);
|
||||
Locale.setDefault(defaultLocale);
|
||||
}
|
||||
}
|
||||
|
||||
public void testFormatUsesDefaultTimezone() {
|
||||
TimeZone defaultTimeZone = TimeZone.getDefault();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
|
||||
Locale defaultLocale = Locale.getDefault();
|
||||
Locale.setDefault(Locale.US);
|
||||
try {
|
||||
assertFormatted("Dec 31, 1969 4:00:00 PM", new DefaultDateTypeAdapter());
|
||||
assertParsed("Dec 31, 1969 4:00:00 PM", new DefaultDateTypeAdapter());
|
||||
} finally {
|
||||
TimeZone.setDefault(defaultTimeZone);
|
||||
Locale.setDefault(defaultLocale);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDateSerialization() throws Exception {
|
||||
int dateStyle = DateFormat.LONG;
|
||||
DefaultDateTypeAdapter dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle);
|
||||
@ -57,4 +151,14 @@ public class DefaultDateTypeAdapterTest extends TestCase {
|
||||
fail("Invalid date pattern should fail.");
|
||||
} catch (IllegalArgumentException expected) { }
|
||||
}
|
||||
|
||||
private void assertFormatted(String formatted, DefaultDateTypeAdapter adapter) {
|
||||
assertEquals(formatted, adapter.serialize(new Date(0), Date.class, null).getAsString());
|
||||
}
|
||||
|
||||
private void assertParsed(String date, DefaultDateTypeAdapter adapter) {
|
||||
assertEquals(date, new Date(0), adapter.deserialize(new JsonPrimitive(date), Date.class, null));
|
||||
assertEquals("ISO 8601", new Date(0), adapter.deserialize(
|
||||
new JsonPrimitive("1970-01-01T00:00:00Z"), Date.class, null));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user