+1 on committing this patch to CVS HEAD (only).

Steve, this is quite a bit of code. Any chance of a JUnit test case to go along with it? It's the perfect sort of code for a unit test.

Steve Quint wrote:
The current DateTool can only deal with one form of ISO 8601 dates - one that look like 
"yyyyMMddThh:mm:ss".  Any time zone information is discarded by the SimpleDateFormat 
object due to the format string.  Even worse, any other variant of ISO 8601 will cause the 
entire XMLRPC transaction to fail.

The included patch will allow the parsing of the following common forms of ISO 8601:
  *) YYYY-MM-DDThh:mm:ss
  *) YYYY-MM-DD hh:mm:ss
  *) YYYYMMDDThh:mm:ss
  *) YYYY-MM-DDThh:mm:ss.sss
  *) YYYY-MM-DDThh:mm:ssZ
  *) YYYY-MM-DDThh:mm:ss+0000

A better way to do this is probably to create a ISO 8601 DateFormatter implementation, 
but that looked too daunting.

Note that patch does not change the date format that the DateTool *generates*, only 
the parsing of incoming dates.


----------
Index: src/java/org/apache/xmlrpc/util/DateTool.java
===================================================================
RCS file: /home/cvspublic/ws-xmlrpc/src/java/org/apache/xmlrpc/util/DateTool.java,v
retrieving revision 1.1
diff -u -r1.1 DateTool.java
--- src/java/org/apache/xmlrpc/util/DateTool.java 21 Nov 2002 21:53:24 -0000 1.1
+++ src/java/org/apache/xmlrpc/util/DateTool.java 23 Apr 2004 22:19:33 -0000
@@ -59,33 +59,54 @@
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.StringTokenizer;
/**
- * Wraps a <code>DateFormat</code> instance to provide thread safety.
+ * <p>Wraps a <code>DateFormat</code> instance to provide thread safety.
+ * Also handle slight differences in the ISO 8601 date format.</p>
+ *
+ * <p>Different XMLRPC libraries have different interpretations of what
+ * format should be used for the ISO 8601 date. The official format
+ * specification allows for several variants. The following are all
+ * valid representations of a date:
+ *
+ * <ul>
+ * <li>YYYY-MM-DDThh:mm:ss</li>
+ * <li>YYYY-MM-DD hh:mm:ss</li>
+ * <li>YYYYMMDDThh:mm:ss</li>
+ * <li>YYYY-MM-DDThh:mm:ss.sss</li>
+ * <li>YYYY-MM-DDThh:mm:ssZ</li>
+ * <li>YYYY-MM-DDThh:mm:ss+0000</li>
+ * </ul>
+ *
+ * This tool will allow dates using the optional &quot;-&quot; character,
+ * as well as specification of a time zone. If no time zone is specified,
+ * the local time zone will be used.</p>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Hannes Wallnoefer</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Daniel Rall</a>
*/
public class DateTool
{
- protected static final String FORMAT = "yyyyMMdd'T'HH:mm:ss";
+ protected static final String DEFAULT_FORMAT = "yyyyMMdd'T'HH:mm:ss";
+ protected static final String TIMEZONE_FORMAT = "yyyyMMdd'T'HH:mm:ssZ";
+ protected static final String EXPECT = "yyyyMMddThh:mm:ss";
private DateFormat df;
/**
- * Uses the <code>DateFormat</code> string
- * <code>yyyyMMdd'T'HH:mm:ss</code>.
- *
- * @see #FORMAT
+ * Create a new instance of this tool to parse dates.
*/
public DateTool()
{
- df = new SimpleDateFormat(FORMAT);
+ df = new SimpleDateFormat(DEFAULT_FORMAT);
}
/**
+ * Convert the given date into an ISO 8601 formatted String.
* @param d The date to format.
* @return The formatted date.
+ * @see #DEFAULT_FORMAT
*/
public synchronized String format(Date d)
{
@@ -93,6 +114,7 @@
}
/**
+ * Parse the given string into a date object.
* @param s The text to parse a date from.
* @return The parsed date.
* @exception ParseException If the date could not be parsed.
@@ -100,6 +122,111 @@
public synchronized Date parse(String s)
throws ParseException
{
- return df.parse(s);
+ Date date = null;
+ ParseException pe = null;
+ + // Let's assume the best case for performance sake. If the input
+ // format is anything but "yyyyMMddThh:mm:ss", we need to parse it
+ // differently.
+ if ( s.length() <= EXPECT.length() )
+ {
+ try
+ {
+ date = df.parse(s);
+ }
+ catch( ParseException e )
+ {
+ pe = e;
+ }
+ }
+ + if ( date == null )
+ {
+ // OK, that didn't work. Let's try to figure out how to parse
+ // the date string. First strip out the year part of the date.
+ // We can't use a single delimiter pattern since the '-' can be
+ // used to separate days, or to specify a timezone.
+ boolean hasTimeZone = false;
+ StringTokenizer tokens = new StringTokenizer( s, " T", true );
+ if ( tokens.countTokens() != 3 )
+ { // no idea what we're looking at, throw original Exception
+ if ( pe != null )
+ {
+ throw pe;
+ }
+ else
+ {
+ throw new ParseException( "Unparsable error.", 0 );
+ }
+ }
+ StringBuffer dateBuff = new StringBuffer();
+ String dayStr = tokens.nextToken();
+ if ( dayStr.indexOf( '-' ) != -1 )
+ {
+ StringTokenizer days = new StringTokenizer( dayStr, "-" );
+ while ( days.hasMoreTokens() )
+ {
+ dateBuff.append( days.nextToken() );
+ }
+ }
+ else
+ {
+ dateBuff.append( dayStr );
+ }
+ // Throw away orginal separator
+ tokens.nextToken();
+ // Don't forget the add the 'T' separator.
+ dateBuff.append( "T" );
+ // Now get other components.
+ String timeStr = tokens.nextToken( ".zZ+-" );
+ dateBuff.append( timeStr );
+ // See if we have any more
+ while ( tokens.hasMoreTokens() )
+ {
+ char nextDelim = tokens.nextToken().charAt(0);
+ switch ( nextDelim )
+ {
+ case '.':
+ // throw away milliseconds component
+ tokens.nextToken();
+ break;
+ case 'z':
+ case 'Z':
+ // This indicated GMT
+ dateBuff.append( "+0000" );
+ hasTimeZone = true;
+ break;
+ case '+':
+ case '-':
+ dateBuff.append( nextDelim );
+ hasTimeZone = true;
+ // Time zone, the value may still contain ':'
+ String part = tokens.nextToken();
+ if ( part.indexOf( ':' ) != -1 )
+ {
+ dateBuff.append( part.substring( 0, 2 ) );
+ dateBuff.append( part.substring( 3, 5 ) );
+ }
+ else
+ {
+ dateBuff.append( part );
+ }
+ break;
+ default: // Nothing
+ }
+ }
+ // Now try to format the string we have
+ if ( hasTimeZone )
+ {
+ SimpleDateFormat tzf = new SimpleDateFormat(TIMEZONE_FORMAT);
+ date = tzf.parse( dateBuff.toString() );
+ }
+ else
+ {
+ date = df.parse( dateBuff.toString() );
+ }
+ }
+ + return date;
}
}





Reply via email to