/******************************************************************************* * Copyright © 2006, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.javart.util; import java.text.FieldPosition; import java.text.ParseException; import java.text.ParsePosition; import java.util.Date; import org.eclipse.edt.javart.Constants; import com.ibm.icu.impl.Utility; import com.ibm.icu.text.DateFormatSymbols; import com.ibm.icu.text.SimpleDateFormat; import com.ibm.icu.util.Calendar; import com.ibm.icu.util.ULocale; /** * A simple date formatter. */ public class JavartSimpleDateFormat extends SimpleDateFormat implements JavartDateFormat { /** * The version ID used in serialization. */ private static final long serialVersionUID = Constants.SERIAL_VERSION_UID; /** * The number of microseconds. */ private int microsecond; /** * The century. */ private int century; public JavartSimpleDateFormat( String pattern, DateFormatSymbols formatData, ULocale loc ) { super( pattern, loc ); setDateFormatSymbols( formatData ); } /** * @return Returns the century. */ public int getCentury() { return century; } /** * @param century * The century to set. */ public void setCentury( int century ) { this.century = century; } /** * @return Returns the microsecond. */ public int getMicrosecond() { return microsecond; } /** * @param microsecond * The microsecond to set. */ public void setMicrosecond( int microsecond ) { this.microsecond = microsecond; } /** * Parse a date/time string. * * @param text the date/time string to be parsed * * @return a Date, or null if the input could not be parsed * @exception ParseException if the given string cannot be parsed as a date. */ public Date parse( String text ) throws ParseException { ParsePosition pos = new ParsePosition( 0 ); Date result = parse( text, pos ); if ( pos.getIndex() == 0 ) { throw new ParseException( "ParseException: \"" + text + "\"", pos.getErrorIndex() ); } else if ( pos.getIndex() != text.length() ) { throw new ParseException( "ParseException: \"" + text + "\"", pos.getIndex() ); } return result; } /** * Converts one field of the input string into a numeric field value in * <code>cal</code>. Returns -start (for ParsePosition) if failed. * * @param text * the time text to be parsed. * @param start * where to start parsing. * @param ch * the pattern character for the date field text to be parsed. * @param count * the count of a pattern character. * @param obeyCount * if true, then the next field directly abuts this one, and we * should use the count to know when to stop parsing. * @param ambiguousYear * return parameter; upon return, if ambiguousYear[0] is true, * then a two-digit year was parsed and may need to be * readjusted. * @return the new start position if matching succeeded; a negative number * indicating matching failure, otherwise. As a side effect, set the * appropriate field of <code>cal</code> with the parsed value. */ protected int subParse( String text, int start, char ch, int count, boolean obeyCount, boolean allowNegative, boolean[] ambiguousYear, Calendar cal ) { // We only need to treat C and S/f specially. Everything else is handled // by the superclass. if ( ch != 'C' && ch != 'S' && ch != 'f' ) { return super.subParse( text, start, ch, count, obeyCount, allowNegative, ambiguousYear, cal ); } // Find the starting point. start = Utility.skipWhitespace( text, start ); ParsePosition pos = new ParsePosition( start ); // Parse the number. Number number = null; if ( obeyCount ) { if ( start + count > text.length() ) { return -start; } number = numberFormat.parse( text.substring( 0, start + count ), pos ); } else { number = numberFormat.parse( text, pos ); } if ( number == null ) { return -start; } // Store the number. int value = number.intValue(); if ( ch == 'C' ) { // It's C for Century. setCentury( value ); } else { // It's S/f for microsecond. We want it as a six-digit number. int i = pos.getIndex() - start; if ( i < 6 ) { value *= 10; i++; while ( i < 6 ) { value *= 10; i++; } } else if ( i > 6 ) { int a = 10; i--; while ( i > 6 ) { a *= 10; i--; } value = (value + (a >> 1)) / a; } setMicrosecond( value ); } return pos.getIndex(); } /** * Format a single field; useFastFormat variant. Reuses a * StringBuffer for results instead of creating a String on the * heap for each call. */ protected void subFormat( StringBuffer buf, char ch, int count, int beginOffset, FieldPosition pos, Calendar cal ) { switch ( ch ) { case 'M': // To get nice output and be compatible with v6, ignore any M's beyond the fourth. super.subFormat( buf, ch, Math.min( count, 4 ), beginOffset, pos, cal ); break; case 'C': // It's C for Century. buf.append( zeroPaddingNumber( getCentury(), count, Integer.MAX_VALUE ) ); break; case 'S': case 'f': // It's S/f for microsecond. We want it as a six-digit number. int value = getMicrosecond(); switch ( count ) { case 1: value = (value + 50000) / 100000; break; case 2: value = (value + 5000) / 10000; break; case 3: value = (value + 500) / 1000; break; case 4: value = (value + 50) / 100; break; case 5: value = (value + 5) / 10; break; } FieldPosition p = new FieldPosition( -1 ); numberFormat.setMinimumIntegerDigits( Math.min( 6, count ) ); numberFormat.setMaximumIntegerDigits( Integer.MAX_VALUE ); numberFormat.format( value, buf, p ); if ( count > 6 ) { numberFormat.setMinimumIntegerDigits( count - 6 ); numberFormat.format( 0L, buf, p ); } break; default: super.subFormat( buf, ch, count, beginOffset, pos, cal ); break; } } }