Author: chas
Date: Mon Jan 13 23:01:23 2014
New Revision: 1557882

URL: http://svn.apache.org/r1557882
Log:
LANG-949 and LANG-950

Modified:
    
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java
    
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java
    
commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java

Modified: 
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java
URL: 
http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java?rev=1557882&r1=1557881&r2=1557882&view=diff
==============================================================================
--- 
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java
 (original)
+++ 
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java
 Mon Jan 13 23:01:23 2014
@@ -39,7 +39,7 @@ import java.util.TimeZone;
  *
  * <p>All patterns are compatible with
  * SimpleDateFormat (except time zones and some year patterns - see below).</p>
- * 
+ *
  * <p>Since 3.2, FastDateFormat supports parsing as well as printing.</p>
  *
  * <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent
@@ -94,7 +94,7 @@ public class FastDateFormat extends Form
 
     private final FastDatePrinter printer;
     private final FastDateParser parser;
-    
+
     //-----------------------------------------------------------------------
     /**
      * <p>Gets a formatter instance using the default pattern in the
@@ -210,7 +210,7 @@ public class FastDateFormat extends Form
     public static FastDateFormat getDateInstance(final int style, final 
TimeZone timeZone) {
         return cache.getDateInstance(style, timeZone, null);
     }
-    
+
     /**
      * <p>Gets a date formatter instance using the specified style, time
      * zone and locale.</p>
@@ -366,8 +366,23 @@ public class FastDateFormat extends Form
      * @throws NullPointerException if pattern, timeZone, or locale is null.
      */
     protected FastDateFormat(final String pattern, final TimeZone timeZone, 
final Locale locale) {
+       this(pattern, timeZone, locale, null);
+    }
+
+    // Constructor
+    //-----------------------------------------------------------------------
+    /**
+     * <p>Constructs a new FastDateFormat.</p>
+     *
+     * @param pattern  {@link java.text.SimpleDateFormat} compatible pattern
+     * @param timeZone  non-null time zone to use
+     * @param locale  non-null locale to use
+     * @param centuryStart The start of the 100 year period to use as the 
"default century" for 2 digit year parsing.  If centuryStart is null, defaults 
to now - 80 years
+     * @throws NullPointerException if pattern, timeZone, or locale is null.
+     */
+    protected FastDateFormat(final String pattern, final TimeZone timeZone, 
final Locale locale, final Date centuryStart) {
         printer= new FastDatePrinter(pattern, timeZone, locale);
-        parser= new FastDateParser(pattern, timeZone, locale);
+        parser= new FastDateParser(pattern, timeZone, locale, centuryStart);
     }
 
     // Format methods
@@ -463,7 +478,7 @@ public class FastDateFormat extends Form
     // Parsing
     //-----------------------------------------------------------------------
 
-    
+
     /* (non-Javadoc)
      * @see DateParser#parse(java.lang.String)
      */

Modified: 
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java
URL: 
http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java?rev=1557882&r1=1557881&r2=1557882&view=diff
==============================================================================
--- 
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java
 (original)
+++ 
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java
 Mon Jan 13 23:01:23 2014
@@ -71,7 +71,7 @@ public class FastDateParser implements D
      *
      * @see java.io.Serializable
      */
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 2L;
 
     static final Locale JAPANESE_IMPERIAL = new Locale("ja","JP","JP");
 
@@ -79,11 +79,12 @@ public class FastDateParser implements D
     private final String pattern;
     private final TimeZone timeZone;
     private final Locale locale;
+    private final int century;
+    private final int startYear;
 
     // derived fields
     private transient Pattern parsePattern;
     private transient Strategy[] strategies;
-    private transient int thisYear;
 
     // dynamic fields to communicate with Strategy
     private transient String currentFormatField;
@@ -96,21 +97,38 @@ public class FastDateParser implements D
      *  pattern
      * @param timeZone non-null time zone to use
      * @param locale non-null locale
+     * @param centuryStart The start of the century for 2 digit year parsing
      */
