bayard      2003/01/07 13:21:43

  Modified:    lang/src/java/org/apache/commons/lang/time
                        FastDateFormat.java
               lang/src/test/org/apache/commons/lang/time
                        FastDateFormatTest.java
  Log:
  Reformatted [dos2unix]. A newline was introduced after the licence so that cvs
  would allow the commit.
  
  Revision  Changes    Path
  1.2       +1180 
-1179jakarta-commons/lang/src/java/org/apache/commons/lang/time/FastDateFormat.java
  
  Index: FastDateFormat.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/lang/src/java/org/apache/commons/lang/time/FastDateFormat.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- FastDateFormat.java       7 Jan 2003 19:55:55 -0000       1.1
  +++ FastDateFormat.java       7 Jan 2003 21:21:42 -0000       1.2
  @@ -1,1179 +1,1180 @@
  -/* ====================================================================
  - * The Apache Software License, Version 1.1
  - *
  - * Copyright (c) 2002 The Apache Software Foundation.  All rights
  - * reserved.
  - *
  - * Redistribution and use in source and binary forms, with or without
  - * modification, are permitted provided that the following conditions
  - * are met:
  - *
  - * 1. Redistributions of source code must retain the above copyright
  - *    notice, this list of conditions and the following disclaimer.
  - *
  - * 2. Redistributions in binary form must reproduce the above copyright
  - *    notice, this list of conditions and the following disclaimer in
  - *    the documentation and/or other materials provided with the
  - *    distribution.
  - *
  - * 3. The end-user documentation included with the redistribution, if
  - *    any, must include the following acknowlegement:
  - *       "This product includes software developed by the
  - *        Apache Software Foundation (http://www.apache.org/)."
  - *    Alternately, this acknowlegement may appear in the software itself,
  - *    if and wherever such third-party acknowlegements normally appear.
  - *
  - * 4. The names "The Jakarta Project", "Commons", and "Apache Software
  - *    Foundation" must not be used to endorse or promote products derived
  - *    from this software without prior written permission. For written
  - *    permission, please contact [EMAIL PROTECTED]
  - *
  - * 5. Products derived from this software may not be called "Apache"
  - *    nor may "Apache" appear in their names without prior written
  - *    permission of the Apache Software Foundation.
  - *
  - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  - * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  - * SUCH DAMAGE.
  - * ====================================================================
  - *
  - * This software consists of voluntary contributions made by many
  - * individuals on behalf of the Apache Software Foundation.  For more
  - * information on the Apache Software Foundation, please see
  - * <http://www.apache.org/>.
  - */
  -package org.apache.commons.lang.time;
  -
  -import java.util.Date;
  -import java.util.Calendar;
  -import java.util.GregorianCalendar;
  -import java.util.Locale;
  -import java.util.TimeZone;
  -import java.util.List;
  -import java.util.ArrayList;
  -import java.util.Map;
  -import java.util.HashMap;
  -import java.text.DateFormatSymbols;
  -import java.text.DateFormat;
  -import java.text.SimpleDateFormat;
  -
  -/******************************************************************************
  - * Similar to {@link java.text.SimpleDateFormat}, but faster and thread-safe.
  - * Only formatting is supported, but all patterns are compatible with
  - * SimpleDateFormat. [Code originally taken from the open source TreeTrove
  - * project.]
  - *
  - * @author Brian S O'Neill
  - * @author Sean Schofield
  - * @since 2.0
  - * @version $Id$
  - */
  -public class FastDateFormat {
  -    /** Style pattern */
  -    public static final Object
  -        FULL = new Integer(SimpleDateFormat.FULL),
  -        LONG = new Integer(SimpleDateFormat.LONG),
  -        MEDIUM = new Integer(SimpleDateFormat.MEDIUM),
  -        SHORT = new Integer(SimpleDateFormat.SHORT);
  -
  -    private static final double LOG_10 = Math.log(10);
  -
  -    private static String cDefaultPattern;
  -    private static TimeZone cDefaultTimeZone = TimeZone.getDefault();
  -
  -    private static Map cTimeZoneDisplayCache = new HashMap();
  -
  -    private static Map cInstanceCache = new HashMap(7);
  -    private static Map cDateInstanceCache = new HashMap(7);
  -    private static Map cTimeInstanceCache = new HashMap(7);
  -    private static Map cDateTimeInstanceCache = new HashMap(7);
  -
  -    public static FastDateFormat getInstance() {
  -        //return getInstance(getDefaultPattern(), null, null, null);
  -        return getInstance(getDefaultPattern(), null, null);
  -    }
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     */
  -    public static FastDateFormat getInstance(String pattern) {
  -        //return getInstance(pattern, null, null, null);
  -        return getInstance(pattern, null, null);
  -    }
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     * @param timeZone optional time zone, overrides time zone of formatted
  -     * date
  -     */
  -    public static FastDateFormat getInstance(String pattern, TimeZone timeZone) {
  -        //return getInstance(pattern, timeZone, null, null);
  -        return getInstance(pattern, timeZone, null);
  -    }
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     * @param locale optional locale, overrides system locale
  -     */
  -    public static FastDateFormat getInstance(String pattern, Locale locale) {
  -        //return getInstance(pattern, null, locale, null);
  -        return getInstance(pattern, null, locale);
  -    }
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     * @param symbols optional date format symbols, overrides symbols for
  -     * system locale
  -     */
  -    /*
  -    public static FastDateFormat getInstance
  -        (String pattern, DateFormatSymbols symbols)
  -        throws IllegalArgumentException
  -    {
  -        return getInstance(pattern, null, null, symbols);
  -    }
  -    */
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     * @param timeZone optional time zone, overrides time zone of formatted
  -     * date
  -     * @param locale optional locale, overrides system locale
  -     */
  -    public static FastDateFormat getInstance(String pattern, TimeZone timeZone, 
Locale locale) {
  -        //return getInstance(pattern, timeZone, locale, null);
  -        Object key = pattern;
  -
  -        if (timeZone != null) {
  -            key = new Pair(key, timeZone);
  -        }
  -        if (locale != null) {
  -            key = new Pair(key, locale);
  -        }
  -
  -        FastDateFormat format = (FastDateFormat)cInstanceCache.get(key);
  -        if (format == null) {
  -            if (locale == null) {
  -                locale = Locale.getDefault();
  -            }
  -
  -            format = new FastDateFormat(pattern, timeZone, locale, new 
DateFormatSymbols(locale));
  -            cInstanceCache.put(key, format);
  -        }
  -        return format;
  -    }
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     * @param timeZone optional time zone, overrides time zone of formatted
  -     * date
  -     * @param locale optional locale, overrides system locale
  -     * @param symbols optional date format symbols, overrides symbols for
  -     * provided locale
  -     */
  -    /*
  -    public static synchronized FastDateFormat getInstance
  -        (String pattern, TimeZone timeZone, Locale locale,
  -         DateFormatSymbols symbols)
  -        throws IllegalArgumentException
  -    {
  -        Object key = pattern;
  -
  -        if (timeZone != null) {
  -            key = new Pair(key, timeZone);
  -        }
  -        if (locale != null) {
  -            key = new Pair(key, locale);
  -        }
  -        if (symbols != null) {
  -            key = new Pair(key, symbols);
  -        }
  -
  -        FastDateFormat format = (FastDateFormat)cInstanceCache.get(key);
  -        if (format == null) {
  -            if (locale == null) {
  -                locale = Locale.getDefault();
  -            }
  -            if (symbols == null) {
  -                symbols = new DateFormatSymbols(locale);
  -            }
  -            format = new FastDateFormat(pattern, timeZone, locale, symbols);
  -            cInstanceCache.put(key, format);
  -        }
  -        return format;
  -    }
  -    */
  -
  -    /**
  -     * @param style date style: FULL, LONG, MEDIUM, or SHORT (corresponds to those 
in java.text.DateFormat)
  -     * @param timeZone optional time zone, overrides time zone of formatted
  -     * date
  -     * @param locale optional locale, overrides system locale
  -     */
  -    public static synchronized FastDateFormat getDateInstance(int style, TimeZone 
timeZone, Locale locale) {
  -        Object key = new Integer(style);
  -
  -        if (timeZone != null) {
  -            key = new Pair(key, timeZone);
  -        }
  -        if (locale == null) {
  -            key = new Pair(key, locale);
  -        }
  -
  -        FastDateFormat format = (FastDateFormat)cDateInstanceCache.get(key);
  -
  -        if (format == null) {
  -            if (locale == null) {
  -                locale = Locale.getDefault();
  -            }
  -
  -            try {
  -                String pattern = 
((SimpleDateFormat)DateFormat.getDateInstance(style, locale)).toPattern();
  -                format = getInstance(pattern, timeZone, locale);
  -                cDateInstanceCache.put(key, format);
  -            }
  -            catch (ClassCastException e) {
  -                throw new IllegalArgumentException
  -                    ("No date pattern for locale: " + locale);
  -            }
  -        }
  -
  -        return format;
  -    }
  -
  -    /**
  -     * @param style time style: FULL, LONG, MEDIUM, or SHORT
  -     * @param timeZone optional time zone, overrides time zone of formatted
  -     * date
  -     * @param locale optional locale, overrides system locale
  -     */
  -    public static synchronized FastDateFormat getTimeInstance(int style, TimeZone 
timeZone, Locale locale) {
  -        Object key = new Integer(style);
  -
  -        if (timeZone != null) {
  -            key = new Pair(key, timeZone);
  -        }
  -        if (locale != null) {
  -            key = new Pair(key, locale);
  -        }
  -
  -        FastDateFormat format = (FastDateFormat)cTimeInstanceCache.get(key);
  -
  -        if (format == null) {
  -
  -            if (locale == null) {
  -                locale = Locale.getDefault();
  -            }
  -
  -            try {
  -                String pattern = 
((SimpleDateFormat)DateFormat.getTimeInstance(style, locale)).toPattern();
  -                format = getInstance(pattern, timeZone, locale);
  -                cTimeInstanceCache.put(key, format);
  -            }
  -            catch (ClassCastException e) {
  -                throw new IllegalArgumentException
  -                    ("No date pattern for locale: " + locale);
  -            }
  -        }
  -
  -        return format;
  -    }
  -
  -    /**
  -     * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
  -     * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
  -     * @param timeZone optional time zone, overrides time zone of formatted
  -     * date
  -     * @param locale optional locale, overrides system locale
  -     */
  -    public static synchronized FastDateFormat getDateTimeInstance(Object dateStyle, 
Object timeStyle,
  -            TimeZone timeZone, Locale locale) {
  -
  -        Object key = new Pair(dateStyle, timeStyle);
  -
  -        if (timeZone != null) {
  -            key = new Pair(key, timeZone);
  -        }
  -        if (locale != null) {
  -            key = new Pair(key, locale);
  -        }
  -
  -        FastDateFormat format =
  -            (FastDateFormat)cDateTimeInstanceCache.get(key);
  -
  -        if (format == null) {
  -            int ds;
  -            try {
  -                ds = ((Integer)dateStyle).intValue();
  -            }
  -            catch (ClassCastException e) {
  -                throw new IllegalArgumentException
  -                    ("Illegal date style: " + dateStyle);
  -            }
  -
  -            int ts;
  -            try {
  -                ts = ((Integer)timeStyle).intValue();
  -            }
  -            catch (ClassCastException e) {
  -                throw new IllegalArgumentException
  -                    ("Illegal time style: " + timeStyle);
  -            }
  -
  -            if (locale == null) {
  -                locale = Locale.getDefault();
  -            }
  -
  -            try {
  -                String pattern = 
((SimpleDateFormat)DateFormat.getDateTimeInstance(ds, ts, locale)).toPattern();
  -                format = getInstance(pattern, timeZone, locale);
  -                cDateTimeInstanceCache.put(key, format);
  -            }
  -            catch (ClassCastException e) {
  -                throw new IllegalArgumentException
  -                    ("No date time pattern for locale: " + locale);
  -            }
  -        }
  -
  -        return format;
  -    }
  -
  -    static synchronized String getTimeZoneDisplay(TimeZone tz, boolean daylight, 
int style, Locale locale) {
  -        Object key = new TimeZoneDisplayKey(tz, daylight, style, locale);
  -        String value = (String)cTimeZoneDisplayCache.get(key);
  -        if (value == null) {
  -            // This is a very slow call, so cache the results.
  -            value = tz.getDisplayName(daylight, style, locale);
  -            cTimeZoneDisplayCache.put(key, value);
  -        }
  -        return value;
  -    }
  -
  -    private static synchronized String getDefaultPattern() {
  -        if (cDefaultPattern == null) {
  -            cDefaultPattern = new SimpleDateFormat().toPattern();
  -        }
  -        return cDefaultPattern;
  -    }
  -
  -    /**
  -     * Returns a list of Rules.
  -     */
  -    private static List parse(String pattern, TimeZone timeZone, Locale locale, 
DateFormatSymbols symbols) {
  -        List rules = new ArrayList();
  -
  -        String[] ERAs = symbols.getEras();
  -        String[] months = symbols.getMonths();
  -        String[] shortMonths = symbols.getShortMonths();
  -        String[] weekdays = symbols.getWeekdays();
  -        String[] shortWeekdays = symbols.getShortWeekdays();
  -        String[] AmPmStrings = symbols.getAmPmStrings();
  -
  -        int length = pattern.length();
  -        int[] indexRef = new int[1];
  -
  -        for (int i=0; i<length; i++) {
  -            indexRef[0] = i;
  -            String token = parseToken(pattern, indexRef);
  -            i = indexRef[0];
  -
  -            int tokenLen = token.length();
  -            if (tokenLen == 0) {
  -                break;
  -            }
  -
  -            Rule rule;
  -            char c = token.charAt(0);
  -
  -            switch (c) {
  -            case 'G': // era designator (text)
  -                rule = new TextField(Calendar.ERA, ERAs);
  -                break;
  -            case 'y': // year (number)
  -                if (tokenLen >= 4) {
  -                    rule = new UnpaddedNumberField(Calendar.YEAR);
  -                }
  -                else {
  -                    rule = new TwoDigitYearField();
  -                }
  -                break;
  -            case 'M': // month in year (text and number)
  -                if (tokenLen >= 4) {
  -                    rule = new TextField(Calendar.MONTH, months);
  -                }
  -                else if (tokenLen == 3) {
  -                    rule = new TextField(Calendar.MONTH, shortMonths);
  -                }
  -                else if (tokenLen == 2) {
  -                    rule = new TwoDigitMonthField();
  -                }
  -                else {
  -                    rule = new UnpaddedMonthField();
  -                }
  -                break;
  -            case 'd': // day in month (number)
  -                rule = selectNumberRule(Calendar.DAY_OF_MONTH, tokenLen);
  -                break;
  -            case 'h': // hour in am/pm (number, 1..12)
  -                rule = new TwelveHourField
  -                    (selectNumberRule(Calendar.HOUR, tokenLen));
  -                break;
  -            case 'H': // hour in day (number, 0..23)
  -                rule = selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen);
  -                break;
  -            case 'm': // minute in hour (number)
  -                rule = selectNumberRule(Calendar.MINUTE, tokenLen);
  -                break;
  -            case 's': // second in minute (number)
  -                rule = selectNumberRule(Calendar.SECOND, tokenLen);
  -                break;
  -            case 'S': // millisecond (number)
  -                rule = selectNumberRule(Calendar.MILLISECOND, tokenLen);
  -                break;
  -            case 'E': // day in week (text)
  -                rule = new TextField
  -                    (Calendar.DAY_OF_WEEK,
  -                     tokenLen < 4 ? shortWeekdays : weekdays);
  -                break;
  -            case 'D': // day in year (number)
  -                rule = selectNumberRule(Calendar.DAY_OF_YEAR, tokenLen);
  -                break;
  -            case 'F': // day of week in month (number)
  -                rule = selectNumberRule
  -                    (Calendar.DAY_OF_WEEK_IN_MONTH, tokenLen);
  -                break;
  -            case 'w': // week in year (number)
  -                rule = selectNumberRule(Calendar.WEEK_OF_YEAR, tokenLen);
  -                break;
  -            case 'W': // week in month (number)
  -                rule = selectNumberRule(Calendar.WEEK_OF_MONTH, tokenLen);
  -                break;
  -            case 'a': // am/pm marker (text)
  -                rule = new TextField(Calendar.AM_PM, AmPmStrings);
  -                break;
  -            case 'k': // hour in day (1..24)
  -                rule = new TwentyFourHourField
  -                    (selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen));
  -                break;
  -            case 'K': // hour in am/pm (0..11)
  -                rule = selectNumberRule(Calendar.HOUR, tokenLen);
  -                break;
  -            case 'z': // time zone (text)
  -                if (tokenLen >= 4) {
  -                    rule = new TimeZoneRule(timeZone, locale, TimeZone.LONG);
  -                }
  -                else {
  -                    rule = new TimeZoneRule(timeZone, locale, TimeZone.SHORT);
  -                }
  -                break;
  -            case '\'': // literal text
  -                String sub = token.substring(1);
  -                if (sub.length() == 1) {
  -                    rule = new CharacterLiteral(sub.charAt(0));
  -                }
  -                else {
  -                    rule = new StringLiteral(new String(sub));
  -                }
  -                break;
  -            default:
  -                throw new IllegalArgumentException
  -                    ("Illegal pattern component: " + token);
  -            }
  -
  -            rules.add(rule);
  -        }
  -
  -        return rules;
  -    }
  -
  -    private static String parseToken(String pattern, int[] indexRef) {
  -        StringBuffer buf = new StringBuffer();
  -
  -        int i = indexRef[0];
  -        int length = pattern.length();
  -
  -        char c = pattern.charAt(i);
  -        if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') {
  -            // Scan a run of the same character, which indicates a time
  -            // pattern.
  -            buf.append(c);
  -
  -            while (i + 1 < length) {
  -                char peek = pattern.charAt(i + 1);
  -                if (peek == c) {
  -                    buf.append(c);
  -                    i++;
  -                }
  -                else {
  -                    break;
  -                }
  -            }
  -        }
  -        else {
  -            // This will identify token as text.
  -            buf.append('\'');
  -
  -            boolean inLiteral = false;
  -
  -            for (; i < length; i++) {
  -                c = pattern.charAt(i);
  -
  -                if (c == '\'') {
  -                    if (i + 1 < length && pattern.charAt(i + 1) == '\'') {
  -                        // '' is treated as escaped '
  -                        i++;
  -                        buf.append(c);
  -                    }
  -                    else {
  -                        inLiteral = !inLiteral;
  -                    }
  -                }
  -                else if (!inLiteral &&
  -                         (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')) {
  -                    i--;
  -                    break;
  -                }
  -                else {
  -                    buf.append(c);
  -                }
  -            }
  -        }
  -
  -        indexRef[0] = i;
  -        return buf.toString();
  -    }
  -
  -    private static NumberRule selectNumberRule(int field, int padding) {
  -        switch (padding) {
  -        case 1:
  -            return new UnpaddedNumberField(field);
  -        case 2:
  -            return new TwoDigitNumberField(field);
  -        default:
  -            return new PaddedNumberField(field, padding);
  -        }
  -    }
  -
  -    private final String mPattern;
  -    private final TimeZone mTimeZone;
  -    private final Locale mLocale;
  -    private final Rule[] mRules;
  -    private final int mMaxLengthEstimate;
  -
  -    private FastDateFormat() {
  -        this(getDefaultPattern(), null, null, null);
  -    }
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     */
  -    private FastDateFormat(String pattern) throws IllegalArgumentException {
  -        this(pattern, null, null, null);
  -    }
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     * @param timeZone optional time zone, overrides time zone of formatted
  -     * date
  -     */
  -    private FastDateFormat(String pattern, TimeZone timeZone) {
  -        this(pattern, timeZone, null, null);
  -    }
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     * @param locale optional locale, overrides system locale
  -     */
  -    private FastDateFormat(String pattern, Locale locale) {
  -        this(pattern, null, locale, null);
  -    }
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     * @param symbols optional date format symbols, overrides symbols for
  -     * system locale
  -     */
  -    private FastDateFormat(String pattern, DateFormatSymbols symbols) {
  -        this(pattern, null, null, symbols);
  -    }
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     * @param timeZone optional time zone, overrides time zone of formatted
  -     * date
  -     * @param locale optional locale, overrides system locale
  -     */
  -    private FastDateFormat(String pattern, TimeZone timeZone, Locale locale) {
  -        this(pattern, timeZone, locale, null);
  -    }
  -
  -    /**
  -     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  -     * @param timeZone optional time zone, overrides time zone of formatted
  -     * date
  -     * @param locale optional locale, overrides system locale
  -     * @param symbols optional date format symbols, overrides symbols for
  -     * provided locale
  -     */
  -    private FastDateFormat(String pattern, TimeZone timeZone, Locale locale, 
DateFormatSymbols symbols) {
  -        if (locale == null) {
  -            locale = Locale.getDefault();
  -        }
  -
  -        mPattern = pattern;
  -        mTimeZone = timeZone;
  -        mLocale = locale;
  -
  -        if (symbols == null) {
  -            symbols = new DateFormatSymbols(locale);
  -        }
  -
  -        List rulesList = parse(pattern, timeZone, locale, symbols);
  -        mRules = (Rule[])rulesList.toArray(new Rule[rulesList.size()]);
  -
  -        int len = 0;
  -        for (int i=mRules.length; --i >= 0; ) {
  -            len += mRules[i].estimateLength();
  -        }
  -
  -        mMaxLengthEstimate = len;
  -    }
  -
  -    public String format(Date date) {
  -        Calendar c = new GregorianCalendar(cDefaultTimeZone);
  -        c.setTime(date);
  -        if (mTimeZone != null) {
  -            c.setTimeZone(mTimeZone);
  -        }
  -        return applyRules(c, new StringBuffer(mMaxLengthEstimate)).toString();
  -    }
  -
  -    public String format(Calendar calendar) {
  -        return format(calendar, new StringBuffer(mMaxLengthEstimate))
  -            .toString();
  -    }
  -
  -    public StringBuffer format(Date date, StringBuffer buf) {
  -        Calendar c = new GregorianCalendar(cDefaultTimeZone);
  -        c.setTime(date);
  -        if (mTimeZone != null) {
  -            c.setTimeZone(mTimeZone);
  -        }
  -        return applyRules(c, buf);
  -    }
  -
  -    public StringBuffer format(Calendar calendar, StringBuffer buf) {
  -        if (mTimeZone != null) {
  -            calendar = (Calendar)calendar.clone();
  -            calendar.setTimeZone(mTimeZone);
  -        }
  -        return applyRules(calendar, buf);
  -    }
  -
  -    private StringBuffer applyRules(Calendar calendar, StringBuffer buf) {
  -        Rule[] rules = mRules;
  -        int len = mRules.length;
  -        for (int i=0; i<len; i++) {
  -            rules[i].appendTo(buf, calendar);
  -        }
  -        return buf;
  -    }
  -
  -    public String getPattern() {
  -        return mPattern;
  -    }
  -
  -    /**
  -     * Returns the time zone used by this formatter, or null if time zone of
  -     * formatted dates is used instead.
  -     */
  -    public TimeZone getTimeZone() {
  -        return mTimeZone;
  -    }
  -
  -    public Locale getLocale() {
  -        return mLocale;
  -    }
  -
  -    /**
  -     * Returns an estimate for the maximum length date that this date
  -     * formatter will produce. The actual formatted length will almost always
  -     * be less than or equal to this amount.
  -     */
  -    public int getMaxLengthEstimate() {
  -        return mMaxLengthEstimate;
  -    }
  -
  -    private interface Rule {
  -        int estimateLength();
  -
  -        void appendTo(StringBuffer buffer, Calendar calendar);
  -    }
  -
  -    private interface NumberRule extends Rule {
  -        void appendTo(StringBuffer buffer, int value);
  -    }
  -
  -    private static class CharacterLiteral implements Rule {
  -        private final char mValue;
  -
  -        CharacterLiteral(char value) {
  -            mValue = value;
  -        }
  -
  -        public int estimateLength() {
  -            return 1;
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            buffer.append(mValue);
  -        }
  -    }
  -
  -    private static class StringLiteral implements Rule {
  -        private final String mValue;
  -
  -        StringLiteral(String value) {
  -            mValue = value;
  -        }
  -
  -        public int estimateLength() {
  -            return mValue.length();
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            buffer.append(mValue);
  -        }
  -    }
  -
  -    private static class TextField implements Rule {
  -        private final int mField;
  -        private final String[] mValues;
  -
  -        TextField(int field, String[] values) {
  -            mField = field;
  -            mValues = values;
  -        }
  -
  -        public int estimateLength() {
  -            int max = 0;
  -            for (int i=mValues.length; --i >= 0; ) {
  -                int len = mValues[i].length();
  -                if (len > max) {
  -                    max = len;
  -                }
  -            }
  -            return max;
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            buffer.append(mValues[calendar.get(mField)]);
  -        }
  -    }
  -
  -    private static class UnpaddedNumberField implements NumberRule {
  -        private final int mField;
  -
  -        UnpaddedNumberField(int field) {
  -            mField = field;
  -        }
  -
  -        public int estimateLength() {
  -            return 4;
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            appendTo(buffer, calendar.get(mField));
  -        }
  -
  -        public final void appendTo(StringBuffer buffer, int value) {
  -            if (value < 10) {
  -                buffer.append((char)(value + '0'));
  -            }
  -            else if (value < 100) {
  -                buffer.append((char)(value / 10 + '0'));
  -                buffer.append((char)(value % 10 + '0'));
  -            }
  -            else {
  -                buffer.append(Integer.toString(value));
  -            }
  -        }
  -    }
  -
  -    private static class UnpaddedMonthField implements NumberRule {
  -        UnpaddedMonthField() {
  -        }
  -
  -        public int estimateLength() {
  -            return 2;
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
  -        }
  -
  -        public final void appendTo(StringBuffer buffer, int value) {
  -            if (value < 10) {
  -                buffer.append((char)(value + '0'));
  -            }
  -            else {
  -                buffer.append((char)(value / 10 + '0'));
  -                buffer.append((char)(value % 10 + '0'));
  -            }
  -        }
  -    }
  -
  -    private static class PaddedNumberField implements NumberRule {
  -        private final int mField;
  -        private final int mSize;
  -
  -        PaddedNumberField(int field, int size) {
  -            if (size < 3) {
  -                // Should use UnpaddedNumberField or TwoDigitNumberField.
  -                throw new IllegalArgumentException();
  -            }
  -            mField = field;
  -            mSize = size;
  -        }
  -
  -        public int estimateLength() {
  -            return 4;
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            appendTo(buffer, calendar.get(mField));
  -        }
  -
  -        public final void appendTo(StringBuffer buffer, int value) {
  -            if (value < 100) {
  -                for (int i = mSize; --i >= 2; ) {
  -                    buffer.append('0');
  -                }
  -                buffer.append((char)(value / 10 + '0'));
  -                buffer.append((char)(value % 10 + '0'));
  -            }
  -            else {
  -                int digits;
  -                if (value < 1000) {
  -                    digits = 3;
  -                }
  -                else {
  -                    digits = (int)(Math.log(value) / LOG_10) + 1;
  -                }
  -                for (int i = mSize; --i >= digits; ) {
  -                    buffer.append('0');
  -                }
  -                buffer.append(Integer.toString(value));
  -            }
  -        }
  -    }
  -
  -    private static class TwoDigitNumberField implements NumberRule {
  -        private final int mField;
  -
  -        TwoDigitNumberField(int field) {
  -            mField = field;
  -        }
  -
  -        public int estimateLength() {
  -            return 2;
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            appendTo(buffer, calendar.get(mField));
  -        }
  -
  -        public final void appendTo(StringBuffer buffer, int value) {
  -            if (value < 100) {
  -                buffer.append((char)(value / 10 + '0'));
  -                buffer.append((char)(value % 10 + '0'));
  -            }
  -            else {
  -                buffer.append(Integer.toString(value));
  -            }
  -        }
  -    }
  -
  -    private static class TwoDigitYearField implements NumberRule {
  -        TwoDigitYearField() {
  -        }
  -
  -        public int estimateLength() {
  -            return 2;
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            appendTo(buffer, calendar.get(Calendar.YEAR) % 100);
  -        }
  -
  -        public final void appendTo(StringBuffer buffer, int value) {
  -            buffer.append((char)(value / 10 + '0'));
  -            buffer.append((char)(value % 10 + '0'));
  -        }
  -    }
  -
  -    private static class TwoDigitMonthField implements NumberRule {
  -        TwoDigitMonthField() {
  -        }
  -
  -        public int estimateLength() {
  -            return 2;
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
  -        }
  -
  -        public final void appendTo(StringBuffer buffer, int value) {
  -            buffer.append((char)(value / 10 + '0'));
  -            buffer.append((char)(value % 10 + '0'));
  -        }
  -    }
  -
  -    private static class TwelveHourField implements NumberRule {
  -        private final NumberRule mRule;
  -
  -        TwelveHourField(NumberRule rule) {
  -            mRule = rule;
  -        }
  -
  -        public int estimateLength() {
  -            return mRule.estimateLength();
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            int value = calendar.get(Calendar.HOUR);
  -            if (value == 0) {
  -                value = calendar.getLeastMaximum(Calendar.HOUR) + 1;
  -            }
  -            mRule.appendTo(buffer, value);
  -        }
  -
  -        public void appendTo(StringBuffer buffer, int value) {
  -            mRule.appendTo(buffer, value);
  -        }
  -    }
  -
  -    private static class TwentyFourHourField implements NumberRule {
  -        private final NumberRule mRule;
  -
  -        TwentyFourHourField(NumberRule rule) {
  -            mRule = rule;
  -        }
  -
  -        public int estimateLength() {
  -            return mRule.estimateLength();
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            int value = calendar.get(Calendar.HOUR_OF_DAY);
  -            if (value == 0) {
  -                value = calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1;
  -            }
  -            mRule.appendTo(buffer, value);
  -        }
  -
  -        public void appendTo(StringBuffer buffer, int value) {
  -            mRule.appendTo(buffer, value);
  -        }
  -    }
  -
  -    private static class TimeZoneRule implements Rule {
  -        private final TimeZone mTimeZone;
  -        private final Locale mLocale;
  -        private final int mStyle;
  -        private final String mStandard;
  -        private final String mDaylight;
  -
  -        TimeZoneRule(TimeZone timeZone, Locale locale, int style) {
  -            mTimeZone = timeZone;
  -            mLocale = locale;
  -            mStyle = style;
  -
  -            if (timeZone != null) {
  -                mStandard = getTimeZoneDisplay(timeZone, false, style, locale);
  -                mDaylight = getTimeZoneDisplay(timeZone, true, style, locale);
  -            }
  -            else {
  -                mStandard = null;
  -                mDaylight = null;
  -            }
  -        }
  -
  -        public int estimateLength() {
  -            if (mTimeZone != null) {
  -                return Math.max(mStandard.length(), mDaylight.length());
  -            }
  -            else if (mStyle == TimeZone.SHORT) {
  -                return 4;
  -            }
  -            else {
  -                return 40;
  -            }
  -        }
  -
  -        public void appendTo(StringBuffer buffer, Calendar calendar) {
  -            TimeZone timeZone;
  -            if ((timeZone = mTimeZone) != null) {
  -                if (timeZone.useDaylightTime() &&
  -                    calendar.get(Calendar.DST_OFFSET) != 0) {
  -
  -                    buffer.append(mDaylight);
  -                }
  -                else {
  -                    buffer.append(mStandard);
  -                }
  -            }
  -            else {
  -                timeZone = calendar.getTimeZone();
  -                if (timeZone.useDaylightTime() &&
  -                    calendar.get(Calendar.DST_OFFSET) != 0) {
  -
  -                    buffer.append(getTimeZoneDisplay
  -                                  (timeZone, true, mStyle, mLocale));
  -                }
  -                else {
  -                    buffer.append(getTimeZoneDisplay
  -                                  (timeZone, false, mStyle, mLocale));
  -                }
  -            }
  -        }
  -    }
  -
  -    private static class TimeZoneDisplayKey {
  -        private final TimeZone mTimeZone;
  -        private final int mStyle;
  -        private final Locale mLocale;
  -
  -        TimeZoneDisplayKey(TimeZone timeZone,
  -                           boolean daylight, int style, Locale locale) {
  -            mTimeZone = timeZone;
  -            if (daylight) {
  -                style |= 0x80000000;
  -            }
  -            mStyle = style;
  -            mLocale = locale;
  -        }
  -
  -        public int hashCode() {
  -            return mStyle * 31 + mLocale.hashCode();
  -        }
  -
  -        public boolean equals(Object obj) {
  -            if (this == obj) {
  -                return true;
  -            }
  -            if (obj instanceof TimeZoneDisplayKey) {
  -                TimeZoneDisplayKey other = (TimeZoneDisplayKey)obj;
  -                return
  -                    mTimeZone.equals(other.mTimeZone) &&
  -                    mStyle == other.mStyle &&
  -                    mLocale.equals(other.mLocale);
  -            }
  -            return false;
  -        }
  -    }
  -
  -    // Pair
  -    // 
