Hi,
In case its of interest to anyone, a while back I wrote a Java DateFormat for formatting/parsing xsd:dateTime's. The parse() method was snarfed from an Axis utility method with better error reporting added. It's pretty robust. The format() method uses: new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" ).parse( date ). Source file and test case attached.
Cheers, Ian
Dittmann Werner wrote:
Javier,
WSS4J uses the following format to parse the times:
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
thus we don't parse milliseconds.
Regards, Werner
-----Urspr�ngliche Nachricht-----
Von: Davanum Srinivas [mailto:[EMAIL PROTECTED] Gesendet: Montag, 25. April 2005 23:36
An: Javier Delgadillo
Cc: [email protected]
Betreff: Re: Created Element
Javier,
don't remember running into any vendor who uses milliseconds...all
that the spec (http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-
message-security-1.0.pdf) says is:
The <wsu:Timestamp> element provides a mechanism for expressing the creation and expiration times of the security semantics in a message. All times MUST be in UTC format as specified by the XML Schema type (dateTime). It should be 1300 noted that times support time precision as defined in the XML Schema specification.
it's an interop thing....
thanks, dims
On 4/25/05, Javier Delgadillo <[EMAIL PROTECTED]> wrote:
All, We were doing some inter-operability tests with webMethods Glue. They
produce the following timestamp: <wsse:Created>2005-04-15T00:47:01.792Z</wsse:Created> webMethods has stated they have some versions in QA that will fix the prefix
and correctly mark it as wsu. My question is about the milliseconds in the
actual timestamp. Even after fixing the prefix, the wss4j libraries throw
an exception trying to parse the actual timestamp. I haven't been able to validate whether or not the milliseconds are allowed
here. Has anyone else run into this? How can I make wss4j parse the timestamp
correctly, assuming the milliseconds are valid?
--- Javier Delgadillo
ESRI Internet Solutions Division
(909) 793-2853 x1068
http://arcweb.esri.com/sc/index.html
package com.hp.wsm.commons.xml;
import com.hp.wsm.commons.logging.Logger; import com.hp.wsm.commons.util.TimeZoneIds; import com.hp.wsm.commons.util.MessageRetriever; import java.text.DateFormat; import java.text.FieldPosition; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.text.ParseException; import java.util.Date; import java.util.TimeZone; /** * A [EMAIL PROTECTED] DateFormat} for the format of the dateTime simpleType as specified in the * XML Schema specification. See <a href="http://www.w3.org/TR/xmlschema-2/#dateTime"> * XML Schema Part 2: Datatypes, W3C Recommendation 02 May 2001, Section 3.2.7.1</a>. * * @author Ian P. Springer */ public class XmlSchemaDateFormat extends DateFormat { /** * Logger. */ private static final Logger LOG = ResourceKeys.LOG; /** * Message retriever. */ private static final MessageRetriever MSG = ResourceKeys.MSG; /** * DateFormat for Zulu (UTC) form of an XML Schema dateTime string. */ private static final DateFormat DATEFORMAT_XSD_ZULU = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" ); static { DATEFORMAT_XSD_ZULU.setTimeZone( TimeZone.getTimeZone( TimeZoneIds.UTC ) ); } /** * This method was snarfed from <tt>org.apache.axis.encoding.ser.CalendarDeserializer</tt>, * which was written by Sam Ruby ([EMAIL PROTECTED]) and Rich Scheuerle ([EMAIL PROTECTED]). * Better error reporting was added. * * @see DateFormat#parse(java.lang.String) */ public Date parse( String src, ParsePosition parse_pos ) { Date date; // validate fixed portion of format int index = 0; try { if ( src != null ) { if ( ( src.charAt( 0 ) == '+' ) || ( src.charAt( 0 ) == '-' ) ) { src = src.substring( 1 ); } if ( src.length( ) < 19 ) { parse_pos.setIndex( src.length() - 1 ); handleParseError( parse_pos, MSG.getMsg( ResourceKeys.TOO_FEW_CHARS ) ); } validateChar( src, parse_pos, index = 4, '-', MSG.getMsg( ResourceKeys.EXPECTED_DASH ) ); validateChar( src, parse_pos, index = 7, '-', MSG.getMsg( ResourceKeys.EXPECTED_DASH ) ); validateChar( src, parse_pos, index = 10, 'T', MSG.getMsg( ResourceKeys.EXPECTED_CAPITAL_T ) ); validateChar( src, parse_pos, index = 13, ':', MSG.getMsg( ResourceKeys.EXPECTED_COLON_IN_TIME ) ); validateChar( src, parse_pos, index = 16, ':', MSG.getMsg( ResourceKeys.EXPECTED_COLON_IN_TIME ) ); } // convert what we have validated so far try { synchronized ( DATEFORMAT_XSD_ZULU ) { date = DATEFORMAT_XSD_ZULU.parse( ( src == null ) ? null : ( src.substring( 0, 19 ) + ".000Z" ) ); } } catch ( Exception e ) { throw new NumberFormatException( e.toString( ) ); } index = 19; // parse optional milliseconds if ( src != null ) { if ( ( index < src.length( ) ) && ( src.charAt( index ) == '.' ) ) { int milliseconds = 0; int start = ++index; while ( ( index < src.length( ) ) && Character.isDigit( src.charAt( index ) ) ) { index++; } String decimal = src.substring( start, index ); if ( decimal.length( ) == 3 ) { milliseconds = Integer.parseInt( decimal ); } else if ( decimal.length( ) < 3 ) { milliseconds = Integer.parseInt( ( decimal + "000" ).substring( 0, 3 ) ); } else { milliseconds = Integer.parseInt( decimal.substring( 0, 3 ) ); if ( decimal.charAt( 3 ) >= '5' ) { ++milliseconds; } } // add milliseconds to the current date date.setTime( date.getTime( ) + milliseconds ); } // parse optional timezone if ( ( ( index + 5 ) < src.length( ) ) && ( ( src.charAt( index ) == '+' ) || ( src.charAt( index ) == '-' ) ) ) { validateCharIsDigit( src, parse_pos, index + 1, MSG.getMsg( ResourceKeys.EXPECTED_NUMERAL ) ); validateCharIsDigit( src, parse_pos, index + 2, MSG.getMsg( ResourceKeys.EXPECTED_NUMERAL ) ); validateChar( src, parse_pos, index + 3, ':', MSG.getMsg( ResourceKeys.EXPECTED_COLON_IN_TIMEZONE ) ); validateCharIsDigit( src, parse_pos, index + 4, MSG.getMsg( ResourceKeys.EXPECTED_NUMERAL ) ); validateCharIsDigit( src, parse_pos, index + 5, MSG.getMsg( ResourceKeys.EXPECTED_NUMERAL ) ); final int hours = ( ( ( src.charAt( index + 1 ) - '0' ) * 10 ) + src.charAt( index + 2 ) ) - '0'; final int mins = ( ( ( src.charAt( index + 4 ) - '0' ) * 10 ) + src.charAt( index + 5 ) ) - '0'; int millisecs = ( ( hours * 60 ) + mins ) * 60 * 1000; // subtract millisecs from current date to obtain GMT if ( src.charAt( index ) == '+' ) { millisecs = -millisecs; } date.setTime( date.getTime( ) + millisecs ); index += 6; } if ( ( index < src.length( ) ) && ( src.charAt( index ) == 'Z' ) ) { index++; } if ( index < src.length( ) ) { handleParseError( parse_pos, MSG.getMsg( ResourceKeys.TOO_MANY_CHARS ) ); } } } catch ( ParseException pe ) { LOG.error( ResourceKeys.OBJ, pe ); index = 0; // IMPORTANT: this tells DateFormat.parse() to throw a ParseException parse_pos.setErrorIndex( index ); date = null; } parse_pos.setIndex( index ); return ( date ); } /** * @see DateFormat#format(java.util.Date) */ public StringBuffer format( Date date, StringBuffer append_buf, FieldPosition field_pos ) { String str; synchronized ( DATEFORMAT_XSD_ZULU ) { str = DATEFORMAT_XSD_ZULU.format( date ); } if ( append_buf == null ) { append_buf = new StringBuffer( ); } append_buf.append( str ); return ( append_buf ); } private void validateChar( String str, ParsePosition parse_pos, int index, char expected, String error_reason ) throws ParseException { if ( str.charAt( index ) != expected ) { handleParseError( parse_pos, error_reason ); } } private void validateCharIsDigit( String str, ParsePosition parse_pos, int index, String error_reason ) throws ParseException { if ( ! Character.isDigit( str.charAt( index ) ) ) { handleParseError( parse_pos, error_reason ); } } private void handleParseError( ParsePosition parse_pos, String error_reason ) throws ParseException { throw new ParseException( MSG.getMsg( ResourceKeys.INVALID_DATETIME, error_reason, parse_pos ), parse_pos.getErrorIndex() ); } }
package com.hp.wsm.commons.xml;
import com.hp.wsm.commons.junit.ExtendedTestCase;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
/**
* Test case for [EMAIL PROTECTED] XmlSchemaDateFormat}.
*
* @author Ian P. Springer
*/
public class XmlSchemaDateFormatTest extends ExtendedTestCase
{
/**
* DOCUMENT_ME
*/
private static final String TIMEZONE_ID__UTC = "UTC";
/**
* DOCUMENT_ME
*/
private static final String DATEFORMAT_PATTERN__XSD_DATETIME_UTC =
"yyyy-MM-dd'T'HH:mm:ss'.'SSS'Z'";
/**
* DOCUMENT_ME
*/
private static final DateFormat DATEFORMAT__XSD_DATETIME_UTC =
new SimpleDateFormat( DATEFORMAT_PATTERN__XSD_DATETIME_UTC );
static
{
DATEFORMAT__XSD_DATETIME_UTC.setTimeZone( TimeZone.getTimeZone(
TIMEZONE_ID__UTC ) );
}
private DateFormat m_date_format = new XmlSchemaDateFormat();
/**
* Test for [EMAIL PROTECTED] XmlSchemaDateFormat#format(java.util.Date)}.
*
* @throws Exception on error
*/
public void testFormat() throws Exception
{
Date date = new Date( 0L );
assertEquals( "1970-01-01T00:00:00.000Z",
m_date_format.format( date ) );
date = new Date( );
Calendar cal = Calendar.getInstance( TimeZone.getTimeZone( "GMT" ) );
cal.setTime( date );
String year = new Integer( cal.get( Calendar.YEAR ) ).toString();
NumberFormat num_format = new DecimalFormat();
num_format.setMinimumIntegerDigits( 2 );
String month = num_format.format( cal.get( Calendar.MONTH ) + 1 );
String day = num_format.format( cal.get( Calendar.DATE ) );
String hour = num_format.format( cal.get( Calendar.HOUR_OF_DAY ) );
String minute = num_format.format( cal.get( Calendar.MINUTE ) );
String second = num_format.format( cal.get( Calendar.SECOND ) );
num_format.setMinimumIntegerDigits( 3 );
String millisecond = num_format.format( new Integer( cal.get(
Calendar.MILLISECOND ) ).longValue() );
assertEquals( year + "-" + month + "-" + day + "T" + hour + ":" + minute
+ ":" + second + "." + millisecond + "Z",
m_date_format.format( date ) );
}
/**
* Test for [EMAIL PROTECTED] XmlSchemaDateFormat#parse(java.lang.String)}.
*
* @throws Exception on error
*/
public void testParse() throws Exception
{
assertEquals( "1970-01-01T15:15:15.000Z",
DATEFORMAT__XSD_DATETIME_UTC.format( m_date_format.parse(
"1970-01-01T15:15:15Z" ) ) );
assertEquals( "1867-09-14T07:26:51.892Z",
DATEFORMAT__XSD_DATETIME_UTC.format( m_date_format.parse(
"1867-09-14T07:26:51.892Z" ) ) );
assertEquals( "2004-04-20T04:20:00.000Z",
DATEFORMAT__XSD_DATETIME_UTC.format( m_date_format.parse(
"2004-04-19T23:20:00-05:00" ) ) );
}
}
