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