----------------------------------------------------------------------------------
  -    /**
  -     * Helper class for creating compound objects.  One use for this class is to 
create a
  -     * hashtable key out of multiple objects.
  -     */
  -    private static class Pair implements Comparable, java.io.Serializable {
  -        private final Object mObj1;
  -        private final Object mObj2;
  -
  -        public Pair(Object obj1, Object obj2) {
  -            mObj1 = obj1;
  -            mObj2 = obj2;
  -        }
  -
  -        public int compareTo(Object obj) {
  -            if (this == obj) {
  -                return 0;
  -            }
  -
  -            Pair other = (Pair)obj;
  -
  -            Object a = mObj1;
  -            Object b = other.mObj1;
  -
  -            firstTest: {
  -                if (a == null) {
  -                    if (b != null) {
  -                        return 1;
  -                    }
  -                    // Both a and b are null.
  -                    break firstTest;
  -                }
  -                else {
  -                    if (b == null) {
  -                        return -1;
  -                    }
  -                }
  -
  -                int result = ((Comparable)a).compareTo(b);
  -
  -                if (result != 0) {
  -                    return result;
  -                }
  -            }
  -
  -            a = mObj2;
  -            b = other.mObj2;
  -
  -            if (a == null) {
  -                if (b != null) {
  -                    return 1;
  -                }
  -                // Both a and b are null.
  -                return 0;
  -            }
  -            else {
  -                if (b == null) {
  -                    return -1;
  -                }
  -            }
  -
  -            return ((Comparable)a).compareTo(b);
  -        }
  -
  -        public boolean equals(Object obj) {
  -            if (this == obj) {
  -                return true;
  -            }
  -
  -            if (!(obj instanceof Pair)) {
  -                return false;
  -            }
  -
  -            Pair key = (Pair)obj;
  -
  -            return
  -                (mObj1 == null ?
  -                 key.mObj1 == null : mObj1.equals(key.mObj1)) &&
  -                (mObj2 == null ?
  -                 key.mObj2 == null : mObj2.equals(key.mObj2));
  -        }
  -
  -        public int hashCode() {
  -            return
  -                (mObj1 == null ? 0 : mObj1.hashCode()) +
  -                (mObj2 == null ? 0 : mObj2.hashCode());
  -        }
  -
  -        public String toString() {
  -            return "[" + mObj1 + ':' + mObj2 + ']';
  -        }
  -    }
  -}
  +/* ====================================================================
  + * The Apache Software License, Version 1.1
  + *
  + * Copyright (c) 2002 The Apache Software Foundation.  All rights
  + * reserved.
  + *
  + * Redistribution and use in source and binary forms, with or without
  + * modification, are permitted provided that the following conditions
  + * are met:
  + *
  + * 1. Redistributions of source code must retain the above copyright
  + *    notice, this list of conditions and the following disclaimer.
  + *
  + * 2. Redistributions in binary form must reproduce the above copyright
  + *    notice, this list of conditions and the following disclaimer in
  + *    the documentation and/or other materials provided with the
  + *    distribution.
  + *
  + * 3. The end-user documentation included with the redistribution, if
  + *    any, must include the following acknowlegement:
  + *       "This product includes software developed by the
  + *        Apache Software Foundation (http://www.apache.org/)."
  + *    Alternately, this acknowlegement may appear in the software itself,
  + *    if and wherever such third-party acknowlegements normally appear.
  + *
  + * 4. The names "The Jakarta Project", "Commons", and "Apache Software
  + *    Foundation" must not be used to endorse or promote products derived
  + *    from this software without prior written permission. For written
  + *    permission, please contact [EMAIL PROTECTED]
  + *
  + * 5. Products derived from this software may not be called "Apache"
  + *    nor may "Apache" appear in their names without prior written
  + *    permission of the Apache Software Foundation.
  + *
  + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  + * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  + * SUCH DAMAGE.
  + * ====================================================================
  + *
  + * This software consists of voluntary contributions made by many
  + * individuals on behalf of the Apache Software Foundation.  For more
  + * information on the Apache Software Foundation, please see
  + * <http://www.apache.org/>.
  + */
  +
  +package org.apache.commons.lang.time;
  +
  +import java.util.Date;
  +import java.util.Calendar;
  +import java.util.GregorianCalendar;
  +import java.util.Locale;
  +import java.util.TimeZone;
  +import java.util.List;
  +import java.util.ArrayList;
  +import java.util.Map;
  +import java.util.HashMap;
  +import java.text.DateFormatSymbols;
  +import java.text.DateFormat;
  +import java.text.SimpleDateFormat;
  +
  +/******************************************************************************
  + * Similar to {@link java.text.SimpleDateFormat}, but faster and thread-safe.
  + * Only formatting is supported, but all patterns are compatible with
  + * SimpleDateFormat. [Code originally taken from the open source TreeTrove
  + * project.]
  + *
  + * @author Brian S O'Neill
  + * @author Sean Schofield
  + * @since 2.0
  + * @version $Id$
  + */
  +public class FastDateFormat {
  +    /** Style pattern */
  +    public static final Object
  +        FULL = new Integer(SimpleDateFormat.FULL),
  +        LONG = new Integer(SimpleDateFormat.LONG),
  +        MEDIUM = new Integer(SimpleDateFormat.MEDIUM),
  +        SHORT = new Integer(SimpleDateFormat.SHORT);
  +
  +    private static final double LOG_10 = Math.log(10);
  +
  +    private static String cDefaultPattern;
  +    private static TimeZone cDefaultTimeZone = TimeZone.getDefault();
  +
  +    private static Map cTimeZoneDisplayCache = new HashMap();
  +
  +    private static Map cInstanceCache = new HashMap(7);
  +    private static Map cDateInstanceCache = new HashMap(7);
  +    private static Map cTimeInstanceCache = new HashMap(7);
  +    private static Map cDateTimeInstanceCache = new HashMap(7);
  +
  +    public static FastDateFormat getInstance() {
  +        //return getInstance(getDefaultPattern(), null, null, null);
  +        return getInstance(getDefaultPattern(), null, null);
  +    }
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     */
  +    public static FastDateFormat getInstance(String pattern) {
  +        //return getInstance(pattern, null, null, null);
  +        return getInstance(pattern, null, null);
  +    }
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     * @param timeZone optional time zone, overrides time zone of formatted
  +     * date
  +     */
  +    public static FastDateFormat getInstance(String pattern, TimeZone timeZone) {
  +        //return getInstance(pattern, timeZone, null, null);
  +        return getInstance(pattern, timeZone, null);
  +    }
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     * @param locale optional locale, overrides system locale
  +     */
  +    public static FastDateFormat getInstance(String pattern, Locale locale) {
  +        //return getInstance(pattern, null, locale, null);
  +        return getInstance(pattern, null, locale);
  +    }
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     * @param symbols optional date format symbols, overrides symbols for
  +     * system locale
  +     */
  +    /*
  +    public static FastDateFormat getInstance
  +        (String pattern, DateFormatSymbols symbols)
  +        throws IllegalArgumentException
  +    {
  +        return getInstance(pattern, null, null, symbols);
  +    }
  +    */
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     * @param timeZone optional time zone, overrides time zone of formatted
  +     * date
  +     * @param locale optional locale, overrides system locale
  +     */
  +    public static FastDateFormat getInstance(String pattern, TimeZone timeZone, 
Locale locale) {
  +        //return getInstance(pattern, timeZone, locale, null);
  +        Object key = pattern;
  +
  +        if (timeZone != null) {
  +            key = new Pair(key, timeZone);
  +        }
  +        if (locale != null) {
  +            key = new Pair(key, locale);
  +        }
  +
  +        FastDateFormat format = (FastDateFormat)cInstanceCache.get(key);
  +        if (format == null) {
  +            if (locale == null) {
  +                locale = Locale.getDefault();
  +            }
  +
  +            format = new FastDateFormat(pattern, timeZone, locale, new 
DateFormatSymbols(locale));
  +            cInstanceCache.put(key, format);
  +        }
  +        return format;
  +    }
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     * @param timeZone optional time zone, overrides time zone of formatted
  +     * date
  +     * @param locale optional locale, overrides system locale
  +     * @param symbols optional date format symbols, overrides symbols for
  +     * provided locale
  +     */
  +    /*
  +    public static synchronized FastDateFormat getInstance
  +        (String pattern, TimeZone timeZone, Locale locale,
  +         DateFormatSymbols symbols)
  +        throws IllegalArgumentException
  +    {
  +        Object key = pattern;
  +
  +        if (timeZone != null) {
  +            key = new Pair(key, timeZone);
  +        }
  +        if (locale != null) {
  +            key = new Pair(key, locale);
  +        }
  +        if (symbols != null) {
  +            key = new Pair(key, symbols);
  +        }
  +
  +        FastDateFormat format = (FastDateFormat)cInstanceCache.get(key);
  +        if (format == null) {
  +            if (locale == null) {
  +                locale = Locale.getDefault();
  +            }
  +            if (symbols == null) {
  +                symbols = new DateFormatSymbols(locale);
  +            }
  +            format = new FastDateFormat(pattern, timeZone, locale, symbols);
  +            cInstanceCache.put(key, format);
  +        }
  +        return format;
  +    }
  +    */
  +
  +    /**
  +     * @param style date style: FULL, LONG, MEDIUM, or SHORT (corresponds to those 
in java.text.DateFormat)
  +     * @param timeZone optional time zone, overrides time zone of formatted
  +     * date
  +     * @param locale optional locale, overrides system locale
  +     */
  +    public static synchronized FastDateFormat getDateInstance(int style, TimeZone 
timeZone, Locale locale) {
  +        Object key = new Integer(style);
  +
  +        if (timeZone != null) {
  +            key = new Pair(key, timeZone);
  +        }
  +        if (locale == null) {
  +            key = new Pair(key, locale);
  +        }
  +
  +        FastDateFormat format = (FastDateFormat)cDateInstanceCache.get(key);
  +
  +        if (format == null) {
  +            if (locale == null) {
  +                locale = Locale.getDefault();
  +            }
  +
  +            try {
  +                String pattern = 
((SimpleDateFormat)DateFormat.getDateInstance(style, locale)).toPattern();
  +                format = getInstance(pattern, timeZone, locale);
  +                cDateInstanceCache.put(key, format);
  +            }
  +            catch (ClassCastException e) {
  +                throw new IllegalArgumentException
  +                    ("No date pattern for locale: " + locale);
  +            }
  +        }
  +
  +        return format;
  +    }
  +
  +    /**
  +     * @param style time style: FULL, LONG, MEDIUM, or SHORT
  +     * @param timeZone optional time zone, overrides time zone of formatted
  +     * date
  +     * @param locale optional locale, overrides system locale
  +     */
  +    public static synchronized FastDateFormat getTimeInstance(int style, TimeZone 
timeZone, Locale locale) {
  +        Object key = new Integer(style);
  +
  +        if (timeZone != null) {
  +            key = new Pair(key, timeZone);
  +        }
  +        if (locale != null) {
  +            key = new Pair(key, locale);
  +        }
  +
  +        FastDateFormat format = (FastDateFormat)cTimeInstanceCache.get(key);
  +
  +        if (format == null) {
  +
  +            if (locale == null) {
  +                locale = Locale.getDefault();
  +            }
  +
  +            try {
  +                String pattern = 
((SimpleDateFormat)DateFormat.getTimeInstance(style, locale)).toPattern();
  +                format = getInstance(pattern, timeZone, locale);
  +                cTimeInstanceCache.put(key, format);
  +            }
  +            catch (ClassCastException e) {
  +                throw new IllegalArgumentException
  +                    ("No date pattern for locale: " + locale);
  +            }
  +        }
  +
  +        return format;
  +    }
  +
  +    /**
  +     * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
  +     * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
  +     * @param timeZone optional time zone, overrides time zone of formatted
  +     * date
  +     * @param locale optional locale, overrides system locale
  +     */
  +    public static synchronized FastDateFormat getDateTimeInstance(Object dateStyle, 
Object timeStyle,
  +            TimeZone timeZone, Locale locale) {
  +
  +        Object key = new Pair(dateStyle, timeStyle);
  +
  +        if (timeZone != null) {
  +            key = new Pair(key, timeZone);
  +        }
  +        if (locale != null) {
  +            key = new Pair(key, locale);
  +        }
  +
  +        FastDateFormat format =
  +            (FastDateFormat)cDateTimeInstanceCache.get(key);
  +
  +        if (format == null) {
  +            int ds;
  +            try {
  +                ds = ((Integer)dateStyle).intValue();
  +            }
  +            catch (ClassCastException e) {
  +                throw new IllegalArgumentException
  +                    ("Illegal date style: " + dateStyle);
  +            }
  +
  +            int ts;
  +            try {
  +                ts = ((Integer)timeStyle).intValue();
  +            }
  +            catch (ClassCastException e) {
  +                throw new IllegalArgumentException
  +                    ("Illegal time style: " + timeStyle);
  +            }
  +
  +            if (locale == null) {
  +                locale = Locale.getDefault();
  +            }
  +
  +            try {
  +                String pattern = 
((SimpleDateFormat)DateFormat.getDateTimeInstance(ds, ts, locale)).toPattern();
  +                format = getInstance(pattern, timeZone, locale);
  +                cDateTimeInstanceCache.put(key, format);
  +            }
  +            catch (ClassCastException e) {
  +                throw new IllegalArgumentException
  +                    ("No date time pattern for locale: " + locale);
  +            }
  +        }
  +
  +        return format;
  +    }
  +
  +    static synchronized String getTimeZoneDisplay(TimeZone tz, boolean daylight, 
int style, Locale locale) {
  +        Object key = new TimeZoneDisplayKey(tz, daylight, style, locale);
  +        String value = (String)cTimeZoneDisplayCache.get(key);
  +        if (value == null) {
  +            // This is a very slow call, so cache the results.
  +            value = tz.getDisplayName(daylight, style, locale);
  +            cTimeZoneDisplayCache.put(key, value);
  +        }
  +        return value;
  +    }
  +
  +    private static synchronized String getDefaultPattern() {
  +        if (cDefaultPattern == null) {
  +            cDefaultPattern = new SimpleDateFormat().toPattern();
  +        }
  +        return cDefaultPattern;
  +    }
  +
  +    /**
  +     * Returns a list of Rules.
  +     */
  +    private static List parse(String pattern, TimeZone timeZone, Locale locale, 
DateFormatSymbols symbols) {
  +        List rules = new ArrayList();
  +
  +        String[] ERAs = symbols.getEras();
  +        String[] months = symbols.getMonths();
  +        String[] shortMonths = symbols.getShortMonths();
  +        String[] weekdays = symbols.getWeekdays();
  +        String[] shortWeekdays = symbols.getShortWeekdays();
  +        String[] AmPmStrings = symbols.getAmPmStrings();
  +
  +        int length = pattern.length();
  +        int[] indexRef = new int[1];
  +
  +        for (int i=0; i<length; i++) {
  +            indexRef[0] = i;
  +            String token = parseToken(pattern, indexRef);
  +            i = indexRef[0];
  +
  +            int tokenLen = token.length();
  +            if (tokenLen == 0) {
  +                break;
  +            }
  +
  +            Rule rule;
  +            char c = token.charAt(0);
  +
  +            switch (c) {
  +            case 'G': // era designator (text)
  +                rule = new TextField(Calendar.ERA, ERAs);
  +                break;
  +            case 'y': // year (number)
  +                if (tokenLen >= 4) {
  +                    rule = new UnpaddedNumberField(Calendar.YEAR);
  +                }
  +                else {
  +                    rule = new TwoDigitYearField();
  +                }
  +                break;
  +            case 'M': // month in year (text and number)
  +                if (tokenLen >= 4) {
  +                    rule = new TextField(Calendar.MONTH, months);
  +                }
  +                else if (tokenLen == 3) {
  +                    rule = new TextField(Calendar.MONTH, shortMonths);
  +                }
  +                else if (tokenLen == 2) {
  +                    rule = new TwoDigitMonthField();
  +                }
  +                else {
  +                    rule = new UnpaddedMonthField();
  +                }
  +                break;
  +            case 'd': // day in month (number)
  +                rule = selectNumberRule(Calendar.DAY_OF_MONTH, tokenLen);
  +                break;
  +            case 'h': // hour in am/pm (number, 1..12)
  +                rule = new TwelveHourField
  +                    (selectNumberRule(Calendar.HOUR, tokenLen));
  +                break;
  +            case 'H': // hour in day (number, 0..23)
  +                rule = selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen);
  +                break;
  +            case 'm': // minute in hour (number)
  +                rule = selectNumberRule(Calendar.MINUTE, tokenLen);
  +                break;
  +            case 's': // second in minute (number)
  +                rule = selectNumberRule(Calendar.SECOND, tokenLen);
  +                break;
  +            case 'S': // millisecond (number)
  +                rule = selectNumberRule(Calendar.MILLISECOND, tokenLen);
  +                break;
  +            case 'E': // day in week (text)
  +                rule = new TextField
  +                    (Calendar.DAY_OF_WEEK,
  +                     tokenLen < 4 ? shortWeekdays : weekdays);
  +                break;
  +            case 'D': // day in year (number)
  +                rule = selectNumberRule(Calendar.DAY_OF_YEAR, tokenLen);
  +                break;
  +            case 'F': // day of week in month (number)
  +                rule = selectNumberRule
  +                    (Calendar.DAY_OF_WEEK_IN_MONTH, tokenLen);
  +                break;
  +            case 'w': // week in year (number)
  +                rule = selectNumberRule(Calendar.WEEK_OF_YEAR, tokenLen);
  +                break;
  +            case 'W': // week in month (number)
  +                rule = selectNumberRule(Calendar.WEEK_OF_MONTH, tokenLen);
  +                break;
  +            case 'a': // am/pm marker (text)
  +                rule = new TextField(Calendar.AM_PM, AmPmStrings);
  +                break;
  +            case 'k': // hour in day (1..24)
  +                rule = new TwentyFourHourField
  +                    (selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen));
  +                break;
  +            case 'K': // hour in am/pm (0..11)
  +                rule = selectNumberRule(Calendar.HOUR, tokenLen);
  +                break;
  +            case 'z': // time zone (text)
  +                if (tokenLen >= 4) {
  +                    rule = new TimeZoneRule(timeZone, locale, TimeZone.LONG);
  +                }
  +                else {
  +                    rule = new TimeZoneRule(timeZone, locale, TimeZone.SHORT);
  +                }
  +                break;
  +            case '\'': // literal text
  +                String sub = token.substring(1);
  +                if (sub.length() == 1) {
  +                    rule = new CharacterLiteral(sub.charAt(0));
  +                }
  +                else {
  +                    rule = new StringLiteral(new String(sub));
  +                }
  +                break;
  +            default:
  +                throw new IllegalArgumentException
  +                    ("Illegal pattern component: " + token);
  +            }
  +
  +            rules.add(rule);
  +        }
  +
  +        return rules;
  +    }
  +
  +    private static String parseToken(String pattern, int[] indexRef) {
  +        StringBuffer buf = new StringBuffer();
  +
  +        int i = indexRef[0];
  +        int length = pattern.length();
  +
  +        char c = pattern.charAt(i);
  +        if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') {
  +            // Scan a run of the same character, which indicates a time
  +            // pattern.
  +            buf.append(c);
  +
  +            while (i + 1 < length) {
  +                char peek = pattern.charAt(i + 1);
  +                if (peek == c) {
  +                    buf.append(c);
  +                    i++;
  +                }
  +                else {
  +                    break;
  +                }
  +            }
  +        }
  +        else {
  +            // This will identify token as text.
  +            buf.append('\'');
  +
  +            boolean inLiteral = false;
  +
  +            for (; i < length; i++) {
  +                c = pattern.charAt(i);
  +
  +                if (c == '\'') {
  +                    if (i + 1 < length && pattern.charAt(i + 1) == '\'') {
  +                        // '' is treated as escaped '
  +                        i++;
  +                        buf.append(c);
  +                    }
  +                    else {
  +                        inLiteral = !inLiteral;
  +                    }
  +                }
  +                else if (!inLiteral &&
  +                         (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')) {
  +                    i--;
  +                    break;
  +                }
  +                else {
  +                    buf.append(c);
  +                }
  +            }
  +        }
  +
  +        indexRef[0] = i;
  +        return buf.toString();
  +    }
  +
  +    private static NumberRule selectNumberRule(int field, int padding) {
  +        switch (padding) {
  +        case 1:
  +            return new UnpaddedNumberField(field);
  +        case 2:
  +            return new TwoDigitNumberField(field);
  +        default:
  +            return new PaddedNumberField(field, padding);
  +        }
  +    }
  +
  +    private final String mPattern;
  +    private final TimeZone mTimeZone;
  +    private final Locale mLocale;
  +    private final Rule[] mRules;
  +    private final int mMaxLengthEstimate;
  +
  +    private FastDateFormat() {
  +        this(getDefaultPattern(), null, null, null);
  +    }
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     */
  +    private FastDateFormat(String pattern) throws IllegalArgumentException {
  +        this(pattern, null, null, null);
  +    }
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     * @param timeZone optional time zone, overrides time zone of formatted
  +     * date
  +     */
  +    private FastDateFormat(String pattern, TimeZone timeZone) {
  +        this(pattern, timeZone, null, null);
  +    }
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     * @param locale optional locale, overrides system locale
  +     */
  +    private FastDateFormat(String pattern, Locale locale) {
  +        this(pattern, null, locale, null);
  +    }
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     * @param symbols optional date format symbols, overrides symbols for
  +     * system locale
  +     */
  +    private FastDateFormat(String pattern, DateFormatSymbols symbols) {
  +        this(pattern, null, null, symbols);
  +    }
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     * @param timeZone optional time zone, overrides time zone of formatted
  +     * date
  +     * @param locale optional locale, overrides system locale
  +     */
  +    private FastDateFormat(String pattern, TimeZone timeZone, Locale locale) {
  +        this(pattern, timeZone, locale, null);
  +    }
  +
  +    /**
  +     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
  +     * @param timeZone optional time zone, overrides time zone of formatted
  +     * date
  +     * @param locale optional locale, overrides system locale
  +     * @param symbols optional date format symbols, overrides symbols for
  +     * provided locale
  +     */
  +    private FastDateFormat(String pattern, TimeZone timeZone, Locale locale, 
DateFormatSymbols symbols) {
  +        if (locale == null) {
  +            locale = Locale.getDefault();
  +        }
  +
  +        mPattern = pattern;
  +        mTimeZone = timeZone;
  +        mLocale = locale;
  +
  +        if (symbols == null) {
  +            symbols = new DateFormatSymbols(locale);
  +        }
  +
  +        List rulesList = parse(pattern, timeZone, locale, symbols);
  +        mRules = (Rule[])rulesList.toArray(new Rule[rulesList.size()]);
  +
  +        int len = 0;
  +        for (int i=mRules.length; --i >= 0; ) {
  +            len += mRules[i].estimateLength();
  +        }
  +
  +        mMaxLengthEstimate = len;
  +    }
  +
  +    public String format(Date date) {
  +        Calendar c = new GregorianCalendar(cDefaultTimeZone);
  +        c.setTime(date);
  +        if (mTimeZone != null) {
  +            c.setTimeZone(mTimeZone);
  +        }
  +        return applyRules(c, new StringBuffer(mMaxLengthEstimate)).toString();
  +    }
  +
  +    public String format(Calendar calendar) {
  +        return format(calendar, new StringBuffer(mMaxLengthEstimate))
  +            .toString();
  +    }
  +
  +    public StringBuffer format(Date date, StringBuffer buf) {
  +        Calendar c = new GregorianCalendar(cDefaultTimeZone);
  +        c.setTime(date);
  +        if (mTimeZone != null) {
  +            c.setTimeZone(mTimeZone);
  +        }
  +        return applyRules(c, buf);
  +    }
  +
  +    public StringBuffer format(Calendar calendar, StringBuffer buf) {
  +        if (mTimeZone != null) {
  +            calendar = (Calendar)calendar.clone();
  +            calendar.setTimeZone(mTimeZone);
  +        }
  +        return applyRules(calendar, buf);
  +    }
  +
  +    private StringBuffer applyRules(Calendar calendar, StringBuffer buf) {
  +        Rule[] rules = mRules;
  +        int len = mRules.length;
  +        for (int i=0; i<len; i++) {
  +            rules[i].appendTo(buf, calendar);
  +        }
  +        return buf;
  +    }
  +
  +    public String getPattern() {
  +        return mPattern;
  +    }
  +
  +    /**
  +     * Returns the time zone used by this formatter, or null if time zone of
  +     * formatted dates is used instead.
  +     */
  +    public TimeZone getTimeZone() {
  +        return mTimeZone;
  +    }
  +
  +    public Locale getLocale() {
  +        return mLocale;
  +    }
  +
  +    /**
  +     * Returns an estimate for the maximum length date that this date
  +     * formatter will produce. The actual formatted length will almost always
  +     * be less than or equal to this amount.
  +     */
  +    public int getMaxLengthEstimate() {
  +        return mMaxLengthEstimate;
  +    }
  +
  +    private interface Rule {
  +        int estimateLength();
  +
  +        void appendTo(StringBuffer buffer, Calendar calendar);
  +    }
  +
  +    private interface NumberRule extends Rule {
  +        void appendTo(StringBuffer buffer, int value);
  +    }
  +
  +    private static class CharacterLiteral implements Rule {
  +        private final char mValue;
  +
  +        CharacterLiteral(char value) {
  +            mValue = value;
  +        }
  +
  +        public int estimateLength() {
  +            return 1;
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            buffer.append(mValue);
  +        }
  +    }
  +
  +    private static class StringLiteral implements Rule {
  +        private final String mValue;
  +
  +        StringLiteral(String value) {
  +            mValue = value;
  +        }
  +
  +        public int estimateLength() {
  +            return mValue.length();
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            buffer.append(mValue);
  +        }
  +    }
  +
  +    private static class TextField implements Rule {
  +        private final int mField;
  +        private final String[] mValues;
  +
  +        TextField(int field, String[] values) {
  +            mField = field;
  +            mValues = values;
  +        }
  +
  +        public int estimateLength() {
  +            int max = 0;
  +            for (int i=mValues.length; --i >= 0; ) {
  +                int len = mValues[i].length();
  +                if (len > max) {
  +                    max = len;
  +                }
  +            }
  +            return max;
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            buffer.append(mValues[calendar.get(mField)]);
  +        }
  +    }
  +
  +    private static class UnpaddedNumberField implements NumberRule {
  +        private final int mField;
  +
  +        UnpaddedNumberField(int field) {
  +            mField = field;
  +        }
  +
  +        public int estimateLength() {
  +            return 4;
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            appendTo(buffer, calendar.get(mField));
  +        }
  +
  +        public final void appendTo(StringBuffer buffer, int value) {
  +            if (value < 10) {
  +                buffer.append((char)(value + '0'));
  +            }
  +            else if (value < 100) {
  +                buffer.append((char)(value / 10 + '0'));
  +                buffer.append((char)(value % 10 + '0'));
  +            }
  +            else {
  +                buffer.append(Integer.toString(value));
  +            }
  +        }
  +    }
  +
  +    private static class UnpaddedMonthField implements NumberRule {
  +        UnpaddedMonthField() {
  +        }
  +
  +        public int estimateLength() {
  +            return 2;
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
  +        }
  +
  +        public final void appendTo(StringBuffer buffer, int value) {
  +            if (value < 10) {
  +                buffer.append((char)(value + '0'));
  +            }
  +            else {
  +                buffer.append((char)(value / 10 + '0'));
  +                buffer.append((char)(value % 10 + '0'));
  +            }
  +        }
  +    }
  +
  +    private static class PaddedNumberField implements NumberRule {
  +        private final int mField;
  +        private final int mSize;
  +
  +        PaddedNumberField(int field, int size) {
  +            if (size < 3) {
  +                // Should use UnpaddedNumberField or TwoDigitNumberField.
  +                throw new IllegalArgumentException();
  +            }
  +            mField = field;
  +            mSize = size;
  +        }
  +
  +        public int estimateLength() {
  +            return 4;
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            appendTo(buffer, calendar.get(mField));
  +        }
  +
  +        public final void appendTo(StringBuffer buffer, int value) {
  +            if (value < 100) {
  +                for (int i = mSize; --i >= 2; ) {
  +                    buffer.append('0');
  +                }
  +                buffer.append((char)(value / 10 + '0'));
  +                buffer.append((char)(value % 10 + '0'));
  +            }
  +            else {
  +                int digits;
  +                if (value < 1000) {
  +                    digits = 3;
  +                }
  +                else {
  +                    digits = (int)(Math.log(value) / LOG_10) + 1;
  +                }
  +                for (int i = mSize; --i >= digits; ) {
  +                    buffer.append('0');
  +                }
  +                buffer.append(Integer.toString(value));
  +            }
  +        }
  +    }
  +
  +    private static class TwoDigitNumberField implements NumberRule {
  +        private final int mField;
  +
  +        TwoDigitNumberField(int field) {
  +            mField = field;
  +        }
  +
  +        public int estimateLength() {
  +            return 2;
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            appendTo(buffer, calendar.get(mField));
  +        }
  +
  +        public final void appendTo(StringBuffer buffer, int value) {
  +            if (value < 100) {
  +                buffer.append((char)(value / 10 + '0'));
  +                buffer.append((char)(value % 10 + '0'));
  +            }
  +            else {
  +                buffer.append(Integer.toString(value));
  +            }
  +        }
  +    }
  +
  +    private static class TwoDigitYearField implements NumberRule {
  +        TwoDigitYearField() {
  +        }
  +
  +        public int estimateLength() {
  +            return 2;
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            appendTo(buffer, calendar.get(Calendar.YEAR) % 100);
  +        }
  +
  +        public final void appendTo(StringBuffer buffer, int value) {
  +            buffer.append((char)(value / 10 + '0'));
  +            buffer.append((char)(value % 10 + '0'));
  +        }
  +    }
  +
  +    private static class TwoDigitMonthField implements NumberRule {
  +        TwoDigitMonthField() {
  +        }
  +
  +        public int estimateLength() {
  +            return 2;
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
  +        }
  +
  +        public final void appendTo(StringBuffer buffer, int value) {
  +            buffer.append((char)(value / 10 + '0'));
  +            buffer.append((char)(value % 10 + '0'));
  +        }
  +    }
  +
  +    private static class TwelveHourField implements NumberRule {
  +        private final NumberRule mRule;
  +
  +        TwelveHourField(NumberRule rule) {
  +            mRule = rule;
  +        }
  +
  +        public int estimateLength() {
  +            return mRule.estimateLength();
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            int value = calendar.get(Calendar.HOUR);
  +            if (value == 0) {
  +                value = calendar.getLeastMaximum(Calendar.HOUR) + 1;
  +            }
  +            mRule.appendTo(buffer, value);
  +        }
  +
  +        public void appendTo(StringBuffer buffer, int value) {
  +            mRule.appendTo(buffer, value);
  +        }
  +    }
  +
  +    private static class TwentyFourHourField implements NumberRule {
  +        private final NumberRule mRule;
  +
  +        TwentyFourHourField(NumberRule rule) {
  +            mRule = rule;
  +        }
  +
  +        public int estimateLength() {
  +            return mRule.estimateLength();
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            int value = calendar.get(Calendar.HOUR_OF_DAY);
  +            if (value == 0) {
  +                value = calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1;
  +            }
  +            mRule.appendTo(buffer, value);
  +        }
  +
  +        public void appendTo(StringBuffer buffer, int value) {
  +            mRule.appendTo(buffer, value);
  +        }
  +    }
  +
  +    private static class TimeZoneRule implements Rule {
  +        private final TimeZone mTimeZone;
  +        private final Locale mLocale;
  +        private final int mStyle;
  +        private final String mStandard;
  +        private final String mDaylight;
  +
  +        TimeZoneRule(TimeZone timeZone, Locale locale, int style) {
  +            mTimeZone = timeZone;
  +            mLocale = locale;
  +            mStyle = style;
  +
  +            if (timeZone != null) {
  +                mStandard = getTimeZoneDisplay(timeZone, false, style, locale);
  +                mDaylight = getTimeZoneDisplay(timeZone, true, style, locale);
  +            }
  +            else {
  +                mStandard = null;
  +                mDaylight = null;
  +            }
  +        }
  +
  +        public int estimateLength() {
  +            if (mTimeZone != null) {
  +                return Math.max(mStandard.length(), mDaylight.length());
  +            }
  +            else if (mStyle == TimeZone.SHORT) {
  +                return 4;
  +            }
  +            else {
  +                return 40;
  +            }
  +        }
  +
  +        public void appendTo(StringBuffer buffer, Calendar calendar) {
  +            TimeZone timeZone;
  +            if ((timeZone = mTimeZone) != null) {
  +                if (timeZone.useDaylightTime() &&
  +                    calendar.get(Calendar.DST_OFFSET) != 0) {
  +
  +                    buffer.append(mDaylight);
  +                }
  +                else {
  +                    buffer.append(mStandard);
  +                }
  +            }
  +            else {
  +                timeZone = calendar.getTimeZone();
  +                if (timeZone.useDaylightTime() &&
  +                    calendar.get(Calendar.DST_OFFSET) != 0) {
  +
  +                    buffer.append(getTimeZoneDisplay
  +                                  (timeZone, true, mStyle, mLocale));
  +                }
  +                else {
  +                    buffer.append(getTimeZoneDisplay
  +                                  (timeZone, false, mStyle, mLocale));
  +                }
  +            }
  +        }
  +    }
  +
  +    private static class TimeZoneDisplayKey {
  +        private final TimeZone mTimeZone;
  +        private final int mStyle;
  +        private final Locale mLocale;
  +
  +        TimeZoneDisplayKey(TimeZone timeZone,
  +                           boolean daylight, int style, Locale locale) {
  +            mTimeZone = timeZone;
  +            if (daylight) {
  +                style |= 0x80000000;
  +            }
  +            mStyle = style;
  +            mLocale = locale;
  +        }
  +
  +        public int hashCode() {
  +            return mStyle * 31 + mLocale.hashCode();
  +        }
  +
  +        public boolean equals(Object obj) {
  +            if (this == obj) {
  +                return true;
  +            }
  +            if (obj instanceof TimeZoneDisplayKey) {
  +                TimeZoneDisplayKey other = (TimeZoneDisplayKey)obj;
  +                return
  +                    mTimeZone.equals(other.mTimeZone) &&
  +                    mStyle == other.mStyle &&
  +                    mLocale.equals(other.mLocale);
  +            }
  +            return false;
  +        }
  +    }
  +
  +    // Pair
  +    // 
