Title: [2001] trunk: DateConverter cannot handle dates in different era (XSTR-711).

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&ouml;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:

http://xircles.codehaus.org/manage_email

Reply via email to