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/22 15:22:30 UTC

svn commit: r1388806 - in /commons/proper/lang/trunk/src: changes/changes.xml main/java/org/apache/commons/lang3/time/DateUtils.java test/java/org/apache/commons/lang3/time/DateUtilsTest.java

Author: sebb
Date: Sat Sep 22 13:22:30 2012
New Revision: 1388806

URL: http://svn.apache.org/viewvc?rev=1388806&view=rev
Log:
LANG-799 - DateUtils#parseDate uses default locale; add Locale support

Modified:
    commons/proper/lang/trunk/src/changes/changes.xml
    commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/DateUtils.java
    commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java

Modified: commons/proper/lang/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/changes/changes.xml?rev=1388806&r1=1388805&r2=1388806&view=diff
==============================================================================
--- commons/proper/lang/trunk/src/changes/changes.xml (original)
+++ commons/proper/lang/trunk/src/changes/changes.xml Sat Sep 22 13:22:30 2012
@@ -29,6 +29,7 @@
     <action issue="LANG-805" type="fix">RandomStringUtils.random(count, 0, 0, false, false, universe, random) always throws java.lang.ArrayIndexOutOfBoundsException</action>
     <action issue="LANG-802" type="fix">LocaleUtils - unnecessary recursive call in SyncAvoid class.</action>
     <action issue="LANG-800" type="fix">Javadoc bug in DateUtils#ceiling for Calendar and Object versions.</action>
+    <action issue="LANG-799" type="update">DateUtils#parseDate uses default locale; add Locale support</action>  
     <action issue="LANG-798" type="update">Use generics in SerializationUtils</action>  
     <action issue="LANG-788" type="fix">SerializationUtils throws ClassNotFoundException when cloning primitive classes</action>
     <action issue="LANG-786" type="fix">StringUtils equals() relies on undefined behavior</action>

Modified: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/DateUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/DateUtils.java?rev=1388806&r1=1388805&r2=1388806&view=diff
==============================================================================
--- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/DateUtils.java (original)
+++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/DateUtils.java Sat Sep 22 13:22:30 2012
@@ -22,6 +22,7 @@ import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.Iterator;
+import java.util.Locale;
 import java.util.NoSuchElementException;
 
 /**
@@ -263,9 +264,32 @@ public class DateUtils {
      * @throws ParseException if none of the date patterns were suitable (or there were none)
      */
     public static Date parseDate(String str, String... parsePatterns) throws ParseException {
-        return parseDateWithLeniency(str, parsePatterns, true);
+        return parseDate(str, null, parsePatterns);
     }
     
