You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by se...@apache.org on 2012/09/26 20:03:25 UTC

svn commit: r1390626 - in /commons/proper/lang/trunk/src: main/java/org/apache/commons/lang3/time/FastDateParser.java test/java/org/apache/commons/lang3/time/FastDateParserTest.java

Author: sebb
Date: Wed Sep 26 18:03:24 2012
New Revision: 1390626

URL: http://svn.apache.org/viewvc?rev=1390626&view=rev
Log:
LANG-828 FastDateParser does not handle non-Gregorian calendars properly
Use Calendar#getDisplayNames() instead of DateFormatSymbols#getEras()

Modified:
    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/FastDateParser.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java?rev=1390626&r1=1390625&r2=1390626&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 Wed Sep 26 18:03:24 2012
@@ -22,7 +22,6 @@ import java.io.Serializable;
 import java.text.DateFormatSymbols;
 import java.text.ParseException;
 import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -30,6 +29,7 @@ import java.util.Comparator;
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.SortedMap;
 import java.util.TimeZone;
 import java.util.TreeMap;
@@ -125,18 +125,6 @@ public class FastDateParser implements D
             throw new IllegalArgumentException("Invalid pattern");
         }
 
-        // These locales don't use the Gregorian calendar
-        // See http://docs.oracle.com/javase/6/docs/technotes/guides/intl/calendar.doc.html
-        // Also, the getEras() methods don't return the correct era names.
-        // N.B. Not safe to use toString() comparison because that changes between Java versions
-        if (locale.equals(JAPANESE_IMPERIAL)
-        || (locale.getLanguage().equals("th") && locale.getCountry().equals("TH"))) {
-            collector.add(new SimpleDateFormatStrategy());
-            strategies= collector.toArray(new Strategy[collector.size()]);
-            parsePattern= Pattern.compile("(.*+)");
-            return;
-        }
-
         currentFormatField= patternMatcher.group();
         Strategy currentStrategy= getStrategy(currentFormatField);
         for(;;) {
@@ -256,7 +244,13 @@ public class FastDateParser implements D
     public Date parse(String source) throws ParseException {
         Date date= parse(source, new ParsePosition(0));
         if(date==null) {
-            throw new ParseException(source+" does not match "+parsePattern.pattern(), 0);            
+            // Add a note re supported date range
+            if (locale.equals(JAPANESE_IMPERIAL)) {
+                throw new ParseException(
+                        "(The " +locale + " locale does not support dates before 1868 AD)\n" +
+                        source+" does not match "+parsePattern.pattern(), 0);
+            }
+            throw new ParseException(source+" does not match "+parsePattern.pattern(), 0);
         }
         return date;
     }
@@ -383,7 +377,14 @@ public class FastDateParser implements D
             DateFormatSymbols symbols= DateFormatSymbols.getInstance(locale);
             switch(field) {
             case Calendar.ERA:
-                fieldKeyValues= createKeyValues(symbols.getEras(), null);
+                // DateFormatSymbols#getEras() only returns AD/BC or translations
+                // It does not work for the Thai Buddhist or Japanese Imperial calendars.
+                // see: https://issues.apache.org/jira/browse/TRINIDAD-2126
+                Calendar c = Calendar.getInstance(locale);
+                // N.B. Some calendars have different short and long symbols, e.g. ja_JP_JP
+                String[] shortEras = toArray(c.getDisplayNames(Calendar.ERA, Calendar.SHORT, locale));
+                String[] longEras = toArray(c.getDisplayNames(Calendar.ERA, Calendar.LONG, locale));
+                fieldKeyValues= createKeyValues(longEras, shortEras);
                 break;
             case Calendar.DAY_OF_WEEK:
                 fieldKeyValues= createKeyValues(symbols.getWeekdays(), symbols.getShortWeekdays());
@@ -404,6 +405,19 @@ public class FastDateParser implements D
         }
         return fieldKeyValues;
     }
+
+    private String[] toArray(Map<String, Integer> era) {
+        String[] eras = new String[era.size()]; // assume no gaps in entry values
+        for(Map.Entry<String, Integer> me : era.entrySet()) {
+            int idx = me.getValue().intValue();
+            final String key = me.getKey();
+            if (key == null) {
+                throw new IllegalArgumentException();
+            }
+            eras[idx] = key;
+        }
+        return eras;
+    }
     
     /**
      * Create key / value pairs from keys 
@@ -820,37 +834,6 @@ public class FastDateParser implements D
     }
 
 
-    /**
-     * Dummy strategy which delegates to SimpleDateFormat.
-     */
-    private static class SimpleDateFormatStrategy implements Strategy {
-
-        @Override
-        public boolean isNumber() {
-            return false;
-        }
-
-        @Override
-        public void setCalendar(FastDateParser parser, Calendar cal, String value) {
-            String pat = parser.pattern;
-            Locale loc = parser.locale;
-            SimpleDateFormat sdf = new SimpleDateFormat(pat, loc);
-            try {
-                Date d = sdf.parse(value);
-                cal.setTime(d);
-            } catch (ParseException e) {
-                throw new IllegalArgumentException(
-                        "Unexpected error using pattern " + pat + " with locale " + loc.toString(), e);
-            }
-        }
-
-        @Override
-        public boolean addRegex(FastDateParser parser, StringBuilder regex) {
-            return false;
-        }
-        
-    }
-
     private static final Strategy ERA_STRATEGY = new TextStrategy(Calendar.ERA);
     private static final Strategy DAY_OF_WEEK_STRATEGY = new TextStrategy(Calendar.DAY_OF_WEEK);
     private static final Strategy AM_PM_STRATEGY = new TextStrategy(Calendar.AM_PM);

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=1390626&r1=1390625&r2=1390626&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 Wed Sep 26 18:03:24 2012
@@ -20,24 +20,19 @@ import static org.junit.Assert.assertEqu
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import java.io.Serializable;
-import java.text.DateFormatSymbols;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 import java.util.TimeZone;
 
 import junit.framework.Assert;
 
 import org.apache.commons.lang3.SerializationUtils;
