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.Set;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.TimeZone;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -287,29 +288,40 @@ final class DefaultTypeAdapters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
|
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() {
|
DefaultDateTypeAdapter() {
|
||||||
this.format = DateFormat.getDateTimeInstance();
|
this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US),
|
||||||
|
DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultDateTypeAdapter(final String datePattern) {
|
DefaultDateTypeAdapter(String datePattern) {
|
||||||
this.format = new SimpleDateFormat(datePattern);
|
this(new SimpleDateFormat(datePattern, Locale.US), new SimpleDateFormat(datePattern));
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultDateTypeAdapter(final int style) {
|
DefaultDateTypeAdapter(int style) {
|
||||||
this.format = DateFormat.getDateInstance(style);
|
this(DateFormat.getDateInstance(style, Locale.US), DateFormat.getDateInstance(style));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultDateTypeAdapter(final int dateStyle, final int timeStyle) {
|
public DefaultDateTypeAdapter(int dateStyle, int timeStyle) {
|
||||||
this.format = DateFormat.getDateTimeInstance(dateStyle, 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
|
// These methods need to be synchronized since JDK DateFormat classes are not thread-safe
|
||||||
// See issue 162
|
// See issue 162
|
||||||
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
|
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
synchronized (format) {
|
synchronized (localFormat) {
|
||||||
String dateFormatAsString = format.format(src);
|
String dateFormatAsString = enUsFormat.format(src);
|
||||||
return new JsonPrimitive(dateFormatAsString);
|
return new JsonPrimitive(dateFormatAsString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,12 +331,20 @@ final class DefaultTypeAdapters {
|
|||||||
if (!(json instanceof JsonPrimitive)) {
|
if (!(json instanceof JsonPrimitive)) {
|
||||||
throw new JsonParseException("The date should be a string value");
|
throw new JsonParseException("The date should be a string value");
|
||||||
}
|
}
|
||||||
try {
|
synchronized (localFormat) {
|
||||||
synchronized (format) {
|
try {
|
||||||
return format.parse(json.getAsString());
|
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() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(DefaultDateTypeAdapter.class.getSimpleName());
|
sb.append(DefaultDateTypeAdapter.class.getSimpleName());
|
||||||
sb.append('(').append(format.getClass().getSimpleName()).append(')');
|
sb.append('(').append(localFormat.getClass().getSimpleName()).append(')');
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,7 +474,7 @@ final class DefaultTypeAdapters {
|
|||||||
throw new JsonParseException(e);
|
throw new JsonParseException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonElement serialize(InetAddress src, Type typeOfSrc,
|
public JsonElement serialize(InetAddress src, Type typeOfSrc,
|
||||||
JsonSerializationContext context) {
|
JsonSerializationContext context) {
|
||||||
return new JsonPrimitive(src.getHostAddress());
|
return new JsonPrimitive(src.getHostAddress());
|
||||||
|
@ -17,12 +17,12 @@
|
|||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter;
|
import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
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.
|
* A simple unit test for the {@link DefaultDateTypeAdapter} class.
|
||||||
@ -31,6 +31,100 @@ import java.util.Date;
|
|||||||
*/
|
*/
|
||||||
public class DefaultDateTypeAdapterTest extends TestCase {
|
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 {
|
public void testDateSerialization() throws Exception {
|
||||||
int dateStyle = DateFormat.LONG;
|
int dateStyle = DateFormat.LONG;
|
||||||
DefaultDateTypeAdapter dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle);
|
DefaultDateTypeAdapter dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle);
|
||||||
@ -57,4 +151,14 @@ public class DefaultDateTypeAdapterTest extends TestCase {
|
|||||||
fail("Invalid date pattern should fail.");
|
fail("Invalid date pattern should fail.");
|
||||||
} catch (IllegalArgumentException expected) { }
|
} 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