Home | History | Annotate | Download | only in calendar
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 /*********************************************************************
      4  * Copyright (C) 2000-2016, International Business Machines Corporation and
      5  * others. All Rights Reserved.
      6  *********************************************************************
      7  */
      8 package com.ibm.icu.dev.test.calendar;
      9 import java.util.Date;
     10 import java.util.Locale;
     11 
     12 import org.junit.Test;
     13 import org.junit.runner.RunWith;
     14 import org.junit.runners.JUnit4;
     15 
     16 import com.ibm.icu.text.ChineseDateFormat;
     17 import com.ibm.icu.text.DateFormat;
     18 import com.ibm.icu.text.DateFormatSymbols;
     19 import com.ibm.icu.text.SimpleDateFormat;
     20 import com.ibm.icu.util.Calendar;
     21 import com.ibm.icu.util.ChineseCalendar;
     22 import com.ibm.icu.util.GregorianCalendar;
     23 import com.ibm.icu.util.TimeZone;
     24 import com.ibm.icu.util.ULocale;
     25 
     26 /**
     27  * Test of ChineseCalendar.
     28  *
     29  * Leap months in this century:
     30  * Wed May 23 2001 = 4638-04*-01, Year 18, Cycle 78
     31  * Sun Mar 21 2004 = 4641-02*-01, Year 21, Cycle 78
     32  * Thu Aug 24 2006 = 4643-07*-01, Year 23, Cycle 78
     33  * Tue Jun 23 2009 = 4646-05*-01, Year 26, Cycle 78
     34  * Mon May 21 2012 = 4649-04*-01, Year 29, Cycle 78
     35  * Fri Oct 24 2014 = 4651-09*-01, Year 31, Cycle 78
     36  * Sun Jul 23 2017 = 4654-06*-01, Year 34, Cycle 78
     37  * Sat May 23 2020 = 4657-04*-01, Year 37, Cycle 78
     38  * Wed Mar 22 2023 = 4660-02*-01, Year 40, Cycle 78
     39  * Fri Jul 25 2025 = 4662-06*-01, Year 42, Cycle 78
     40  * Fri Jun 23 2028 = 4665-05*-01, Year 45, Cycle 78
     41  * Tue Apr 22 2031 = 4668-03*-01, Year 48, Cycle 78
     42  * Thu Dec 22 2033 = 4670-11*-01, Year 50, Cycle 78
     43  * Wed Jul 23 2036 = 4673-06*-01, Year 53, Cycle 78
     44  * Wed Jun 22 2039 = 4676-05*-01, Year 56, Cycle 78
     45  * Sat Mar 22 2042 = 4679-02*-01, Year 59, Cycle 78
     46  * Tue Aug 23 2044 = 4681-07*-01, Year 01, Cycle 79
     47  * Sun Jun 23 2047 = 4684-05*-01, Year 04, Cycle 79
     48  * Thu Apr 21 2050 = 4687-03*-01, Year 07, Cycle 79
     49  * Mon Sep 23 2052 = 4689-08*-01, Year 09, Cycle 79
     50  * Sat Jul 24 2055 = 4692-06*-01, Year 12, Cycle 79
     51  * Wed May 22 2058 = 4695-04*-01, Year 15, Cycle 79
     52  * Wed Apr 20 2061 = 4698-03*-01, Year 18, Cycle 79
     53  * Fri Aug 24 2063 = 4700-07*-01, Year 20, Cycle 79
     54  * Wed Jun 23 2066 = 4703-05*-01, Year 23, Cycle 79
     55  * Tue May 21 2069 = 4706-04*-01, Year 26, Cycle 79
     56  * Thu Sep 24 2071 = 4708-08*-01, Year 28, Cycle 79
     57  * Tue Jul 24 2074 = 4711-06*-01, Year 31, Cycle 79
     58  * Sat May 22 2077 = 4714-04*-01, Year 34, Cycle 79
     59  * Sat Apr 20 2080 = 4717-03*-01, Year 37, Cycle 79
     60  * Mon Aug 24 2082 = 4719-07*-01, Year 39, Cycle 79
     61  * Fri Jun 22 2085 = 4722-05*-01, Year 42, Cycle 79
     62  * Fri May 21 2088 = 4725-04*-01, Year 45, Cycle 79
     63  * Sun Sep 24 2090 = 4727-08*-01, Year 47, Cycle 79
     64  * Thu Jul 23 2093 = 4730-06*-01, Year 50, Cycle 79
     65  * Tue May 22 2096 = 4733-04*-01, Year 53, Cycle 79
     66  * Sun Mar 22 2099 = 4736-02*-01, Year 56, Cycle 79
     67  */
     68 @RunWith(JUnit4.class)
     69 public class ChineseTest extends CalendarTestFmwk {
     70     /**
     71      * Test basic mapping to and from Gregorian.
     72      */
     73     @Test
     74     public void TestMapping() {
     75 
     76         final int[] DATA = {
     77             // (Note: months are 1-based)
     78             // Gregorian    Chinese
     79             1964,  9,  4,   4601,  7,0, 28,
     80             1964,  9,  5,   4601,  7,0, 29,
     81             1964,  9,  6,   4601,  8,0,  1,
     82             1964,  9,  7,   4601,  8,0,  2,
     83             1961, 12, 25,   4598, 11,0, 18,
     84             1999,  6,  4,   4636,  4,0, 21,
     85 
     86             1990,  5, 23,   4627,  4,0, 29,
     87             1990,  5, 24,   4627,  5,0,  1,
     88             1990,  6, 22,   4627,  5,0, 30,
     89             1990,  6, 23,   4627,  5,1,  1,
     90             1990,  7, 20,   4627,  5,1, 28,
     91             1990,  7, 21,   4627,  5,1, 29,
     92             1990,  7, 22,   4627,  6,0,  1,
     93         };
     94 
     95         ChineseCalendar cal = new ChineseCalendar();
     96         StringBuffer buf = new StringBuffer();
     97 
     98         logln("Gregorian -> Chinese");
     99         //java.util.Calendar grego = java.util.Calendar.getInstance();
    100         Calendar grego = Calendar.getInstance();
    101         grego.clear();
    102         for (int i=0; i<DATA.length; ) {
    103             grego.set(DATA[i++], DATA[i++]-1, DATA[i++]);
    104             Date date = grego.getTime();
    105             cal.setTime(date);
    106             int y = cal.get(Calendar.EXTENDED_YEAR);
    107             int m = cal.get(Calendar.MONTH)+1; // 0-based -> 1-based
    108             int L = cal.get(Calendar.IS_LEAP_MONTH);
    109             int d = cal.get(Calendar.DAY_OF_MONTH);
    110             int yE = DATA[i++]; // Expected y, m, isLeapMonth, d
    111             int mE = DATA[i++]; // 1-based
    112             int LE = DATA[i++];
    113             int dE = DATA[i++];
    114             buf.setLength(0);
    115             buf.append(date + " -> ");
    116             buf.append(y + "/" + m + (L==1?"(leap)":"") + "/" + d);
    117             if (y == yE && m == mE && L == LE && d == dE) {
    118                 logln("OK: " + buf.toString());
    119             } else {
    120                 errln("Fail: " + buf.toString() + ", expected " +
    121                       yE + "/" + mE + (LE==1?"(leap)":"") + "/" + dE);
    122             }
    123         }
    124 
    125         logln("Chinese -> Gregorian");
    126         for (int i=0; i<DATA.length; ) {
    127             grego.set(DATA[i++], DATA[i++]-1, DATA[i++]);
    128             Date dexp = grego.getTime();
    129             int cyear = DATA[i++];
    130             int cmonth = DATA[i++];
    131             int cisleapmonth = DATA[i++];
    132             int cdayofmonth = DATA[i++];
    133             cal.clear();
    134             cal.set(Calendar.EXTENDED_YEAR, cyear);
    135             cal.set(Calendar.MONTH, cmonth-1);
    136             cal.set(Calendar.IS_LEAP_MONTH, cisleapmonth);
    137             cal.set(Calendar.DAY_OF_MONTH, cdayofmonth);
    138             Date date = cal.getTime();
    139             buf.setLength(0);
    140             buf.append(cyear + "/" + cmonth +
    141                        (cisleapmonth==1?"(leap)":"") + "/" + cdayofmonth);
    142             buf.append(" -> " + date);
    143             if (date.equals(dexp)) {
    144                 logln("OK: " + buf.toString());
    145             } else {
    146                 errln("Fail: " + buf.toString() + ", expected " + dexp);
    147             }
    148         }
    149     }
    150 
    151     /**
    152      * Make sure no Gregorian dates map to Chinese 1-based day of
    153      * month zero.  This was a problem with some of the astronomical
    154      * new moon determinations.
    155      */
    156     @Test
    157     public void TestZeroDOM() {
    158         ChineseCalendar cal = new ChineseCalendar();
    159         GregorianCalendar greg = new GregorianCalendar(1989, Calendar.SEPTEMBER, 1);
    160         logln("Start: " + greg.getTime());
    161         for (int i=0; i<1000; ++i) {
    162             cal.setTimeInMillis(greg.getTimeInMillis());
    163             if (cal.get(Calendar.DAY_OF_MONTH) == 0) {
    164                 errln("Fail: " + greg.getTime() + " -> " +
    165                       cal.get(Calendar.EXTENDED_YEAR) + "/" +
    166                       cal.get(Calendar.MONTH) +
    167                       (cal.get(Calendar.IS_LEAP_MONTH)==1?"(leap)":"") +
    168                       "/" + cal.get(Calendar.DAY_OF_MONTH));
    169             }
    170             greg.add(Calendar.DAY_OF_YEAR, 1);
    171         }
    172         logln("End: " + greg.getTime());
    173     }
    174 
    175     /**
    176      * Test minimum and maximum functions.
    177      */
    178     @Test
    179     public void TestLimits() {
    180         // The number of days and the start date can be adjusted
    181         // arbitrarily to either speed up the test or make it more
    182         // thorough, but try to test at least a full year, preferably a
    183         // full non-leap and a full leap year.
    184 
    185         // Final parameter is either number of days, if > 0, or test
    186         // duration in seconds, if < 0.
    187         java.util.Calendar tempcal = java.util.Calendar.getInstance();
    188         tempcal.clear();
    189         tempcal.set(1989, Calendar.NOVEMBER, 1);
    190         ChineseCalendar chinese = new ChineseCalendar();
    191         doLimitsTest(chinese, null, tempcal.getTime());
    192         doTheoreticalLimitsTest(chinese, true);
    193     }
    194 
    195     /**
    196      * Run through several standard tests from Dershowitz & Reingold.
    197      */
    198     @Test
    199     public void TestJulianDayMapping() {
    200 
    201         final TestCase[] tests = {
    202             //
    203             // From Dershowitz & Reingold, "Calendrical Calculations".
    204             //
    205             // The months in this table are 1-based rather than 0-based.
    206             //
    207             // * Failing fields->millis
    208             // ** Millis->fields gives 0-based month -1
    209             // These failures were fixed by changing the start search date
    210             // for the winter solstice from Dec 15 to Dec 1.
    211             //
    212             //                  Julian Day   Era  Year Month  Leap   DOM WkDay
    213             new ChineseTestCase(1507231.5,   35,   11,    6, false,   12,  SUN),
    214             new ChineseTestCase(1660037.5,   42,    9,   10, false,   27,  WED),
    215             new ChineseTestCase(1746893.5,   46,    7,    8, false,    4,  WED),
    216             new ChineseTestCase(1770641.5,   47,   12,    8, false,    9,  SUN),
    217             new ChineseTestCase(1892731.5,   52,   46,   11, false,   20,  WED),
    218             new ChineseTestCase(1931579.5,   54,   33,    4, false,    5,  MON),
    219             new ChineseTestCase(1974851.5,   56,   31,   10, false,   15,  SAT),
    220             new ChineseTestCase(2091164.5,   61,   50,    3, false,    7,  SUN),
    221             new ChineseTestCase(2121509.5,   63,   13,    4, false,   24,  SUN),
    222             new ChineseTestCase(2155779.5,   64,   47,    2, false,    9,  FRI),
    223             new ChineseTestCase(2174029.5,   65,   37,    2, false,    9,  SAT),
    224             new ChineseTestCase(2191584.5,   66,   25,    2, false,   23,  FRI),
    225             new ChineseTestCase(2195261.5,   66,   35,    3, false,    9,  SUN), //*
    226             new ChineseTestCase(2229274.5,   68,    8,    5, false,    2,  SUN), //*
    227             new ChineseTestCase(2245580.5,   68,   53,    1, false,    8,  WED), //**
    228             new ChineseTestCase(2266100.5,   69,   49,    3, false,    4,  SAT),
    229             new ChineseTestCase(2288542.5,   70,   50,    8, false,    2,  SAT), //*
    230             new ChineseTestCase(2290901.5,   70,   57,    1, false,   29,  SAT), //*
    231             new ChineseTestCase(2323140.5,   72,   25,    4,  true,   20,  WED), //*
    232             new ChineseTestCase(2334848.5,   72,   57,    6, false,    5,  SUN),
    233             new ChineseTestCase(2348020.5,   73,   33,    6, false,    6,  FRI),
    234             new ChineseTestCase(2366978.5,   74,   25,    5, false,    5,  SUN),
    235             new ChineseTestCase(2385648.5,   75,   16,    6, false,   12,  MON),
    236             new ChineseTestCase(2392825.5,   75,   36,    2, false,   13,  WED),
    237             new ChineseTestCase(2416223.5,   76,   40,    3, false,   22,  SUN),
    238             new ChineseTestCase(2425848.5,   77,    6,    7, false,   21,  SUN),
    239             new ChineseTestCase(2430266.5,   77,   18,    8, false,    9,  MON),
    240             new ChineseTestCase(2430833.5,   77,   20,    3, false,   15,  MON),
    241             new ChineseTestCase(2431004.5,   77,   20,    9, false,    9,  THU),
    242             new ChineseTestCase(2448698.5,   78,    9,    2, false,   14,  TUE),
    243             new ChineseTestCase(2450138.5,   78,   13,    1, false,    7,  SUN),
    244             new ChineseTestCase(2465737.5,   78,   55,   10, false,   14,  WED),
    245             new ChineseTestCase(2486076.5,   79,   51,    6, false,    7,  SUN),
    246 
    247             // Additional tests not from D&R
    248             new ChineseTestCase(2467496.5,   78,   60,    8, false,    2,  FRI), // year 60
    249         };
    250 
    251         ChineseCalendar cal = new ChineseCalendar();
    252         cal.setLenient(true);
    253         doTestCases(tests, cal);
    254     }
    255 
    256     /**
    257      * Test formatting.
    258      * Q: Why is this in Calendar tests instead of Format tests?
    259      * Note: This test assumes that Chinese calendar formatted dates can be parsed
    260      * unambiguously to recover the original Date that was formatted. This is not
    261      * currently true since Chinese calendar formatted dates do not include an era.
    262      * To address this will require formatting/parsing of fields from some other
    263      * associated calendar, as per ICU ticket #9043. This test should be timebombed
    264      * until that ticket is addressed.
    265      */
    266     @Test
    267     public void TestFormat() {
    268         ChineseCalendar cal = new ChineseCalendar();
    269         DateFormat fmt = DateFormat.getDateTimeInstance(cal,
    270                                     DateFormat.DEFAULT, DateFormat.DEFAULT);
    271 
    272         java.util.Calendar tempcal = java.util.Calendar.getInstance();
    273         tempcal.clear();
    274 
    275         Date[] DATA = new Date[2];
    276         tempcal.set(2001, Calendar.MAY, 22);
    277         DATA[0] = tempcal.getTime();
    278         tempcal.set(2001, Calendar.MAY, 23);
    279         DATA[1] = tempcal.getTime();
    280         // Wed May 23 2001 = Month 4(leap), Day 1, Year 18, Cycle 78
    281 
    282         for (int i=0; i<DATA.length; ++i) {
    283             String s = fmt.format(DATA[i]);
    284             try {
    285                 Date e = fmt.parse(s);
    286                 if (e.equals(DATA[i])) {
    287                     logln("Ok: " + DATA[i] + " -> " + s + " -> " + e);
    288                 } else {
    289                     errln("FAIL: " + DATA[i] + " -> " + s + " -> " + e);
    290                 }
    291             } catch (java.text.ParseException e) {
    292                 errln("Fail: " + s + " -> parse failure at " + e.getErrorOffset());
    293                 errln(e.toString());
    294             }
    295         }
    296     }
    297 
    298     /**
    299      * Make sure IS_LEAP_MONTH participates in field resolution.
    300      */
    301     @Test
    302     public void TestResolution() {
    303         ChineseCalendar cal = new ChineseCalendar();
    304         DateFormat fmt = DateFormat.getDateInstance(cal, DateFormat.DEFAULT);
    305 
    306         // May 22 2001 = y4638 m4 d30 doy119
    307         // May 23 2001 = y4638 m4* d1 doy120
    308 
    309         final int THE_YEAR = 4638;
    310         final int END = -1;
    311 
    312         int[] DATA = {
    313             // Format:
    314             // (field, value)+, END, exp.month, exp.isLeapMonth, exp.DOM
    315             // Note: exp.month is ONE-BASED
    316 
    317             // If we set DAY_OF_YEAR only, that should be used
    318             Calendar.DAY_OF_YEAR, 1,
    319             END,
    320             1,0,1, // Expect 1-1
    321 
    322             // If we set MONTH only, that should be used
    323             Calendar.IS_LEAP_MONTH, 1,
    324             Calendar.DAY_OF_MONTH, 1,
    325             Calendar.MONTH, 3,
    326             END,
    327             4,1,1, // Expect 4*-1
    328 
    329             // If we set the DOY last, that should take precedence
    330             Calendar.MONTH, 1, // Should ignore
    331             Calendar.IS_LEAP_MONTH, 1, // Should ignore
    332             Calendar.DAY_OF_MONTH, 1, // Should ignore
    333             Calendar.DAY_OF_YEAR, 121,
    334             END,
    335             4,1,2, // Expect 4*-2
    336 
    337             // I've disabled this test because it doesn't work this way,
    338             // not even with a GregorianCalendar!  MONTH alone isn't enough
    339             // to supersede DAY_OF_YEAR.  Some other month-related field is
    340             // also required. - Liu 11/28/00
    341             //! // If we set MONTH last, that should take precedence
    342             //! ChineseCalendar.IS_LEAP_MONTH, 1,
    343             //! Calendar.DAY_OF_MONTH, 1,
    344             //! Calendar.DAY_OF_YEAR, 5, // Should ignore
    345             //! Calendar.MONTH, 3,
    346             //! END,
    347             //! 4,1,1, // Expect 4*-1
    348 
    349             // If we set IS_LEAP_MONTH last, that should take precedence
    350             Calendar.MONTH, 3,
    351             Calendar.DAY_OF_MONTH, 1,
    352             Calendar.DAY_OF_YEAR, 5, // Should ignore
    353             Calendar.IS_LEAP_MONTH, 1,
    354             END,
    355             4,1,1, // Expect 4*-1
    356         };
    357 
    358         StringBuffer buf = new StringBuffer();
    359         for (int i=0; i<DATA.length; ) {
    360             cal.clear();
    361             cal.set(Calendar.EXTENDED_YEAR, THE_YEAR);
    362             buf.setLength(0);
    363             buf.append("EXTENDED_YEAR=" + THE_YEAR);
    364             while (DATA[i] != END) {
    365                 cal.set(DATA[i++], DATA[i++]);
    366                 buf.append(" " + fieldName(DATA[i-2]) + "=" + DATA[i-1]);
    367             }
    368             ++i; // Skip over END mark
    369             int expMonth = DATA[i++]-1;
    370             int expIsLeapMonth = DATA[i++];
    371             int expDOM = DATA[i++];
    372             int month = cal.get(Calendar.MONTH);
    373             int isLeapMonth = cal.get(Calendar.IS_LEAP_MONTH);
    374             int dom = cal.get(Calendar.DAY_OF_MONTH);
    375             if (expMonth == month && expIsLeapMonth == isLeapMonth &&
    376                 dom == expDOM) {
    377                 logln("OK: " + buf + " => " + fmt.format(cal.getTime()));
    378             } else {
    379                 String s = fmt.format(cal.getTime());
    380                 cal.clear();
    381                 cal.set(Calendar.EXTENDED_YEAR, THE_YEAR);
    382                 cal.set(Calendar.MONTH, expMonth);
    383                 cal.set(Calendar.IS_LEAP_MONTH, expIsLeapMonth);
    384                 cal.set(Calendar.DAY_OF_MONTH, expDOM);
    385                 errln("Fail: " + buf + " => " + s +
    386                       "=" + (month+1) + "," + isLeapMonth + "," + dom +
    387                       ", expected " + fmt.format(cal.getTime()) +
    388                       "=" + (expMonth+1) + "," + expIsLeapMonth + "," + expDOM);
    389             }
    390         }
    391     }
    392 
    393     /**
    394      * Test the behavior of fields that are out of range.
    395      */
    396     @Test
    397     public void TestOutOfRange() {
    398         int[] DATA = new int[] {
    399             // Input       Output
    400             4638, 13,  1,   4639,  1,  1,
    401             4638, 18,  1,   4639,  6,  1,
    402             4639,  0,  1,   4638, 12,  1,
    403             4639, -6,  1,   4638,  6,  1,
    404             4638,  1, 32,   4638,  2,  2, // 1-4638 has 30 days
    405             4638,  2, -1,   4638,  1, 29,
    406         };
    407         ChineseCalendar cal = new ChineseCalendar();
    408         for (int i=0; i<DATA.length; ) {
    409             int y1 = DATA[i++];
    410             int m1 = DATA[i++]-1;
    411             int d1 = DATA[i++];
    412             int y2 = DATA[i++];
    413             int m2 = DATA[i++]-1;
    414             int d2 = DATA[i++];
    415             cal.clear();
    416             cal.set(Calendar.EXTENDED_YEAR, y1);
    417             cal.set(MONTH, m1);
    418             cal.set(DATE, d1);
    419             int y = cal.get(Calendar.EXTENDED_YEAR);
    420             int m = cal.get(MONTH);
    421             int d = cal.get(DATE);
    422             if (y!=y2 || m!=m2 || d!=d2) {
    423                 errln("Fail: " + y1 + "/" + (m1+1) + "/" + d1 + " resolves to " +
    424                       y + "/" + (m+1) + "/" + d + ", expected " +
    425                       y2 + "/" + (m2+1) + "/" + d2);
    426             } else  if (isVerbose()) {
    427                 logln("OK: " + y1 + "/" + (m1+1) + "/" + d1 + " resolves to " +
    428                       y + "/" + (m+1) + "/" + d);
    429             }
    430         }
    431     }
    432 
    433     /**
    434      * Test the behavior of ChineseCalendar.add().  The only real
    435      * nastiness with roll is the MONTH field around leap months.
    436      */
    437     @Test
    438     public void TestAdd() {
    439         int[][] tests = new int[][] {
    440             // MONTHS ARE 1-BASED HERE
    441             // input               add           output
    442             // year  mon    day    field amount  year  mon    day
    443             {  4642,   3,0,  15,   MONTH,   3,   4642,   6,0,  15 }, // normal
    444             {  4639,  12,0,  15,   MONTH,   1,   4640,   1,0,  15 }, // across year
    445             {  4640,   1,0,  15,   MONTH,  -1,   4639,  12,0,  15 }, // across year
    446             {  4638,   3,0,  15,   MONTH,   3,   4638,   5,0,  15 }, // 4=leap
    447             {  4638,   3,0,  15,   MONTH,   2,   4638,   4,1,  15 }, // 4=leap
    448             {  4638,   4,0,  15,   MONTH,   1,   4638,   4,1,  15 }, // 4=leap
    449             {  4638,   4,1,  15,   MONTH,   1,   4638,   5,0,  15 }, // 4=leap
    450             {  4638,   4,0,  30,   MONTH,   1,   4638,   4,1,  29 }, // dom should pin
    451             {  4638,   4,0,  30,   MONTH,   2,   4638,   5,0,  30 }, // no dom pin
    452             {  4638,   4,0,  30,   MONTH,   3,   4638,   6,0,  29 }, // dom should pin
    453         };
    454 
    455         ChineseCalendar cal = new ChineseCalendar();
    456         doRollAdd(ADD, cal, tests);
    457     }
    458 
    459     /**
    460      * Test the behavior of ChineseCalendar.roll().  The only real
    461      * nastiness with roll is the MONTH field around leap months.
    462      */
    463     @Test
    464     public void TestRoll() {
    465         int[][] tests = new int[][] {
    466             // MONTHS ARE 1-BASED HERE
    467             // input               add           output
    468             // year  mon    day    field amount  year  mon    day
    469             {  4642,   3,0,  15,   MONTH,   3,   4642,   6,0,  15 }, // normal
    470             {  4642,   3,0,  15,   MONTH,  11,   4642,   2,0,  15 }, // normal
    471             {  4639,  12,0,  15,   MONTH,   1,   4639,   1,0,  15 }, // across year
    472             {  4640,   1,0,  15,   MONTH,  -1,   4640,  12,0,  15 }, // across year
    473             {  4638,   3,0,  15,   MONTH,   3,   4638,   5,0,  15 }, // 4=leap
    474             {  4638,   3,0,  15,   MONTH,  16,   4638,   5,0,  15 }, // 4=leap
    475             {  4638,   3,0,  15,   MONTH,   2,   4638,   4,1,  15 }, // 4=leap
    476             {  4638,   3,0,  15,   MONTH,  28,   4638,   4,1,  15 }, // 4=leap
    477             {  4638,   4,0,  15,   MONTH,   1,   4638,   4,1,  15 }, // 4=leap
    478             {  4638,   4,0,  15,   MONTH, -12,   4638,   4,1,  15 }, // 4=leap
    479             {  4638,   4,1,  15,   MONTH,   1,   4638,   5,0,  15 }, // 4=leap
    480             {  4638,   4,1,  15,   MONTH, -25,   4638,   5,0,  15 }, // 4=leap
    481             {  4638,   4,0,  30,   MONTH,   1,   4638,   4,1,  29 }, // dom should pin
    482             {  4638,   4,0,  30,   MONTH,  14,   4638,   4,1,  29 }, // dom should pin
    483             {  4638,   4,0,  30,   MONTH,  15,   4638,   5,0,  30 }, // no dom pin
    484             {  4638,   4,0,  30,   MONTH, -10,   4638,   6,0,  29 }, // dom should pin
    485         };
    486 
    487         ChineseCalendar cal = new ChineseCalendar();
    488         doRollAdd(ROLL, cal, tests);
    489     }
    490 
    491     void doRollAdd(boolean roll, ChineseCalendar cal, int[][] tests) {
    492         String name = roll ? "rolling" : "adding";
    493 
    494         for (int i = 0; i < tests.length; i++) {
    495             int[] test = tests[i];
    496 
    497             cal.clear();
    498                 cal.set(Calendar.EXTENDED_YEAR, test[0]);
    499                 cal.set(Calendar.MONTH, test[1]-1);
    500                 cal.set(Calendar.IS_LEAP_MONTH, test[2]);
    501                 cal.set(Calendar.DAY_OF_MONTH, test[3]);
    502             if (roll) {
    503                 cal.roll(test[4], test[5]);
    504             } else {
    505                 cal.add(test[4], test[5]);
    506             }
    507             if (cal.get(Calendar.EXTENDED_YEAR) != test[6] ||
    508                 cal.get(MONTH) != (test[7]-1) ||
    509                 cal.get(Calendar.IS_LEAP_MONTH) != test[8] ||
    510                 cal.get(DATE) != test[9]) {
    511                 errln("Fail: " + name + " " +
    512                       ymdToString(test[0], test[1]-1, test[2], test[3])
    513                       + " " + fieldName(test[4]) + " by " + test[5]
    514                       + ": expected " +
    515                       ymdToString(test[6], test[7]-1, test[8], test[9])
    516                       + ", got " + ymdToString(cal));
    517             } else if (isVerbose()) {
    518                 logln("OK: " + name + " " +
    519                       ymdToString(test[0], test[1]-1, test[2], test[3])
    520                     + " " + fieldName(test[4]) + " by " + test[5]
    521                     + ": got " + ymdToString(cal));
    522             }
    523         }
    524     }
    525 
    526     /**
    527      * Convert year,month,day values to the form "year/month/day".
    528      * On input the month value is zero-based, but in the result string it is one-based.
    529      */
    530     static public String ymdToString(int year, int month, int isLeapMonth, int day) {
    531         return "" + year + "/" + (month+1) +
    532             ((isLeapMonth!=0)?"(leap)":"") +
    533             "/" + day;
    534     }
    535 
    536 //    public void TestFindLeapMonths() {
    537 //        ChineseCalendar cal = new ChineseCalendar();
    538 //        cal.setTime(new Date(2000-1900, Calendar.JANUARY, 1));
    539 //        long end = new Date(2100-1900, Calendar.JANUARY, 1).getTime();
    540 //        ChineseDateFormat fmt = (ChineseDateFormat) DateFormat.getInstance(cal);
    541 //        fmt.applyPattern("u-MMl-dd, 'Year' y, 'Cycle' G");
    542 //        while (cal.getTimeInMillis() < end) {
    543 //            if (cal.get(ChineseCalendar.IS_LEAP_MONTH) != 0) {
    544 //                cal.set(Calendar.DAY_OF_MONTH, 1);
    545 //                logln(cal.getTime() + " = " + fmt.format(cal.getTime()));
    546 //                cal.set(Calendar.DAY_OF_MONTH, 29);
    547 //            }
    548 //            cal.add(Calendar.DAY_OF_YEAR, 25);
    549 //        }
    550 //    }
    551 
    552     @Test
    553     public void TestCoverage() {
    554         // Coverage for constructors
    555         {
    556             // new ChineseCalendar(Date)
    557             ChineseCalendar cal = new ChineseCalendar(new Date());
    558             if(cal == null){
    559                 errln("could not create ChineseCalendar with Date");
    560             }
    561         }
    562 
    563         {
    564             // new ChineseCalendar(int year, int month, int isLeapMonth, int date)
    565             ChineseCalendar cal = new ChineseCalendar(23, Calendar.JULY, 1, 2);
    566             if(cal == null){
    567                 errln("could not create ChineseCalendar with year,month,isLeapMonth,date");
    568             }
    569             // Make sure the given values are properly set
    570             if (cal.get(Calendar.YEAR) != 23 || cal.get(Calendar.MONTH) != Calendar.JULY
    571                     || cal.get(Calendar.IS_LEAP_MONTH) != 1 || cal.get(Calendar.DATE) != 2
    572                     || cal.get(Calendar.MILLISECONDS_IN_DAY) != 0) {
    573                 errln("ChineseCalendar was initialized incorrectly with year,month,isLeapMonth,date");
    574             }
    575         }
    576 
    577         {
    578             // new ChineseCalendar(int year, int month, int isLeapMonth, int date, int hour, int minute, int second)
    579             ChineseCalendar cal = new ChineseCalendar(23, Calendar.JULY, 1, 2, 12, 34, 56);
    580             if(cal == null){
    581                 errln("could not create ChineseCalendar with year,month,isLeapMonth,date,hour,minute,second");
    582             }
    583             // Make sure the given values are properly set
    584             if (cal.get(Calendar.YEAR) != 23 || cal.get(Calendar.MONTH) != Calendar.JULY
    585                     || cal.get(Calendar.IS_LEAP_MONTH) != 1 || cal.get(Calendar.DATE) != 2
    586                     || cal.get(Calendar.HOUR_OF_DAY) != 12 || cal.get(Calendar.MINUTE) != 34
    587                     || cal.get(Calendar.SECOND) != 56 || cal.get(Calendar.MILLISECOND) != 0) {
    588                 errln("ChineseCalendar was initialized incorrectly with year,month,isLeapMonth,date,hour,minute,second");
    589             }
    590         }
    591 
    592         {
    593             // new ChineseCalendar(Locale)
    594             ChineseCalendar cal = new ChineseCalendar(Locale.getDefault());
    595             if(cal == null){
    596                 errln("could not create ChineseCalendar with Locale");
    597             }
    598         }
    599 
    600         {
    601             // new ChineseCalendar(ULocale)
    602             ChineseCalendar cal = new ChineseCalendar(ULocale.getDefault());
    603             if(cal == null){
    604                 errln("could not create ChineseCalendar with ULocale");
    605             }
    606         }
    607 
    608 
    609         {
    610             // new ChineseCalendar(TimeZone)
    611             ChineseCalendar cal = new ChineseCalendar(TimeZone.getDefault());
    612             if(cal == null){
    613                 errln("could not create ChineseCalendar with TimeZone");
    614             }
    615         }
    616 
    617         {
    618             // new ChineseCalendar(TimeZone, Locale)
    619             ChineseCalendar cal = new ChineseCalendar(TimeZone.getDefault(), Locale.getDefault());
    620             if(cal == null){
    621                 errln("could not create ChineseCalendar with TimeZone,Locale");
    622             }
    623         }
    624 
    625         {
    626             // new ChineseCalendar(TimeZone, ULocale)
    627             ChineseCalendar cal = new ChineseCalendar(TimeZone.getDefault(), ULocale.getDefault());
    628             if(cal == null){
    629                 errln("could not create ChineseCalendar with TimeZone,ULocale");
    630             }
    631         }
    632 
    633         // Note: ICU 50 or later versions, DateFormat.getInstance(ChineseCalendar) no longer
    634         // returns an instance of ChineseDateFormat. Chinese calendar formatting support was
    635         // changed and integrated into SimpleDateFormat since ICU 49. Also, ChineseDateFormat
    636         // specific pattern letter "l" is no longer used by the new implementation.
    637 
    638 //        ChineseCalendar cal = new ChineseCalendar();
    639 //        DateFormat format = DateFormat.getInstance(cal);
    640 //        if(!(format instanceof ChineseDateFormat)){
    641 //            errln("DateFormat.getInstance("+cal+") did not return a ChineseDateFormat");
    642 //        }
    643 //        ChineseDateFormat fmt = (ChineseDateFormat)format;
    644 //        fmt.applyPattern("llyyll");
    645 //        Date time = getDate(2100, Calendar.JANUARY, 1);
    646 //        String str = fmt.format(time);
    647 //        try {
    648 //            Date e = fmt.parse(str);
    649 //            logln("chinese calendar time: " + time + " result: " + str + " --> " + e);
    650 //        } catch (java.text.ParseException ex) {
    651 //            logln(ex.getMessage()); // chinese calendar can't parse this, no error for now
    652 //        }
    653 
    654         //new ChineseCalendar(TimeZone,ULocale)
    655         ChineseCalendar ccal2 = new ChineseCalendar(TimeZone.getDefault(),
    656                                                    ULocale.CHINA);
    657         if(ccal2==null){
    658             errln("could not create ChineseCalendar with TimeZone ULocale");
    659         } else {
    660             DateFormat fmt2 = DateFormat.getDateInstance(ccal2, DateFormat.DEFAULT, ULocale.CHINA);
    661             Date time2 = getDate(2001, Calendar.MAY, 23);
    662             String str2 = fmt2.format(time2);
    663             logln("Chinese calendar time: " + time2 + " result: " + str2);
    664         }
    665     }
    666     @Test
    667     public void TestScratch(){
    668         String[] strMonths = {"Januari", "Pebruari", "Maret", "April", "Mei", "Juni",
    669                 "Juli", "Agustus", "September", "Oktober", "Nopember", "Desember"};
    670         String[] strShortMonths = {"Jan", "Peb", "Mar", "Apr", "Mei", "Jun",
    671                      "Jul", "Agt", "Sep", "Okt", "Nop", "Des"};
    672         String[] strWeeks = {"", "Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"};
    673         DateFormatSymbols dfsDate = new DateFormatSymbols(new Locale("id", "ID"));
    674         dfsDate.setMonths(strMonths);
    675         dfsDate.setShortMonths(strShortMonths);
    676         dfsDate.setWeekdays(strWeeks);
    677         ULocale uloInd = dfsDate.getLocale(ULocale.ACTUAL_LOCALE);
    678         if(uloInd==null){
    679             errln("did not get the expected ULocale");
    680         }
    681         logln(uloInd.toString());
    682         Locale locInd = uloInd.toLocale();
    683         if(locInd==null){
    684             errln("did not get the expected result");
    685         }
    686         logln(locInd.toString());
    687     }
    688 
    689     @Test
    690     public void TestInitWithCurrentTime() {
    691         // jb4555
    692         // if the chinese calendar current millis isn't called, the default year is wrong.
    693         // this test is assuming the 'year' is the current cycle
    694         // so when we cross a cycle boundary, the target will need to change
    695         // that shouldn't be for awhile yet...
    696 
    697         ChineseCalendar cc = new ChineseCalendar();
    698         cc.set(Calendar.YEAR, 22);
    699         cc.set(Calendar.MONTH, 0);
    700          // need to set leap month flag off, otherwise, the test case always fails when
    701          // current time is in a leap month
    702         cc.set(Calendar.IS_LEAP_MONTH, 0);
    703         cc.set(Calendar.DATE, 19);
    704         cc.set(Calendar.HOUR_OF_DAY, 0);
    705         cc.set(Calendar.MINUTE, 0);
    706         cc.set(Calendar.SECOND, 0);
    707         cc.set(Calendar.MILLISECOND, 0);
    708 
    709         cc.add(Calendar.DATE, 1);
    710 
    711         Calendar cal = new GregorianCalendar(2005, Calendar.FEBRUARY, 28);
    712         Date target = cal.getTime();
    713         Date result = cc.getTime();
    714 
    715         assertEquals("chinese and gregorian date should match", target, result);
    716     }
    717 
    718     @Test
    719     public void Test6510()
    720     {
    721         Calendar gregorianCalendar;
    722         ChineseCalendar chineseCalendar, chineseCalendar2;
    723         ChineseDateFormat dateFormat;
    724         SimpleDateFormat simpleDateFormat;
    725 
    726         simpleDateFormat = new com.ibm.icu.text.SimpleDateFormat("MM/dd/yyyy G 'at' HH:mm:ss vvvv", Locale.US);
    727         dateFormat = new com.ibm.icu.text.ChineseDateFormat("MM/dd/yyyy(G) HH:mm:ss", Locale.CHINA);
    728 
    729         // lunar to gregorian
    730         chineseCalendar = new ChineseCalendar(77, 26, Calendar.JANUARY, 0, 6, 0, 0, 0);
    731         // coverage
    732         assertEquals("equivalent ChineseCalendar() constructors", chineseCalendar,
    733                 new ChineseCalendar(77, 26, Calendar.JANUARY, 0, 6));
    734 
    735         gregorianCalendar = Calendar.getInstance(Locale.US);
    736         gregorianCalendar.setTime(chineseCalendar.getTime());
    737 
    738         // gregorian to lunar
    739         chineseCalendar2 = new ChineseCalendar();
    740         chineseCalendar2.setTimeInMillis(gregorianCalendar.getTimeInMillis());
    741 
    742         // validate roundtrip
    743         if (chineseCalendar.getTimeInMillis() != chineseCalendar2.getTimeInMillis())
    744         {
    745             errln("time1: " + chineseCalendar.getTimeInMillis());
    746             errln("time2: " + chineseCalendar2.getTimeInMillis());
    747             errln("Lunar [MM/dd/y(G) HH:mm:ss] " + dateFormat.format(chineseCalendar));
    748             errln("**PROBLEM Grego [MM/dd/y(G) HH:mm:ss] " + simpleDateFormat.format(gregorianCalendar));
    749             errln("Grego [MM/dd/y(G) HH:mm:ss] " + simpleDateFormat.format(gregorianCalendar));
    750             errln("Lunar [MM/dd/y(G) HH:mm:ss] " + dateFormat.format(chineseCalendar2));
    751         }
    752     }
    753 }
    754