-import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.junit.Test;
 
 /**
@@ -234,85 +229,6 @@ public class FastDateParserTest {
     }
 
     @Test
-    // Check that all Locales generate Strings containing the expected eras
-    public void testEras() throws Exception {
-        Set<ImmutablePair<Locale, String>> locale2Absent = new HashSet<ImmutablePair<Locale, String>>();
-        Map<Locale, String[]> locale2Eras = new HashMap<Locale, String[]>();
-        for(Locale locale : Locale.getAvailableLocales()) {
-            for(TimeZone tz : new TimeZone[]{GMT}) {
-                Calendar cal = Calendar.getInstance(tz);
-                String[] eras = DateFormatSymbols.getInstance(locale).getEras();
-                String[] erasPrint = new String[eras.length];
-                for(int i = 0; i < eras.length ; i++) {
-                    String s = eras[i];
-                    if (s.length() > 4) {
-                        erasPrint[i] = s;
-                    } else {
-                        erasPrint[i] = display(s);
-                    }
-                }
-                for(int year : new int[]{2003, 1927, 1913, 1868, 1867, -2003}) {
-                    cal.clear();
-                    if (year < 0) {
-                        cal.set(-year, 1, 10);
-                        cal.set(Calendar.ERA, GregorianCalendar.BC);
-                    } else {
-                        cal.set(year, 1, 10);
-                    }
-                    Date in = cal.getTime();
-                    for(String format : new String[]{"GGGG","G"}) {
-                        SimpleDateFormat sdf = new SimpleDateFormat(format, locale);
-                        String fmt = sdf.format(in);
-                        boolean found = false;
-                        for(String era : eras) {
-                            if (fmt.startsWith(era)) {
-                                found=true;
-                            }
-                        }
-                        if (!found) {
-                            locale2Absent.add(ImmutablePair.of(locale, fmt));
-                            locale2Eras.put(locale, erasPrint);
-                        }
-                    }
-                }
-            }
-        }
-        
-        if (locale2Absent.size() > 0) {
-            System.out.println("FastDateParserTest: one or more missing era designators detected:");
-            for(ImmutablePair<Locale, String> me : locale2Absent) {
-                Locale loc = me.getKey();
-                String [] erasPrint = locale2Eras.get(loc);
-                System.out.println("Locale: "+loc.toString()+" era: '"+display(me.getValue())+"' not found in eras: " + Arrays.toString(erasPrint));                
-            }
-        }
-//        assertFalse("One or more failures detected",fail);
-    }
-
-    private String display(String fmt) {
-        if (fmt.matches("\\p{ASCII}*")) {
-            return fmt;
-        }
-        StringBuilder sb = new StringBuilder();
-        sb.append(fmt);
-        sb.append(" = ");
-        for(int i =0; i < fmt.length(); i++) {
-            if (i > 0) {
-                sb.append(' ');
-            }
-            String s = fmt.substring(i,i+1);
-            if (s.matches("\\p{ASCII}")) {
-                sb.append(s);
-            } else {
-                char charAt = fmt.charAt(i);
-                sb.append("\\u");
-                sb.append(Integer.toHexString(charAt));                
-            }
-        }
-        return sb.toString();
-    }
-
-    @Test
     public void testLocales_Long_AD() throws Exception {
         testLocales(LONG_FORMAT, false);
     }
@@ -360,10 +276,9 @@ public class FastDateParserTest {
         if (eraBC) {
             cal.set(Calendar.ERA, GregorianCalendar.BC);
         }
-        boolean failed = false;
         for(Locale locale : Locale.getAvailableLocales()) {
             // ja_JP_JP cannot handle dates before 1868 properly
-            if (eraBC && format.equals(SHORT_FORMAT) && locale.equals(FastDateParser.JAPANESE_IMPERIAL)) {
+            if (eraBC && locale.equals(FastDateParser.JAPANESE_IMPERIAL)) {
                 continue;
             }
             SimpleDateFormat sdf = new SimpleDateFormat(format, locale);
@@ -372,16 +287,9 @@ public class FastDateParserTest {
             try {
                 checkParse(locale, cal, sdf, fdf);
             } catch(ParseException ex) {
-                failed = true;
-                // TODO: are these Java bugs?
-                // ja_JP_JP, th_TH, and th_TH_TH fail with both eras because the generated era name does not match
-                // ja_JP_JP fails with era BC because it converts to -2002
-                System.out.println("Locale "+locale+ " failed with "+format+" era "+(eraBC?"BC":"AD")+"\n" + trimMessage(ex.toString()));
+                Assert.fail("Locale "+locale+ " failed with "+format+" era "+(eraBC?"BC":"AD")+"\n" + trimMessage(ex.toString()));
             }
         }
-        if (failed) {
-            Assert.fail("One or more tests failed, see above");
-        }
     }
 
     private String trimMessage(String msg) {
@@ -399,7 +307,8 @@ public class FastDateParserTest {
         String formattedDate= sdf.format(cal.getTime());                
         Date expectedTime = sdf.parse(formattedDate);
         Date actualTime = fdf.parse(formattedDate);
-        assertEquals(locale.toString()+" "+formattedDate,expectedTime, actualTime);
+        assertEquals(locale.toString()+" "+formattedDate
+                +"\n",expectedTime, actualTime);
     }
     
     @Test