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