/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2013 Oracle and/or its affiliates. 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_1_1.html
 * or packager/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 packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [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.util.logging.Level;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.NumberFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.ParseException;

import com.sun.mail.util.MailLogger;

/**
 * Formats and parses date specification based on
 * <a href="http://www.ietf.org/rfc/rfc2822.txt">RFC 2822</a>. <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.  This section specifies
 * the syntax for a full date and time specification.  Though folding
 * white space is permitted throughout the date-time specification, it is
 * RECOMMENDED that a single space be used in each place that FWS appears
 * (whether it is required or optional); some older implementations may
 * not interpret other occurrences of folding white space correctly.
 * <pre>
 * date-time       =       [ day-of-week "," ] date FWS time [CFWS]
 *
 * day-of-week     =       ([FWS] day-name) / obs-day-of-week
 *
 * day-name        =       "Mon" / "Tue" / "Wed" / "Thu" /
 *                         "Fri" / "Sat" / "Sun"
 *
 * date            =       day month year
 *
 * year            =       4*DIGIT / obs-year
 *
 * month           =       (FWS month-name FWS) / obs-month
 *
 * month-name      =       "Jan" / "Feb" / "Mar" / "Apr" /
 *                         "May" / "Jun" / "Jul" / "Aug" /
 *                         "Sep" / "Oct" / "Nov" / "Dec"
 *
 * day             =       ([FWS] 1*2DIGIT) / obs-day
 *
 * time            =       time-of-day FWS zone
 *
 * time-of-day     =       hour ":" minute [ ":" second ]
 *
 * hour            =       2DIGIT / obs-hour
 *
 * minute          =       2DIGIT / obs-minute
 *
 * second          =       2DIGIT / obs-second
 *
 * zone            =       (( "+" / "-" ) 4DIGIT) / obs-zone
 * </pre>
 * The day is the numeric day of the month.  The year is any numeric year
 * 1900 or later.
 * <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 (i.e., east of) or behind (i.e., west of)
 * 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 and therefore indicates that the
 * date-time contains no information about the local time zone.
 * <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 [STD12]), and the zone MUST be within
 * the range -9959 through +9959.
 *
 * @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 
     * RFC 2822 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
     * RFC 2822 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;
    private static MailLogger logger = new MailLogger(
	MailDateFormat.class,
	"DEBUG",
	debug,
	System.out);

    /**
     * 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 (logger.isLoggable(Level.FINE)) {
		    logger.log(Level.FINE,
			"No timezone? : '" + new String(orig) + "'", pe);
		}
	    }
			
	    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 (logger.isLoggable(Level.FINE)) {
		logger.log(Level.FINE,
		    "Bad date: '" + new String(orig) + "'", e);
	    }
	    pos.setIndex(1); // to prevent DateFormat.parse() from throwing ex
	    return null;
	}
    }
	
    private static final Calendar cal =
	    new GregorianCalendar(TimeZone.getTimeZone("GMT"));
    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 c) throws 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 c) throws 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) {
	    switch( orig[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 / 100) * 60  + (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;
    }
}
