12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others.
22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Copyright (C) 1996-2010, International Business Machines Corporation and    *
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * others. All Rights Reserved.                                                *
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.dev.test.calendar;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Date;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashMap;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.dev.test.TestFmwk;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.DateFormat;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.SimpleDateFormat;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Calendar;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ChineseCalendar;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.GregorianCalendar;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.SimpleTimeZone;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A base class for classes that test individual Calendar subclasses.
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Defines various useful utility methods and constants
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
292d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertpublic class CalendarTestFmwk extends TestFmwk {
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Constants for use by subclasses, solely to save typing
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int SUN = Calendar.SUNDAY;
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int MON = Calendar.MONDAY;
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int TUE = Calendar.TUESDAY;
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int WED = Calendar.WEDNESDAY;
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int THU = Calendar.THURSDAY;
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int FRI = Calendar.FRIDAY;
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int SAT = Calendar.SATURDAY;
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int ERA     = Calendar.ERA;
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int YEAR    = Calendar.YEAR;
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int MONTH   = Calendar.MONTH;
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int DATE    = Calendar.DATE;
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int HOUR    = Calendar.HOUR;
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int MINUTE  = Calendar.MINUTE;
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int SECOND  = Calendar.SECOND;
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int DOY     = Calendar.DAY_OF_YEAR;
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int WOY     = Calendar.WEEK_OF_YEAR;
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int WOM     = Calendar.WEEK_OF_MONTH;
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int DOW     = Calendar.DAY_OF_WEEK;
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static int DOWM    = Calendar.DAY_OF_WEEK_IN_MONTH;
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final static SimpleTimeZone UTC = new SimpleTimeZone(0, "GMT");
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String[] FIELD_NAME = {
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "ERA", "YEAR", "MONTH", "WEEK_OF_YEAR", "WEEK_OF_MONTH",
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "DAY_OF_MONTH", "DAY_OF_YEAR", "DAY_OF_WEEK",
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "DAY_OF_WEEK_IN_MONTH", "AM_PM", "HOUR", "HOUR_OF_DAY",
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "MINUTE", "SECOND", "MILLISECOND", "ZONE_OFFSET",
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "DST_OFFSET", "YEAR_WOY", "DOW_LOCAL", "EXTENDED_YEAR",
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "JULIAN_DAY", "MILLISECONDS_IN_DAY",
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "IS_LEAP_MONTH" // (ChineseCalendar only)
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    };
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final String fieldName(int f) {
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return (f>=0 && f<FIELD_NAME.length) ?
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            FIELD_NAME[f] : ("<Field " + f + ">");
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Iterates through a list of calendar <code>TestCase</code> objects and
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * makes sure that the time-to-fields and fields-to-time calculations work
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * correnctly for the values in each test case.
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
752d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    protected void doTestCases(TestCase[] cases, Calendar cal)
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cal.setTimeZone(UTC);
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Get a format to use for printing dates in the calendar system we're testing
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateFormat format = DateFormat.getDateTimeInstance(cal, DateFormat.SHORT, -1, Locale.getDefault());
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final String pattern = (cal instanceof ChineseCalendar) ?
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            "E MMl/dd/y G HH:mm:ss.S z" :
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            "E, MM/dd/yyyy G HH:mm:ss.S z";
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ((SimpleDateFormat)format).applyPattern(pattern);
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // This format is used for printing Gregorian dates.
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateFormat gregFormat = new SimpleDateFormat(pattern);
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        gregFormat.setTimeZone(UTC);
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        GregorianCalendar pureGreg = new GregorianCalendar(UTC);
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pureGreg.setGregorianChange(new Date(Long.MIN_VALUE));
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateFormat pureGregFmt = new SimpleDateFormat("E M/d/yyyy G");
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pureGregFmt.setCalendar(pureGreg);
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Now iterate through the test cases and see what happens
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < cases.length; i++)
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            logln("\ntest case: " + i);
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TestCase test = cases[i];
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // First we want to make sure that the millis -> fields calculation works
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // test.applyTime will call setTime() on the calendar object, and
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // test.fieldsEqual will retrieve all of the field values and make sure
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // that they're the same as the ones in the testcase
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            test.applyTime(cal);
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!test.fieldsEqual(cal, this)) {
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errln("Fail: (millis=>fields) " +
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                      gregFormat.format(test.getTime()) + " => " +
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                      format.format(cal.getTime()) +
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                      ", expected " + test);
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // If that was OK, check the fields -> millis calculation
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // test.applyFields will set all of the calendar's fields to
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // match those in the test case.
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            cal.clear();
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            test.applyFields(cal);
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!test.equals(cal)) {
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errln("Fail: (fields=>millis) " + test + " => " +
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                      pureGregFmt.format(cal.getTime()) +
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                      ", expected " + pureGregFmt.format(test.getTime()));
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static public final boolean ROLL = true;
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static public final boolean ADD = false;
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Process test cases for <code>add</code> and <code>roll</code> methods.
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Each test case is an array of integers, as follows:
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <ul>
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  <li>0: input year
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  <li>1:       month  (zero-based)
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  <li>2:       day
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  <li>3: field to roll or add to
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  <li>4: amount to roll or add
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  <li>5: result year
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  <li>6:        month (zero-based)
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  <li>7:        day
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * </ul>
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example:
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <pre>
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   //       input                add by          output
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   //  year  month     day     field amount    year  month     day
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   {   5759, HESHVAN,   2,     MONTH,   1,     5759, KISLEV,    2 },
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * </pre>
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param roll  <code>true</code> or <code>ROLL</code> to test the <code>roll</code> method;
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *              <code>false</code> or <code>ADD</code> to test the <code>add</code method
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1582d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    protected void doRollAdd(boolean roll, Calendar cal, int[][] tests)
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String name = roll ? "rolling" : "adding";
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < tests.length; i++) {
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int[] test = tests[i];
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            cal.clear();
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (cal instanceof ChineseCalendar) {
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cal.set(Calendar.EXTENDED_YEAR, test[0]);
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cal.set(Calendar.MONTH, test[1]);
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cal.set(Calendar.DAY_OF_MONTH, test[2]);
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cal.set(test[0], test[1], test[2]);
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            double day0 = getJulianDay(cal);
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (roll) {
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cal.roll(test[3], test[4]);
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cal.add(test[3], test[4]);
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int y = cal.get(cal instanceof ChineseCalendar ?
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            Calendar.EXTENDED_YEAR : YEAR);
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (y != test[5] || cal.get(MONTH) != test[6]
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    || cal.get(DATE) != test[7])
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            {
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errln("Fail: " + name + " "+ ymdToString(test[0], test[1], test[2])
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    + " (" + day0 + ")"
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    + " " + FIELD_NAME[test[3]] + " by " + test[4]
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    + ": expected " + ymdToString(test[5], test[6], test[7])
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    + ", got " + ymdToString(cal));
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (isVerbose()) {
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                logln("OK: " + name + " "+ ymdToString(test[0], test[1], test[2])
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    + " (" + day0 + ")"
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    + " " + FIELD_NAME[test[3]] + " by " + test[4]
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    + ": got " + ymdToString(cal));
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Test the functions getXxxMinimum() and getXxxMaximum() by marching a
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * test calendar 'cal' through 'numberOfDays' sequential days starting
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * with 'startDate'.  For each date, read a field value along with its
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * reported actual minimum and actual maximum.  These values are
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * checked against one another as well as against getMinimum(),
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * getGreatestMinimum(), getLeastMaximum(), and getMaximum().  We
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * expect to see:
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. minimum <= actualMinimum <= greatestMinimum <=
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    leastMaximum <= actualMaximum <= maximum
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. actualMinimum <= value <= actualMaximum
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Note: In addition to outright failures, this test reports some
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * results as warnings.  These are not generally of concern, but they
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * should be evaluated by a human.  To see these, run this test in
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * verbose mode.
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param cal the calendar to be tested
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fieldsToTest an array of field values to be tested, e.g., new
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * int[] { Calendar.MONTH, Calendar.DAY_OF_MONTH }.  It only makes
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * sense to test the day fields; the time fields are not tested by this
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * method.  If null, then test all standard fields.
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param startDate the first date to test
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param testDuration if positive, the number of days to be tested.
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If negative, the number of seconds to run the test.
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2252d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    protected void doLimitsTest(Calendar cal, int[] fieldsToTest,
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                Date startDate, int testDuration) {
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        GregorianCalendar greg = new GregorianCalendar();
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        greg.setTime(startDate);
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        logln("Start: " + startDate);
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (fieldsToTest == null) {
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fieldsToTest = new int[] {
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Calendar.ERA, Calendar.YEAR, Calendar.MONTH,
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH,
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Calendar.DAY_OF_MONTH, Calendar.DAY_OF_YEAR,
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Calendar.DAY_OF_WEEK_IN_MONTH, Calendar.YEAR_WOY,
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Calendar.EXTENDED_YEAR
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            };
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Keep a record of minima and maxima that we actually see.
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // These are kept in an array of arrays of hashes.
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Map[][] limits = new Map[fieldsToTest.length][2];
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Object nub = new Object(); // Meaningless placeholder
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // This test can run for a long time; show progress.
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long millis = System.currentTimeMillis();
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long mark = millis + 5000; // 5 sec
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        millis -= testDuration * 1000; // stop time if testDuration<0
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i=0;
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             testDuration>0 ? i<testDuration
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            : System.currentTimeMillis()<millis;
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             ++i) {
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (System.currentTimeMillis() >= mark) {
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                logln("(" + i + " days)");
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                mark += 5000; // 5 sec
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            cal.setTimeInMillis(greg.getTimeInMillis());
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int j=0; j<fieldsToTest.length; ++j) {
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int f = fieldsToTest[j];
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int v = cal.get(f);
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int minActual = cal.getActualMinimum(f);
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int maxActual = cal.getActualMaximum(f);
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int minLow = cal.getMinimum(f);
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int minHigh = cal.getGreatestMinimum(f);
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int maxLow = cal.getLeastMaximum(f);
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int maxHigh = cal.getMaximum(f);
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Fetch the hash for this field and keep track of the
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // minima and maxima.
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Map[] h = limits[j];
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (h[0] == null) {
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    h[0] = new HashMap();
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    h[1] = new HashMap();
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                h[0].put(new Integer(minActual), nub);
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                h[1].put(new Integer(maxActual), nub);
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (minActual < minLow || minActual > minHigh) {
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    errln("Fail: " + ymdToString(cal) +
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          " Range for min of " + FIELD_NAME[f] + "(" + f +
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          ")=" + minLow + ".." + minHigh +
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          ", actual_min=" + minActual);
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (maxActual < maxLow || maxActual > maxHigh) {
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    errln("Fail: " + ymdToString(cal) +
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          " Range for max of " + FIELD_NAME[f] + "(" + f +
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          ")=" + maxLow + ".." + maxHigh +
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          ", actual_max=" + maxActual);
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (v < minActual || v > maxActual) {
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    errln("Fail: " + ymdToString(cal) +
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          " " + FIELD_NAME[f] + "(" + f + ")=" + v +
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          ", actual range=" + minActual + ".." + maxActual +
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          ", allowed=(" + minLow + ".." + minHigh + ")..(" +
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          maxLow + ".." + maxHigh + ")");
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            greg.add(Calendar.DAY_OF_YEAR, 1);
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Check actual maxima and minima seen against ranges returned
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // by API.
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuffer buf = new StringBuffer();
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int j=0; j<fieldsToTest.length; ++j) {
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int f = fieldsToTest[j];
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.setLength(0);
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(FIELD_NAME[f]);
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Map[] h = limits[j];
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean fullRangeSeen = true;
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int k=0; k<2; ++k) {
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int rangeLow = (k==0) ?
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    cal.getMinimum(f) : cal.getLeastMaximum(f);
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int rangeHigh = (k==0) ?
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    cal.getGreatestMinimum(f) : cal.getMaximum(f);
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // If either the top of the range or the bottom was never
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // seen, then there may be a problem.
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (h[k].get(new Integer(rangeLow)) == null ||
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    h[k].get(new Integer(rangeHigh)) == null) {
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fullRangeSeen = false;
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(k==0 ? " minima seen=(" : "; maxima seen=(");
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (Object v : h[k].keySet()) {
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append(" " + v);
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(") range=" + rangeLow + ".." + rangeHigh);
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (fullRangeSeen) {
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                logln("OK: " + buf.toString());
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // This may or may not be an error -- if the range of dates
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // we scan over doesn't happen to contain a minimum or
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // maximum, it doesn't mean some other range won't.
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                logln("Warning: " + buf.toString());
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        logln("End: " + greg.getTime());
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * doLimitsTest with default test duration
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3452d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    protected void doLimitsTest(Calendar cal, int[] fieldsToTest, Date startDate) {
3462d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        int testTime = TestFmwk.getExhaustiveness() <= 5 ? -3 : -120; // in seconds
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        doLimitsTest(cal, fieldsToTest, startDate, testTime);
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Test the functions getMaximum/getGeratestMinimum logically correct.
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This method assumes day of week cycle is consistent.
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param cal The calendar instance to be tested.
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param leapMonth true if the calendar system has leap months
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3562d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    protected void doTheoreticalLimitsTest(Calendar cal, boolean leapMonth) {
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int nDOW = cal.getMaximum(Calendar.DAY_OF_WEEK);
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int maxDOY = cal.getMaximum(Calendar.DAY_OF_YEAR);
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int lmaxDOW = cal.getLeastMaximum(Calendar.DAY_OF_YEAR);
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int maxWOY = cal.getMaximum(Calendar.WEEK_OF_YEAR);
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int lmaxWOY = cal.getLeastMaximum(Calendar.WEEK_OF_YEAR);
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int maxM = cal.getMaximum(Calendar.MONTH) + 1;
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int lmaxM = cal.getLeastMaximum(Calendar.MONTH) + 1;
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int maxDOM = cal.getMaximum(Calendar.DAY_OF_MONTH);
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int lmaxDOM = cal.getLeastMaximum(Calendar.DAY_OF_MONTH);
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int maxDOWIM = cal.getMaximum(Calendar.DAY_OF_WEEK_IN_MONTH);
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int lmaxDOWIM = cal.getLeastMaximum(Calendar.DAY_OF_WEEK_IN_MONTH);
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int maxWOM = cal.getMaximum(Calendar.WEEK_OF_MONTH);
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int lmaxWOM = cal.getLeastMaximum(Calendar.WEEK_OF_MONTH);
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int minDaysInFirstWeek = cal.getMinimalDaysInFirstWeek();
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Day of year
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int expected;
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!leapMonth) {
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            expected = maxM*maxDOM;
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (maxDOY > expected) {
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errln("FAIL: Maximum value of DAY_OF_YEAR is too big: " + maxDOY + "/expected: <=" + expected);
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            expected = lmaxM*lmaxDOM;
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (lmaxDOW < expected) {
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                errln("FAIL: Least maximum value of DAY_OF_YEAR is too small: " + lmaxDOW + "/expected: >=" + expected);
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Week of year
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        expected = maxDOY/nDOW + 1;
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (maxWOY > expected) {
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            errln("FAIL: Maximum value of WEEK_OF_YEAR is too big: " + maxWOY + "/expected: <=" + expected);
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        expected = lmaxDOW/nDOW;
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (lmaxWOY < expected) {
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            errln("FAIL: Least maximum value of WEEK_OF_YEAR is too small: " + lmaxWOY + "/expected >=" + expected);
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Day of week in month
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        expected = (maxDOM + nDOW - 1)/nDOW;
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (maxDOWIM != expected) {
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            errln("FAIL: Maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: " + maxDOWIM + "/expected: " + expected);
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        expected = (lmaxDOM + nDOW - 1)/nDOW;
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (lmaxDOWIM != expected) {
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            errln("FAIL: Least maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: " + lmaxDOWIM + "/expected: " + expected);
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Week of month
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        expected = (maxDOM + (nDOW - 1) + (nDOW - minDaysInFirstWeek)) / nDOW;
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (maxWOM != expected) {
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            errln("FAIL: Maximum value of WEEK_OF_MONTH is incorrect: " + maxWOM + "/expected: " + expected);
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        expected = (lmaxDOM + (nDOW - minDaysInFirstWeek)) / nDOW;
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (lmaxWOM != expected) {
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            errln("FAIL: Least maximum value of WEEK_OF_MONTH is incorrect: " + lmaxWOM + "/expected: " + expected);
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convert year,month,day values to the form "year/month/day".
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * On input the month value is zero-based, but in the result string it is one-based.
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static public String ymdToString(int year, int month, int day) {
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return "" + year + "/" + (month+1) + "/" + day;
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convert year,month,day values to the form "year/month/day".
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static public String ymdToString(Calendar cal) {
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        double day = getJulianDay(cal);
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (cal instanceof ChineseCalendar) {
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return "" + cal.get(Calendar.EXTENDED_YEAR) + "/" +
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                (cal.get(Calendar.MONTH)+1) +
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                (cal.get(Calendar.IS_LEAP_MONTH)==1?"(leap)":"") + "/" +
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cal.get(Calendar.DATE) + " (" + day + ", time=" + cal.getTimeInMillis() + ")";
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return ymdToString(cal.get(Calendar.EXTENDED_YEAR),
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            cal.get(MONTH), cal.get(DATE)) +
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            " (" + day + ", time=" + cal.getTimeInMillis() + ")";
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static double getJulianDay(Calendar cal) {
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return (cal.getTime().getTime() - JULIAN_EPOCH) / DAY_MS;
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final double DAY_MS = 24*60*60*1000.0;
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final long JULIAN_EPOCH = -210866760000000L;   // 1/1/4713 BC 12:00
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
447