Code example for Calendar

Methods: get, getTimeZone

0
     * @param needSpace 
     *            <tt>true</tt> if a space character should be inserted before 
     *            any data 
     */ 
    private static final void appendTimeString(StringBuffer buf, Calendar cal, boolean needSpace) {
        final int hour = cal.get(Calendar.HOUR_OF_DAY);
        final int minute = cal.get(Calendar.MINUTE);
        final int second = cal.get(Calendar.SECOND);
        final int milli = cal.get(Calendar.MILLISECOND);
 
        if (hour != 0 || minute != 0 || second != 0 || milli != 0) {
            if (needSpace) {
                buf.append(' ');
            } 
            if (hour < 10) {
                buf.append(' ');
            } 
            buf.append(hour);
 
            if (minute < 10) {
                buf.append(":0");
            } else { 
                buf.append(':');
            } 
            buf.append(minute);
 
            if (second != 0 || milli != 0) {
                if (second < 10) {
                    buf.append(":0");
                } else { 
                    buf.append(':');
                } 
                buf.append(second);
 
                if (milli != 0) {
                    if (milli < 10) {
                        buf.append(".00");
                    } else if (milli < 100) {
                        buf.append(".0");
                    } else { 
                        buf.append('.');
                    } 
                    buf.append(milli);
                } 
            } 
        } 
 
        TimeZone tz = cal.getTimeZone();
        if (tz.getRawOffset() == 0) {
            buf.append(" GMT");
        } else { 
            buf.append(' ');
 
            int offset = tz.getRawOffset() / (60 * 1000);
            if (offset < 0) {
                buf.append('-');
                offset = -offset;
            } else { 
                buf.append('+');
            } 
 
            int hrOff = offset / 60;
            if (hrOff < 10) {
                buf.append('0');
            } 
            buf.append(hrOff);
            buf.append(':');
 
            int minOff = offset % 60;
            if (minOff < 10) {
                buf.append('0');
            } 
            buf.append(minOff);
        } 
    } 
 
    /** 
     * Return a string representation of the order value. 
     *  
     * @param order 
     *            order 
     *  
     * @return order string 
     */ 
    public static final String getOrderString(int order) {
        switch (order) {
        case DD_MM_YY:
            return "DD_MM_YY"; 
        case MM_DD_YY:
            return "MM_DD_YY"; 
        case MM_YY_DD:
            return "MM_YY_DD"; 
        case DD_YY_MM:
            return "DD_YY_MM"; 
        case YY_DD_MM:
            return "YY_DD_MM"; 
        case YY_MM_DD:
            return "YY_MM_DD"; 
        default: 
            break; 
        } 
 
        return "??" + order + "??";
    } 
 
    /** 
     * Translate a string representation of an ordinal number to the appropriate 
     * numeric value.<br> 
     * For example, <tt>"1st"</tt> would return <tt>1</tt>, <tt>"23rd"</tt> 
     * would return <tt>23</tt>, etc. 
     *  
     * @param str 
     *            ordinal string 
     *  
     * @return the numeric value of the ordinal number, or 
     *         <tt>CalendarParser.UNSET</tt> if the supplied string is not a 
     *         valid ordinal number. 
     */ 
    private static final int getOrdinalNumber(String str) {
        final int len = (str == null ? 0 : str.length());
        if (len >= 3) {
 
            String suffix = str.substring(len - 2);
            if (suffix.equalsIgnoreCase("st") || suffix.equalsIgnoreCase("nd")
                    || suffix.equalsIgnoreCase("rd")
                    || suffix.equalsIgnoreCase("th")) {
                try { 
                    return Integer.parseInt(str.substring(0, len - 2));
                } catch (NumberFormatException nfe) {
                    // fall through if number was not parsed 
                } 
            } 
        } 
 
        return UNSET;
    } 
 
    /** 
     * Get name of current place in time. 
     *  
     * @param place 
     *            place ID 
     *  
     * @return place name (<tt>"hour"</tt>, <tt>"minute"</tt>, etc. 
     */ 
    private static final String getTimePlaceString(int place) {
        switch (place) {
        case PLACE_HOUR:
            return "hour"; 
        case PLACE_MINUTE:
            return "minute"; 
        case PLACE_SECOND:
            return "second"; 
        case PLACE_MILLI:
            return "millisecond"; 
        default: 
            break; 
        } 
 
        return "unknown"; 
    } 
 
    /** 
     * Determine is the supplied string is a value weekday name. 
     *  
     * @param str 
     *            weekday name to check 
     *  
     * @return <tt>true</tt> if the supplied string is a weekday name. 
     */ 
    private static final boolean isWeekdayName(String str) {
        if (str == null || str.length() < 3) {
            return false; 
        } 
 
        String lstr = str.toLowerCase();
        for (String element : WEEKDAY_NAMES) {
            if (lstr.startsWith(element)
                    || element.toLowerCase().startsWith(lstr)) {
                return true; 
            } 
        } 
 
        return false; 
    } 
 
    /** 
     * Load list of time zones if sun.util.calendar.ZoneInfo exists. 
     *  
     * @return <tt>null</tt> if time zone list cannot be loaded. 
     */ 
    private static final String[] loadTimeZoneNames() {
        Class<?> zoneInfo;
        try { 
            zoneInfo = Class.forName("sun.util.calendar.ZoneInfo");
        } catch (ClassNotFoundException cnfe) {
            return null; 
        } 
 
        Method method;
        try { 
            method = zoneInfo.getDeclaredMethod("getAvailableIDs", new Class[0]);
        } catch (NoSuchMethodException nsme) {
            return null; 
        } 
 
        Object result;
        try { 
            result = method.invoke((Object) null);
        } catch (IllegalAccessException iae) {
            return null; 
        } catch (InvocationTargetException ite) {
            return null; 
        } 
 
        String[] tmpList = (String[]) result;
 
        int numSaved = 0;
        String[] finalList = null;
 
        for (int i = 0; i < 2; i++) {
            if (i > 0) {
                if (numSaved == 0) {
                    return null; 
                } 
 
                finalList = new String[numSaved];
                numSaved = 0;
            } 
 
            for (int j = 0; j < tmpList.length; j++) {
                final int len = tmpList[j].length();
                if ((len > 2 && Character.isUpperCase(tmpList[j].charAt(1)))
                        && (len != 7 || !Character.isDigit(tmpList[j].charAt(3)))) {
                    if (finalList == null) {
                        numSaved++;
                    } else { 
                        finalList[numSaved++] = tmpList[j];
                    } 
 
                    if (len == 3 && tmpList[j].charAt(1) == 'S'
                            && tmpList[j].charAt(2) == 'T') {
                        if (finalList == null) {
                            numSaved++;
                        } else { 
                            StringBuffer dst = new StringBuffer();
                            dst.append(tmpList[j].charAt(0));
                            dst.append("DT");
                            finalList[numSaved++] = dst.toString();
                        } 
                    } 
                } 
            } 
        } 
 
        return finalList;
    } 
 
    /** 
     * Convert the supplied month name to its numeric representation. <br> 
     * For example, <tt>"January"</tt> (or any substring) would return 
     * <tt>1</tt> and <tt>"December"</tt> would return <tt>12</tt>. 
     *  
     * @param str 
     *            month name 
     *  
     * @return the numeric month, or <tt>CalendarParser.UNSET</tt> if the 
     *         supplied string is not a valid month name. 
     */ 
    public static int monthNameToNumber(String str) {
        if (str != null && str.length() >= 3) {
            String lstr = str.toLowerCase();
            for (int i = 0; i < MONTHS.length; i++) {
                if (lstr.startsWith(MONTHS[i][0])
                        || MONTHS[i][1].toLowerCase().startsWith(lstr)) {
                    return i + 1;
                } 
            } 
        } 
 
        return UNSET;
    } 
 
    /** 
     * Extract a date from a string, defaulting to YY-MM-DD order for 
     * all-numeric strings. 
     *  
     * @param dateStr 
     *            date string 
     *  
     * @return parsed date 
     *  
     * @throws CalendarParserException 
     *             if there was a problem parsing the string. 
     */ 
    public static final Calendar parse(String dateStr)
            throws CalendarParserException { 
        return parse(dateStr, YY_MM_DD);
    } 
 
    /** 
     * Extract a date from a string. 
     *  
     * @param dateStr 
     *            date string 
     * @param order 
     *            order in which pieces of numeric strings are assigned (should 
     *            be one of <tt>YY_MM_DD</tt>, <tt>MM_DD_YY</tt>, etc.) 
     *  
     * @return parsed date 
     *  
     * @throws CalendarParserException 
     *             if there was a problem parsing the string. 
     */ 
    public static final Calendar parse(String dateStr, int order)
            throws CalendarParserException { 
        return parse(dateStr, order, true);
    } 
 
    /** 
     * Extract a date from a string. 
     *  
     * @param dateStr 
     *            date string 
     * @param order 
     *            order in which pieces of numeric strings are assigned (should 
     *            be one of <tt>YY_MM_DD</tt>, <tt>MM_DD_YY</tt>, etc.) 
     * @param ignoreChanges 
     *            if <tt>true</tt>, ignore date changes such as <tt>Feb 31</tt> 
     *            being changed to <tt>Mar 3</tt>. 
     *  
     * @return parsed date 
     *  
     * @throws CalendarParserException 
     *             if there was a problem parsing the string. 
     */ 
    public static final Calendar parse(String dateStr, int order,
            boolean ignoreChanges) throws CalendarParserException {
        if (dateStr == null) {
            return null; 
        } 
 
        return parseString(dateStr, order, ignoreChanges);
    } 
 
    /** 
     * Parse a non-numeric token from the date string. 
     *  
     * @param dateStr 
     *            full date string 
     * @param state 
     *            parser state 
     * @param token 
     *            string being parsed 
     *  
     * @throws CalendarParserException 
     *             if there was a problem parsing the token 
     */ 
    private static final void parseNonNumericToken(String dateStr,
            ParserState state, String token) throws CalendarParserException {
        // if it's a weekday name, ignore it 
        if (isWeekdayName(token)) {
            if (DEBUG) {
                System.err.println("IGNORE \"" + token + "\" (weekday)");
            } 
            return; 
        } 
 
        // if it looks like a time, deal with it 
        if (token.indexOf(':') > 0) {
            final char firstChar = token.charAt(0);
            if (Character.isDigit(firstChar)) {
                parseTime(dateStr, state, token);
                return; 
            } else if (firstChar == '+' || firstChar == '-') {
                parseTimeZoneOffset(dateStr, state, token);
                return; 
            } else { 
                throw new CalendarParserException("Unrecognized time \"" 
                        + token + "\" in date \"" + dateStr + "\"");
            } 
        } 
 
        // try to parse month name 
        int tmpMon = monthNameToNumber(token);
 
        // if token isn't a month name ... PUKE 
        if (tmpMon != UNSET) {
 
            // if month number is unset, set it and move on 
            if (!state.isMonthSet()) {
                state.setMonth(tmpMon);
                if (DEBUG) {
                    System.err.println("MONTH="
                            + MONTHS[state.getMonth() - 1][0] + " (" + token
                            + ") name"); 
                } 
                return; 
            } 
 
            // try to move the current month value to the year or day 
            if (!state.isYearSet()) {
                if (state.isDateSet() || state.isYearBeforeDay()) {
                    state.setYear(state.getMonth());
                    state.setMonth(tmpMon);
                    if (DEBUG) {
                        System.err.println("MONTH="
                                + MONTHS[state.getMonth() - 1][0] + ", YEAR="
                                + state.getYear() + " (" + token
                                + ") name swap"); 
                    } 
                } else { 
                    state.setDate(state.getMonth());
                    state.setMonth(tmpMon);
                    if (DEBUG) {
                        System.err.println("MONTH="
                                + MONTHS[state.getMonth() - 1][0] + ", DAY="
                                + state.getDate() + " (" + token
                                + ") name swap"); 
                    } 
                } 
 
                return; 
            } 
 
            // year was already set, so try to move month value to day 
            if (!state.isDateSet()) {
                state.setDate(state.getMonth());
                state.setMonth(tmpMon);
                if (DEBUG) {
                    System.err.println("MONTH="
                            + MONTHS[state.getMonth() - 1][0] + ", DAY="
                            + state.getDate() + " (" + token + ") name swap 2");
                } 
 
                return; 
            } 
 
            // can't move month value to year or day ... PUKE 
            if (DEBUG) {
                System.err.println("*** Too many numbers in \"" + dateStr
                        + "\""); 
            } 
            throw new CalendarParserException("Too many numbers in" 
                    + " date \"" + dateStr + "\"");
        } 
 
        // maybe it's an ordinal number list "1st", "23rd", etc. 
        int val = getOrdinalNumber(token);
        if (val == UNSET) {
            final String lToken = token.toLowerCase();
 
            if (lToken.equals("am")) {
                // don't need to do anything 
                if (DEBUG) {
                    System.err.println("TIME=AM (" + token + ")");
                } 
                return; 
            } else if (lToken.equals("pm")) {
                if (!state.isHourSet()) {
                    state.setTimePostMeridian(true);
                } else { 
                    state.setHour(state.getHour() + 12);
                } 
 
                if (DEBUG) {
                    System.err.println("TIME=PM (" + token + ")");
                } 
                return; 
            } else if (zoneNames != null) {
                // maybe it's a time zone name 
                for (String zoneName : zoneNames) {
                    if (token.equalsIgnoreCase(zoneName)) {
                        TimeZone tz = TimeZone.getTimeZone(token);
                        if (tz.getRawOffset() != 0 || lToken.equals("gmt")) {
                            state.setTimeZone(tz);
                            return; 
                        } 
                    } 
                } 
            } 
 
            if (DEBUG) {
                System.err.println("*** Unknown string \"" + token + "\"");
            } 
            throw new CalendarParserException("Unknown string \"" + token
                    + "\" in date \"" + dateStr + "\"");
        } 
 
        // if no day yet, we're done 
        if (!state.isDateSet()) {
            state.setDate(val);
            if (DEBUG) {
                System.err.println("DAY=" + state.getDate() + " (" + token
                        + ") ord"); 
            } 
            return; 
        } 
 
        // if either year or month is unset... 
        if (!state.isYearSet() || !state.isMonthSet()) {
 
            // if day can't be a month, shift it into year 
            if (state.getDate() > 12) {
                if (!state.isYearSet()) {
                    state.setYear(state.getDate());
                    state.setDate(val);
                    if (DEBUG) {
                        System.err.println("YEAR=" + state.getYear() + ", DAY="
                                + state.getDate() + " (" + token
                                + ") ord>12 swap"); 
                    } 
                    return; 
                } 
 
                // year was already set, maybe we can move it to month 
                if (state.getYear() <= 12) {
                    state.setMonth(state.getYear());
                    state.setYear(state.getDate());
                    state.setDate(val);
 
                    if (DEBUG) {
                        System.err.println("YEAR=" + state.getYear()
                                + ", MONTH=" + state.getMonth() + ", DAY="
                                + state.getDate() + " (" + token
                                + ") ord megaswap"); 
                    } 
 
                    return; 
                } 
 
                // try to shift day value to either year or month 
            } else if (!state.isYearSet()) {
                if (!state.isMonthSet() && !state.isYearBeforeMonth()) {
                    state.setMonth(state.getDate());
                    state.setDate(val);
                    if (DEBUG) {
                        System.err.println("MONTH=" + state.getMonth()
                                + ", DAY=" + state.getDate() + " (" + token
                                + ") ord swap"); 
                    } 
                    return; 
                } 
 
                state.setYear(state.getDate());
                state.setDate(val);
                if (DEBUG) {
                    System.err.println("YEAR=" + state.getYear() + ", DAY="
                            + state.getDate() + " (" + token + ") ord swap");
                } 
                return; 
 
                // year was set, so we know month is unset 
            } else { 
 
                state.setMonth(state.getDate());
                state.setDate(val);
                if (DEBUG) {
                    System.err.println("MONTH=" + state.getMonth() + ", DAY="
                            + state.getDate() + " (" + token + ") ord swap#2");
                } 
                return; 
            } 
        } 
 
        if (DEBUG) {
            System.err.println("*** Extra number \"" + token + "\"");
        } 
        throw new CalendarParserException("Cannot assign ordinal in \"" 
                + dateStr + "\"");
    } 
 
    /** 
     * Split a large numeric value into a year/month/date values. 
     *  
     * @param dateStr 
     *            full date string 
     * @param state 
     *            parser state 
     * @param val 
     *            numeric value to use 
     *  
     * @throws CalendarParserException 
     *             if there was a problem splitting the value 
     */ 
    private static final void parseNumericBlob(String dateStr,
            ParserState state, int val) throws CalendarParserException {
        if (state.isYearSet() || state.isMonthSet() || state.isDateSet()) {
            throw new CalendarParserException("Unknown value " + val
                    + " in date \"" + dateStr + "\"");
        } 
 
        int tmpVal = val;
        if (state.isYearBeforeMonth()) {
            if (state.isYearBeforeDay()) {
                final int last = tmpVal % 100;
                tmpVal /= 100;
 
                final int middle = tmpVal % 100;
                tmpVal /= 100;
 
                state.setYear(tmpVal);
                if (state.isMonthBeforeDay()) {
                    // YYYYMMDD 
                    state.setMonth(middle);
                    state.setDate(last);
                } else { 
                    // YYYYDDMM 
                    state.setDate(middle);
                    state.setMonth(last);
                } 
            } else { 
                // DDYYYYMM 
                state.setMonth(tmpVal % 100);
                tmpVal /= 100;
 
                state.setYear(tmpVal % 10000);
                tmpVal /= 10000;
 
                state.setDate(tmpVal);
            } 
        } else if (state.isYearBeforeDay()) {
            // MMYYYYDD 
            state.setDate(tmpVal % 100);
            tmpVal /= 100;
 
            state.setYear(tmpVal % 10000);
            tmpVal /= 10000;
 
            state.setMonth(tmpVal);
        } else { 
            state.setYear(tmpVal % 10000);
            tmpVal /= 10000;
 
            final int middle = tmpVal % 100;
            tmpVal /= 100;
            if (state.isMonthBeforeDay()) {
                // MMDDYYYY 
                state.setDate(middle);
                state.setMonth(tmpVal);
            } else { 
                // DDMMYYYY 
                state.setDate(tmpVal);
                state.setMonth(middle);
            } 
        } 
 
        if (DEBUG) {
            System.err.println("YEAR=" + state.getYear() + " MONTH="
                    + state.getMonth() + " DAY=" + state.getDate() + " (" + val
                    + ") blob"); 
        } 
    } 
 
    /** 
     * Use a numeric token from the date string. 
     *  
     * @param dateStr 
     *            full date string 
     * @param state 
     *            parser state 
     * @param val 
     *            numeric value to use 
     *  
     * @throws CalendarParserException 
     *             if there was a problem parsing the token 
     */ 
    private static final void parseNumericToken(String dateStr,
            ParserState state, int val) throws CalendarParserException {
        // puke if we've already found 3 values 
        if (state.isYearSet() && state.isMonthSet() && state.isDateSet()) {
            if (DEBUG) {
                System.err.println("*** Extra number " + val);
            } 
            throw new CalendarParserException("Extra value \"" + val
                    + "\" in date \"" + dateStr + "\"");
        } 
 
        // puke up on negative numbers 
        if (val < 0) {
            if (DEBUG) {
                System.err.println("*** Negative number " + val);
            } 
            throw new CalendarParserException("Found negative number in" 
                    + " date \"" + dateStr + "\"");
        } 
 
        if (val > 9999) {
            parseNumericBlob(dateStr, state, val);
            return; 
        } 
 
        // deal with obvious years first 
        if (val > 31) {
 
            // if no year yet, assign it and move on 
            if (!state.isYearSet()) {
                state.setYear(val);
                if (DEBUG) {
                    System.err.println("YEAR=" + state.getYear() + " (" + val
                            + ") >31"); 
                } 
                return; 
            } 
 
            // puke if the year value can't possibly be a day or month 
            if (state.getYear() > 31) {
                if (DEBUG) {
                    System.err.println("*** Ambiguous year " + state.getYear()
                            + " vs. " + val);
                } 
                String errMsg = "Couldn't decide on year number in date \""
                        + dateStr + "\"";
                throw new CalendarParserException(errMsg);
            } 
 
            // if the year value can't be a month... 
            if (state.getYear() > 12) {
 
                // if day isn't set, use old val as day and new val as year 
                if (!state.isDateSet()) {
                    state.setDate(state.getYear());
                    state.setYear(val);
 
                    if (DEBUG) {
                        System.err.println("YEAR=" + state.getYear() + ", DAY="
                                + state.getDate() + " (" + val + ") >31 swap");
                    } 
 
                    return; 
                } 
 
                // NOTE: both day and year are set 
 
                // try using day value as month so we can move year 
                // value to day and use new value as year 
                if (state.getDate() <= 12) {
                    state.setMonth(state.getDate());
                    state.setDate(state.getYear());
                    state.setYear(val);
 
                    if (DEBUG) {
                        System.err.println("YEAR=" + state.getYear()
                                + ", MONTH=" + state.getMonth() + ", DAY="
                                + state.getDate() + " (" + val
                                + ") >31 megaswap"); 
                    } 
 
                    return; 
                } 
 
                if (DEBUG) {
                    System.err.println("*** Unassignable year-like"
                            + " number " + val);
                } 
                throw new CalendarParserException("Bad number " + val
                        + " found in date \"" + dateStr + "\"");
            } 
 
            // NOTE: year <= 12 
 
            if (!state.isDateSet() && !state.isMonthSet()) {
                if (state.isMonthBeforeDay()) {
                    state.setMonth(state.getYear());
                    state.setYear(val);
                    if (DEBUG) {
                        System.err.println("YEAR=" + state.getYear()
                                + ", MONTH=" + state.getMonth() + " (" + val
                                + ") >31 swap"); 
                    } 
                } else { 
                    state.setDate(state.getYear());
                    state.setYear(val);
                    if (DEBUG) {
                        System.err
                                .println("YEAR=" + state.getYear() + ", DAY="
                                        + state.getDate() + " (" + val
                                        + ") >31 swap#2"); 
                    } 
                } 
 
                return; 
            } 
 
            if (!state.isDateSet()) {
                state.setDate(state.getYear());
                state.setYear(val);
                if (DEBUG) {
                    System.err.println("YEAR=" + state.getYear() + ", DAY="
                            + state.getDate() + " (" + val + ") >31 day swap");
                } 
                return; 
            } 
 
            // assume this was a mishandled month 
            state.setMonth(state.getYear());
            state.setYear(val);
 
            if (DEBUG) {
                System.err.println("YEAR=" + state.getYear() + ", MONTH="
                        + state.getMonth() + " (" + val + ") >31 mon swap");
            } 
 
            return; 
        } 
 
        // now deal with non-month values 
        if (val > 12) {
 
            // if no year value yet... 
            if (!state.isYearSet()) {
 
                // if the day is set, or if we assign year before day... 
                if (state.isDateSet() || state.isYearBeforeDay()) {
                    state.setYear(val);
                    if (DEBUG) {
                        System.err.println("YEAR=" + state.getYear() + " ("
                                + val + ") >12");
                    } 
                } else { 
                    state.setDate(val);
                    if (DEBUG) {
                        System.err.println("DAY=" + state.getDate() + " ("
                                + val + ") >12");
                    } 
                } 
 
                return; 
            } 
 
            // NOTE: year is set 
 
            // if no day value yet, assign it and move on 
            if (!state.isDateSet()) {
                state.setDate(val);
 
                if (DEBUG) {
                    System.err.println("DAY=" + state.getDate() + " (" + val
                            + ") >12 !yr"); 
                } 
 
                return; 
            } 
 
            // NOTE: both year and day are set 
 
            // XXX see if we can shift things around 
 
            if (DEBUG) {
                System.err.println("*** Unassignable year/day number " + val);
            } 
            throw new CalendarParserException("Bad number " + val
                    + " found in date \"" + dateStr + "\"");
        } 
 
        // NOTE: ambiguous value 
 
        // if year is set, this must be either the month or day 
        if (state.isYearSet()) {
            if (state.isMonthSet()
                    || (!state.isDateSet() && !state.isMonthBeforeDay())) {
                state.setDate(val);
                if (DEBUG) {
                    System.err.println("DAY=" + state.getDate() + " (" + val
                            + ") ambig!yr"); 
                } 
            } else { 
                state.setMonth(val);
                if (DEBUG) {
                    System.err.println("MONTH=" + state.getMonth() + " (" + val
                            + ") ambig!yr"); 
                } 
            } 
 
            return; 
        } 
 
        // NOTE: year not set 
 
        // if month is set, this must be either the year or day 
        if (state.isMonthSet()) {
            if (state.isDateSet() || state.isYearBeforeDay()) {
                state.setYear(val);
                if (DEBUG) {
                    System.err.println("YEAR=" + state.getYear() + " (" + val
                            + ") ambig!mo"); 
                } 
            } else { 
                state.setDate(val);
                if (DEBUG) {
                    System.err.println("DAY=" + state.getDate() + " (" + val
                            + ") ambig!mo"); 
                } 
            } 
 
            return; 
        } 
 
        // NOTE: neither year nor month is set 
 
        // if day is set, this must be either the year or month 
        if (state.isDateSet()) {
            if (state.isYearBeforeMonth()) {
                state.setYear(val);
                if (DEBUG) {
                    System.err.println("YEAR=" + state.getYear() + " (" + val
                            + ") ambig!day"); 
                } 
            } else { 
                state.setMonth(val);
                if (DEBUG) {
                    System.err.println("MONTH=" + state.getMonth() + " (" + val
                            + ") ambig!day"); 
                } 
            } 
 
            return; 
        } 
 
        // NOTE: no value set yet 
        if (state.isYearBeforeMonth()) {
            if (state.isYearBeforeDay()) {
                state.setYear(val);
                if (DEBUG) {
                    System.err.println("YEAR=" + state.getYear() + " (" + val
                            + ") YM|YD"); 
                } 
            } else { 
                state.setDate(val);
                if (DEBUG) {
                    System.err.println("DAY=" + state.getDate() + " (" + val
                            + ") YM!YD"); 
                } 
            } 
        } else if (state.isMonthBeforeDay()) {
            state.setMonth(val);
            if (DEBUG) {
                System.err.println("MONTH=" + state.getMonth() + " (" + val
                        + ") !YM|MD"); 
            } 
        } else { 
            state.setDate(val);
            if (DEBUG) {
                System.err.println("DAY=" + state.getDate() + " (" + val
                        + ") !YM!MD"); 
            } 
        } 
    } 
 
    /** 
     * Extract a date from the supplied string. 
     *  
     * @param dateStr 
     *            string to parse 
     * @param order 
     *            year/month/day order (YY_MM_DD, MM_DD_YY, etc.) 
     * @param ignoreChanges 
     *            if <tt>true</tt>, ignore date changes such as <tt>Feb 31</tt> 
     *            being changed to <tt>Mar 3</tt>. 
     *  
     * @return parsed date 
     *  
     * @throws CalendarParserException 
     *             if no valid date was found. 
     */ 
    private static final Calendar parseString(String dateStr, int order,
            boolean ignoreChanges) throws CalendarParserException {
        ParserState state = new ParserState(order);
 
        Pattern pat = Pattern.compile("([\\s/,]+|(\\S)\\-)");
        Matcher matcher = pat.matcher(dateStr);
 
        int prevEnd = 0;
        while (prevEnd < dateStr.length()) {
            String token;
            if (!matcher.find()) {
                token = dateStr.substring(prevEnd);
                prevEnd = dateStr.length();
            } else { 
                final boolean isMinus = (matcher.groupCount() == 2 && matcher
                        .group(2) != null);
 
                if (!isMinus) {
                    token = dateStr.substring(prevEnd, matcher.start());
                } else { 
                    token = dateStr.substring(prevEnd, matcher.start())
                            + matcher.group(2);
                } 
 
                prevEnd = matcher.end();
            } 
 
            if (DEBUG) {
                System.err.println("YEAR "
                        + (state.isYearSet() ? Integer
                                .toString(state.getYear()) : "UNSET")
                        + ", MONTH " 
                        + (state.isMonthSet() ? Integer.toString(state
                                .getMonth()) : "UNSET") 
                        + ", DAY " 
                        + (state.isDateSet() ? Integer
                                .toString(state.getDate()) : "UNSET")
                        + ", TOKEN=\"" + token + "\"");
            } 
 
            // try to decipher next token as a number 
            try { 
                final int val = Integer.parseInt(token);
                parseNumericToken(dateStr, state, val);
            } catch (NumberFormatException e) {
                parseNonNumericToken(dateStr, state, token);
            } 
        } 
 
        // before checking for errors, check for missing year 
        if (!state.isDateSet() && state.getYear() <= 31) {
            int tmp = state.getDate();
            state.setDate(state.getYear());
            state.setYear(tmp);
        } 
 
        if (!state.isDateSet()) {
            if (!state.isMonthSet()) {
                if (!state.isYearSet()) {
                    throw new CalendarParserException("No date found in \"" 
                            + dateStr + "\"");
                } else { 
                    throw new CalendarParserException("Day and month missing" 
                            + " from \"" + dateStr + "\"");
                } 
            } else { 
                throw new CalendarParserException("Day missing from \"" 
                        + dateStr + "\"");
            } 
        } else if (!state.isMonthSet()) {
            if (!state.isYearSet()) {
                throw new CalendarParserException("Year and month missing" 
                        + " from \"" + dateStr + "\"");
            } else { 
                throw new CalendarParserException("Month missing from \"" 
                        + dateStr + "\"");
            } 
        } else if (!state.isYearSet()) {
            throw new CalendarParserException("Year missing from \"" + dateStr
                    + "\""); 
        } 
 
        final int tmpYear = state.getYear();
        if (tmpYear < 50) {
            state.setYear(tmpYear + CENTURY_OFFSET);
        } else if (tmpYear < 100) {
            state.setYear(tmpYear + (CENTURY_OFFSET - 100));
        } 
 
        GregorianCalendar cal = new GregorianCalendar();
 
        state.setCalendar(cal, ignoreChanges);
 
        if (DEBUG) {
            System.err.println("Y" + state.getYear() + " M" + state.getMonth()
                    + " D" + state.getDate() + " H" + state.getHour() + " M"
                    + state.getMinute() + " S" + state.getSecond() + " L"
                    + state.getMillisecond() + " => " + toString(cal));
        } 
 
        return cal;
    } 
 
    /** 
     * Parse a time string. 
     *  
     * @param dateStr 
     *            full date string 
     * @param state 
     *            parser state 
     * @param timeStr 
     *            string containing colon-separated time 
     *  
     * @throws CalendarParserException 
     *             if there is a problem with the time 
     */ 
    private static final void parseTime(String dateStr, ParserState state,
            String timeStr) throws CalendarParserException {
        int place = PLACE_HOUR;
 
        String tmpTime;
 
        final char lastChar = timeStr.charAt(timeStr.length() - 1);
        if (lastChar != 'm' && lastChar != 'M') {
            if (DEBUG) {
                System.err.println("No AM/PM in \"" + timeStr + "\" (time)");
            } 
            tmpTime = timeStr;
        } else { 
            final char preLast = timeStr.charAt(timeStr.length() - 2);
            if (preLast == 'a' || preLast == 'A') {
                state.setTimePostMeridian(false);
            } else if (preLast == 'p' || preLast == 'P') {
                state.setTimePostMeridian(true);
            } else { 
                throw new CalendarParserException("Bad time \"" + timeStr
                        + "\" in date \"" + dateStr + "\"");
            } 
 
            tmpTime = timeStr.substring(0, timeStr.length() - 2);
            if (DEBUG) {
                System.err.println("Found "
                        + (state.isTimePostMeridian() ? "PM" : "AM")
                        + ". now \"" + tmpTime + "\" (time)");
            } 
        } 
 
        String[] tList = tmpTime.split("[:\\.]");
        for (String token : tList) {
            if (DEBUG) {
                System.err.println("HOUR "
                        + (state.isHourSet() ? Integer
                                .toString(state.getHour()) : "UNSET")
                        + ", MINUTE " 
                        + (state.isMinuteSet() ? Integer.toString(state
                                .getMinute()) : "UNSET") 
                        + ", SECOND " 
                        + (state.isSecondSet() ? Integer.toString(state
                                .getSecond()) : "UNSET") 
                        + ", MILLISECOND " 
                        + (state.isMillisecondSet() ? Integer.toString(state
                                .getMillisecond()) : "UNSET") + ", TOKEN=\"" 
                        + token + "\"");
            } 
 
            final int val;
            try { 
                val = Integer.parseInt(token);
            } catch (NumberFormatException nfe) {
                throw new CalendarParserException("Bad " 
                        + getTimePlaceString(place) + " string \"" + token
                        + "\" in \"" + dateStr + "\"");
            } 
 
            switch (place) {
            case PLACE_HOUR:
                try { 
                    state.setHour(val);
                } catch (CalendarParserException dfe) {
                    throw new CalendarParserException(dfe.getMessage()
                            + " in \"" + dateStr + "\"");
                } 
                if (DEBUG) {
                    System.err.println("Set hour to " + val);
                } 
                place = PLACE_MINUTE;
                break; 
            case PLACE_MINUTE:
                try { 
                    state.setMinute(val);
                } catch (CalendarParserException dfe) {
                    throw new CalendarParserException(dfe.getMessage()
                            + " in \"" + dateStr + "\"");
                } 
                if (DEBUG) {
                    System.err.println("Set minute to " + val);
                } 
                place = PLACE_SECOND;
                break; 
            case PLACE_SECOND:
                try { 
                    state.setSecond(val);
                } catch (CalendarParserException dfe) {
                    throw new CalendarParserException(dfe.getMessage()
                            + " in \"" + dateStr + "\"");
                } 
                if (DEBUG) {
                    System.err.println("Set second to " + val);
                } 
                place = PLACE_MILLI;
                break; 
            case PLACE_MILLI:
                try { 
                    state.setMillisecond(val);
                } catch (CalendarParserException dfe) {
                    throw new CalendarParserException(dfe.getMessage()
                            + " in \"" + dateStr + "\"");
                } 
                if (DEBUG) {
                    System.err.println("Set millisecond to " + val);
                } 
                place = PLACE_UNKNOWN;
                break; 
            default: 
                throw new CalendarParserException("Unexpected place value " 
                        + place);
            } 
        } 
    } 
 
    /** 
     * Parse a time zone offset string. 
     *  
     * @param dateStr 
     *            full date string 
     * @param state 
     *            parser state 
     * @param zoneStr 
     *            string containing colon-separated time zone offset 
     *  
     * @throws CalendarParserException 
     *             if there is a problem with the time 
     */ 
    private static final void parseTimeZoneOffset(String dateStr,
            ParserState state, String zoneStr) throws CalendarParserException {
        int place = PLACE_HOUR;
 
        final boolean isNegative = (zoneStr.charAt(0) == '-');
        if (!isNegative && zoneStr.charAt(0) != '+') {
            throw new CalendarParserException("Bad time zone offset \"" 
                    + zoneStr + "\" in date \"" + dateStr + "\"");
        } 
 
        int hour = UNSET;
        int minute = UNSET;
 
        String[] tList = zoneStr.substring(1).split(":");
        for (String token : tList) {
            if (DEBUG) {
                System.err
                        .println("TZ_HOUR "
                                + (hour != UNSET ? Integer.toString(hour)
                                        : "UNSET") 
                                + ", TZ_MINUTE " 
                                + (minute != UNSET ? Integer.toString(minute)
                                        : "UNSET") + ", TOKEN=\"" + token
                                + "\""); 
            } 
 
            final int val;
            try { 
                val = Integer.parseInt(token);
            } catch (NumberFormatException nfe) {
                throw new CalendarParserException("Bad time zone " 
                        + getTimePlaceString(place) + " offset \"" + token
                        + "\" in \"" + dateStr + "\"");
            } 
 
            switch (place) {
            case PLACE_HOUR:
                hour = val;
                if (DEBUG) {
                    System.err.println("Set time zone offset hour to " + val);
                } 
                place = PLACE_MINUTE;
                break; 
            case PLACE_MINUTE:
                minute = val;
                if (DEBUG) {
                    System.err.println("Set time zone offset minute to " + val);
                } 
                place = PLACE_UNKNOWN;
                break; 
            default: 
                throw new CalendarParserException("Unexpected place value " 
                        + place);
            } 
        } 
 
        String customID = "GMT" + (isNegative ? "-" : "+") + hour + ":"
                + (minute < 10 ? "0" : "") + minute;
 
        state.setTimeZone(TimeZone.getTimeZone(customID));
    } 
 
    /** 
     * Return a printable representation of the date. 
     *  
     * @param cal 
     *            calendar to convert to a string 
     *  
     * @return a printable string. 
     */ 
    public static final String prettyString(Calendar cal) {
        if (cal == null) {
            return null; 
        } 
 
        final int calYear = cal.get(Calendar.YEAR);
        final int calMonth = cal.get(Calendar.MONTH);
        final int calDay = cal.get(Calendar.DATE);
 
        boolean needSpace = false;
        StringBuffer buf = new StringBuffer();
 
        if (calMonth >= 0 && calMonth < MONTHS.length) {