----------------------------------------------------------------------------------
  +    /**
  +     * Helper class for creating compound objects.  One use for this class is to 
create a
  +     * hashtable key out of multiple objects.
  +     */
  +    private static class Pair implements Comparable, java.io.Serializable {
  +        private final Object mObj1;
  +        private final Object mObj2;
  +
  +        public Pair(Object obj1, Object obj2) {
  +            mObj1 = obj1;
  +            mObj2 = obj2;
  +        }
  +
  +        public int compareTo(Object obj) {
  +            if (this == obj) {
  +                return 0;
  +            }
  +
  +            Pair other = (Pair)obj;
  +
  +            Object a = mObj1;
  +            Object b = other.mObj1;
  +
  +            firstTest: {
  +                if (a == null) {
  +                    if (b != null) {
  +                        return 1;
  +                    }
  +                    // Both a and b are null.
  +                    break firstTest;
  +                }
  +                else {
  +                    if (b == null) {
  +                        return -1;
  +                    }
  +                }
  +
  +                int result = ((Comparable)a).compareTo(b);
  +
  +                if (result != 0) {
  +                    return result;
  +                }
  +            }
  +
  +            a = mObj2;
  +            b = other.mObj2;
  +
  +            if (a == null) {
  +                if (b != null) {
  +                    return 1;
  +                }
  +                // Both a and b are null.
  +                return 0;
  +            }
  +            else {
  +                if (b == null) {
  +                    return -1;
  +                }
  +            }
  +
  +            return ((Comparable)a).compareTo(b);
  +        }
  +
  +        public boolean equals(Object obj) {
  +            if (this == obj) {
  +                return true;
  +            }
  +
  +            if (!(obj instanceof Pair)) {
  +                return false;
  +            }
  +
  +            Pair key = (Pair)obj;
  +
  +            return
  +                (mObj1 == null ?
  +                 key.mObj1 == null : mObj1.equals(key.mObj1)) &&
  +                (mObj2 == null ?
  +                 key.mObj2 == null : mObj2.equals(key.mObj2));
  +        }
  +
  +        public int hashCode() {
  +            return
  +                (mObj1 == null ? 0 : mObj1.hashCode()) +
  +                (mObj2 == null ? 0 : mObj2.hashCode());
  +        }
  +
  +        public String toString() {
  +            return "[" + mObj1 + ':' + mObj2 + ']';
  +        }
  +    }
  +}
  
  
  
  1.2       +167 -166  