-    protected FastDateParser(final String pattern, final TimeZone timeZone, 
final Locale locale) {
+    protected FastDateParser(final String pattern, final TimeZone timeZone, 
final Locale locale, Date centuryStart) {
         this.pattern = pattern;
         this.timeZone = timeZone;
         this.locale = locale;
-        init();
+
+        final Calendar definingCalendar = Calendar.getInstance(timeZone, 
locale);
+        int centuryStartYear;
+        if(centuryStart!=null) {
+               definingCalendar.setTime(centuryStart);
+               centuryStartYear= definingCalendar.get(Calendar.YEAR);
+        }
+        else if(locale.equals(JAPANESE_IMPERIAL)) {
+               centuryStartYear= 0;
+        }
+        else {
+               // from 80 years ago to 20 years from now
+               definingCalendar.setTime(new Date());
+               centuryStartYear= definingCalendar.get(Calendar.YEAR)-80;
+        }
+        century= centuryStartYear / 100 * 100;
+        startYear= centuryStartYear - century;
+
+        init(definingCalendar);
     }
 
-    /**
+       /**
      * Initialize derived fields from defining fields.
      * This is called from constructor and from readObject (de-serialization)
      */
-    private void init() {
-        final Calendar definingCalendar = Calendar.getInstance(timeZone, 
locale);
-        thisYear= definingCalendar.get(Calendar.YEAR);
+    private void init(Calendar definingCalendar) {
 
         final StringBuilder regex= new StringBuilder();
         final List<Strategy> collector = new ArrayList<Strategy>();
@@ -176,7 +194,7 @@ public class FastDateParser implements D
 
     /**
      * Returns the generated pattern (for testing purposes).
-     * 
+     *
      * @return the generated pattern
      */
     Pattern getParsePattern() {
@@ -234,7 +252,9 @@ public class FastDateParser implements D
      */
     private void readObject(final ObjectInputStream in) throws IOException, 
ClassNotFoundException {
         in.defaultReadObject();
-        init();
+
+        final Calendar definingCalendar = Calendar.getInstance(timeZone, 
locale);
+        init(definingCalendar);
     }
 
     /* (non-Javadoc)
@@ -319,11 +339,11 @@ public class FastDateParser implements D
             case '\\':
                 if(++i==value.length()) {
                     break;
-                }                
+                }
                 /*
                  * If we have found \E, we replace it with \E\\E\Q, i.e. we 
stop the quoting,
                  * quote the \ in \E, then restart the quoting.
-                 * 
+                 *
                  * Otherwise we just output the two characters.
                  * In each case the initial \ needs to be output and the final 
char is done at the end
                  */
@@ -354,16 +374,13 @@ public class FastDateParser implements D
     }
 
     /**
-     * Adjust dates to be within 80 years before and 20 years after 
instantiation
+     * Adjust dates to be within appropriate century
      * @param twoDigitYear The year to adjust
-     * @return A value within -80 and +20 years from instantiation of this 
instance
+     * @return A value between centuryStart(inclusive) to 
centuryStart+100(exclusive)
      */
-    int adjustYear(final int twoDigitYear) {
-        final int trial= twoDigitYear + thisYear - thisYear%100;
-        if(trial < thisYear+20) {
-            return trial;
-        }
-        return trial-100;
+    private int adjustYear(final int twoDigitYear) {
+               int trial= century + twoDigitYear;
+       return twoDigitYear>=startYear ?trial :trial+100;
     }
 
     /**
@@ -389,7 +406,7 @@ public class FastDateParser implements D
         /**
          * Is this field a number?
          * The default implementation returns false.
-         * 
+         *
          * @return true, if field is a number
          */
         boolean isNumber() {
@@ -397,15 +414,15 @@ public class FastDateParser implements D
         }
         /**
          * Set the Calendar with the parsed field.
-         * 
+         *
          * The default implementation does nothing.
-         * 
+         *
          * @param parser The parser calling this strategy
          * @param cal The <code>Calendar</code> to set
          * @param value The parsed field to translate and set in cal
          */
         void setCalendar(final FastDateParser parser, final Calendar cal, 
final String value) {
-            
+
         }
         /**
          * Generate a <code>Pattern</code> regular expression to the 
<code>StringBuilder</code>

Modified: 
commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java?rev=1557882&r1=1557881&r2=1557882&view=diff
==============================================================================
--- 
commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java
 (original)
+++ 
commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java
 Mon Jan 13 23:01:23 2014
@@ -19,6 +19,7 @@ package org.apache.commons.lang3.time;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+
 import java.io.Serializable;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -30,9 +31,8 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.TimeZone;
 
-import org.junit.Assert;
-
 import org.apache.commons.lang3.SerializationUtils;
+import org.junit.Assert;
 import org.junit.Test;
 
 /**
@@ -42,8 +42,8 @@ import org.junit.Test;
  * @since 3.2
  */
 public class FastDateParserTest {
-    private static final String SHORT_FORMAT_NOERA = "y/M/d/h/a/m/E/Z";
-    private static final String LONG_FORMAT_NOERA = 
"yyyy/MMMM/dddd/hhhh/mmmm/aaaa/EEEE/ZZZZ";
+    private static final String SHORT_FORMAT_NOERA = "y/M/d/h/a/m/s/E/Z";
+    private static final String LONG_FORMAT_NOERA = 
"yyyy/MMMM/dddd/hhhh/mmmm/ss/aaaa/EEEE/ZZZZ";
     private static final String SHORT_FORMAT = "G/" + SHORT_FORMAT_NOERA;
     private static final String LONG_FORMAT = "GGGG/" + LONG_FORMAT_NOERA;
 
@@ -79,7 +79,7 @@ public class FastDateParserTest {
      * Override this method in derived tests to change the construction of 
instances
      */
     protected DateParser getInstance(final String format, final TimeZone 
timeZone, final Locale locale) {
-        return new FastDateParser(format, timeZone, locale);
+        return new FastDateParser(format, timeZone, locale, null);
     }
 
     @Test
@@ -188,41 +188,58 @@ public class FastDateParserTest {
         assertEquals(cal.getTime(), H.parse("2010-08-01 12:33:20"));
     }
 
+    private Calendar getEraStart(int year, TimeZone zone, Locale locale) {
+        Calendar cal = Calendar.getInstance(zone, locale);
+        cal.clear();
+
+        // 
http://docs.oracle.com/javase/6/docs/technotes/guides/intl/calendar.doc.html
+        if (locale.equals(FastDateParser.JAPANESE_IMPERIAL)) {
+            if(year < 1868) {
+                cal.set(Calendar.ERA, 0);
+                cal.set(Calendar.YEAR, 1868-year);
+            }
+        }
+        else {
+            if (year < 0) {
+                cal.set(Calendar.ERA, GregorianCalendar.BC);
+                year= -year;
+            }
+            cal.set(Calendar.YEAR, year/100 * 100);
+        }
+        return cal;
+    }
+
+    private void validateSdfFormatFdpParseEquality(String format, Locale 
locale, TimeZone tz, DateParser fdp, Date in, int year, Date cs) throws 
ParseException {
+        final SimpleDateFormat sdf = new SimpleDateFormat(format, locale);
+        if (format.equals(SHORT_FORMAT)) {
+            sdf.set2DigitYearStart( cs );
+        }
+        final String fmt = sdf.format(in);
+        try {
+            final Date out = fdp.parse(fmt);
+            assertEquals(locale.toString()+" "+in+" "+ format+ " "+tz.getID(), 
in, out);
+        } catch (final ParseException pe) {
+            System.out.println(fmt+" "+locale.toString()+" "+year+" "+ format+ 
" "+tz.getID());
+            throw pe;
+        }
+    }
+
     @Test
     // Check that all Locales can parse the formats we use
     public void testParses() throws Exception {
-        for(final Locale locale : Locale.getAvailableLocales()) {
-            for(final TimeZone tz : new TimeZone[]{NEW_YORK, GMT}) {
-                final Calendar cal = Calendar.getInstance(tz);
-                for(final int year : new int[]{2003, 1940, 1868, 1867, 0, 
-1940}) {
-                    // 
http://docs.oracle.com/javase/6/docs/technotes/guides/intl/calendar.doc.html
-                    if (year < 1868 && 
locale.equals(FastDateParser.JAPANESE_IMPERIAL)) {
-                        continue; // Japanese imperial calendar does not 
support eras before 1868
-                    }
-                    cal.clear();
-                    if (year < 0) {
-                        cal.set(-year, 1, 10);
-                        cal.set(Calendar.ERA, GregorianCalendar.BC);
-                    } else {
-                        cal.set(year, 1, 10);
-                    }
-                    final Date in = cal.getTime();
-                    for(final String format : new String[]{LONG_FORMAT, 
SHORT_FORMAT}) {
-                        final SimpleDateFormat sdf = new 
SimpleDateFormat(format, locale);
-                        if (format.equals(SHORT_FORMAT)) {
-                            if (year < 1930) {
-                                sdf.set2DigitYearStart(cal.getTime());
-                            }
-                        }
-                        final String fmt = sdf.format(in);
-                        try {
-                            final Date out = sdf.parse(fmt);
-
-                            assertEquals(locale.toString()+" "+year+" "+ 
format+ " "+tz.getID(), in, out);
-                        } catch (final ParseException pe) {
-                            System.out.println(fmt+" "+locale.toString()+" 
"+year+" "+ format+ " "+tz.getID());
-                            throw pe;
-                        }
+        for(final String format : new String[]{LONG_FORMAT, SHORT_FORMAT}) {
+            for(final Locale locale : Locale.getAvailableLocales()) {
+                for(final TimeZone tz :  new TimeZone[]{NEW_YORK, REYKJAVIK, 
GMT}) {
+                     for(final int year : new int[]{2003, 1940, 1868, 1867, 1, 
-1, -1940}) {
+                        Calendar cal= getEraStart(year, tz, locale);
+                        Date centuryStart= cal.getTime();
+
+                        cal.set(Calendar.MONTH, 1);
+                        cal.set(Calendar.DAY_OF_MONTH, 10);
+                        Date in= cal.getTime();
+
+                        final FastDateParser fdp= new FastDateParser(format, 
tz, locale, centuryStart);
+                        validateSdfFormatFdpParseEquality(format, locale, tz, 
fdp, in, year, centuryStart);
                     }
                 }
             }


Reply via email to