Log Message
DateConverter cannot handle dates in different era (XSTR-711).
Modified Paths
- trunk/xstream/src/java/com/thoughtworks/xstream/converters/basic/DateConverter.java
- trunk/xstream/src/java/com/thoughtworks/xstream/core/util/ThreadSafeSimpleDateFormat.java
- trunk/xstream/src/test/com/thoughtworks/xstream/converters/basic/DateConverterTest.java
- trunk/xstream-distribution/src/content/changes.html
Diff
Modified: trunk/xstream/src/java/com/thoughtworks/xstream/converters/basic/DateConverter.java (2000 => 2001)
--- trunk/xstream/src/java/com/thoughtworks/xstream/converters/basic/DateConverter.java 2012-11-30 10:19:03 UTC (rev 2000)
+++ trunk/xstream/src/java/com/thoughtworks/xstream/converters/basic/DateConverter.java 2012-12-24 13:09:56 UTC (rev 2001)
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2003, 2004 Joe Walnes.
- * Copyright (C) 2006, 2007, 2008, 2009, 2011 XStream Committers.
+ * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
@@ -18,8 +18,10 @@
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Date;
import java.util.List;
+import java.util.Locale;
import java.util.TimeZone;
import com.thoughtworks.xstream.core.JVM;
@@ -28,11 +30,16 @@
/**
* Converts a java.util.Date to a String as a date format, retaining precision down to
- * milliseconds. The formatted string is by default in UTC. You can provide a different
- * {@link TimeZone} that is used for serialization or <code>null</code> to use always the
- * current TimeZone. Note, that the default format uses 3-letter time zones that can be
- * ambiguous and may cause wrong results at deserialization.
+ * milliseconds.
*
+ * <p>The formatted string is by default in UTC and English locale. You can provide
+ * a different {@link Locale} and {@link TimeZone} that are used for serialization or
+ * <code>null</code> to use always the current TimeZone. Note, that the default format uses
+ * 3-letter time zones that can be ambiguous and may cause wrong results at deserialization and
+ * is localized since Java 6.</p>
+ *
+ * <p>Dates in a different era are using a special default pattern that contains the era itself.</p>
+ *
* @author Joe Walnes
* @author Jörg Schaible
*/
@@ -40,12 +47,19 @@
private static final String[] DEFAULT_ACCEPTABLE_FORMATS;
private static final String DEFAULT_PATTERN;
- private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
+ private static final String DEFAULT_ERA_PATTERN;
+ private static final TimeZone UTC;
+ private static final long ERA_START;
static {
+ UTC = TimeZone.getTimeZone("UTC");
+
final String defaultPattern = "yyyy-MM-dd HH:mm:ss.S z";
+ final String defaultEraPattern = "yyyy-MM-dd G HH:mm:ss.S z";
final List acceptablePatterns = new ArrayList();
final boolean utcSupported = JVM.canParseUTCDateFormat();
DEFAULT_PATTERN = utcSupported ? defaultPattern : "yyyy-MM-dd HH:mm:ss.S 'UTC'";
+ DEFAULT_ERA_PATTERN = utcSupported ? defaultEraPattern : "yyyy-MM-dd G HH:mm:ss.S 'UTC'";
+ acceptablePatterns.add("yyyy-MM-dd HH:mm:ss.S z");
if (!utcSupported) {
acceptablePatterns.add(defaultPattern);
}
@@ -59,9 +73,17 @@
// backwards compatibility
acceptablePatterns.add("yyyy-MM-dd HH:mm:ssa");
DEFAULT_ACCEPTABLE_FORMATS = (String[]) acceptablePatterns.toArray(new String[acceptablePatterns.size()]);
+
+ final Calendar cal = Calendar.getInstance();
+ cal.setTimeZone(UTC);
+ cal.clear();
+ cal.set(1, Calendar.JANUARY, 1);
+ ERA_START = cal.getTime().getTime(); // calendar.getTimeInMillis() not available under JDK 1.3
}
private final ThreadSafeSimpleDateFormat defaultFormat;
+ private final ThreadSafeSimpleDateFormat defaultEraFormat;
private final ThreadSafeSimpleDateFormat[] acceptableFormats;
+ private final Locale locale;
/**
* Construct a DateConverter with standard formats and lenient set off.
@@ -135,8 +157,33 @@
*/
public DateConverter(
String defaultFormat, String[] acceptableFormats, TimeZone timeZone, boolean lenient) {
+ this(DEFAULT_ERA_PATTERN, defaultFormat, acceptableFormats, Locale.ENGLISH, timeZone, lenient);
+ }
+
+ /**
+ * Construct a DateConverter.
+ *
+ * @param defaultEraFormat the default format for dates in a different era (may be
+ * <code>null</code> to drop era support)
+ * @param defaultFormat the default format
+ * @param acceptableFormats fallback formats
+ * @param locale locale to use for the format
+ * @param timeZone the TimeZone used to serialize the Date
+ * @param lenient the lenient setting of {@link SimpleDateFormat#setLenient(boolean)}
+ * @since upcoming
+ */
+ public DateConverter(
+ String defaultEraFormat, String defaultFormat, String[] acceptableFormats,
+ Locale locale, TimeZone timeZone, boolean lenient) {
+ this.locale = locale;
+ if (defaultEraFormat != null) {
+ this.defaultEraFormat = new ThreadSafeSimpleDateFormat(
+ defaultEraFormat, timeZone, locale, 4, 20, lenient);
+ } else {
+ this.defaultEraFormat = null;
+ }
this.defaultFormat = new ThreadSafeSimpleDateFormat(
- defaultFormat, timeZone, 4, 20, lenient);
+ defaultFormat, timeZone, locale, 4, 20, lenient);
this.acceptableFormats = acceptableFormats != null
? new ThreadSafeSimpleDateFormat[acceptableFormats.length]
: new ThreadSafeSimpleDateFormat[0];
@@ -151,27 +198,45 @@
}
public Object fromString(String str) {
- try {
- return defaultFormat.parse(str);
- } catch (ParseException e) {
- for (int i = 0; i < acceptableFormats.length; i++ ) {
- try {
- return acceptableFormats[i].parse(str);
- } catch (ParseException e2) {
- // no worries, let's try the next format.
- }
+ if (defaultEraFormat != null) {
+ try {
+ return defaultEraFormat.parse(str);
+ } catch (ParseException e) {
+ // try next ...
}
- // no dateFormats left to try
- throw new ConversionException("Cannot parse date " + str);
}
+ if (defaultEraFormat != defaultFormat) {
+ try {
+ return defaultFormat.parse(str);
+ } catch (ParseException e) {
+ // try next ...
+ }
+ }
+ for (int i = 0; i < acceptableFormats.length; i++ ) {
+ try {
+ return acceptableFormats[i].parse(str);
+ } catch (ParseException e3) {
+ // no worries, let's try the next format.
+ }
+ }
+ // no dateFormats left to try
+ throw new ConversionException("Cannot parse date " + str);
}
public String toString(Object obj) {
- return defaultFormat.format((Date)obj);
+ final Date date = (Date)obj;
+ if (date.getTime() < ERA_START && defaultEraFormat != null) {
+ return defaultEraFormat.format(date);
+ } else {
+ return defaultFormat.format(date);
+ }
}
public void appendErrors(ErrorWriter errorWriter) {
errorWriter.add("Default date pattern", defaultFormat.toString());
+ if (defaultEraFormat != null) {
+ errorWriter.add("Default era date pattern", defaultEraFormat.toString());
+ }
for (int i = 0; i < acceptableFormats.length; i++ ) {
errorWriter.add("Alternative date pattern", acceptableFormats[i].toString());
}
Modified: trunk/xstream/src/java/com/thoughtworks/xstream/core/util/ThreadSafeSimpleDateFormat.java (2000 => 2001)
--- trunk/xstream/src/java/com/thoughtworks/xstream/core/util/ThreadSafeSimpleDateFormat.java 2012-11-30 10:19:03 UTC (rev 2000)
+++ trunk/xstream/src/java/com/thoughtworks/xstream/core/util/ThreadSafeSimpleDateFormat.java 2012-12-24 13:09:56 UTC (rev 2001)
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2004, 2005 Joe Walnes.
- * Copyright (C) 2006, 2007, 2009, 2011 XStream Committers.
+ * Copyright (C) 2006, 2007, 2009, 2011, 2012 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
@@ -39,16 +39,24 @@
private final Pool pool;
private final TimeZone timeZone;
- public ThreadSafeSimpleDateFormat(String format, TimeZone timeZone, int initialPoolSize, int maxPoolSize, final boolean lenient) {
+ public ThreadSafeSimpleDateFormat(
+ String format, TimeZone timeZone, int initialPoolSize, int maxPoolSize,
+ final boolean lenient) {
+ this(format, timeZone, Locale.ENGLISH, initialPoolSize, maxPoolSize, lenient);
+ }
+
+ public ThreadSafeSimpleDateFormat(
+ String format, TimeZone timeZone, final Locale locale, int initialPoolSize,
+ int maxPoolSize, final boolean lenient) {
formatString = format;
this.timeZone = timeZone;
pool = new Pool(initialPoolSize, maxPoolSize, new Pool.Factory() {
public Object newInstance() {
- SimpleDateFormat dateFormat = new SimpleDateFormat(formatString, Locale.ENGLISH);
+ SimpleDateFormat dateFormat = new SimpleDateFormat(formatString, locale);
dateFormat.setLenient(lenient);
return dateFormat;
}
-
+
});
}
Modified: trunk/xstream/src/test/com/thoughtworks/xstream/converters/basic/DateConverterTest.java (2000 => 2001)
--- trunk/xstream/src/test/com/thoughtworks/xstream/converters/basic/DateConverterTest.java 2012-11-30 10:19:03 UTC (rev 2000)
+++ trunk/xstream/src/test/com/thoughtworks/xstream/converters/basic/DateConverterTest.java 2012-12-24 13:09:56 UTC (rev 2001)
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2004, 2005 Joe Walnes.
- * Copyright (C) 2006, 2007, 2008, 2009 XStream Committers.
+ * Copyright (C) 2006, 2007, 2008, 2009, 2012 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
@@ -23,6 +23,7 @@
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
+import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
@@ -101,7 +102,7 @@
Date simpleDate = f.parse(strIST);
assertEquals(simpleDate, dateRetrieved);
// DateConverter does not get influenced by change of current TZ ...
- TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
dateRetrieved = (Date)converter.fromString(strIST);
assertEquals(simpleDate, dateRetrieved);
// ... as well as the SimpleDateFormat
@@ -109,7 +110,11 @@
simpleDate = f.parse(strIST);
assertEquals(simpleDate, dateRetrieved);
assertEquals(date, f.parse("2004-02-22 20:16:04.0 UTC"));
- // assertEquals(date, simpleDate); off by +03:30 ... but why ??
+ // 'date' was created for IST time zone, so let parser return in IST
+ f.setTimeZone(TimeZone.getTimeZone("IST"));
+ simpleDate = f.parse(strIST);
+ assertEquals(date, simpleDate);
+ assertEquals(date, f.parse("2004-02-22 20:16:04.0 UTC"));
}
public void testIsThreadSafe() throws InterruptedException {
@@ -196,4 +201,18 @@
Date expected = new Date(0);
assertEquals(expected, converter.fromString(converter.toString(expected)));
}
+
+ public void testDatesWithEpoche() {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeZone(TimeZone.getTimeZone("UTC"));
+ assertEquals(GregorianCalendar.AD, cal.get(Calendar.ERA));
+ cal.clear();
+ cal.set(1, Calendar.JANUARY, 1);
+ cal.add(Calendar.MILLISECOND, -1);
+ Date date = cal.getTime();
+ assertEquals("0001-12-31 BC 23:59:59.999 UTC", converter.toString(date));
+ assertEquals(date, converter.fromString("0001-12-31 BC 23:59:59.999 UTC"));
+ cal.add(Calendar.MILLISECOND, 1);
+ assertEquals(cal.getTime(), converter.fromString("0001-01-01 AD 00:00:00.000 UTC"));
+ }
}
Modified: trunk/xstream-distribution/src/content/changes.html (2000 => 2001)
--- trunk/xstream-distribution/src/content/changes.html 2012-11-30 10:19:03 UTC (rev 2000)
+++ trunk/xstream-distribution/src/content/changes.html 2012-12-24 13:09:56 UTC (rev 2001)
@@ -41,6 +41,8 @@
<h2>Minor changes</h2>
<ul>
+ <li>JIRA:XSTR-711: DateConverter cannot handle dates in different era.</li>
+ <li>DateConverter supports now localized formats.</li>
<li>JIRA:XSTR-710: JsonWriter does not write BigInteger and BigDecimal as number values.</li>
<li>JIRA:XSTR-708: SqlTimestampConverter does not ignore timezone.</li>
<li>JIRA:XSTR-707: Creation of XmllPullParser with the XmlPullParserFactory may fail in OSGi environment.</li>
To unsubscribe from this list please visit:
