Open Source Repository

Home /mail/mail-1.4.3 | Repository Home



javax/mail/internet/MailDateFormat.java
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 *
 * Contributor(s):
 *
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package javax.mail.internet;

import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.NumberFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.ParseException;

/**
 * Formats and parses date specification based on the
 * draft-ietf-drums-msg-fmt-08 dated January 26, 2000. This is a followup
 * spec to RFC822.<p>
 *
 * This class does not take pattern strings. It always formats the
 * date based on the specification below.<p>
 *
 * 3.3 Date and Time Specification<p>
 *
 * Date and time occur in several header fields of a message. This section
 * specifies the syntax for a full date and time specification. Though folding
 * whitespace is permitted throughout the date-time specification, it is
 * recommended that only a single space be used where FWS is required and no
 * space be used where FWS is optional in the date-time specification; some
 * older implementations may not interpret other occurrences of folding
 * whitespace correctly.<p>
 *
 * date-time = [ day-of-week "," ] date FWS time [CFWS]<p>
 *
 * day-of-week = ([FWS] day-name) / obs-day-of-week<p>
 *
 * day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"<p>
 *
 * date = day month year<p>
 *
 * year = 4*DIGIT / obs-year<p>
 *
 * month = (FWS month-name FWS) / obs-month<p>
 *
 *<pre>month-name = "Jan" / "Feb" / "Mar" / "Apr" /
 *             "May" / "Jun" / "Jul" / "Aug" /
 *             "Sep" / "Oct" / "Nov" / "Dec"
 </pre><p>
 * day = ([FWS] 1*2DIGIT) / obs-day<p>
 *
 * time = time-of-day FWS zone<p>
 *
 * time-of-day = hour ":" minute [ ":" second ]<p>
 *
 * hour = 2DIGIT / obs-hour<p>
 *
 * minute = 2DIGIT / obs-minute<p>
 *
 * second = 2DIGIT / obs-second<p>
 *
 * zone = (( "+" / "-" ) 4DIGIT) / obs-zone<p>
 *
 *
 * The day is the numeric day of the month. The year is any numeric year in
 * the common era.<p>
 *
 * The time-of-day specifies the number of hours, minutes, and optionally
 * seconds since midnight of the date indicated.<p>
 *
 * The date and time-of-day SHOULD express local time.<p>
 *
 * The zone specifies the offset from Coordinated Universal Time (UTC,
 * formerly referred to as "Greenwich Mean Time") that the date and
 * time-of-day represent. The "+" or "-" indicates whether the time-of-day is
 * ahead of or behind Universal Time. The first two digits indicate the number
 * of hours difference from Universal Time, and the last two digits indicate
 * the number of minutes difference from Universal Time. (Hence, +hhmm means
 * +(hh * 60 + mm) minutes, and -hhmm means -(hh * 60 + mm) minutes). The form
 * "+0000" SHOULD be used to indicate a time zone at Universal Time. Though
 * "-0000" also indicates Universal Time, it is used to indicate that the time
 * was generated on a system that may be in a local time zone other than
 * Universal Time.<p>
 *
 * A date-time specification MUST be semantically valid. That is, the
 * day-of-the week (if included) MUST be the day implied by the date, the
 * numeric day-of-month MUST be between 1 and the number of days allowed for
 * the specified month (in the specified year), the time-of-day MUST be in the
 * range 00:00:00 through 23:59:60 (the number of seconds allowing for a leap
 * second; see [STD-12]), and the zone MUST be within the range -9959 through
 * +9959.<p>
 *
 @author Max Spivak
 @since    JavaMail 1.2
 */

public class MailDateFormat extends SimpleDateFormat {

    private static final long serialVersionUID = -8148227605210628779L;

    public MailDateFormat() {
  super("EEE, d MMM yyyy HH:mm:ss 'XXXXX' (z)", Locale.US);
    }

    /**
     * Formats the given date in the format specified by 
     * draft-ietf-drums-msg-fmt-08 in the current TimeZone.
     *
     @param   date            the Date object
     @param   dateStrBuf      the formatted string
     @param   fieldPosition   the current field position
     @return  StringBuffer    the formatted String
     @since      JavaMail 1.2
     */
    public StringBuffer format(Date date, StringBuffer dateStrBuf,
             FieldPosition fieldPosition) {

  /* How this method works: First format the date with the
   * format specified in the constructor inserting string 'XXXXX' 
   * where the timezone offset goes. Find where in the string the
   * string 'XXXXX' appears and remember that in var "pos". 
   * Calculate the offset, taking the DST into account and insert
   * it into the stringbuffer at position pos.
   */

  int start = dateStrBuf.length();
  super.format(date, dateStrBuf, fieldPosition);
  int pos = 0;
  // find the beginning of the 'XXXXX' string in the formatted date
  // 25 is the first position that we expect to find XXXXX at
  for (pos = start + 25; dateStrBuf.charAt(pos!= 'X'; pos++)
      ;

  // set the timezone to +HHMM or -HHMM
  calendar.clear();
  calendar.setTime(date);
  int offset = calendar.get(Calendar.ZONE_OFFSET+
      calendar.get(Calendar.DST_OFFSET);
  // take care of the sign
  if (offset < 0) {
      dateStrBuf.setCharAt(pos++, '-');
      offset = (-offset);
  else
      dateStrBuf.setCharAt(pos++, '+');

  int rawOffsetInMins = offset / 60 1000// offset from GMT in mins
   int offsetInHrs = rawOffsetInMins / 60;
  int offsetInMins = rawOffsetInMins % 60;

  dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInHrs/10)10));
  dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInHrs%10)10));
  dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInMins/10)10));
  dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInMins%10)10));
  // done with timezone
  
  return dateStrBuf;
    }

    ////////////////////////////////////////////////////////////

    /**
     * Parses the given date in the format specified by
     * draft-ietf-drums-msg-fmt-08 in the current TimeZone.
     *
     @param   text    the formatted date to be parsed
     @param   pos     the current parse position
     @return  Date    the parsed date in a Date object
     @since    JavaMail 1.2
     */
    public Date parse(String text, ParsePosition pos) {
  return parseDate(text.toCharArray(), pos, isLenient());
    }

    /*
      Valid Examples:
      
      Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)
      Date: 
      Date: Mon, 22 Mar 1993 09:41:09 -0800 (PST)
      Date:     26 Aug 76 14:29 EDT

      */
    
    /**
     * method of what to look for:
     *
     *
     *  skip WS
     *  skip day ","    (this is "Mon", "Tue")
     *  skip WS
     *
     *  parse number (until WS) ==> 1*2DIGIT (day of month)
     *
     *  skip WS
     *
     *  parse alpha chars (until WS) ==> find month
     *
     *  skip WS
     *
     *  parse number (until WS) ==> 2*4DIGIT (year)
     *
     *  skip WS
     *
     *  // now looking for time
     *  parse number (until ':') ==> hours
     *  parse number (until ':') ==> minutes
     *  parse number (until WS) ==> seconds
     *
     *  // now look for Time Zone
     *  skip WS
     *  if ('+' or '-') then numerical time zone offset
     *  if (alpha) then alpha time zone offset
     */

    static boolean debug = false;

    /**
     * create a Date by parsing the char array
     */
    static private Date parseDate(char[] orig, ParsePosition pos,
        boolean lenient) {
  try {
      int day = -1;
      int month = -1;
      int year = -1;
      int hours = 0;
      int minutes = 0;
      int seconds = 0;
      int offset = 0;

      MailDateParser p = new MailDateParser(orig, pos.getIndex());

      // get the day
      p.skipUntilNumber();
      day = p.parseNumber();

      if (!p.skipIfChar('-')) {  // for IMAP internal Date
    p.skipWhiteSpace();
      }

      // get the month    
      month = p.parseMonth();
      if (!p.skipIfChar('-')) {  // for IMAP internal Date
    p.skipWhiteSpace();
      }
      
      // get the year
      year = p.parseNumber();  // should not return a negative number
      if (year < 50) {
    year += 2000;
      else if (year < 100) {
    year += 1900;
      // otherwise the year is correct (and should be 4 digits)
      
      
      // get the time
      // first get hours
      p.skipWhiteSpace();
      hours = p.parseNumber();
      
      // get minutes
      p.skipChar(':');
      minutes = p.parseNumber();
      
      // get seconds  (may be no seconds)
      if (p.skipIfChar(':')) {
    seconds = p.parseNumber();
      }
      
      
      // try to get a Time Zone
      try {
    p.skipWhiteSpace();
    offset = p.parseTimeZone();
      catch (ParseException pe) {
    if (debug) {
        System.out.println("No timezone? : '" +
            new String(orig"'");
    }
      }
      
      pos.setIndex(p.getIndex());
      Date d = ourUTC(year, month, day, hours, minutes, seconds, offset,
              lenient);
      return d;

  catch (Exception e) {
      // Catch *all* exceptions, including RuntimeExceptions like
      // ArrayIndexOutofBoundsException ... we need to be
      // extra tolerant of all those bogus dates that might screw
      // up our parser. Sigh.

      if (debug) {
    System.out.println("Bad date: '" new String(orig"'");
    e.printStackTrace();
      }
      pos.setIndex(1)// to prevent DateFormat.parse() from throwing ex
      return null;
  }
    }
  
    private static TimeZone tz = TimeZone.getTimeZone("GMT");
    private static Calendar cal = new GregorianCalendar(tz);
    private synchronized static Date ourUTC(int year, int mon, int mday,
             int hour, int min, int sec,
             int tzoffset, boolean lenient) {
  // clear the time and then set all the values
  cal.clear();
  cal.setLenient(lenient);
  cal.set(Calendar.YEAR, year);
  cal.set(Calendar.MONTH, mon);
  cal.set(Calendar.DATE, mday);
  cal.set(Calendar.HOUR_OF_DAY, hour);
  cal.set(Calendar.MINUTE, min);
  cal.add(Calendar.MINUTE, tzoffset);  // adjust for the timezone
  cal.set(Calendar.SECOND, sec);

  return cal.getTime();
    }  


    ////////////////////////////////////////////////////////////
    
    /** Don't allow setting the calendar */
    public void setCalendar(Calendar newCalendar) {
  throw new RuntimeException("Method setCalendar() shouldn't be called");
    }

    /** Don't allow setting the NumberFormat */
    public void setNumberFormat(NumberFormat newNumberFormat) {
  throw new RuntimeException("Method setNumberFormat() shouldn't be called");
    }

    /* test code for MailDateFormat */
    /*
    public static void main(String[] args) {
  DateFormat df = new MailDateFormat();
  Date d = new Date();

  // test output in all the timezones
  System.out.println("------- test all timezones  ---------------");
  System.out.println("Current date: " + d);
  String[] allIDs = TimeZone.getAvailableIDs();
  for (int i = 0; i < allIDs.length; i++) {
      TimeZone tz = TimeZone.getTimeZone(allIDs[i]);
      df.setTimeZone(tz);
      System.out.println("Date in " + tz.getID() + ":    " +
             df.format(new Date()));
  }
  try {
        System.out.println(df.parse("Sun, 21 Mar 1993 23:56:48 -0800 (PST)"));
  System.out.println(df.parse("Mon, 22 Mar 1994 13:34:51 +0000"));
  System.out.println(df.parse("26 Aug 76 14:29 EDT"));
  System.out.println(df.parse("15 Apr 11 23:49 EST"));
  System.out.println(df.parse("15 Apr 11 23:49 ABC"));
  } catch (ParseException pex) {
      pex.printStackTrace();
  }

  // reset DateFormat TZ
  df.setTimeZone(TimeZone.getDefault());

  // test all days in a month
  System.out.println();
  System.out.println("------- test all days in a month ---------------");
  Calendar cal = Calendar.getInstance();
  cal.set(Calendar.YEAR, 1972);
  cal.set(Calendar.MONTH, Calendar.OCTOBER);
  cal.set(Calendar.DATE, 1);
  cal.set(Calendar.HOUR, 10);
  cal.set(Calendar.MINUTE, 50);
  cal.set(Calendar.AM_PM, Calendar.PM);
  System.out.println("Initial Date: " + cal.getTime());
  System.out.println("Current Date: " + df.format(cal.getTime()));
  for (int i = 0; i < 30; i++) {
      cal.roll(Calendar.DATE, true);
      System.out.println("Current Date: " + df.format(cal.getTime()));
  }

  // test all months
  System.out.println();
  System.out.println("------- test all months in a year -----------");
  cal.set(Calendar.MONTH, Calendar.JANUARY);
  cal.set(Calendar.DATE, 7);
  System.out.println("Initial Date: " + cal.getTime());
  System.out.println("Current Date: " + df.format(cal.getTime()));
  for (int i = 1; i < 12; i++) {
      cal.roll(Calendar.MONTH, true);
      System.out.println("Current Date: " + df.format(cal.getTime()));
  }

  // test leap years
  System.out.println();
  System.out.println("------- test leap years -----------");
  cal.set(Calendar.YEAR, 1999);
  cal.set(Calendar.MONTH, Calendar.JANUARY);
  cal.set(Calendar.DATE, 31);
  cal.roll(Calendar.MONTH, true);
  System.out.println("Initial Date: " + cal.getTime());
  System.out.println("Current Date: " + df.format(cal.getTime()));
  for (int i = 1; i < 12; i++) {
      cal.set(Calendar.MONTH, Calendar.JANUARY);
      cal.set(Calendar.DATE, 31);
      cal.roll(Calendar.YEAR, true);
      cal.roll(Calendar.MONTH, true);
      System.out.println("Current Date: " + df.format(cal.getTime()));
  }
    }
    */

}

/**
 * Helper class to deal with parsing the characters
 */
class MailDateParser {
    
    int index = 0;
    char[] orig = null;

    public MailDateParser(char[] orig, int index) {
  this.orig = orig;
  this.index = index;
    }

    /**
     * skips chars until it finds a number (0-9)
     *
     * if it does not find a number, it will throw
     * an ArrayIndexOutOfBoundsException
     */  
    public void skipUntilNumber() throws ParseException {
  try {
      while (true) {
    switch orig[index] ) {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
        return;
        
    default:
        index++;
        break;  
    }
      }
  catch (ArrayIndexOutOfBoundsException e) {
      throw new ParseException("No Number Found", index);
  }
    }

    /**
     * skips any number of tabs, spaces, CR, and LF - folding whitespace
     */
    public void skipWhiteSpace() {
  int len = orig.length;
  while (index < len) {
      switch (orig[index]) {
      case ' '// space
      case '\t'// tab
      case '\r'// CR
      case '\n'// LF
    index++;
    break;
          
      default:
    return;
      }  
  }
    }


    /**
     * used to look at the next character without "parsing" that
     * character.
     */
    public int peekChar() throws ParseException {
  if (index < orig.length)
      return orig[index];
  else
      throw new ParseException("No more characters", index);
    }
  
    /**
     * skips the given character.  if the current char does not
     * match a ParseException will be thrown
     */
    public void skipChar(char cthrows ParseException {
  if (index < orig.length) {
      if (orig[index== c) {
    index++;
      else {
    throw new ParseException("Wrong char", index);
      }
  else {
      throw new ParseException("No more characters", index);
  }
    }

    /**
     * will only skip the current char if it matches the given
     * char
     */
    public boolean skipIfChar(char cthrows ParseException {
  if (index < orig.length) {
      if (orig[index== c) {
    index++;
    return true;
      else {
    return false;
      }
  else {
      throw new ParseException("No more characters", index);
  }
    }
     

    /**
     * current char must point to a number.  the number will be
     * parsed and the resulting number will be returned.  if a
     * number is not found, a ParseException will be thrown
     */
    public int parseNumber() throws ParseException {
  int length = orig.length;
  boolean gotNum = false;
  int result = 0;
        
  while (index < length) {
      switchorig[index] ) {
    case '0':
        result *= 10;
        gotNum = true;
        break;
        
    case '1':
        result = result * 10 1;
        gotNum = true;
        break;
        
    case '2':
        result = result * 10 2;
        gotNum = true;
        break;
        
    case '3':
        result = result * 10 3;
        gotNum = true;
        break;
        
    case '4':
        result = result * 10 4;
        gotNum = true;
        break;
        
    case '5':
        result = result * 10 5;
        gotNum = true;
        break;
        
    case '6':
        result = result * 10 6;
        gotNum = true;
        break;
        
    case '7':
        result = result * 10 7;
        gotNum = true;
        break;
        
    case '8':
        result = result * 10 8;
        gotNum = true;
        break;
        
    case '9':
        result = result * 10 9;
        gotNum = true;
        break;
  
    default:
        if (gotNum)
      return result;
        else
      throw new ParseException("No Number found", index);
      }
      
      index++;
  }
    
  // check the result
  if (gotNum)
      return result;
      
  // else, throw a parse error
  throw new ParseException("No Number found", index);
    }
  

    /**
     * will look for one of "Jan/Feb/Mar/Apr/May/Jun/Jul/Aug/Sep/Oct/Nov/Dev"
     * and return the numerical version of the month. (0-11).  a ParseException
     * error is thrown if a month cannot be found.
     */
    public int parseMonth() throws ParseException {
  char curr;
    
  try {
      switch(orig[index++]) {
      case 'J':
      case 'j'// "Jan" (0) /  "Jun" (5) /  "Jul" (6)
    // check next char
    switch(orig[index++]) {
    case 'A':
    case 'a':
        curr = orig[index++];
        if (curr == 'N' || curr == 'n') {
      return 0;
        }
        break;
              
    case 'U':
    case 'u':
        curr = orig[index++];
        if (curr == 'N' || curr == 'n') {
      return 5;
        else if (curr == 'L' || curr == 'l') {
      return 6;
        }
        break;
    }
    break;
        
      case 'F':
      case 'f'// "Feb"
    curr = orig[index++];
    if (curr == 'E' || curr == 'e') {
        curr = orig[index++];
        if (curr == 'B' || curr == 'b') {
      return 1;
        }
    }
    break;
        
      case 'M':
      case 'm'// "Mar" (2) /  "May" (4)
    curr = orig[index++];
    if (curr == 'A' || curr == 'a') {
        curr = orig[index++];
        if (curr == 'R' || curr == 'r') {
      return 2;
        else if (curr == 'Y' || curr == 'y') {
      return 4;
        }
    }
    break;      
        
      case 'A':
      case 'a'// "Apr" (3) /  "Aug" (7)
    curr = orig[index++];
    if (curr == 'P' || curr == 'p') {
        curr = orig[index++];
        if (curr == 'R' || curr == 'r') {
      return 3;
        }
    else if (curr == 'U' || curr == 'u') {
        curr = orig[index++];
        if (curr == 'G' || curr == 'g') {
      return 7;
        }
    }
    break;      
        
      case 'S':
      case 's'// "Sep" (8)
    curr = orig[index++];
    if (curr == 'E' || curr == 'e') {
        curr = orig[index++];
        if (curr == 'P' || curr == 'p') {
      return 8;
        }
    }
    break;      
        
      case 'O':
      case 'o'// "Oct"
    curr = orig[index++];
    if (curr == 'C' || curr == 'c') {
        curr = orig[index++];
        if (curr == 'T' || curr == 't') {
      return 9;
        }
    }
    break;      
        
      case 'N':
      case 'n'// "Nov"
    curr = orig[index++];
    if (curr == 'O' || curr == 'o') {
        curr = orig[index++];
        if (curr == 'V' || curr == 'v') {
      return 10;
        }
    }
    break;      

      case 'D':
      case 'd'// "Dec"
    curr = orig[index++];
    if (curr == 'E' || curr == 'e') {
        curr = orig[index++];
        if (curr == 'C' || curr == 'c') {
      return 11;
        }
    }
    break;  
      }
  catch (ArrayIndexOutOfBoundsException e) {
  }
    
  throw new ParseException("Bad Month", index);
    }
  
    /**
     * will parse the timezone - either Numerical version (e.g. +0800, -0500)
     * or the alpha version (e.g. PDT, PST).  the result will be returned in
     * minutes needed to be added to the date to bring it to GMT.
     */
    public int parseTimeZone() throws ParseException {
  if (index >= orig.length
      throw new ParseException("No more characters", index);
    
  char test = orig[index];
  if test == '+' || test == '-' ) {
      return parseNumericTimeZone();
  else {
      return parseAlphaTimeZone();
  }
    }

    
    /**
     * will parse the Numerical time zone version (e.g. +0800, -0500)
     * the result will be returned in minutes needed to be added
     * to the date to bring it to GMT.
     */
    public int parseNumericTimeZone() throws ParseException {
  // we switch the sign if it is a '+'
  // since the time in the string we are
  // parsing is off from GMT by that amount.
  // and we want to get the time back into
  // GMT, so we substract it.
  boolean switchSign = false;
  char first = orig[index++];
  if (first == '+') {
      switchSign = true;
  else if (first != '-') {
      throw new ParseException("Bad Numeric TimeZone", index);  
  }

  int oindex = index;
  int tz = parseNumber();
  if (tz >= 2400)
      throw new ParseException("Numeric TimeZone out of range", oindex);  
  int offset = (tz / 10060  (tz % 100);
  if (switchSign) {
      return -offset;
  else {
      return offset;
  }
    }

    /**
     * will parse the alpha time zone version (e.g. PDT, PST).
     * the result will be returned in minutes needed to be added
     * to the date to bring it to GMT.
     */
    public int parseAlphaTimeZone() throws ParseException {
  int result = 0;
  boolean foundCommon = false;
  char curr;
    
  try {
      switch(orig[index++]) {
      case 'U':
      case 'u'// "UT"  /  Universal Time
    curr = orig[index++];
    if (curr == 'T' || curr == 't') {
        result = 0;
        break;
    }
    throw new ParseException("Bad Alpha TimeZone", index);
          
      case 'G':
      case 'g'// "GMT" ; Universal Time
    curr = orig[index++];
    if (curr == 'M' || curr == 'm') {
        curr = orig[index++];
        if (curr == 'T' || curr == 't') {
      result = 0;
      break;
        }    
    }
    throw new ParseException("Bad Alpha TimeZone", index);  
          
      case 'E':
      case 'e'// "EST" / "EDT" ;  Eastern:  - 5/ - 4
    result = 300;
    foundCommon = true;
    break;
          
      case 'C':
      case 'c'// "CST" / "CDT" ;  Central:  - 6/ - 5
    result = 360;
    foundCommon = true;
    break;
          
      case 'M':
      case 'm'// "MST" / "MDT" ;  Mountain: - 7/ - 6
    result = 420;
    foundCommon = true;
    break;
          
      case 'P':
      case 'p'// "PST" / "PDT" ;  Pacific:  - 8/ - 7 
    result = 480;
    foundCommon = true;
    break;

      default:
    throw new ParseException("Bad Alpha TimeZone", index);
      }
  catch (ArrayIndexOutOfBoundsException e) {
      throw new ParseException("Bad Alpha TimeZone", index);
  }
    
  if (foundCommon) {
      curr = orig[index++];
      if (curr == 'S' || curr == 's') {
    curr = orig[index++];
    if (curr != 'T' && curr != 't') {
        throw new ParseException("Bad Alpha TimeZone", index);
    }
      else if (curr == 'D' || curr == 'd') {
    curr = orig[index++];
    if (curr == 'T' || curr != 't') {
        // for daylight time
        result -= 60;
    else {
        throw new ParseException("Bad Alpha TimeZone", index);
    }
      }
  }

  return result;
    }

    int getIndex() {
  return index;
    }
}