Charles, I think I've told you this several times now. Please add issues you fix to changes.xml! This is necessary to automatically create release notes.
Thank you. Benedikt 2015-03-23 3:33 GMT+01:00 <c...@apache.org>: > Author: chas > Date: Mon Mar 23 02:33:41 2015 > New Revision: 1668511 > > URL: http://svn.apache.org/r1668511 > Log: > LANG-1101 FastDateParser and FastDatePrinter support 'X' format > > Modified: > > commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateParser.java > > commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java > > commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java > > commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.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=1668511&r1=1668510&r2=1668511&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 Mar 23 02:33:41 2015 > @@ -96,7 +96,10 @@ public class FastDateParser implements D > > /** > * <p>Constructs a new FastDateParser.</p> > - * > + * > + * Use {@link FastDateFormat#getInstance(String, TimeZone, Locale)} > or another variation of the > + * factory methods of {@link FastDateFormat} to get a cached > FastDateParser instance. > + * > * @param pattern non-null {@link java.text.SimpleDateFormat} > compatible > * pattern > * @param timeZone non-null time zone to use > @@ -467,13 +470,14 @@ public class FastDateParser implements D > * false, if this field is a constant value > */ > abstract boolean addRegex(FastDateParser parser, StringBuilder > regex); > + > } > > /** > * A <code>Pattern</code> to parse the user supplied SimpleDateFormat > pattern > */ > private static final Pattern formatPattern= Pattern.compile( > - > "D+|E+|F+|G+|H+|K+|M+|S+|W+|Z+|a+|d+|h+|k+|m+|s+|w+|y+|z+|''|'[^']++(''[^']*+)*+'|[^'A-Za-z]++"); > + > "D+|E+|F+|G+|H+|K+|M+|S+|W+|X+|Z+|a+|d+|h+|k+|m+|s+|w+|y+|z+|''|'[^']++(''[^']*+)*+'|[^'A-Za-z]++"); > > /** > * Obtain a Strategy given a field from a SimpleDateFormat pattern > @@ -524,6 +528,8 @@ public class FastDateParser implements D > return WEEK_OF_YEAR_STRATEGY; > case 'y': > return formatField.length()>2 ?LITERAL_YEAR_STRATEGY > :ABBREVIATED_YEAR_STRATEGY; > + case 'X': > + return > ISO8601TimeZoneStrategy.getStrategy(formatField.length()); > case 'Z': > if (formatField.equals("ZZ")) { > return ISO_8601_STRATEGY; > @@ -834,14 +840,18 @@ public class FastDateParser implements D > > private static class ISO8601TimeZoneStrategy extends Strategy { > // Z, +hh, -hh, +hhmm, -hhmm, +hh:mm or -hh:mm > - private static final String PATTERN = > "(Z|(?:[+-]\\d{2}(?::?\\d{2})?))"; > + private final String pattern; > + > + ISO8601TimeZoneStrategy(String pattern) { > + this.pattern = pattern; > + } > > /** > * {@inheritDoc} > */ > @Override > boolean addRegex(FastDateParser parser, StringBuilder regex) { > - regex.append(PATTERN); > + regex.append(pattern); > return true; > } > > @@ -856,6 +866,23 @@ public class FastDateParser implements D > cal.setTimeZone(TimeZone.getTimeZone("GMT" + value)); > } > } > + > + private static final Strategy ISO_8601_1_STRATEGY = new > ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}))"); > + private static final Strategy ISO_8601_2_STRATEGY = new > ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}\\d{2}))"); > + private static final Strategy ISO_8601_3_STRATEGY = new > ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}(?::)\\d{2}))"); > + > + static Strategy getStrategy(int tokenLen) { > + switch(tokenLen) { > + case 1: > + return ISO_8601_1_STRATEGY; > + case 2: > + return ISO_8601_2_STRATEGY; > + case 3: > + return ISO_8601_3_STRATEGY; > + default: > + throw new IllegalArgumentException("invalid number > of X"); > + } > + } > } > > private static final Strategy NUMBER_MONTH_STRATEGY = new > NumberStrategy(Calendar.MONTH) { > @@ -887,5 +914,7 @@ public class FastDateParser implements D > private static final Strategy MINUTE_STRATEGY = new > NumberStrategy(Calendar.MINUTE); > private static final Strategy SECOND_STRATEGY = new > NumberStrategy(Calendar.SECOND); > private static final Strategy MILLISECOND_STRATEGY = new > NumberStrategy(Calendar.MILLISECOND); > - private static final Strategy ISO_8601_STRATEGY = new > ISO8601TimeZoneStrategy(); > + private static final Strategy ISO_8601_STRATEGY = new > ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}(?::?\\d{2})?))"); > + > + > } > > Modified: > commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java > URL: > http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java?rev=1668511&r1=1668510&r2=1668511&view=diff > > ============================================================================== > --- > commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java > (original) > +++ > commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java > Mon Mar 23 02:33:41 2015 > @@ -38,7 +38,7 @@ import org.apache.commons.lang3.Validate > * <p>FastDatePrinter is a fast and thread-safe version of > * {@link java.text.SimpleDateFormat}.</p> > * > - * <p>To obtain a proxy to a FastDatePrinter, use {@link > FastDateFormat#getInstance(String, TimeZone, Locale)} > + * <p>To obtain a FastDatePrinter, use {@link > FastDateFormat#getInstance(String, TimeZone, Locale)} > * or another variation of the factory methods of {@link > FastDateFormat}.</p> > * > * <p>Since FastDatePrinter is thread safe, you can use a static member > instance:</p> > @@ -64,6 +64,10 @@ import org.apache.commons.lang3.Validate > * ISO 8601 full format time zones (eg. {@code +08:00} or {@code -11:00}). > * This introduces a minor incompatibility with Java 1.4, but at a gain of > * useful functionality.</p> > + * > + * <p>Starting with JDK7, ISO 8601 support was added using the pattern > {@code 'X'}. > + * To maintain compatibility, {@code 'ZZ'} will continue to be supported, > but using > + * one of the {@code 'X'} formats is recommended. > * > * <p>Javadoc cites for the year pattern: <i>For formatting, if the > number of > * pattern letters is 2, the year is truncated to 2 digits; otherwise it > is > @@ -137,6 +141,8 @@ public class FastDatePrinter implements > > //----------------------------------------------------------------------- > /** > * <p>Constructs a new FastDatePrinter.</p> > + * Use {@link FastDateFormat#getInstance(String, TimeZone, Locale)} > or another variation of the > + * factory methods of {@link FastDateFormat} to get a cached > FastDatePrinter instance. > * > * @param pattern {@link java.text.SimpleDateFormat} compatible > pattern > * @param timeZone non-null time zone to use > @@ -265,6 +271,9 @@ public class FastDatePrinter implements > case 'K': // hour in am/pm (0..11) > rule = selectNumberRule(Calendar.HOUR, tokenLen); > break; > + case 'X': // ISO 8601 > + rule = Iso8601_Rule.getRule(tokenLen); > + break; > case 'z': // time zone (text) > if (tokenLen >= 4) { > rule = new TimeZoneNameRule(mTimeZone, mLocale, > TimeZone.LONG); > @@ -581,6 +590,11 @@ public class FastDatePrinter implements > init(); > } > > + private static void appendDigits(final StringBuffer buffer, final > int value) { > + buffer.append((char)(value / 10 + '0')); > + buffer.append((char)(value % 10 + '0')); > + } > + > // Rules > > //----------------------------------------------------------------------- > /** > @@ -588,7 +602,7 @@ public class FastDatePrinter implements > */ > private interface Rule { > /** > - * Returns the estimated lentgh of the result. > + * Returns the estimated length of the result. > * > * @return the estimated length > */ > @@ -810,8 +824,7 @@ public class FastDatePrinter implements > if (value < 10) { > buffer.append((char)(value + '0')); > } else { > - buffer.append((char)(value / 10 + '0')); > - buffer.append((char)(value % 10 + '0')); > + appendDigits(buffer, value); > } > } > } > @@ -863,8 +876,7 @@ public class FastDatePrinter implements > for (int i = mSize; --i >= 2; ) { > buffer.append('0'); > } > - buffer.append((char)(value / 10 + '0')); > - buffer.append((char)(value % 10 + '0')); > + appendDigits(buffer, value); > } else { > int digits; > if (value < 1000) { > @@ -918,8 +930,7 @@ public class FastDatePrinter implements > @Override > public final void appendTo(final StringBuffer buffer, final int > value) { > if (value < 100) { > - buffer.append((char)(value / 10 + '0')); > - buffer.append((char)(value % 10 + '0')); > + appendDigits(buffer, value); > } else { > buffer.append(Integer.toString(value)); > } > @@ -960,8 +971,7 @@ public class FastDatePrinter implements > */ > @Override > public final void appendTo(final StringBuffer buffer, final int > value) { > - buffer.append((char)(value / 10 + '0')); > - buffer.append((char)(value % 10 + '0')); > + appendDigits(buffer, value); > } > } > > @@ -999,8 +1009,7 @@ public class FastDatePrinter implements > */ > @Override > public final void appendTo(final StringBuffer buffer, final int > value) { > - buffer.append((char)(value / 10 + '0')); > - buffer.append((char)(value % 10 + '0')); > + appendDigits(buffer, value); > } > } > > @@ -1121,7 +1130,7 @@ public class FastDatePrinter implements > return value; > } > > - /** > + /** > * <p>Inner class to output a time zone name.</p> > */ > private static class TimeZoneNameRule implements Rule { > @@ -1178,7 +1187,7 @@ public class FastDatePrinter implements > static final TimeZoneNumberRule INSTANCE_COLON = new > TimeZoneNumberRule(true, false); > static final TimeZoneNumberRule INSTANCE_NO_COLON = new > TimeZoneNumberRule(false, false); > static final TimeZoneNumberRule INSTANCE_ISO_8601 = new > TimeZoneNumberRule(true, true); > - > + > final boolean mColon; > final boolean mISO8601; > > @@ -1221,16 +1230,95 @@ public class FastDatePrinter implements > } > > final int hours = offset / (60 * 60 * 1000); > - buffer.append((char)(hours / 10 + '0')); > - buffer.append((char)(hours % 10 + '0')); > + appendDigits(buffer, hours); > > if (mColon) { > buffer.append(':'); > } > > final int minutes = offset / (60 * 1000) - 60 * hours; > - buffer.append((char)(minutes / 10 + '0')); > - buffer.append((char)(minutes % 10 + '0')); > + appendDigits(buffer, minutes); > + } > + } > + > + /** > + * <p>Inner class to output a time zone as a number {@code +/-HHMM} > + * or {@code +/-HH:MM}.</p> > + */ > + private static class Iso8601_Rule implements Rule { > + > + // Sign TwoDigitHours or Z > + static final Iso8601_Rule ISO8601_HOURS = new Iso8601_Rule(3); > + // Sign TwoDigitHours Minutes or Z > + static final Iso8601_Rule ISO8601_HOURS_MINUTES = new > Iso8601_Rule(5); > + // Sign TwoDigitHours : Minutes or Z > + static final Iso8601_Rule ISO8601_HOURS_COLON_MINUTES = new > Iso8601_Rule(6); > + > + static Iso8601_Rule getRule(int tokenLen) { > + switch(tokenLen) { > + case 1: > + return Iso8601_Rule.ISO8601_HOURS; > + case 2: > + return Iso8601_Rule.ISO8601_HOURS_MINUTES; > + case 3: > + return Iso8601_Rule.ISO8601_HOURS_COLON_MINUTES; > + default: > + throw new IllegalArgumentException("invalid number > of X"); > + } > + } > + > + final int length; > + > + /** > + * Constructs an instance of {@code Iso8601_Rule} with the > specified properties. > + * > + * @param length The number of characters in output (unless Z is > output) > + */ > + Iso8601_Rule(final int length) { > + this.length = length; > + } > + > + /** > + * {@inheritDoc} > + */ > + @Override > + public int estimateLength() { > + return length; > + } > + > + /** > + * {@inheritDoc} > + */ > + @Override > + public void appendTo(final StringBuffer buffer, final Calendar > calendar) { > + int zoneOffset = calendar.get(Calendar.ZONE_OFFSET); > + if (zoneOffset == 0) { > + buffer.append("Z"); > + return; > + } > + > + int offset = zoneOffset + calendar.get(Calendar.DST_OFFSET); > + > + if (offset < 0) { > + buffer.append('-'); > + offset = -offset; > + } else { > + buffer.append('+'); > + } > + > + final int hours = offset / (60 * 60 * 1000); > + appendDigits(buffer, hours); > + > + if (length<5) { > + return; > + } > + > + if (length==6) { > + buffer.append(':'); > + } > + > + final int minutes = offset / (60 * 1000) - 60 * hours; > + appendDigits(buffer, minutes); > } > } > > > 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=1668511&r1=1668510&r2=1668511&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 Mar 23 02:33:41 2015 > @@ -56,6 +56,7 @@ public class FastDateParserTest { > private static final TimeZone REYKJAVIK = > TimeZone.getTimeZone("Atlantic/Reykjavik"); > private static final TimeZone NEW_YORK = > TimeZone.getTimeZone("America/New_York"); > private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); > + private static final TimeZone INDIA = > TimeZone.getTimeZone("Asia/Calcutta"); > > private static final Locale SWEDEN = new Locale("sv", "SE"); > > @@ -556,4 +557,63 @@ public class FastDateParserTest { > assertEquals(expected.getTime(), fdp.parse("14MAY2014")); > assertEquals(expected.getTime(), fdp.parse("14May2014")); > } > + > + @Test(expected = IllegalArgumentException.class) > + public void test1806Argument() { > + getInstance("XXXX"); > + } > + > + private static Calendar initializeCalendar(TimeZone tz) { > + Calendar cal = Calendar.getInstance(tz); > + cal.set(Calendar.YEAR, 2001); > + cal.set(Calendar.MONTH, 1); // not daylight savings > + cal.set(Calendar.DAY_OF_MONTH, 4); > + cal.set(Calendar.HOUR_OF_DAY, 12); > + cal.set(Calendar.MINUTE, 8); > + cal.set(Calendar.SECOND, 56); > + cal.set(Calendar.MILLISECOND, 235); > + return cal; > + } > + > + private static enum Expected1806 { > + India(INDIA, "+05", "+0530", "+05:30", true), > + Greenwich(GMT, "Z", "Z", "Z", false), > + NewYork(NEW_YORK, "-05", "-0500", "-05:00", false); > + > + private Expected1806(TimeZone zone, String one, String > two, String three, boolean hasHalfHourOffset) { > + this.zone = zone; > + this.one = one; > + this.two = two; > + this.three = three; > + this.offset = hasHalfHourOffset ?30*60*1000 :0; > + } > + > + final TimeZone zone; > + final String one; > + final String two; > + final String three; > + final long offset; > + } > + > + @Test > + public void test1806() throws ParseException { > + String formatStub = "yyyy-MM-dd'T'HH:mm:ss.SSS"; > + String dateStub = "2001-02-04T12:08:56.235"; > + > + for (Expected1806 trial : Expected1806.values()) { > + Calendar cal = initializeCalendar(trial.zone); > + > + String message = trial.zone.getDisplayName()+";"; > + > + DateParser parser = getInstance(formatStub+"X", > trial.zone); > + assertEquals(message+trial.one, > cal.getTime().getTime(), > parser.parse(dateStub+trial.one).getTime()-trial.offset); > + > + parser = getInstance(formatStub+"XX", trial.zone); > + assertEquals(message+trial.two, cal.getTime(), > parser.parse(dateStub+trial.two)); > + > + parser = getInstance(formatStub+"XXX", trial.zone); > + assertEquals(message+trial.three, cal.getTime(), > parser.parse(dateStub+trial.three)); > + } > + } > + > } > > Modified: > commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java > URL: > http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java?rev=1668511&r1=1668510&r2=1668511&view=diff > > ============================================================================== > --- > commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java > (original) > +++ > commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java > Mon Mar 23 02:33:41 2015 > @@ -19,6 +19,7 @@ package org.apache.commons.lang3.time; > import static org.junit.Assert.*; > > import java.io.Serializable; > +import java.text.ParseException; > import java.text.SimpleDateFormat; > import java.util.Calendar; > import java.util.Date; > @@ -39,6 +40,8 @@ public class FastDatePrinterTest { > > private static final String YYYY_MM_DD = "yyyy/MM/dd"; > private static final TimeZone NEW_YORK = > TimeZone.getTimeZone("America/New_York"); > + private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); > + private static final TimeZone INDIA = > TimeZone.getTimeZone("Asia/Calcutta"); > private static final Locale SWEDEN = new Locale("sv", "SE"); > > DatePrinter getInstance(final String format) { > @@ -272,4 +275,55 @@ public class FastDatePrinterTest { > FastDateFormat colonFormat = FastDateFormat.getInstance("ZZZ"); > assertEquals("+00:00", colonFormat.format(c)); > } > + > + private static Calendar initializeCalendar(TimeZone tz) { > + Calendar cal = Calendar.getInstance(tz); > + cal.set(Calendar.YEAR, 2001); > + cal.set(Calendar.MONTH, 1); // not daylight savings > + cal.set(Calendar.DAY_OF_MONTH, 4); > + cal.set(Calendar.HOUR_OF_DAY, 12); > + cal.set(Calendar.MINUTE, 8); > + cal.set(Calendar.SECOND, 56); > + cal.set(Calendar.MILLISECOND, 235); > + return cal; > + } > + > + @Test(expected = IllegalArgumentException.class) > + public void test1806Argument() { > + getInstance("XXXX"); > + } > + > + private static enum Expected1806 { > + India(INDIA, "+05", "+0530", "+05:30"), Greenwich(GMT, > "Z", "Z", "Z"), NewYork( > + NEW_YORK, "-05", "-0500", "-05:00"); > + > + private Expected1806(TimeZone zone, String one, String > two, String three) { > + this.zone = zone; > + this.one = one; > + this.two = two; > + this.three = three; > + } > + > + final TimeZone zone; > + final String one; > + final String two; > + final String three; > + } > + > + > + @Test > + public void test1806() throws ParseException { > + for (Expected1806 trial : Expected1806.values()) { > + Calendar cal = initializeCalendar(trial.zone); > + > + DatePrinter printer = getInstance("X", trial.zone); > + assertEquals(trial.one, printer.format(cal)); > + > + printer = getInstance("XX", trial.zone); > + assertEquals(trial.two, printer.format(cal)); > + > + printer = getInstance("XXX", trial.zone); > + assertEquals(trial.three, printer.format(cal)); > + } > + } > } > > > -- http://people.apache.org/~britter/ http://www.systemoutprintln.de/ http://twitter.com/BenediktRitter http://github.com/britter