jakarta-commons/lang/src/test/org/apache/commons/lang/time/FastDateFormatTest.java
  
  Index: FastDateFormatTest.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/lang/src/test/org/apache/commons/lang/time/FastDateFormatTest.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- FastDateFormatTest.java   7 Jan 2003 19:55:55 -0000       1.1
  +++ FastDateFormatTest.java   7 Jan 2003 21:21:43 -0000       1.2
  @@ -1,166 +1,167 @@
  -/* ====================================================================
  - * The Apache Software License, Version 1.1
  - *
  - * Copyright (c) 2002 The Apache Software Foundation.  All rights
  - * reserved.
  - *
  - * Redistribution and use in source and binary forms, with or without
  - * modification, are permitted provided that the following conditions
  - * are met:
  - *
  - * 1. Redistributions of source code must retain the above copyright
  - *    notice, this list of conditions and the following disclaimer.
  - *
  - * 2. Redistributions in binary form must reproduce the above copyright
  - *    notice, this list of conditions and the following disclaimer in
  - *    the documentation and/or other materials provided with the
  - *    distribution.
  - *
  - * 3. The end-user documentation included with the redistribution, if
  - *    any, must include the following acknowlegement:
  - *       "This product includes software developed by the
  - *        Apache Software Foundation (http://www.apache.org/)."
  - *    Alternately, this acknowlegement may appear in the software itself,
  - *    if and wherever such third-party acknowlegements normally appear.
  - *
  - * 4. The names "The Jakarta Project", "Commons", and "Apache Software
  - *    Foundation" must not be used to endorse or promote products derived
  - *    from this software without prior written permission. For written
  - *    permission, please contact [EMAIL PROTECTED]
  - *
  - * 5. Products derived from this software may not be called "Apache"
  - *    nor may "Apache" appear in their names without prior written
  - *    permission of the Apache Software Foundation.
  - *
  - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  - * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  - * SUCH DAMAGE.
  - * ====================================================================
  - *
  - * This software consists of voluntary contributions made by many
  - * individuals on behalf of the Apache Software Foundation.  For more
  - * information on the Apache Software Foundation, please see
  - * <http://www.apache.org/>.
  - */
  -package org.apache.commons.lang.time;
  -
  -import junit.framework.*;
  -import junit.textui.TestRunner;
  -import java.util.*;
  -import java.text.*;
  -
  -/**
  - * Unit tests {@link org.apache.commons.lang.time.FastDateFormat}.
  - *
  - * @author Sean Schofield
  - * @since 2.0
  - * @version $Id$
  - */
  -public class FastDateFormatTest extends TestCase {
  -
  -    private FastDateFormat fastDateFormat = null;
  -
  -    public FastDateFormatTest(String name) {
  -        super(name);
  -    }
  -
  -    public static void main(String[] args) {
  -        TestRunner.run(suite());
  -    }
  -
  -    public static Test suite() {
  -        TestSuite suite = new TestSuite(FastDateFormatTest.class);
  -        suite.setName("FastDateFormat Tests");
  -
  -        return suite;
  -    }
  -
  -    protected void setUp() throws Exception {
  -        super.setUp();
  -    }
  -
  -    protected void tearDown() throws Exception {
  -        super.tearDown();
  -    }
  -
  -    public void test_getInstance() {
  -        FastDateFormat format1 = FastDateFormat.getInstance();
  -        FastDateFormat format2 = FastDateFormat.getInstance();
  -        assertSame(format1, format2);
  -    }
  -
  -    public void test_getInstance_String() {
  -        FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy");
  -        FastDateFormat format2 = FastDateFormat.getInstance("MM-DD-yyyy");
  -        assertTrue(format1 != format2); // -- junit 3.8 version -- 
assertFalse(format1 == format2);
  -        assertSame(format1, FastDateFormat.getInstance("MM/DD/yyyy"));
  -    }
  -
  -    public void test_getInstance_String_TimeZone() {
  -        Locale realDefaultLocale = Locale.getDefault();
  -        Locale.setDefault(Locale.US);
  -        TimeZone realDefaultZone = TimeZone.getDefault();
  -        TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
  -
  -        FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy",
  -                TimeZone.getTimeZone("Atlantic/Reykjavik"));
  -        FastDateFormat format2 = FastDateFormat.getInstance("MM/DD/yyyy");
  -        FastDateFormat format3 = FastDateFormat.getInstance("MM/DD/yyyy", 
TimeZone.getDefault());
  -        FastDateFormat format4 = FastDateFormat.getInstance("MM/DD/yyyy", 
TimeZone.getDefault());
  -        FastDateFormat format5 = FastDateFormat.getInstance("MM-DD-yyyy", 
TimeZone.getDefault());
  -
  -        assertTrue(format1 != format2); // -- junit 3.8 version -- 
assertFalse(format1 == format2);
  -        
assertTrue(format1.getTimeZone().equals(TimeZone.getTimeZone("Atlantic/Reykjavik")));
  -        assertNull(format2.getTimeZone());
  -        assertSame(format3, format4);
  -        assertTrue(format3 != format5); // -- junit 3.8 version -- 
assertFalse(format3 == format5);
  -
  -        Locale.setDefault(realDefaultLocale);
  -        TimeZone.setDefault(realDefaultZone);
  -    }
  -
  -    public void test_getInstance_String_Locale() {
  -        Locale realDefaultLocale = Locale.getDefault();
  -        Locale.setDefault(Locale.US);
  -        FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy", 
Locale.GERMANY);
  -        FastDateFormat format2 = FastDateFormat.getInstance("MM/DD/yyyy");
  -        FastDateFormat format3 = FastDateFormat.getInstance("MM/DD/yyyy", 
Locale.GERMANY);
  -
  -        assertTrue(format1 != format2); // -- junit 3.8 version -- 
