1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // 2016 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 /* 5 ******************************************************************************* 6 * Copyright (C) 2012, International Business Machines Corporation and * 7 * others. All Rights Reserved. * 8 ******************************************************************************* 9 */ 10 package android.icu.dev.test.calendar; 11 import java.util.Date; 12 13 import org.junit.Test; 14 import org.junit.runner.RunWith; 15 import org.junit.runners.JUnit4; 16 17 import android.icu.text.DateFormat; 18 import android.icu.util.Calendar; 19 import android.icu.util.DangiCalendar; 20 import android.icu.util.GregorianCalendar; 21 import android.icu.util.TimeZone; 22 import android.icu.util.ULocale; 23 import android.icu.testsharding.MainTestShard; 24 25 @MainTestShard 26 @RunWith(JUnit4.class) 27 public class DangiTest extends CalendarTestFmwk { 28 /** 29 * Test basic mapping to and from Gregorian. 30 */ 31 @Test 32 public void TestMapping() { 33 final int[] DATA = { 34 // (Note: months are 1-based) 35 // Gregorian Korean (Dan-gi) 36 1964, 9, 4, 4297, 7,0, 28, 37 1964, 9, 5, 4297, 7,0, 29, 38 1964, 9, 6, 4297, 8,0, 1, 39 1964, 9, 7, 4297, 8,0, 2, 40 1961, 12, 25, 4294, 11,0, 18, 41 1999, 6, 4, 4332, 4,0, 21, 42 43 1990, 5, 23, 4323, 4,0, 29, 44 1990, 5, 24, 4323, 5,0, 1, 45 1990, 6, 22, 4323, 5,0, 30, 46 1990, 6, 23, 4323, 5,1, 1, 47 1990, 7, 20, 4323, 5,1, 28, 48 1990, 7, 21, 4323, 5,1, 29, 49 1990, 7, 22, 4323, 6,0, 1, 50 51 // Some tricky dates (where GMT+8 doesn't agree with GMT+9) 52 // 53 // The list is from http://www.math.snu.ac.kr/~kye/others/lunar.html ('kye ref'). 54 // However, for some dates disagree with the above reference so KASI's 55 // calculation was cross-referenced: 56 // http://astro.kasi.re.kr/Life/ConvertSolarLunarForm.aspx?MenuID=115 57 1880, 11, 3, 4213, 10,0, 1, // astronomer's GMT+8 / KASI disagrees with the kye ref 58 1882, 12, 10, 4215, 11,0, 1, 59 1883, 7, 4, 4216, 6,0, 1, 60 1884, 4, 25, 4217, 4,0, 1, 61 1885, 5, 14, 4218, 4,0, 1, 62 1891, 1, 10, 4223, 12,0, 1, 63 1893, 4, 16, 4226, 3,0, 1, 64 1894, 5, 5, 4227, 4,0, 1, 65 1897, 7, 29, 4230, 7,0, 1, // astronomer's GMT+8 disagrees with all other ref (looks like our astronomer's error, see ad hoc fix at ChineseCalendar::getTimezoneOffset) 66 1903, 10, 20, 4236, 9,0, 1, 67 1904, 1, 17, 4236, 12,0, 1, 68 1904, 11, 7, 4237, 10,0, 1, 69 1905, 5, 4, 4238, 4,0, 1, 70 1907, 7, 10, 4240, 6,0, 1, 71 1908, 4, 30, 4241, 4,0, 1, 72 1908, 9, 25, 4241, 9,0, 1, 73 1909, 9, 14, 4242, 8,0, 1, 74 1911, 12, 20, 4244, 11,0, 1, 75 1976, 11, 22, 4309, 10,0, 1, 76 }; 77 78 Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi")); 79 StringBuilder buf = new StringBuilder(); 80 81 logln("Gregorian -> Korean Lunar (Dangi)"); 82 83 Calendar grego = Calendar.getInstance(); 84 grego.clear(); 85 for (int i = 0; i < DATA.length;) { 86 grego.set(DATA[i++], DATA[i++] - 1, DATA[i++]); 87 Date date = grego.getTime(); 88 cal.setTime(date); 89 int y = cal.get(Calendar.EXTENDED_YEAR); 90 int m = cal.get(Calendar.MONTH) + 1; // 0-based -> 1-based 91 int L = cal.get(Calendar.IS_LEAP_MONTH); 92 int d = cal.get(Calendar.DAY_OF_MONTH); 93 int yE = DATA[i++]; // Expected y, m, isLeapMonth, d 94 int mE = DATA[i++]; // 1-based 95 int LE = DATA[i++]; 96 int dE = DATA[i++]; 97 buf.setLength(0); 98 buf.append(date + " -> "); 99 buf.append(y + "/" + m + (L == 1 ? "(leap)" : "") + "/" + d); 100 if (y == yE && m == mE && L == LE && d == dE) { 101 logln("OK: " + buf.toString()); 102 } else { 103 errln("Fail: " + buf.toString() + ", expected " + yE + "/" + mE + (LE == 1 ? "(leap)" : "") + "/" + dE); 104 } 105 } 106 107 logln("Korean Lunar (Dangi) -> Gregorian"); 108 for (int i = 0; i < DATA.length;) { 109 grego.set(DATA[i++], DATA[i++] - 1, DATA[i++]); 110 Date dexp = grego.getTime(); 111 int cyear = DATA[i++]; 112 int cmonth = DATA[i++]; 113 int cisleapmonth = DATA[i++]; 114 int cdayofmonth = DATA[i++]; 115 cal.clear(); 116 cal.set(Calendar.EXTENDED_YEAR, cyear); 117 cal.set(Calendar.MONTH, cmonth - 1); 118 cal.set(Calendar.IS_LEAP_MONTH, cisleapmonth); 119 cal.set(Calendar.DAY_OF_MONTH, cdayofmonth); 120 Date date = cal.getTime(); 121 buf.setLength(0); 122 buf.append(cyear + "/" + cmonth + (cisleapmonth == 1 ? "(leap)" : "") + "/" + cdayofmonth); 123 buf.append(" -> " + date); 124 if (date.equals(dexp)) { 125 logln("OK: " + buf.toString()); 126 } else { 127 errln("Fail: " + buf.toString() + ", expected " + dexp); 128 } 129 } 130 } 131 132 /** 133 * Make sure no Gregorian dates map to Chinese 1-based day of 134 * month zero. This was a problem with some of the astronomical 135 * new moon determinations. 136 */ 137 @Test 138 public void TestZeroDOM() { 139 Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi")); 140 GregorianCalendar greg = new GregorianCalendar(1989, Calendar.SEPTEMBER, 1); 141 logln("Start: " + greg.getTime()); 142 for (int i=0; i<1000; ++i) { 143 cal.setTimeInMillis(greg.getTimeInMillis()); 144 if (cal.get(Calendar.DAY_OF_MONTH) == 0) { 145 errln("Fail: " + greg.getTime() + " -> " + 146 cal.get(Calendar.YEAR) + "/" + 147 cal.get(Calendar.MONTH) + 148 (cal.get(Calendar.IS_LEAP_MONTH)==1?"(leap)":"") + 149 "/" + cal.get(Calendar.DAY_OF_MONTH)); 150 } 151 greg.add(Calendar.DAY_OF_YEAR, 1); 152 } 153 logln("End: " + greg.getTime()); 154 } 155 156 /** 157 * Test minimum and maximum functions. 158 */ 159 @Test 160 public void TestLimits() { 161 // The number of days and the start date can be adjusted 162 // arbitrarily to either speed up the test or make it more 163 // thorough, but try to test at least a full year, preferably a 164 // full non-leap and a full leap year. 165 166 // Final parameter is either number of days, if > 0, or test 167 // duration in seconds, if < 0. 168 Calendar tempcal = Calendar.getInstance(); 169 tempcal.clear(); 170 tempcal.set(1989, Calendar.NOVEMBER, 1); 171 Calendar dangi = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi")); 172 doLimitsTest(dangi, null, tempcal.getTime()); 173 doTheoreticalLimitsTest(dangi, true); 174 } 175 176 /** 177 * Make sure IS_LEAP_MONTH participates in field resolution. 178 */ 179 @Test 180 public void TestResolution() { 181 Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi")); 182 DateFormat fmt = DateFormat.getDateInstance(cal, DateFormat.DEFAULT); 183 184 // May 22 4334 = y4334 m4 d30 doy119 185 // May 23 4334 = y4334 m4* d1 doy120 186 187 final int THE_YEAR = 4334; 188 final int END = -1; 189 190 int[] DATA = { 191 // Format: 192 // (field, value)+, END, exp.month, exp.isLeapMonth, exp.DOM 193 // Note: exp.month is ONE-BASED 194 195 // If we set DAY_OF_YEAR only, that should be used 196 Calendar.DAY_OF_YEAR, 1, 197 END, 198 1,0,1, // Expect 1-1 199 200 // If we set MONTH only, that should be used 201 Calendar.IS_LEAP_MONTH, 1, 202 Calendar.DAY_OF_MONTH, 1, 203 Calendar.MONTH, 3, 204 END, 205 4,1,1, // Expect 4*-1 206 207 // If we set the DOY last, that should take precedence 208 Calendar.MONTH, 1, // Should ignore 209 Calendar.IS_LEAP_MONTH, 1, // Should ignore 210 Calendar.DAY_OF_MONTH, 1, // Should ignore 211 Calendar.DAY_OF_YEAR, 121, 212 END, 213 4,1,2, // Expect 4*-2 214 215 // If we set IS_LEAP_MONTH last, that should take precedence 216 Calendar.MONTH, 3, 217 Calendar.DAY_OF_MONTH, 1, 218 Calendar.DAY_OF_YEAR, 5, // Should ignore 219 Calendar.IS_LEAP_MONTH, 1, 220 END, 221 4,1,1, // Expect 4*-1 222 }; 223 224 StringBuilder buf = new StringBuilder(); 225 for (int i=0; i<DATA.length; ) { 226 cal.clear(); 227 cal.set(Calendar.EXTENDED_YEAR, THE_YEAR); 228 buf.setLength(0); 229 buf.append("EXTENDED_YEAR=" + THE_YEAR); 230 while (DATA[i] != END) { 231 cal.set(DATA[i++], DATA[i++]); 232 buf.append(" " + fieldName(DATA[i-2]) + "=" + DATA[i-1]); 233 } 234 ++i; // Skip over END mark 235 int expMonth = DATA[i++]-1; 236 int expIsLeapMonth = DATA[i++]; 237 int expDOM = DATA[i++]; 238 int month = cal.get(Calendar.MONTH); 239 int isLeapMonth = cal.get(Calendar.IS_LEAP_MONTH); 240 int dom = cal.get(Calendar.DAY_OF_MONTH); 241 if (expMonth == month && expIsLeapMonth == isLeapMonth && 242 dom == expDOM) { 243 logln("OK: " + buf + " => " + fmt.format(cal.getTime())); 244 } else { 245 String s = fmt.format(cal.getTime()); 246 cal.clear(); 247 cal.set(Calendar.EXTENDED_YEAR, THE_YEAR); 248 cal.set(Calendar.MONTH, expMonth); 249 cal.set(Calendar.IS_LEAP_MONTH, expIsLeapMonth); 250 cal.set(Calendar.DAY_OF_MONTH, expDOM); 251 errln("Fail: " + buf + " => " + s + 252 "=" + (month+1) + "," + isLeapMonth + "," + dom + 253 ", expected " + fmt.format(cal.getTime()) + 254 "=" + (expMonth+1) + "," + expIsLeapMonth + "," + expDOM); 255 } 256 } 257 } 258 259 /** 260 * Test the behavior of fields that are out of range. 261 */ 262 @Test 263 public void TestOutOfRange() { 264 int[] DATA = new int[] { 265 // Input Output 266 4334, 13, 1, 4335, 1, 1, 267 4334, 18, 1, 4335, 6, 1, 268 4335, 0, 1, 4334, 12, 1, 269 4335, -6, 1, 4334, 6, 1, 270 4334, 1, 32, 4334, 2, 2, // 1-4334 has 30 days 271 4334, 2, -1, 4334, 1, 29, 272 }; 273 Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi")); 274 for (int i = 0; i < DATA.length;) { 275 int y1 = DATA[i++]; 276 int m1 = DATA[i++] - 1; 277 int d1 = DATA[i++]; 278 int y2 = DATA[i++]; 279 int m2 = DATA[i++] - 1; 280 int d2 = DATA[i++]; 281 cal.clear(); 282 cal.set(Calendar.EXTENDED_YEAR, y1); 283 cal.set(MONTH, m1); 284 cal.set(DATE, d1); 285 int y = cal.get(Calendar.EXTENDED_YEAR); 286 int m = cal.get(MONTH); 287 int d = cal.get(DATE); 288 if (y != y2 || m != m2 || d != d2) { 289 errln("Fail: " + y1 + "/" + (m1 + 1) + "/" + d1 + " resolves to " + y + "/" + (m + 1) + "/" + d 290 + ", expected " + y2 + "/" + (m2 + 1) + "/" + d2); 291 } else if (isVerbose()) { 292 logln("OK: " + y1 + "/" + (m1 + 1) + "/" + d1 + " resolves to " + y + "/" + (m + 1) + "/" + d); 293 } 294 } 295 } 296 297 /** 298 * Test the behavior of KoreanLunarCalendar.add(). The only real 299 * nastiness with roll is the MONTH field around leap months. 300 */ 301 @Test 302 public void TestAdd() { 303 int[][] tests = new int[][] { 304 // MONTHS ARE 1-BASED HERE 305 // input add output 306 // year mon day field amount year mon day 307 { 4338, 3,0, 15, MONTH, 3, 4338, 6,0, 15 }, // normal 308 { 4335, 12,0, 15, MONTH, 1, 4336, 1,0, 15 }, // across year 309 { 4336, 1,0, 15, MONTH, -1, 4335, 12,0, 15 }, // across year 310 { 4334, 3,0, 15, MONTH, 3, 4334, 5,0, 15 }, // 4=leap 311 { 4334, 3,0, 15, MONTH, 2, 4334, 4,1, 15 }, // 4=leap 312 { 4334, 4,0, 15, MONTH, 1, 4334, 4,1, 15 }, // 4=leap 313 { 4334, 4,1, 15, MONTH, 1, 4334, 5,0, 15 }, // 4=leap 314 { 4334, 3,0, 30, MONTH, 2, 4334, 4,1, 29 }, // dom should pin 315 { 4334, 3,0, 30, MONTH, 3, 4334, 5,0, 30 }, // no dom pin 316 { 4334, 3,0, 30, MONTH, 4, 4334, 6,0, 29 }, // dom should pin 317 }; 318 319 Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi")); 320 doRollAddDangi(ADD, cal, tests); 321 } 322 323 /** 324 * Test the behavior of KoreanLunarCalendar.roll(). The only real 325 * nastiness with roll is the MONTH field around leap months. 326 */ 327 @Test 328 public void TestRoll() { 329 int[][] tests = new int[][] { 330 // MONTHS ARE 1-BASED HERE 331 // input add output 332 // year mon day field amount year mon day 333 { 4338, 3,0, 15, MONTH, 3, 4338, 6,0, 15 }, // normal 334 { 4338, 3,0, 15, MONTH, 11, 4338, 2,0, 15 }, // normal 335 { 4335, 12,0, 15, MONTH, 1, 4335, 1,0, 15 }, // across year 336 { 4336, 1,0, 15, MONTH, -1, 4336, 12,0, 15 }, // across year 337 { 4334, 3,0, 15, MONTH, 3, 4334, 5,0, 15 }, // 4=leap 338 { 4334, 3,0, 15, MONTH, 16, 4334, 5,0, 15 }, // 4=leap 339 { 4334, 3,0, 15, MONTH, 2, 4334, 4,1, 15 }, // 4=leap 340 { 4334, 3,0, 15, MONTH, 28, 4334, 4,1, 15 }, // 4=leap 341 { 4334, 4,0, 15, MONTH, 1, 4334, 4,1, 15 }, // 4=leap 342 { 4334, 4,0, 15, MONTH, -12, 4334, 4,1, 15 }, // 4=leap 343 { 4334, 4,1, 15, MONTH, 1, 4334, 5,0, 15 }, // 4=leap 344 { 4334, 4,1, 15, MONTH, -25, 4334, 5,0, 15 }, // 4=leap 345 { 4334, 3,0, 30, MONTH, 2, 4334, 4,1, 29 }, // dom should pin 346 { 4334, 3,0, 30, MONTH, 15, 4334, 4,1, 29 }, // dom should pin 347 { 4334, 3,0, 30, MONTH, 16, 4334, 5,0, 30 }, // no dom pin 348 { 4334, 3,0, 30, MONTH, -9, 4334, 6,0, 29 }, // dom should pin 349 }; 350 351 Calendar cal = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi")); 352 doRollAddDangi(ROLL, cal, tests); 353 } 354 355 void doRollAddDangi(boolean roll, Calendar cal, int[][] tests) { 356 String name = roll ? "rolling" : "adding"; 357 358 for (int i = 0; i < tests.length; i++) { 359 int[] test = tests[i]; 360 361 cal.clear(); 362 cal.set(Calendar.EXTENDED_YEAR, test[0]); 363 cal.set(Calendar.MONTH, test[1] - 1); 364 cal.set(Calendar.IS_LEAP_MONTH, test[2]); 365 cal.set(Calendar.DAY_OF_MONTH, test[3]); 366 if (roll) { 367 cal.roll(test[4], test[5]); 368 } else { 369 cal.add(test[4], test[5]); 370 } 371 if (cal.get(Calendar.EXTENDED_YEAR) != test[6] || cal.get(MONTH) != (test[7] - 1) 372 || cal.get(Calendar.IS_LEAP_MONTH) != test[8] || cal.get(DATE) != test[9]) { 373 errln("Fail: " + name + " " + ymdToString(test[0], test[1] - 1, test[2], test[3]) + " " 374 + fieldName(test[4]) + " by " + test[5] + ": expected " 375 + ymdToString(test[6], test[7] - 1, test[8], test[9]) + ", got " + ymdToString(cal)); 376 } else if (isVerbose()) { 377 logln("OK: " + name + " " + ymdToString(test[0], test[1] - 1, test[2], test[3]) + " " 378 + fieldName(test[4]) + " by " + test[5] + ": got " + ymdToString(cal)); 379 } 380 } 381 } 382 383 /** 384 * Convert year,month,day values to the form "year/month/day". 385 * On input the month value is zero-based, but in the result string it is one-based. 386 */ 387 static public String ymdToString(int year, int month, int isLeapMonth, int day) { 388 return "" + year + "/" + (month + 1) + ((isLeapMonth != 0) ? "(leap)" : "") + "/" + day; 389 } 390 391 @Test 392 public void TestCoverage() { 393 // DangiCalendar() 394 // DangiCalendar(Date) 395 // DangiCalendar(TimeZone, ULocale) 396 Date d = new Date(); 397 398 DangiCalendar cal1 = new DangiCalendar(); 399 cal1.setTime(d); 400 401 DangiCalendar cal2 = new DangiCalendar(d); 402 403 DangiCalendar cal3 = new DangiCalendar(TimeZone.getDefault(), ULocale.getDefault()); 404 cal3.setTime(d); 405 406 assertEquals("DangiCalendar() and DangiCalendar(Date)", cal1, cal2); 407 assertEquals("DangiCalendar() and DangiCalendar(TimeZone,ULocale)", cal1, cal3); 408 409 // String getType() 410 String type = cal1.getType(); 411 assertEquals("getType()", "dangi", type); 412 } 413 414 @Test 415 public void TestInitWithCurrentTime() { 416 // If the chinese calendar current millis isn't called, the default year is wrong. 417 // this test is assuming the 'year' is the current cycle 418 // so when we cross a cycle boundary, the target will need to change 419 // that shouldn't be for awhile yet... 420 421 Calendar cc = Calendar.getInstance(new ULocale("ko_KR@calendar=dangi")); 422 cc.set(Calendar.EXTENDED_YEAR, 4338); 423 cc.set(Calendar.MONTH, 0); 424 // need to set leap month flag off, otherwise, the test case always fails when 425 // current time is in a leap month 426 cc.set(Calendar.IS_LEAP_MONTH, 0); 427 cc.set(Calendar.DATE, 19); 428 cc.set(Calendar.HOUR_OF_DAY, 0); 429 cc.set(Calendar.MINUTE, 0); 430 cc.set(Calendar.SECOND, 0); 431 cc.set(Calendar.MILLISECOND, 0); 432 433 cc.add(Calendar.DATE, 1); 434 435 Calendar cal = new GregorianCalendar(2005, Calendar.FEBRUARY, 28); 436 Date target = cal.getTime(); 437 Date result = cc.getTime(); 438 439 assertEquals("chinese and gregorian date should match", target, result); 440 } 441 } 442