+    //-----------------------------------------------------------------------
+    /**
+     * <p>Parses a string representing a date by trying a variety of different parsers,
+     * using the default date format symbols for the given locale.</p>
+     * 
+     * <p>The parse will try each parse pattern in turn.
+     * A parse is only deemed successful if it parses the whole of the input string.
+     * If no parse patterns match, a ParseException is thrown.</p>
+     * The parser will be lenient toward the parsed date.
+     * 
+     * @param str  the date to parse, not null
+     * @param locale the locale whose date format symbols should be used. If <code>null</code>,
+     * the system locale is used (as per {@link #parseDate(String, String...)}).
+     * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
+     * @return the parsed date
+     * @throws IllegalArgumentException if the date string or pattern array is null
+     * @throws ParseException if none of the date patterns were suitable (or there were none)
+     * @since 3.2
+     */
+    public static Date parseDate(String str, Locale locale, String... parsePatterns) throws ParseException {
+        return parseDateWithLeniency(str, locale, parsePatterns, true);
+    }    
+
   //-----------------------------------------------------------------------
     /**
      * <p>Parses a string representing a date by trying a variety of different parsers.</p>
@@ -283,10 +307,32 @@ public class DateUtils {
      * @since 2.5
      */
     public static Date parseDateStrictly(String str, String... parsePatterns) throws ParseException {
-        return parseDateWithLeniency(str, parsePatterns, false);
+        return parseDateStrictly(str, null, parsePatterns);
     }
 
     /**
+     * <p>Parses a string representing a date by trying a variety of different parsers,
+     * using the default date format symbols for the given locale..</p>
+     * 
+     * <p>The parse will try each parse pattern in turn.
+     * A parse is only deemed successful if it parses the whole of the input string.
+     * If no parse patterns match, a ParseException is thrown.</p>
+     * The parser parses strictly - it does not allow for dates such as "February 942, 1996". 
+     * 
+     * @param str  the date to parse, not null
+     * @param locale the locale whose date format symbols should be used. If <code>null</code>,
+     * the system locale is used (as per {@link #parseDateStrictly(String, String...)}).
+     * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
+     * @return the parsed date
+     * @throws IllegalArgumentException if the date string or pattern array is null
+     * @throws ParseException if none of the date patterns were suitable
+     * @since 3.2
+     */
+    public static Date parseDateStrictly(String str, Locale locale, String... parsePatterns) throws ParseException {
+        return parseDateWithLeniency(str, null, parsePatterns, false);
+    }    
+
+    /**
      * <p>Parses a string representing a date by trying a variety of different parsers.</p>
      * 
      * <p>The parse will try each parse pattern in turn.
@@ -294,6 +340,8 @@ public class DateUtils {
      * If no parse patterns match, a ParseException is thrown.</p>
      * 
      * @param str  the date to parse, not null
+     * @param locale the locale to use when interpretting the pattern, can be null in which
+     * case the default system locale is used
      * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
      * @param lenient Specify whether or not date/time parsing is to be lenient.
      * @return the parsed date
@@ -302,12 +350,18 @@ public class DateUtils {
      * @see java.util.Calender#isLenient()
      */
     private static Date parseDateWithLeniency(
-            String str, String[] parsePatterns, boolean lenient) throws ParseException {
+            String str, Locale locale, String[] parsePatterns, boolean lenient) throws ParseException {
         if (str == null || parsePatterns == null) {
             throw new IllegalArgumentException("Date and Patterns must not be null");
         }
         
-        SimpleDateFormat parser = new SimpleDateFormat();
+        SimpleDateFormat parser;
+        if (locale == null) {
+            parser = new SimpleDateFormat();
+        } else {
+            parser = new SimpleDateFormat("", locale);
+        }
+        
         parser.setLenient(lenient);
         ParsePosition pos = new ParsePosition(0);
         for (String parsePattern : parsePatterns) {

Modified: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java?rev=1388806&r1=1388805&r2=1388806&view=diff
==============================================================================
--- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java (original)
+++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java Sat Sep 22 13:22:30 2012
@@ -16,27 +16,40 @@
  */
 package org.apache.commons.lang3.time;
 
-import org.junit.Test;
-import org.junit.Before;
-import static org.junit.Assert.*;
 import static org.apache.commons.lang3.JavaVersion.JAVA_1_4;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Modifier;
 import java.text.DateFormat;
 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.HashSet;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.NoSuchElementException;
+import java.util.Set;
 import java.util.TimeZone;
 
 import junit.framework.AssertionFailedError;
+
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.SystemUtils;
 
+import org.junit.Before;
+import org.junit.Test;
+
 /**
  * Unit tests {@link org.apache.commons.lang3.time.DateUtils}.
  *
@@ -1604,6 +1617,99 @@ public class DateUtilsTest {
             Locale.setDefault(dflt);            
         }
     }
+    
+    /**
+     * Tests that an IllegalArgumentException is thrown if the
+     * locale supplied is not supported by the DateFormat class
+     * hierarchy. Note: this test is likely to pass without testing
+     * the behaviour, as typically all locales are supported.
+     * 
+     * @throws Exception
+     */
+    @Test(expected=IllegalArgumentException.class)
+    public void testParseBadLocale() throws Exception {
+        Set<Locale> availableLocales = new HashSet<Locale>(
+                Arrays.asList(Locale.getAvailableLocales()));
+        
+        Set<Locale> dateLocales = new HashSet<Locale>(
+                Arrays.asList(DateFormat.getAvailableLocales()));
+
+        Set<Locale> intersection = new HashSet<Locale>(availableLocales);
+        intersection.retainAll(dateLocales);
+        availableLocales.removeAll(intersection);
+        
+        // availableLocales now contains only those Locales that are
+        // not supported by the DateFormat class hierarchy. Could be
+        // empty, in which case we skip the test.
+        assumeTrue(availableLocales.size() > 0);
+        
+        Locale invalidLocale = availableLocales.iterator().next();
+        
+        String[] parsers = {"yyyy"};        
+        DateUtils.parseDate("foo", invalidLocale, parsers);         
+    }
+    
+    /**
+     * Retrieves a non-system locale date pattern string and attempts
+     * to use it.
+     * 
+     * @throws Exception
+     */
+    @Test
+    public void testParseNonSystemLocale() throws Exception {
+        // Retrieve standard long form date pattern
+        String localPattern = getLongDateFormatForLocale(Locale.getDefault());
+        assumeTrue(localPattern != null);
+        
+        // Find a pattern from another locale that doesn't match
+        String nonMatchingPattern = null;        
+        Locale foreignLocale = null;
+        for (Locale locale : DateFormat.getAvailableLocales()) {
+            String foreignPattern = getLongDateFormatForLocale(locale);
+            if (foreignPattern != null && !foreignPattern.equals(localPattern)) {
+                getLongDateFormatForLocale(locale);
+                nonMatchingPattern = foreignPattern;
+                foreignLocale = locale;
+                break;
+            }
+        }
+        
+        // There is a slim chance that we can't find a date string that
+        // differs from the system default. Skip test in that case.
+        assumeTrue(nonMatchingPattern != null && foreignLocale != null);
+        
+        Date testDate = new Date();
+        SimpleDateFormat sdf = new SimpleDateFormat("", foreignLocale);
+        sdf.applyLocalizedPattern(nonMatchingPattern);
+        String testDateString = sdf.format(testDate);
+        
+        Date resultDate = DateUtils.parseDate(testDateString, foreignLocale, 
+                new String[] {nonMatchingPattern});
+        
+        assertTrue(DateUtils.isSameDay(testDate, resultDate));
+    }
+    
+    /**
+     * Retrieves the long date format pattern string for the supplied
+     * locale.
+     * 
+     * @param locale the locale to retrieve the pattern for
+     * @return the long date pattern string, or <code>null</code> if
+     * not found
+     */
+    private String getLongDateFormatForLocale(Locale locale) {
+        if (! ArrayUtils.contains(DateFormat.getAvailableLocales(), locale)) {
+            return null;            
+        }
+        
+        DateFormat localFormat = DateFormat.getDateInstance(DateFormat.LONG, 
+                locale);
+        if (!(localFormat instanceof SimpleDateFormat)) {
+            return null;
+        }
+        
+        return ((SimpleDateFormat) localFormat).toLocalizedPattern();        
+    }
 
     /**
      * This checks that this is a 7 element iterator of Calendar objects