assertFalse(format1 == format2);
  -        assertSame(format1, format3);
  -        assertSame(Locale.GERMANY, format1.getLocale());
  -
  -        Locale.setDefault(realDefaultLocale);
  -    }
  -
  -    public void test_getInstance_String_TimeZone_Locale() {
  -        Locale realDefaultLocale = Locale.getDefault();
  -        Locale.setDefault(Locale.US);
  -        TimeZone realDefaultZone = TimeZone.getDefault();
  -        TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
  -
  -        FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy",
  -                TimeZone.getTimeZone("Atlantic/Reykjavik"), Locale.GERMANY);
  -        FastDateFormat format2 = FastDateFormat.getInstance("MM/DD/yyyy", 
Locale.GERMANY);
  -        FastDateFormat format3 = FastDateFormat.getInstance("MM/DD/yyyy",
  -                TimeZone.getDefault(), Locale.GERMANY);
  -
  -        assertTrue(format1 != format2); // -- junit 3.8 version -- 
assertNotSame(format1, format2);
  -        assertEquals(format1.getTimeZone(), 
TimeZone.getTimeZone("Atlantic/Reykjavik"));
  -        assertNull(format2.getTimeZone());
  -        assertEquals(format3.getTimeZone(), TimeZone.getDefault());
  -        assertEquals(format3.getTimeZone(), 
TimeZone.getTimeZone("America/New_York"));
  -
  -        Locale.setDefault(realDefaultLocale);
  -        TimeZone.setDefault(realDefaultZone);
  -    }
  -}
  +/* ====================================================================
  + * The Apache Software License, Version 1.1
  + *
  + * Copyright (c) 2002 The Apache Software Foundation.  All rights
  + * reserved.
  + *
  + * Redistribution and use in source and binary forms, with or without
  + * modification, are permitted provided that the following conditions
  + * are met:
  + *
  + * 1. Redistributions of source code must retain the above copyright
  + *    notice, this list of conditions and the following disclaimer.
  + *
  + * 2. Redistributions in binary form must reproduce the above copyright
  + *    notice, this list of conditions and the following disclaimer in
  + *    the documentation and/or other materials provided with the
  + *    distribution.
  + *
  + * 3. The end-user documentation included with the redistribution, if
  + *    any, must include the following acknowlegement:
  + *       "This product includes software developed by the
  + *        Apache Software Foundation (http://www.apache.org/)."
  + *    Alternately, this acknowlegement may appear in the software itself,
  + *    if and wherever such third-party acknowlegements normally appear.
  + *
  + * 4. The names "The Jakarta Project", "Commons", and "Apache Software
  + *    Foundation" must not be used to endorse or promote products derived
  + *    from this software without prior written permission. For written
  + *    permission, please contact [EMAIL PROTECTED]
  + *
  + * 5. Products derived from this software may not be called "Apache"
  + *    nor may "Apache" appear in their names without prior written
  + *    permission of the Apache Software Foundation.
  + *
  + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  + * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  + * SUCH DAMAGE.
  + * ====================================================================
  + *
  + * This software consists of voluntary contributions made by many
  + * individuals on behalf of the Apache Software Foundation.  For more
  + * information on the Apache Software Foundation, please see
  + * <http://www.apache.org/>.
  + */
  +
  +package org.apache.commons.lang.time;
  +
  +import junit.framework.*;
  +import junit.textui.TestRunner;
  +import java.util.*;
  +import java.text.*;
  +
  +/**
  + * Unit tests {@link org.apache.commons.lang.time.FastDateFormat}.
  + *
  + * @author Sean Schofield
  + * @since 2.0
  + * @version $Id$
  + */
  +public class FastDateFormatTest extends TestCase {
  +
  +    private FastDateFormat fastDateFormat = null;
  +
  +    public FastDateFormatTest(String name) {
  +        super(name);
  +    }
  +
  +    public static void main(String[] args) {
  +        TestRunner.run(suite());
  +    }
  +
  +    public static Test suite() {
  +        TestSuite suite = new TestSuite(FastDateFormatTest.class);
  +        suite.setName("FastDateFormat Tests");
  +
  +        return suite;
  +    }
  +
  +    protected void setUp() throws Exception {
  +        super.setUp();
  +    }
  +
  +    protected void tearDown() throws Exception {
  +        super.tearDown();
  +    }
  +
  +    public void test_getInstance() {
  +        FastDateFormat format1 = FastDateFormat.getInstance();
  +        FastDateFormat format2 = FastDateFormat.getInstance();
  +        assertSame(format1, format2);
  +    }
  +
  +    public void test_getInstance_String() {
  +        FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy");
  +        FastDateFormat format2 = FastDateFormat.getInstance("MM-DD-yyyy");
  +        assertTrue(format1 != format2); // -- junit 3.8 version -- 
assertFalse(format1 == format2);
  +        assertSame(format1, FastDateFormat.getInstance("MM/DD/yyyy"));
  +    }
  +
  +    public void test_getInstance_String_TimeZone() {
  +        Locale realDefaultLocale = Locale.getDefault();
  +        Locale.setDefault(Locale.US);
  +        TimeZone realDefaultZone = TimeZone.getDefault();
  +        TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
  +
  +        FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy",
  +                TimeZone.getTimeZone("Atlantic/Reykjavik"));
  +        FastDateFormat format2 = FastDateFormat.getInstance("MM/DD/yyyy");
  +        FastDateFormat format3 = FastDateFormat.getInstance("MM/DD/yyyy", 
TimeZone.getDefault());
  +        FastDateFormat format4 = FastDateFormat.getInstance("MM/DD/yyyy", 
TimeZone.getDefault());
  +        FastDateFormat format5 = FastDateFormat.getInstance("MM-DD-yyyy", 
TimeZone.getDefault());
  +
  +        assertTrue(format1 != format2); // -- junit 3.8 version -- 
assertFalse(format1 == format2);
  +        
assertTrue(format1.getTimeZone().equals(TimeZone.getTimeZone("Atlantic/Reykjavik")));
  +        assertNull(format2.getTimeZone());
  +        assertSame(format3, format4);
  +        assertTrue(format3 != format5); // -- junit 3.8 version -- 
assertFalse(format3 == format5);
  +
  +        Locale.setDefault(realDefaultLocale);
  +        TimeZone.setDefault(realDefaultZone);
  +    }
  +
  +    public void test_getInstance_String_Locale() {
  +        Locale realDefaultLocale = Locale.getDefault();
  +        Locale.setDefault(Locale.US);
  +        FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy", 
Locale.GERMANY);
  +        FastDateFormat format2 = FastDateFormat.getInstance("MM/DD/yyyy");
  +        FastDateFormat format3 = FastDateFormat.getInstance("MM/DD/yyyy", 
Locale.GERMANY);
  +
  +        assertTrue(format1 != format2); // -- junit 3.8 version -- 
