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