commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From c...@apache.org
Subject svn commit: r1668511 - in /commons/proper/lang/trunk/src: main/java/org/apache/commons/lang3/time/ test/java/org/apache/commons/lang3/time/
Date Mon, 23 Mar 2015 02:33:42 GMT
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));
+		}
+	}
 }



Mime
View raw message