mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			779 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			779 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Java
		
	
	
	
/* SimpleDateFormat.java -- A class for parsing/formating simple 
 | 
						|
   date constructs
 | 
						|
   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 | 
						|
02111-1307 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
 | 
						|
package java.text;
 | 
						|
 | 
						|
import java.util.Calendar;
 | 
						|
import java.util.Date;
 | 
						|
import java.util.Enumeration;
 | 
						|
import java.util.GregorianCalendar;
 | 
						|
import java.util.Locale;
 | 
						|
import java.util.TimeZone;
 | 
						|
import java.util.SimpleTimeZone;
 | 
						|
import java.util.Vector;
 | 
						|
import java.io.ObjectInputStream;
 | 
						|
import java.io.IOException;
 | 
						|
 | 
						|
/**
 | 
						|
 * SimpleDateFormat provides convenient methods for parsing and formatting
 | 
						|
 * dates using Gregorian calendars (see java.util.GregorianCalendar). 
 | 
						|
 */
 | 
						|
public class SimpleDateFormat extends DateFormat 
 | 
						|
{
 | 
						|
  /** A pair class used by SimpleDateFormat as a compiled representation
 | 
						|
   *  of a format string.
 | 
						|
   */
 | 
						|
  private class FieldSizePair 
 | 
						|
  {
 | 
						|
    public int field;
 | 
						|
    public int size;
 | 
						|
 | 
						|
    /** Constructs a pair with the given field and size values */
 | 
						|
    public FieldSizePair(int f, int s) {
 | 
						|
      field = f;
 | 
						|
      size = s;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  private transient Vector tokens;
 | 
						|
  private DateFormatSymbols formatData;  // formatData
 | 
						|
  private Date defaultCenturyStart;
 | 
						|
  private transient int defaultCentury;
 | 
						|
  private String pattern;
 | 
						|
  private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier
 | 
						|
  private static final long serialVersionUID = 4774881970558875024L;
 | 
						|
 | 
						|
  // This string is specified in the JCL.  We set it here rather than
 | 
						|
  // do a DateFormatSymbols(Locale.US).getLocalPatternChars() since
 | 
						|
  // someone could theoretically change those values (though unlikely).
 | 
						|
  private static final String standardChars = "GyMdkHmsSEDFwWahKz";
 | 
						|
 | 
						|
  private void readObject(ObjectInputStream stream)
 | 
						|
    throws IOException, ClassNotFoundException
 | 
						|
  {
 | 
						|
    stream.defaultReadObject();
 | 
						|
    if (serialVersionOnStream < 1)
 | 
						|
      {
 | 
						|
        computeCenturyStart ();
 | 
						|
	serialVersionOnStream = 1;
 | 
						|
      }
 | 
						|
    else
 | 
						|
      // Ensure that defaultCentury gets set.
 | 
						|
      set2DigitYearStart(defaultCenturyStart);
 | 
						|
 | 
						|
    // Set up items normally taken care of by the constructor.
 | 
						|
    tokens = new Vector();
 | 
						|
    compileFormat(pattern);
 | 
						|
  }
 | 
						|
 | 
						|
  private void compileFormat(String pattern) 
 | 
						|
  {
 | 
						|
    // Any alphabetical characters are treated as pattern characters
 | 
						|
    // unless enclosed in single quotes.
 | 
						|
 | 
						|
    char thisChar;
 | 
						|
    int pos;
 | 
						|
    int field;
 | 
						|
    FieldSizePair current = null;
 | 
						|
 | 
						|
    for (int i=0; i<pattern.length(); i++) {
 | 
						|
      thisChar = pattern.charAt(i);
 | 
						|
      field = formatData.getLocalPatternChars().indexOf(thisChar);
 | 
						|
      if (field == -1) {
 | 
						|
	current = null;
 | 
						|
	if (Character.isLetter(thisChar)) {
 | 
						|
	  // Not a valid letter
 | 
						|
	  tokens.addElement(new FieldSizePair(-1,0));
 | 
						|
	} else if (thisChar == '\'') {
 | 
						|
	  // Quoted text section; skip to next single quote
 | 
						|
	  pos = pattern.indexOf('\'',i+1);
 | 
						|
	  if (pos == -1) {
 | 
						|
	    // This ought to be an exception, but spec does not
 | 
						|
	    // let us throw one.
 | 
						|
	    tokens.addElement(new FieldSizePair(-1,0));
 | 
						|
	  }
 | 
						|
	  if ((pos+1 < pattern.length()) && (pattern.charAt(pos+1) == '\'')) {
 | 
						|
	    tokens.addElement(pattern.substring(i+1,pos+1));
 | 
						|
	  } else {
 | 
						|
	    tokens.addElement(pattern.substring(i+1,pos));
 | 
						|
	  }
 | 
						|
	  i = pos;
 | 
						|
	} else {
 | 
						|
	  // A special character
 | 
						|
	  tokens.addElement(new Character(thisChar));
 | 
						|
	}
 | 
						|
      } else {
 | 
						|
	// A valid field
 | 
						|
	if ((current != null) && (field == current.field)) {
 | 
						|
	  current.size++;
 | 
						|
	} else {
 | 
						|
	  current = new FieldSizePair(field,1);
 | 
						|
	  tokens.addElement(current);
 | 
						|
	}
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
    
 | 
						|
  public String toString() 
 | 
						|
  {
 | 
						|
    StringBuffer output = new StringBuffer();
 | 
						|
    Enumeration e = tokens.elements();
 | 
						|
    while (e.hasMoreElements()) {
 | 
						|
      output.append(e.nextElement().toString());
 | 
						|
    }
 | 
						|
    return output.toString();
 | 
						|
  }
 | 
						|
      
 | 
						|
  /**
 | 
						|
   * Constructs a SimpleDateFormat using the default pattern for
 | 
						|
   * the default locale.
 | 
						|
   */
 | 
						|
  public SimpleDateFormat() 
 | 
						|
  {
 | 
						|
    /*
 | 
						|
     * There does not appear to be a standard API for determining 
 | 
						|
     * what the default pattern for a locale is, so use package-scope
 | 
						|
     * variables in DateFormatSymbols to encapsulate this.
 | 
						|
     */
 | 
						|
    super();
 | 
						|
    Locale locale = Locale.getDefault();
 | 
						|
    calendar = new GregorianCalendar(locale);
 | 
						|
    computeCenturyStart();
 | 
						|
    tokens = new Vector();
 | 
						|
    formatData = new DateFormatSymbols(locale);
 | 
						|
    pattern = (formatData.dateFormats[DEFAULT] + ' '
 | 
						|
	       + formatData.timeFormats[DEFAULT]);
 | 
						|
    compileFormat(pattern);
 | 
						|
    numberFormat = NumberFormat.getInstance(locale);
 | 
						|
    numberFormat.setGroupingUsed (false);
 | 
						|
  }
 | 
						|
  
 | 
						|
  /**
 | 
						|
   * Creates a date formatter using the specified pattern, with the default
 | 
						|
   * DateFormatSymbols for the default locale.
 | 
						|
   */
 | 
						|
  public SimpleDateFormat(String pattern) 
 | 
						|
  {
 | 
						|
    this(pattern, Locale.getDefault());
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a date formatter using the specified pattern, with the default
 | 
						|
   * DateFormatSymbols for the given locale.
 | 
						|
   */
 | 
						|
  public SimpleDateFormat(String pattern, Locale locale) 
 | 
						|
  {
 | 
						|
    super();
 | 
						|
    calendar = new GregorianCalendar(locale);
 | 
						|
    computeCenturyStart();
 | 
						|
    tokens = new Vector();
 | 
						|
    formatData = new DateFormatSymbols(locale);
 | 
						|
    compileFormat(pattern);
 | 
						|
    this.pattern = pattern;
 | 
						|
    numberFormat = NumberFormat.getInstance(locale);
 | 
						|
    numberFormat.setGroupingUsed (false);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a date formatter using the specified pattern. The
 | 
						|
   * specified DateFormatSymbols will be used when formatting.
 | 
						|
   */
 | 
						|
  public SimpleDateFormat(String pattern, DateFormatSymbols formatData)
 | 
						|
  {
 | 
						|
    super();
 | 
						|
    calendar = new GregorianCalendar();
 | 
						|
    computeCenturyStart ();
 | 
						|
    tokens = new Vector();
 | 
						|
    this.formatData = formatData;
 | 
						|
    compileFormat(pattern);
 | 
						|
    this.pattern = pattern;
 | 
						|
    numberFormat = NumberFormat.getInstance();
 | 
						|
    numberFormat.setGroupingUsed (false);
 | 
						|
  }
 | 
						|
 | 
						|
  // What is the difference between localized and unlocalized?  The
 | 
						|
  // docs don't say.
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method returns a string with the formatting pattern being used
 | 
						|
   * by this object.  This string is unlocalized.
 | 
						|
   *
 | 
						|
   * @return The format string.
 | 
						|
   */
 | 
						|
  public String toPattern()
 | 
						|
  {
 | 
						|
    return pattern;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method returns a string with the formatting pattern being used
 | 
						|
   * by this object.  This string is localized.
 | 
						|
   *
 | 
						|
   * @return The format string.
 | 
						|
   */
 | 
						|
  public String toLocalizedPattern()
 | 
						|
  {
 | 
						|
    String localChars = formatData.getLocalPatternChars();
 | 
						|
    return applyLocalizedPattern (pattern, standardChars, localChars);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method sets the formatting pattern that should be used by this
 | 
						|
   * object.  This string is not localized.
 | 
						|
   *
 | 
						|
   * @param pattern The new format pattern.
 | 
						|
   */
 | 
						|
  public void applyPattern(String pattern)
 | 
						|
  {
 | 
						|
    tokens = new Vector();
 | 
						|
    compileFormat(pattern);
 | 
						|
    this.pattern = pattern;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method sets the formatting pattern that should be used by this
 | 
						|
   * object.  This string is localized.
 | 
						|
   *
 | 
						|
   * @param pattern The new format pattern.
 | 
						|
   */
 | 
						|
  public void applyLocalizedPattern(String pattern)
 | 
						|
  {
 | 
						|
    String localChars = formatData.getLocalPatternChars();
 | 
						|
    pattern = applyLocalizedPattern (pattern, localChars, standardChars);
 | 
						|
    applyPattern(pattern);
 | 
						|
  }
 | 
						|
 | 
						|
  private String applyLocalizedPattern(String pattern,
 | 
						|
				       String oldChars, String newChars)
 | 
						|
  {
 | 
						|
    int len = pattern.length();
 | 
						|
    StringBuffer buf = new StringBuffer(len);
 | 
						|
    boolean quoted = false;
 | 
						|
    for (int i = 0;  i < len;  i++)
 | 
						|
      {
 | 
						|
	char ch = pattern.charAt(i);
 | 
						|
	if (ch == '\'')
 | 
						|
	  quoted = ! quoted;
 | 
						|
	if (! quoted)
 | 
						|
	  {
 | 
						|
	    int j = oldChars.indexOf(ch);
 | 
						|
	    if (j >= 0)
 | 
						|
	      ch = newChars.charAt(j);
 | 
						|
	  }
 | 
						|
	buf.append(ch);
 | 
						|
      }
 | 
						|
    return buf.toString();
 | 
						|
  }
 | 
						|
 | 
						|
  /** 
 | 
						|
   * Returns the start of the century used for two digit years.
 | 
						|
   *
 | 
						|
   * @return A <code>Date</code> representing the start of the century
 | 
						|
   * for two digit years.
 | 
						|
   */
 | 
						|
  public Date get2DigitYearStart()
 | 
						|
  {
 | 
						|
    return defaultCenturyStart;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the start of the century used for two digit years.
 | 
						|
   *
 | 
						|
   * @param date A <code>Date</code> representing the start of the century for
 | 
						|
   * two digit years.
 | 
						|
   */
 | 
						|
  public void set2DigitYearStart(Date date)
 | 
						|
  {
 | 
						|
    defaultCenturyStart = date;
 | 
						|
    calendar.clear();
 | 
						|
    calendar.setTime(date);
 | 
						|
    int year = calendar.get(Calendar.YEAR);
 | 
						|
    defaultCentury = year - (year % 100);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method returns the format symbol information used for parsing
 | 
						|
   * and formatting dates.
 | 
						|
   *
 | 
						|
   * @return The date format symbols.
 | 
						|
   */
 | 
						|
  public DateFormatSymbols getDateFormatSymbols()
 | 
						|
  {
 | 
						|
    return formatData;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method sets the format symbols information used for parsing
 | 
						|
   * and formatting dates.
 | 
						|
   *
 | 
						|
   * @param formatData The date format symbols.
 | 
						|
   */
 | 
						|
   public void setDateFormatSymbols(DateFormatSymbols formatData)
 | 
						|
   {
 | 
						|
     this.formatData = formatData;
 | 
						|
   }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This methods tests whether the specified object is equal to this
 | 
						|
   * object.  This will be true if and only if the specified object:
 | 
						|
   * <p>
 | 
						|
   * <ul>
 | 
						|
   * <li>Is not <code>null</code>.
 | 
						|
   * <li>Is an instance of <code>SimpleDateFormat</code>.
 | 
						|
   * <li>Is equal to this object at the superclass (i.e., <code>DateFormat</code>)
 | 
						|
   *     level.
 | 
						|
   * <li>Has the same formatting pattern.
 | 
						|
   * <li>Is using the same formatting symbols.
 | 
						|
   * <li>Is using the same century for two digit years.
 | 
						|
   * </ul>
 | 
						|
   *
 | 
						|
   * @param obj The object to compare for equality against.
 | 
						|
   *
 | 
						|
   * @return <code>true</code> if the specified object is equal to this object,
 | 
						|
   * <code>false</code> otherwise.
 | 
						|
   */
 | 
						|
  public boolean equals(Object o)
 | 
						|
  {
 | 
						|
    if (o == null)
 | 
						|
      return false;
 | 
						|
 | 
						|
    if (!super.equals(o))
 | 
						|
      return false;
 | 
						|
 | 
						|
    if (!(o instanceof SimpleDateFormat))
 | 
						|
      return false;
 | 
						|
 | 
						|
    SimpleDateFormat sdf = (SimpleDateFormat)o;
 | 
						|
 | 
						|
    if (!toPattern().equals(sdf.toPattern()))
 | 
						|
      return false;
 | 
						|
 | 
						|
    if (!get2DigitYearStart().equals(sdf.get2DigitYearStart()))
 | 
						|
      return false;
 | 
						|
 | 
						|
    if (!getDateFormatSymbols().equals(sdf.getDateFormatSymbols()))
 | 
						|
      return false;
 | 
						|
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Formats the date input according to the format string in use,
 | 
						|
   * appending to the specified StringBuffer.  The input StringBuffer
 | 
						|
   * is returned as output for convenience.
 | 
						|
   */
 | 
						|
  public StringBuffer format(Date date, StringBuffer buffer, FieldPosition pos)
 | 
						|
  {
 | 
						|
    String temp;
 | 
						|
    calendar.setTime(date);
 | 
						|
    
 | 
						|
    // go through vector, filling in fields where applicable, else toString
 | 
						|
    Enumeration e = tokens.elements();
 | 
						|
    while (e.hasMoreElements()) {
 | 
						|
      Object o = e.nextElement();
 | 
						|
      if (o instanceof FieldSizePair) {
 | 
						|
	FieldSizePair p = (FieldSizePair) o;
 | 
						|
	int beginIndex = buffer.length();
 | 
						|
	switch (p.field) {
 | 
						|
	case ERA_FIELD:
 | 
						|
	  buffer.append(formatData.eras[calendar.get(Calendar.ERA)]);
 | 
						|
	  break;
 | 
						|
	case YEAR_FIELD:
 | 
						|
	  temp = String.valueOf(calendar.get(Calendar.YEAR));
 | 
						|
	  if (p.size < 4)
 | 
						|
	    buffer.append(temp.substring(temp.length()-2));
 | 
						|
	  else
 | 
						|
	    buffer.append(temp);
 | 
						|
	  break;
 | 
						|
	case MONTH_FIELD:
 | 
						|
	  if (p.size < 3)
 | 
						|
	    withLeadingZeros(calendar.get(Calendar.MONTH)+1,p.size,buffer);
 | 
						|
	  else if (p.size < 4)
 | 
						|
	    buffer.append(formatData.shortMonths[calendar.get(Calendar.MONTH)]);
 | 
						|
	  else
 | 
						|
	    buffer.append(formatData.months[calendar.get(Calendar.MONTH)]);
 | 
						|
	  break;
 | 
						|
	case DATE_FIELD:
 | 
						|
	  withLeadingZeros(calendar.get(Calendar.DATE),p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case HOUR_OF_DAY1_FIELD: // 1-24
 | 
						|
	  withLeadingZeros(((calendar.get(Calendar.HOUR_OF_DAY)+23)%24)+1,p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case HOUR_OF_DAY0_FIELD: // 0-23
 | 
						|
	  withLeadingZeros(calendar.get(Calendar.HOUR_OF_DAY),p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case MINUTE_FIELD:
 | 
						|
	  withLeadingZeros(calendar.get(Calendar.MINUTE),p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case SECOND_FIELD:
 | 
						|
	  withLeadingZeros(calendar.get(Calendar.SECOND),p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case MILLISECOND_FIELD:
 | 
						|
	  withLeadingZeros(calendar.get(Calendar.MILLISECOND),p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case DAY_OF_WEEK_FIELD:
 | 
						|
	  if (p.size < 4)
 | 
						|
	    buffer.append(formatData.shortWeekdays[calendar.get(Calendar.DAY_OF_WEEK)]);
 | 
						|
	  else
 | 
						|
	    buffer.append(formatData.weekdays[calendar.get(Calendar.DAY_OF_WEEK)]);
 | 
						|
	  break;
 | 
						|
	case DAY_OF_YEAR_FIELD:
 | 
						|
	  withLeadingZeros(calendar.get(Calendar.DAY_OF_YEAR),p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case DAY_OF_WEEK_IN_MONTH_FIELD:
 | 
						|
	  withLeadingZeros(calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH),p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case WEEK_OF_YEAR_FIELD:
 | 
						|
	  withLeadingZeros(calendar.get(Calendar.WEEK_OF_YEAR),p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case WEEK_OF_MONTH_FIELD:
 | 
						|
	  withLeadingZeros(calendar.get(Calendar.WEEK_OF_MONTH),p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case AM_PM_FIELD:
 | 
						|
	  buffer.append(formatData.ampms[calendar.get(Calendar.AM_PM)]);
 | 
						|
	  break;
 | 
						|
	case HOUR1_FIELD: // 1-12
 | 
						|
	  withLeadingZeros(((calendar.get(Calendar.HOUR)+11)%12)+1,p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case HOUR0_FIELD: // 0-11
 | 
						|
	  withLeadingZeros(calendar.get(Calendar.HOUR),p.size,buffer);
 | 
						|
	  break;
 | 
						|
	case TIMEZONE_FIELD:
 | 
						|
	  TimeZone zone = calendar.getTimeZone();
 | 
						|
	  boolean isDST = calendar.get(Calendar.DST_OFFSET) != 0;
 | 
						|
	  // FIXME: XXX: This should be a localized time zone.
 | 
						|
	  String zoneID = zone.getDisplayName(isDST, p.size > 3 ? TimeZone.LONG : TimeZone.SHORT);
 | 
						|
	  buffer.append(zoneID);
 | 
						|
	  break;
 | 
						|
	default:
 | 
						|
	  throw new IllegalArgumentException("Illegal pattern character");
 | 
						|
	}
 | 
						|
	if (pos != null && p.field == pos.getField())
 | 
						|
	  {
 | 
						|
	    pos.setBeginIndex(beginIndex);
 | 
						|
	    pos.setEndIndex(buffer.length());
 | 
						|
	  }
 | 
						|
      } else {
 | 
						|
	buffer.append(o.toString());
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return buffer;
 | 
						|
  }
 | 
						|
 | 
						|
  private void withLeadingZeros(int value, int length, StringBuffer buffer) 
 | 
						|
  {
 | 
						|
    String valStr = String.valueOf(value);
 | 
						|
    for (length -= valStr.length(); length > 0; length--)
 | 
						|
      buffer.append('0');
 | 
						|
    buffer.append(valStr);
 | 
						|
  }
 | 
						|
 | 
						|
  private final boolean expect (String source, ParsePosition pos, char ch)
 | 
						|
  {
 | 
						|
    int x = pos.getIndex();
 | 
						|
    boolean r = x < source.length() && source.charAt(x) == ch;
 | 
						|
    if (r)
 | 
						|
      pos.setIndex(x + 1);
 | 
						|
    else
 | 
						|
      pos.setErrorIndex(x);
 | 
						|
    return r;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method parses the specified string into a date.
 | 
						|
   * 
 | 
						|
   * @param dateStr The date string to parse.
 | 
						|
   * @param pos The input and output parse position
 | 
						|
   *
 | 
						|
   * @return The parsed date, or <code>null</code> if the string cannot be
 | 
						|
   * parsed.
 | 
						|
   */
 | 
						|
  public Date parse (String dateStr, ParsePosition pos)
 | 
						|
  {
 | 
						|
    int fmt_index = 0;
 | 
						|
    int fmt_max = pattern.length();
 | 
						|
 | 
						|
    calendar.clear();
 | 
						|
    boolean saw_timezone = false;
 | 
						|
    int quote_start = -1;
 | 
						|
    boolean is2DigitYear = false;
 | 
						|
    for (; fmt_index < fmt_max; ++fmt_index)
 | 
						|
      {
 | 
						|
	char ch = pattern.charAt(fmt_index);
 | 
						|
	if (ch == '\'')
 | 
						|
	  {
 | 
						|
	    int index = pos.getIndex();
 | 
						|
	    if (fmt_index < fmt_max - 1
 | 
						|
		&& pattern.charAt(fmt_index + 1) == '\'')
 | 
						|
	      {
 | 
						|
		if (! expect (dateStr, pos, ch))
 | 
						|
		  return null;
 | 
						|
		++fmt_index;
 | 
						|
	      }
 | 
						|
	    else
 | 
						|
	      quote_start = quote_start < 0 ? fmt_index : -1;
 | 
						|
	    continue;
 | 
						|
	  }
 | 
						|
 | 
						|
	if (quote_start != -1
 | 
						|
	    || ((ch < 'a' || ch > 'z')
 | 
						|
		&& (ch < 'A' || ch > 'Z')))
 | 
						|
	  {
 | 
						|
	    if (! expect (dateStr, pos, ch))
 | 
						|
	      return null;
 | 
						|
	    continue;
 | 
						|
	  }
 | 
						|
 | 
						|
	// We've arrived at a potential pattern character in the
 | 
						|
	// pattern.
 | 
						|
	int first = fmt_index;
 | 
						|
	while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch)
 | 
						|
	  ;
 | 
						|
	int fmt_count = fmt_index - first;
 | 
						|
	--fmt_index;
 | 
						|
 | 
						|
	// We can handle most fields automatically: most either are
 | 
						|
	// numeric or are looked up in a string vector.  In some cases
 | 
						|
	// we need an offset.  When numeric, `offset' is added to the
 | 
						|
	// resulting value.  When doing a string lookup, offset is the
 | 
						|
	// initial index into the string array.
 | 
						|
	int calendar_field;
 | 
						|
	boolean is_numeric = true;
 | 
						|
	String[] match = null;
 | 
						|
	int offset = 0;
 | 
						|
	boolean maybe2DigitYear = false;
 | 
						|
	switch (ch)
 | 
						|
	  {
 | 
						|
	  case 'd':
 | 
						|
	    calendar_field = Calendar.DATE;
 | 
						|
	    break;
 | 
						|
	  case 'D':
 | 
						|
	    calendar_field = Calendar.DAY_OF_YEAR;
 | 
						|
	    break;
 | 
						|
	  case 'F':
 | 
						|
	    calendar_field = Calendar.DAY_OF_WEEK_IN_MONTH;
 | 
						|
	    break;
 | 
						|
	  case 'E':
 | 
						|
	    is_numeric = false;
 | 
						|
	    offset = 1;
 | 
						|
	    calendar_field = Calendar.DAY_OF_WEEK;
 | 
						|
	    match = (fmt_count <= 3
 | 
						|
		     ? formatData.getShortWeekdays()
 | 
						|
		     : formatData.getWeekdays());
 | 
						|
	    break;
 | 
						|
	  case 'w':
 | 
						|
	    calendar_field = Calendar.WEEK_OF_YEAR;
 | 
						|
	    break;
 | 
						|
	  case 'W':
 | 
						|
	    calendar_field = Calendar.WEEK_OF_MONTH;
 | 
						|
	    break;
 | 
						|
	  case 'M':
 | 
						|
	    calendar_field = Calendar.MONTH;
 | 
						|
	    if (fmt_count <= 2)
 | 
						|
	      offset = -1;
 | 
						|
	    else
 | 
						|
	      {
 | 
						|
		is_numeric = false;
 | 
						|
		match = (fmt_count <= 3
 | 
						|
			 ? formatData.getShortMonths()
 | 
						|
			 : formatData.getMonths());
 | 
						|
	      }
 | 
						|
	    break;
 | 
						|
	  case 'y':
 | 
						|
	    calendar_field = Calendar.YEAR;
 | 
						|
	    if (fmt_count <= 2)
 | 
						|
	      maybe2DigitYear = true;
 | 
						|
	    break;
 | 
						|
	  case 'K':
 | 
						|
	    calendar_field = Calendar.HOUR;
 | 
						|
	    break;
 | 
						|
	  case 'h':
 | 
						|
	    calendar_field = Calendar.HOUR;
 | 
						|
	    break;
 | 
						|
	  case 'H':
 | 
						|
	    calendar_field = Calendar.HOUR_OF_DAY;
 | 
						|
	    break;
 | 
						|
	  case 'k':
 | 
						|
	    calendar_field = Calendar.HOUR_OF_DAY;
 | 
						|
	    break;
 | 
						|
	  case 'm':
 | 
						|
	    calendar_field = Calendar.MINUTE;
 | 
						|
	    break;
 | 
						|
	  case 's':
 | 
						|
	    calendar_field = Calendar.SECOND;
 | 
						|
	    break;
 | 
						|
	  case 'S':
 | 
						|
	    calendar_field = Calendar.MILLISECOND;
 | 
						|
	    break;
 | 
						|
	  case 'a':
 | 
						|
	    is_numeric = false;
 | 
						|
	    calendar_field = Calendar.AM_PM;
 | 
						|
	    match = formatData.getAmPmStrings();
 | 
						|
	    break;
 | 
						|
	  case 'z':
 | 
						|
	    // We need a special case for the timezone, because it
 | 
						|
	    // uses a different data structure than the other cases.
 | 
						|
	    is_numeric = false;
 | 
						|
	    calendar_field = Calendar.DST_OFFSET;
 | 
						|
	    String[][] zoneStrings = formatData.getZoneStrings();
 | 
						|
	    int zoneCount = zoneStrings.length;
 | 
						|
	    int index = pos.getIndex();
 | 
						|
	    boolean found_zone = false;
 | 
						|
	    for (int j = 0;  j < zoneCount;  j++)
 | 
						|
	      {
 | 
						|
		String[] strings = zoneStrings[j];
 | 
						|
		int k;
 | 
						|
		for (k = 1; k < strings.length; ++k)
 | 
						|
		  {
 | 
						|
		    if (dateStr.startsWith(strings[k], index))
 | 
						|
		      break;
 | 
						|
		  }
 | 
						|
		if (k != strings.length)
 | 
						|
		  {
 | 
						|
		    found_zone = true;
 | 
						|
		    saw_timezone = true;
 | 
						|
		    TimeZone tz = TimeZone.getTimeZone (strings[0]);
 | 
						|
		    calendar.setTimeZone (tz);
 | 
						|
		    calendar.set (Calendar.ZONE_OFFSET, tz.getRawOffset ());
 | 
						|
		    offset = 0;
 | 
						|
		    if (k > 2 && tz instanceof SimpleTimeZone)
 | 
						|
		      {
 | 
						|
			SimpleTimeZone stz = (SimpleTimeZone) tz;
 | 
						|
			offset = stz.getDSTSavings ();
 | 
						|
		      }
 | 
						|
		    pos.setIndex(index + strings[k].length());
 | 
						|
		    break;
 | 
						|
		  }
 | 
						|
	      }
 | 
						|
	    if (! found_zone)
 | 
						|
	      {
 | 
						|
		pos.setErrorIndex(pos.getIndex());
 | 
						|
		return null;
 | 
						|
	      }
 | 
						|
	    break;
 | 
						|
	  default:
 | 
						|
	    pos.setErrorIndex(pos.getIndex());
 | 
						|
	    return null;
 | 
						|
	  }
 | 
						|
 | 
						|
	// Compute the value we should assign to the field.
 | 
						|
	int value;
 | 
						|
	int index = -1;
 | 
						|
	if (is_numeric)
 | 
						|
	  {
 | 
						|
	    numberFormat.setMinimumIntegerDigits(fmt_count);
 | 
						|
	    if (maybe2DigitYear)
 | 
						|
	      index = pos.getIndex();
 | 
						|
	    Number n = numberFormat.parse(dateStr, pos);
 | 
						|
	    if (pos == null || ! (n instanceof Long))
 | 
						|
	      return null;
 | 
						|
	    value = n.intValue() + offset;
 | 
						|
	  }
 | 
						|
	else if (match != null)
 | 
						|
	  {
 | 
						|
	    index = pos.getIndex();
 | 
						|
	    int i;
 | 
						|
	    for (i = offset; i < match.length; ++i)
 | 
						|
	      {
 | 
						|
		if (dateStr.startsWith(match[i], index))
 | 
						|
		  break;
 | 
						|
	      }
 | 
						|
	    if (i == match.length)
 | 
						|
	      {
 | 
						|
		pos.setErrorIndex(index);
 | 
						|
		return null;
 | 
						|
	      }
 | 
						|
	    pos.setIndex(index + match[i].length());
 | 
						|
	    value = i;
 | 
						|
	  }
 | 
						|
	else
 | 
						|
	  value = offset;
 | 
						|
	  
 | 
						|
	if (maybe2DigitYear)
 | 
						|
	  {
 | 
						|
	    // Parse into default century if the numeric year string has 
 | 
						|
	    // exactly 2 digits.
 | 
						|
	    int digit_count = pos.getIndex() - index;
 | 
						|
	    if (digit_count == 2)
 | 
						|
	      is2DigitYear = true;
 | 
						|
	  }
 | 
						|
 | 
						|
	// Assign the value and move on.
 | 
						|
	calendar.set(calendar_field, value);
 | 
						|
      }
 | 
						|
    
 | 
						|
    if (is2DigitYear)
 | 
						|
      {
 | 
						|
	// Apply the 80-20 heuristic to dermine the full year based on 
 | 
						|
	// defaultCenturyStart. 
 | 
						|
	int year = defaultCentury + calendar.get(Calendar.YEAR);
 | 
						|
	calendar.set(Calendar.YEAR, year);
 | 
						|
	if (calendar.getTime().compareTo(defaultCenturyStart) < 0)
 | 
						|
	  calendar.set(Calendar.YEAR, year + 100);      
 | 
						|
      }
 | 
						|
 | 
						|
    try
 | 
						|
      {
 | 
						|
	if (! saw_timezone)
 | 
						|
	  {
 | 
						|
	    // Use the real rules to determine whether or not this
 | 
						|
	    // particular time is in daylight savings.
 | 
						|
	    calendar.clear (Calendar.DST_OFFSET);
 | 
						|
	    calendar.clear (Calendar.ZONE_OFFSET);
 | 
						|
	  }
 | 
						|
        return calendar.getTime();
 | 
						|
      }
 | 
						|
    catch (IllegalArgumentException x)
 | 
						|
      {
 | 
						|
        pos.setErrorIndex(pos.getIndex());
 | 
						|
	return null;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  // Compute the start of the current century as defined by
 | 
						|
  // get2DigitYearStart.
 | 
						|
  private void computeCenturyStart()
 | 
						|
  {
 | 
						|
    int year = calendar.get(Calendar.YEAR);
 | 
						|
    calendar.set(Calendar.YEAR, year - 80);
 | 
						|
    set2DigitYearStart(calendar.getTime());
 | 
						|
  }
 | 
						|
}
 |