Hello Charles, 2015-03-23 16:08 GMT+01:00 Chas Honton <[email protected]>:
> Benedikt, > > I apologize. Thank you for the reminder. I will update changes.xml this > evening (GMT-07). > > I will also investigate when LANG-1088 was fixed and update changes.xml > for that. > Thank you! I've already added LANG-1100 and LANG-1101. Regards, Benedikt > > > Chas > > On Mar 22, 2015, at 11:56 PM, Benedikt Ritter <[email protected]> wrote: > > 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 <[email protected]>: > >> 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 > > -- http://people.apache.org/~britter/ http://www.systemoutprintln.de/ http://twitter.com/BenediktRitter http://github.com/britter