assertFalse(format1 == format2);
  +        assertSame(format1, format3);
  +        assertSame(Locale.GERMANY, format1.getLocale());
  +
  +        Locale.setDefault(realDefaultLocale);
  +    }
  +
  +    public void test_getInstance_String_TimeZone_Locale() {
  +        Locale realDefaultLocale = Locale.getDefault();
  +        Locale.setDefault(Locale.US);
  +        TimeZone realDefaultZone = TimeZone.getDefault();
  +        TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
  +
  +        FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy",
  +                TimeZone.getTimeZone("Atlantic/Reykjavik"), Locale.GERMANY);
  +        FastDateFormat format2 = FastDateFormat.getInstance("MM/DD/yyyy", 
Locale.GERMANY);
  +        FastDateFormat format3 = FastDateFormat.getInstance("MM/DD/yyyy",
  +                TimeZone.getDefault(), Locale.GERMANY);
  +
  +        assertTrue(format1 != format2); // -- junit 3.8 version -- 
assertNotSame(format1, format2);
  +        assertEquals(format1.getTimeZone(), 
TimeZone.getTimeZone("Atlantic/Reykjavik"));
  +        assertNull(format2.getTimeZone());
  +        assertEquals(format3.getTimeZone(), TimeZone.getDefault());
  +        assertEquals(format3.getTimeZone(), 
TimeZone.getTimeZone("America/New_York"));
  +
  +        Locale.setDefault(realDefaultLocale);
  +        TimeZone.setDefault(realDefaultZone);
  +    }
  +}
  
  
  

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to