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);
}
}
}