]> git.saurik.com Git - apple/icu.git/blame_incremental - icuSources/test/intltest/callimts.cpp
ICU-551.51.4.tar.gz
[apple/icu.git] / icuSources / test / intltest / callimts.cpp
... / ...
CommitLineData
1/***********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 1997-2013, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 ***********************************************************************/
6
7#include "unicode/utypes.h"
8
9#if !UCONFIG_NO_FORMATTING
10
11#include "callimts.h"
12#include "caltest.h"
13#include "unicode/calendar.h"
14#include "unicode/gregocal.h"
15#include "unicode/datefmt.h"
16#include "unicode/smpdtfmt.h"
17#include "putilimp.h"
18#include "cstring.h"
19
20U_NAMESPACE_USE
21void CalendarLimitTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
22{
23 if (exec) logln("TestSuite TestCalendarLimit");
24 switch (index) {
25 // Re-enable this later
26 case 0:
27 name = "TestCalendarExtremeLimit";
28 if (exec) {
29 logln("TestCalendarExtremeLimit---"); logln("");
30 TestCalendarExtremeLimit();
31 }
32 break;
33 case 1:
34 name = "TestLimits";
35 if (exec) {
36 logln("TestLimits---"); logln("");
37 TestLimits();
38 }
39 break;
40
41 default: name = ""; break;
42 }
43}
44
45
46// *****************************************************************************
47// class CalendarLimitTest
48// *****************************************************************************
49
50// -------------------------------------
51void
52CalendarLimitTest::test(UDate millis, icu::Calendar* cal, icu::DateFormat* fmt)
53{
54 static const UDate kDrift = 1e-10;
55 UErrorCode exception = U_ZERO_ERROR;
56 UnicodeString theDate;
57 UErrorCode status = U_ZERO_ERROR;
58 cal->setTime(millis, exception);
59 if (U_SUCCESS(exception)) {
60 fmt->format(millis, theDate);
61 UDate dt = fmt->parse(theDate, status);
62 // allow a small amount of error (drift)
63 if(! withinErr(dt, millis, kDrift)) {
64 errln("FAIL:round trip for large milli, got: %.1lf wanted: %.1lf. (delta %.2lf greater than %.2lf)",
65 dt, millis, uprv_fabs(millis-dt), uprv_fabs(dt*kDrift));
66 logln(UnicodeString(" ") + theDate + " " + CalendarTest::calToStr(*cal));
67 } else {
68 logln(UnicodeString("OK: got ") + dt + ", wanted " + millis);
69 logln(UnicodeString(" ") + theDate);
70 }
71 }
72}
73
74// -------------------------------------
75
76// bug 986c: deprecate nextDouble/previousDouble
77//|double
78//|CalendarLimitTest::nextDouble(double a)
79//|{
80//| return uprv_nextDouble(a, TRUE);
81//|}
82//|
83//|double
84//|CalendarLimitTest::previousDouble(double a)
85//|{
86//| return uprv_nextDouble(a, FALSE);
87//|}
88
89UBool
90CalendarLimitTest::withinErr(double a, double b, double err)
91{
92 return ( uprv_fabs(a - b) < uprv_fabs(a * err) );
93}
94
95void
96CalendarLimitTest::TestCalendarExtremeLimit()
97{
98 UErrorCode status = U_ZERO_ERROR;
99 Calendar *cal = Calendar::createInstance(status);
100 if (failure(status, "Calendar::createInstance", TRUE)) return;
101 cal->adoptTimeZone(TimeZone::createTimeZone("GMT"));
102 DateFormat *fmt = DateFormat::createDateTimeInstance();
103 if(!fmt || !cal) {
104 dataerrln("can't open cal and/or fmt");
105 return;
106 }
107 fmt->adoptCalendar(cal);
108 ((SimpleDateFormat*) fmt)->applyPattern("HH:mm:ss.SSS Z, EEEE, MMMM d, yyyy G");
109
110
111 // This test used to test the algorithmic limits of the dates that
112 // GregorianCalendar could handle. However, the algorithm has
113 // been rewritten completely since then and the prior limits no
114 // longer apply. Instead, we now do basic round-trip testing of
115 // some extreme (but still manageable) dates.
116 UDate m;
117 logln("checking 1e16..1e17");
118 for ( m = 1e16; m < 1e17; m *= 1.1) {
119 test(m, cal, fmt);
120 }
121 logln("checking -1e14..-1e15");
122 for ( m = -1e14; m > -1e15; m *= 1.1) {
123 test(m, cal, fmt);
124 }
125
126 // This is 2^52 - 1, the largest allowable mantissa with a 0
127 // exponent in a 64-bit double
128 UDate VERY_EARLY_MILLIS = - 4503599627370495.0;
129 UDate VERY_LATE_MILLIS = 4503599627370495.0;
130
131 // I am removing the previousDouble and nextDouble calls below for
132 // two reasons: 1. As part of jitterbug 986, I am deprecating
133 // these methods and removing calls to them. 2. This test is a
134 // non-critical boundary behavior test.
135 test(VERY_EARLY_MILLIS, cal, fmt);
136 //test(previousDouble(VERY_EARLY_MILLIS), cal, fmt);
137 test(VERY_LATE_MILLIS, cal, fmt);
138 //test(nextDouble(VERY_LATE_MILLIS), cal, fmt);
139 delete fmt;
140}
141
142void
143CalendarLimitTest::TestLimits(void) {
144 static const UDate DEFAULT_START = 944006400000.0; // 1999-12-01T00:00Z
145 static const int32_t DEFAULT_END = -120; // Default for non-quick is run 2 minutes
146
147 static const struct {
148 const char *type;
149 UBool hasLeapMonth;
150 UDate actualTestStart;
151 int32_t actualTestEnd;
152 } TestCases[] = {
153 {"gregorian", FALSE, DEFAULT_START, DEFAULT_END},
154 {"japanese", FALSE, 596937600000.0, DEFAULT_END}, // 1988-12-01T00:00Z, Showa 63
155 {"buddhist", FALSE, DEFAULT_START, DEFAULT_END},
156 {"roc", FALSE, DEFAULT_START, DEFAULT_END},
157 {"persian", FALSE, DEFAULT_START, DEFAULT_END},
158 {"islamic-civil", FALSE, DEFAULT_START, DEFAULT_END},
159 {"islamic", FALSE, DEFAULT_START, 800000}, // Approx. 2250 years from now, after which some rounding errors occur in Islamic calendar
160 {"hebrew", TRUE, DEFAULT_START, DEFAULT_END},
161 {"chinese", TRUE, DEFAULT_START, DEFAULT_END},
162 {"dangi", TRUE, DEFAULT_START, DEFAULT_END},
163 {"indian", FALSE, DEFAULT_START, DEFAULT_END},
164 {"coptic", FALSE, DEFAULT_START, DEFAULT_END},
165 {"ethiopic", FALSE, DEFAULT_START, DEFAULT_END},
166 {"ethiopic-amete-alem", FALSE, DEFAULT_START, DEFAULT_END},
167 {NULL, FALSE, 0, 0}
168 };
169
170 int16_t i = 0;
171 char buf[64];
172
173 for (i = 0; TestCases[i].type; i++) {
174 UErrorCode status = U_ZERO_ERROR;
175 uprv_strcpy(buf, "root@calendar=");
176 strcat(buf, TestCases[i].type);
177 Calendar *cal = Calendar::createInstance(buf, status);
178 if (failure(status, "Calendar::createInstance", TRUE)) {
179 continue;
180 }
181 if (uprv_strcmp(cal->getType(), TestCases[i].type) != 0) {
182 errln((UnicodeString)"FAIL: Wrong calendar type: " + cal->getType()
183 + " Requested: " + TestCases[i].type);
184 delete cal;
185 continue;
186 }
187 // Do the test
188 doTheoreticalLimitsTest(*cal, TestCases[i].hasLeapMonth);
189 doLimitsTest(*cal, TestCases[i].actualTestStart,TestCases[i].actualTestEnd);
190 delete cal;
191 }
192}
193
194void
195CalendarLimitTest::doTheoreticalLimitsTest(Calendar& cal, UBool leapMonth) {
196 const char* calType = cal.getType();
197
198 int32_t nDOW = cal.getMaximum(UCAL_DAY_OF_WEEK);
199 int32_t maxDOY = cal.getMaximum(UCAL_DAY_OF_YEAR);
200 int32_t lmaxDOW = cal.getLeastMaximum(UCAL_DAY_OF_YEAR);
201 int32_t maxWOY = cal.getMaximum(UCAL_WEEK_OF_YEAR);
202 int32_t lmaxWOY = cal.getLeastMaximum(UCAL_WEEK_OF_YEAR);
203 int32_t maxM = cal.getMaximum(UCAL_MONTH) + 1;
204 int32_t lmaxM = cal.getLeastMaximum(UCAL_MONTH) + 1;
205 int32_t maxDOM = cal.getMaximum(UCAL_DAY_OF_MONTH);
206 int32_t lmaxDOM = cal.getLeastMaximum(UCAL_DAY_OF_MONTH);
207 int32_t maxDOWIM = cal.getMaximum(UCAL_DAY_OF_WEEK_IN_MONTH);
208 int32_t lmaxDOWIM = cal.getLeastMaximum(UCAL_DAY_OF_WEEK_IN_MONTH);
209 int32_t maxWOM = cal.getMaximum(UCAL_WEEK_OF_MONTH);
210 int32_t lmaxWOM = cal.getLeastMaximum(UCAL_WEEK_OF_MONTH);
211 int32_t minDaysInFirstWeek = cal.getMinimalDaysInFirstWeek();
212
213 // Day of year
214 int32_t expected;
215 if (!leapMonth) {
216 expected = maxM*maxDOM;
217 if (maxDOY > expected) {
218 errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of DAY_OF_YEAR is too big: "
219 + maxDOY + "/expected: <=" + expected);
220 }
221 expected = lmaxM*lmaxDOM;
222 if (lmaxDOW < expected) {
223 errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of DAY_OF_YEAR is too small: "
224 + lmaxDOW + "/expected: >=" + expected);
225 }
226 }
227
228 // Week of year
229 expected = maxDOY/nDOW + 1;
230 if (maxWOY > expected) {
231 errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of WEEK_OF_YEAR is too big: "
232 + maxWOY + "/expected: <=" + expected);
233 }
234 expected = lmaxDOW/nDOW;
235 if (lmaxWOY < expected) {
236 errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of WEEK_OF_YEAR is too small: "
237 + lmaxWOY + "/expected >=" + expected);
238 }
239
240 // Day of week in month
241 expected = (maxDOM + nDOW - 1)/nDOW;
242 if (maxDOWIM != expected) {
243 errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: "
244 + maxDOWIM + "/expected: " + expected);
245 }
246 expected = (lmaxDOM + nDOW - 1)/nDOW;
247 if (lmaxDOWIM != expected) {
248 errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of DAY_OF_WEEK_IN_MONTH is incorrect: "
249 + lmaxDOWIM + "/expected: " + expected);
250 }
251
252 // Week of month
253 expected = (maxDOM + (nDOW - 1) + (nDOW - minDaysInFirstWeek)) / nDOW;
254 if (maxWOM != expected) {
255 errln((UnicodeString)"FAIL: [" + calType + "] Maximum value of WEEK_OF_MONTH is incorrect: "
256 + maxWOM + "/expected: " + expected);
257 }
258 expected = (lmaxDOM + (nDOW - minDaysInFirstWeek)) / nDOW;
259 if (lmaxWOM != expected) {
260 errln((UnicodeString)"FAIL: [" + calType + "] Least maximum value of WEEK_OF_MONTH is incorrect: "
261 + lmaxWOM + "/expected: " + expected);
262 }
263}
264
265void
266CalendarLimitTest::doLimitsTest(Calendar& cal, UDate startDate, int32_t endTime) {
267 int32_t testTime = quick ? ( endTime / 40 ) : endTime;
268 doLimitsTest(cal, NULL /*default fields*/, startDate, testTime);
269}
270
271void
272CalendarLimitTest::doLimitsTest(Calendar& cal,
273 const int32_t* fieldsToTest,
274 UDate startDate,
275 int32_t testDuration) {
276 static const int32_t FIELDS[] = {
277 UCAL_ERA,
278 UCAL_YEAR,
279 UCAL_MONTH,
280 UCAL_WEEK_OF_YEAR,
281 UCAL_WEEK_OF_MONTH,
282 UCAL_DAY_OF_MONTH,
283 UCAL_DAY_OF_YEAR,
284 UCAL_DAY_OF_WEEK_IN_MONTH,
285 UCAL_YEAR_WOY,
286 UCAL_EXTENDED_YEAR,
287 -1,
288 };
289
290 static const char* FIELD_NAME[] = {
291 "ERA", "YEAR", "MONTH", "WEEK_OF_YEAR", "WEEK_OF_MONTH",
292 "DAY_OF_MONTH", "DAY_OF_YEAR", "DAY_OF_WEEK",
293 "DAY_OF_WEEK_IN_MONTH", "AM_PM", "HOUR", "HOUR_OF_DAY",
294 "MINUTE", "SECOND", "MILLISECOND", "ZONE_OFFSET",
295 "DST_OFFSET", "YEAR_WOY", "DOW_LOCAL", "EXTENDED_YEAR",
296 "JULIAN_DAY", "MILLISECONDS_IN_DAY",
297 "IS_LEAP_MONTH"
298 };
299
300 UErrorCode status = U_ZERO_ERROR;
301 int32_t i, j;
302 UnicodeString ymd;
303
304 GregorianCalendar greg(status);
305 if (failure(status, "new GregorianCalendar")) {
306 return;
307 }
308 greg.setTime(startDate, status);
309 if (failure(status, "GregorianCalendar::setTime")) {
310 return;
311 }
312 logln((UnicodeString)"Start: " + startDate);
313
314 if (fieldsToTest == NULL) {
315 fieldsToTest = FIELDS;
316 }
317
318
319 // Keep a record of minima and maxima that we actually see.
320 // These are kept in an array of arrays of hashes.
321 int32_t limits[UCAL_FIELD_COUNT][4];
322 for (j = 0; j < UCAL_FIELD_COUNT; j++) {
323 limits[j][0] = INT32_MAX;
324 limits[j][1] = INT32_MIN;
325 limits[j][2] = INT32_MAX;
326 limits[j][3] = INT32_MIN;
327 }
328
329 // This test can run for a long time; show progress.
330 UDate millis = ucal_getNow();
331 UDate mark = millis + 5000; // 5 sec
332 millis -= testDuration * 1000; // stop time if testDuration<0
333
334 for (i = 0;
335 testDuration > 0 ? i < testDuration
336 : ucal_getNow() < millis;
337 ++i) {
338 if (ucal_getNow() >= mark) {
339 logln((UnicodeString)"(" + i + " days)");
340 mark += 5000; // 5 sec
341 }
342 UDate testMillis = greg.getTime(status);
343
344 if (testMillis == 2768943600000.0) {
345 // unusual failure, get day of month 0.
346 // doesn't happen with runConfigure and make check inside
347 // icuSources directory (i.e. using ICU build without wrapper makefile)
348 continue;
349 }
350
351 cal.setTime(testMillis, status);
352 cal.setMinimalDaysInFirstWeek(1);
353 if (failure(status, "Calendar set/getTime")) {
354 return;
355 }
356 for (j = 0; fieldsToTest[j] >= 0; ++j) {
357 UCalendarDateFields f = (UCalendarDateFields)fieldsToTest[j];
358 int32_t v = cal.get(f, status);
359 int32_t minActual = cal.getActualMinimum(f, status);
360 int32_t maxActual = cal.getActualMaximum(f, status);
361 int32_t minLow = cal.getMinimum(f);
362 int32_t minHigh = cal.getGreatestMinimum(f);
363 int32_t maxLow = cal.getLeastMaximum(f);
364 int32_t maxHigh = cal.getMaximum(f);
365
366 if (limits[j][0] > minActual) {
367 // the minimum
368 limits[j][0] = minActual;
369 }
370 if (limits[j][1] < minActual) {
371 // the greatest minimum
372 limits[j][1] = minActual;
373 }
374 if (limits[j][2] > maxActual) {
375 // the least maximum
376 limits[j][2] = maxActual;
377 }
378 if (limits[j][3] < maxActual) {
379 // the maximum
380 limits[j][3] = maxActual;
381 }
382
383 if (minActual < minLow || minActual > minHigh) {
384 errln((UnicodeString)"Fail: [" + cal.getType() + "] " +
385 ymdToString(cal, ymd) +
386 " Range for min of " + FIELD_NAME[f] + "(" + f +
387 ")=" + minLow + ".." + minHigh +
388 ", actual_min=" + minActual);
389 }
390 if (maxActual < maxLow || maxActual > maxHigh) {
391 errln((UnicodeString)"Fail: [" + cal.getType() + "] " +
392 ymdToString(cal, ymd) +
393 " Range for max of " + FIELD_NAME[f] + "(" + f +
394 ")=" + maxLow + ".." + maxHigh +
395 ", actual_max=" + maxActual);
396 }
397 if (v < minActual || v > maxActual) {
398 // timebomb per #9967, fix with #9972
399 if ( uprv_strcmp(cal.getType(), "dangi") == 0 &&
400 testMillis >= 1865635198000.0 &&
401 logKnownIssue("9972", "as per #9967")) { // Feb 2029 gregorian, end of dangi 4361
402 logln((UnicodeString)"Fail: [" + cal.getType() + "] " +
403 ymdToString(cal, ymd) +
404 " " + FIELD_NAME[f] + "(" + f + ")=" + v +
405 ", actual=" + minActual + ".." + maxActual +
406 ", allowed=(" + minLow + ".." + minHigh + ")..(" +
407 maxLow + ".." + maxHigh + ")");
408 } else {
409 errln((UnicodeString)"Fail: [" + cal.getType() + "] " +
410 ymdToString(cal, ymd) +
411 " " + FIELD_NAME[f] + "(" + f + ")=" + v +
412 ", actual=" + minActual + ".." + maxActual +
413 ", allowed=(" + minLow + ".." + minHigh + ")..(" +
414 maxLow + ".." + maxHigh + ")");
415 }
416 }
417 }
418 greg.add(UCAL_DAY_OF_YEAR, 1, status);
419 if (failure(status, "Calendar::add")) {
420 return;
421 }
422 }
423
424 // Check actual maxima and minima seen against ranges returned
425 // by API.
426 UnicodeString buf;
427 for (j = 0; fieldsToTest[j] >= 0; ++j) {
428 int32_t rangeLow, rangeHigh;
429 UBool fullRangeSeen = TRUE;
430 UCalendarDateFields f = (UCalendarDateFields)fieldsToTest[j];
431
432 buf.remove();
433 buf.append((UnicodeString)"[" + cal.getType() + "] " + FIELD_NAME[f]);
434
435 // Minumum
436 rangeLow = cal.getMinimum(f);
437 rangeHigh = cal.getGreatestMinimum(f);
438 if (limits[j][0] != rangeLow || limits[j][1] != rangeHigh) {
439 fullRangeSeen = FALSE;
440 }
441 buf.append((UnicodeString)" minima range=" + rangeLow + ".." + rangeHigh);
442 buf.append((UnicodeString)" minima actual=" + limits[j][0] + ".." + limits[j][1]);
443
444 // Maximum
445 rangeLow = cal.getLeastMaximum(f);
446 rangeHigh = cal.getMaximum(f);
447 if (limits[j][2] != rangeLow || limits[j][3] != rangeHigh) {
448 fullRangeSeen = FALSE;
449 }
450 buf.append((UnicodeString)" maxima range=" + rangeLow + ".." + rangeHigh);
451 buf.append((UnicodeString)" maxima actual=" + limits[j][2] + ".." + limits[j][3]);
452
453 if (fullRangeSeen) {
454 logln((UnicodeString)"OK: " + buf);
455 } else {
456 // This may or may not be an error -- if the range of dates
457 // we scan over doesn't happen to contain a minimum or
458 // maximum, it doesn't mean some other range won't.
459 logln((UnicodeString)"Warning: " + buf);
460 }
461 }
462
463 logln((UnicodeString)"End: " + greg.getTime(status));
464}
465
466UnicodeString&
467CalendarLimitTest::ymdToString(const Calendar& cal, UnicodeString& str) {
468 UErrorCode status = U_ZERO_ERROR;
469 str.remove();
470 str.append((UnicodeString)"" + cal.get(UCAL_EXTENDED_YEAR, status)
471 + "/" + (cal.get(UCAL_MONTH, status) + 1)
472 + (cal.get(UCAL_IS_LEAP_MONTH, status) == 1 ? "(leap)" : "")
473 + "/" + cal.get(UCAL_DATE, status)
474 + " " + cal.get(UCAL_HOUR_OF_DAY, status)
475 + ":" + cal.get(UCAL_MINUTE, status)
476 + " zone(hrs) " + cal.get(UCAL_ZONE_OFFSET, status)/(60.0*60.0*1000.0)
477 + " dst(hrs) " + cal.get(UCAL_DST_OFFSET, status)/(60.0*60.0*1000.0)
478 + ", time(millis)=" + cal.getTime(status));
479 return str;
480}
481
482#endif /* #if !UCONFIG_NO_FORMATTING */
483
484// eof