home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Programming / ICU / src / icu / source / test / intltest / callimts.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-19  |  13.2 KB  |  403 lines

  1.  
  2. /*
  3. ********************************************************************
  4. * COPYRIGHT: 
  5. * (C) Copyright Taligent, Inc., 1997
  6. * (C) Copyright International Business Machines Corporation, 1997 - 1998
  7. * Licensed Material - Program-Property of IBM - All Rights Reserved. 
  8. * US Government Users Restricted Rights - Use, duplication, or disclosure 
  9. * restricted by GSA ADP Schedule Contract with IBM Corp. 
  10. *
  11. ********************************************************************
  12. */
  13.  
  14. #include "callimts.h"
  15. #include "calendar.h"
  16. #include "gregocal.h"
  17. #include "datefmt.h"
  18. #include "smpdtfmt.h"
  19. #include <float.h>
  20. #include <math.h>
  21.  
  22. void CalendarLimitTest::runIndexedTest( int32_t index, bool_t exec, char* &name, char* par )
  23. {
  24.     if (exec) logln("TestSuite TestCalendarLimit");
  25.     switch (index) {
  26.         // Re-enable this later
  27.         case 0:
  28.             name = "TestCalendarLimit";
  29.             if (exec) {
  30.                 logln("TestCalendarLimit---"); logln("");
  31.                 TestCalendarLimit();
  32.             }
  33.             break;
  34.         default: name = ""; break;
  35.     }
  36. }
  37.  
  38.  
  39. // *****************************************************************************
  40. // class CalendarLimitTest
  41. // *****************************************************************************
  42.  
  43. // this is 2^52 - 1, the largest allowable mantissa with a 0 exponent in a 64-bit double
  44. const UDate CalendarLimitTest::EARLIEST_SUPPORTED_MILLIS = - 4503599627370495.0;
  45. const UDate CalendarLimitTest::LATEST_SUPPORTED_MILLIS    =   4503599627370495.0;
  46.  
  47. // -------------------------------------
  48.  
  49. void
  50. CalendarLimitTest::test(UDate millis, Calendar* cal, DateFormat* fmt)
  51. {
  52.     UErrorCode exception = U_ZERO_ERROR;
  53.     UnicodeString theDate;
  54.     UErrorCode status = U_ZERO_ERROR;
  55.     UDate d = millis;
  56.     cal->setTime(millis, exception);
  57.     if (U_SUCCESS(exception)) {
  58.         fmt->format(millis, theDate);
  59.         UDate dt = fmt->parse(theDate, status);
  60.         // allow a small amount of error (drift)
  61.         if(! withinErr(dt, millis, 1e-10))
  62.             errln(UnicodeString("FAIL:round trip for large milli, got: ") + dt + " wanted: " + millis);
  63.         else {
  64.             logln(UnicodeString("OK: got ") + dt + ", wanted " + millis);
  65.             logln(UnicodeString("    ") + theDate);
  66.         }
  67.     }        
  68. }
  69.  
  70. // -------------------------------------
  71.  
  72. double
  73. CalendarLimitTest::nextDouble(double a)
  74. {
  75.     return icu_nextDouble(a, TRUE);
  76. }
  77.  
  78. double
  79. CalendarLimitTest::previousDouble(double a)
  80. {
  81.     return icu_nextDouble(a, FALSE);
  82. }
  83.  
  84. bool_t
  85. CalendarLimitTest::withinErr(double a, double b, double err)
  86. {
  87.     return ( icu_fabs(a - b) < icu_fabs(a * err) ); 
  88. }
  89.  
  90. void
  91. CalendarLimitTest::TestCalendarLimit()
  92. {
  93.     logln("Limit tests");
  94.     logln("--------------------");
  95.     UErrorCode status = U_ZERO_ERROR;
  96.     explore2(EARLIEST_SUPPORTED_MILLIS);
  97.     explore3(LATEST_SUPPORTED_MILLIS);
  98.     Calendar *cal = Calendar::createInstance(status);
  99.     if (failure(status, "Calendar::createInstance")) return;
  100.     cal->adoptTimeZone(TimeZone::createTimeZone("Africa/Casablanca"));
  101.     DateFormat *fmt = DateFormat::createDateTimeInstance();
  102.     fmt->adoptCalendar(cal);
  103.     ((SimpleDateFormat*) fmt)->applyPattern("HH:mm:ss.SSS zzz, EEEE, MMMM d, yyyy G");
  104.  
  105.     logln("");
  106.     logln("Round trip tests");
  107.     logln("--------------------");
  108.     // We happen to know that the upper failure point is between
  109.     // 1e17 and 1e18.
  110.     UDate m;
  111.     for ( m = 1e17; m < 1e18; m *= 1.1)
  112.     {
  113.         test(m, cal, fmt);
  114.     }
  115.     for ( m = -1e14; m > -1e15; m *= 1.1) {
  116.         test(m, cal, fmt);
  117.     }
  118.  
  119.     test(EARLIEST_SUPPORTED_MILLIS, cal, fmt);
  120.     test(previousDouble(EARLIEST_SUPPORTED_MILLIS), cal, fmt);
  121.     delete fmt;
  122. }
  123.  
  124. // -------------------------------------
  125.  
  126. void
  127. CalendarLimitTest::explore2(UDate expectedEarlyLimit)
  128. {
  129.     UDate millis = - 1;
  130.     int32_t* fields = new int32_t[3];
  131.     while (timeToFields(millis, fields)) 
  132.         millis *= 2;
  133.     UDate bad = millis;
  134.     UDate good = millis / 2;
  135.     UDate mid;
  136.     while ( ! withinErr(good, bad, 1e-15) ) { 
  137.         mid = (good + bad) / 2;
  138.         if (timeToFields(mid, fields)) 
  139.             good = mid;
  140.         else 
  141.             bad = mid;
  142.     }
  143.     timeToFields(good, fields);
  144.     logln(UnicodeString(UnicodeString("Good: "))  + good + " " + fields[0] + "/" + fields[1] + "/" + fields[2]);
  145.     timeToFields(bad, fields);
  146.     logln(UnicodeString(UnicodeString("Bad:  "))  + bad + " " + fields[0] + "/" + fields[1] + "/" + fields[2]);
  147.     if (good <= expectedEarlyLimit) {
  148.         logln("PASS: Limit <= expected.");
  149.     }
  150.     else 
  151.         errln(UnicodeString("FAIL: Expected limit ") + expectedEarlyLimit + "; got " + good);
  152.     delete[] fields;
  153. }
  154.  
  155. void
  156. CalendarLimitTest::explore3(UDate expectedLateLimit)
  157. {
  158.     UDate millis = 1;
  159.     int32_t* fields = new int32_t[3];
  160.     int32_t oldYear = -1;
  161.     int32_t newYear = -1;
  162.     while (TRUE) {
  163.         if(! timeToFields(millis, fields))
  164.             break;
  165.         newYear = fields[0];
  166.         if(newYear < oldYear)
  167.             break;
  168.         oldYear = newYear;
  169.         millis *= 2;
  170.     }
  171.  
  172.     // narrow the range a little
  173.     oldYear = -1;
  174.     newYear = -1;
  175.     millis /= 2;
  176.     while (TRUE) {
  177.         if(! timeToFields(millis, fields))
  178.             break;
  179.         newYear = fields[0];
  180.         if(newYear < oldYear)
  181.             break;
  182.         oldYear = newYear;
  183.         millis *= 1.01;
  184.     }
  185.  
  186.     // this isn't strictly accurate, but we are only trying to verify that
  187.     // the Calendar breaks AFTER the latest date it is promised to work with
  188.     UDate good = millis / 1.01;
  189.     UDate bad = millis;
  190.  
  191.     timeToFields(good, fields);
  192.     logln(UnicodeString(UnicodeString("Good:  "))  + good + " " + fields[0] + "/" + fields[1] + "/" + fields[2]);
  193.     timeToFields(bad, fields);
  194.     logln(UnicodeString(UnicodeString("Bad:   "))  + bad + " " + fields[0] + "/" + fields[1] + "/" + fields[2]);
  195.     if (good >= expectedLateLimit) {
  196.         logln("PASS: Limit >= expected.");
  197.     }
  198.     else 
  199.         errln(UnicodeString("FAIL: Expected limit ") + expectedLateLimit + "; got " + good);
  200.  
  201.     delete[] fields;
  202. }
  203.  
  204. UDate CalendarLimitTest::gregorianCutover = - 12219292800000.0;
  205.  
  206. // -------------------------------------
  207.  
  208. const int32_t CalendarLimitTest::kEpochStartAsJulianDay    = 2440588; // January 1, 1970 (Gregorian)
  209.  
  210. double
  211. CalendarLimitTest::millisToJulianDay(UDate millis)
  212. {
  213.     return (double)kEpochStartAsJulianDay + floorDivide(millis, (double)millisPerDay);
  214. }
  215.  
  216.  
  217. int32_t CalendarLimitTest::julianDayOffset = 2440588;
  218.  
  219. int32_t CalendarLimitTest::millisPerDay = 24 * 60 * 60 * 1000;
  220.  
  221. int32_t CalendarLimitTest::YEAR = 0;
  222.  
  223. int32_t CalendarLimitTest::MONTH = 1;
  224.  
  225. int32_t CalendarLimitTest::DATE = 2;
  226.  
  227. // -------------------------------------
  228.  
  229. double
  230. CalendarLimitTest::floorDivide(double numerator, double denominator) 
  231. {
  232.     // We do this computation in order to handle
  233.     // a numerator of Long.MIN_VALUE correctly
  234.     return icu_floor(numerator / denominator);
  235.     /*
  236.     return (numerator >= 0) ?
  237.         icu_trunc(numerator / denominator) :
  238.         icu_trunc((numerator + 1) / denominator) - 1;
  239. */
  240. }
  241.  
  242. // -------------------------------------
  243.  
  244. int32_t 
  245. CalendarLimitTest::floorDivide(int32_t numerator, int32_t denominator) 
  246. {
  247.     // We do this computation in order to handle
  248.     // a numerator of Long.MIN_VALUE correctly
  249.     return (numerator >= 0) ?
  250.         numerator / denominator :
  251.         ((numerator + 1) / denominator) - 1;
  252. }
  253.  
  254. // -------------------------------------
  255.  
  256. int32_t 
  257. CalendarLimitTest::floorDivide(int32_t numerator, int32_t denominator, int32_t remainder[])
  258. {
  259.     if (numerator >= 0) {
  260.         remainder[0] = numerator % denominator;
  261.         return numerator / denominator;
  262.     }
  263.     int32_t quotient = ((numerator + 1) / denominator) - 1;
  264.     remainder[0] = numerator - (quotient * denominator);
  265.     return quotient;
  266. }
  267.  
  268. // -------------------------------------
  269.  
  270. int32_t
  271. CalendarLimitTest::floorDivide(double numerator, int32_t denominator, int32_t remainder[]) 
  272. {
  273.     if (numerator >= 0) {
  274.         remainder[0] = (int32_t)icu_fmod(numerator, denominator);
  275.         return (int32_t)icu_trunc(numerator / denominator);
  276.     }
  277.     int32_t quotient = (int32_t)(icu_trunc((numerator + 1) / denominator) - 1);
  278.     remainder[0] = (int32_t)(numerator - (quotient * denominator));
  279.     return quotient;
  280. }
  281.  
  282. // -------------------------------------
  283.  
  284. const UDate CalendarLimitTest::kPapalCutover = 
  285.     (2299161.0 - kEpochStartAsJulianDay) * (double)millisPerDay;
  286.  
  287. const int32_t CalendarLimitTest::kJan1_1JulianDay = 1721426; // January 1, year 1 (Gregorian)
  288.  
  289. const int32_t CalendarLimitTest::kNumDays[]
  290.     = {0,31,59,90,120,151,181,212,243,273,304,334}; // 0-based, for day-in-year
  291. const int32_t CalendarLimitTest::kLeapNumDays[]
  292.     = {0,31,60,91,121,152,182,213,244,274,305,335}; // 0-based, for day-in-year
  293. const int32_t CalendarLimitTest::kMonthLength[]
  294.     = {31,28,31,30,31,30,31,31,30,31,30,31}; // 0-based
  295. const int32_t CalendarLimitTest::kLeapMonthLength[]
  296.     = {31,29,31,30,31,30,31,31,30,31,30,31}; // 0-based
  297.  
  298. bool_t
  299. CalendarLimitTest::timeToFields(UDate theTime, int32_t* fields)
  300. {
  301.     if(icu_isInfinite(theTime))
  302.         return FALSE;
  303.  
  304.     int32_t rawYear;
  305.     int32_t year, month, date, dayOfWeek, dayOfYear, era;
  306.     bool_t isLeap;
  307.  
  308.     // Compute the year, month, and day of month from the given millis
  309.     // {sfb} for simplicity's sake, assume no one will change the cutover date
  310.     if (theTime >= kPapalCutover/*fNormalizedGregorianCutover*/) {
  311.         // The Gregorian epoch day is zero for Monday January 1, year 1.
  312.         double gregorianEpochDay = millisToJulianDay(theTime) - kJan1_1JulianDay;
  313.         // Here we convert from the day number to the multiple radix
  314.         // representation.  We use 400-year, 100-year, and 4-year cycles.
  315.         // For example, the 4-year cycle has 4 years + 1 leap day; giving
  316.         // 1461 == 365*4 + 1 days.
  317.         int32_t rem[1];
  318.         int32_t n400 = floorDivide(gregorianEpochDay, 146097, rem); // 400-year cycle length
  319.         int32_t n100 = floorDivide(rem[0], 36524, rem); // 100-year cycle length
  320.         int32_t n4 = floorDivide(rem[0], 1461, rem); // 4-year cycle length
  321.         int32_t n1 = floorDivide(rem[0], 365, rem);
  322.         rawYear = 400*n400 + 100*n100 + 4*n4 + n1;
  323.         dayOfYear = rem[0]; // zero-based day of year
  324.         if (n100 == 4 || n1 == 4) 
  325.             dayOfYear = 365; // Dec 31 at end of 4- or 400-yr cycle
  326.         else 
  327.             ++rawYear;
  328.         
  329.         isLeap = ((rawYear&0x3) == 0) && // equiv. to (rawYear%4 == 0)
  330.             (rawYear%100 != 0 || rawYear%400 == 0);
  331.         
  332.         // Gregorian day zero is a Monday
  333.         dayOfWeek = (int32_t)icu_fmod(gregorianEpochDay + 1, 7);
  334.     }
  335.     else {
  336.         // The Julian epoch day (not the same as Julian Day)
  337.         // is zero on Saturday December 30, 0 (Gregorian).
  338.         double julianEpochDay = millisToJulianDay(theTime) - (kJan1_1JulianDay - 2);
  339.         //rawYear = floorDivide(4 * julianEpochDay + 1464, 1461);
  340.             rawYear = (int32_t) floorDivide(4*julianEpochDay + 1464, 1461.0);
  341.         
  342.         // Compute the Julian calendar day number for January 1, rawYear
  343.         //double january1 = 365 * (rawYear - 1) + floorDivide(rawYear - 1, 4);
  344.         double january1 = 365 * (rawYear - 1) + floorDivide(rawYear - 1, 4L);
  345.         dayOfYear = (int32_t)(julianEpochDay - january1); // 0-based
  346.         
  347.         // Julian leap years occurred historically every 4 years starting
  348.         // with 8 AD.  Before 8 AD the spacing is irregular; every 3 years
  349.         // from 45 BC to 9 BC, and then none until 8 AD.  However, we don't
  350.         // implement this historical detail; instead, we implement the
  351.         // computatinally cleaner proleptic calendar, which assumes
  352.         // consistent 4-year cycles throughout time.
  353.         isLeap = ((rawYear & 0x3) == 0); // equiv. to (rawYear%4 == 0)
  354.         
  355.         // Julian calendar day zero is a Saturday
  356.         dayOfWeek = (int32_t)icu_fmod(julianEpochDay-1, 7);
  357.     }
  358.     
  359.     // Common Julian/Gregorian calculation
  360.     int32_t correction = 0;
  361.     int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
  362.     if (dayOfYear >= march1) 
  363.         correction = isLeap ? 1 : 2;
  364.     month = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month
  365.     date = dayOfYear -
  366.         (isLeap ? kLeapNumDays[month] : kNumDays[month]) + 1; // one-based DOM
  367.     
  368.     // Normalize day of week
  369.     dayOfWeek += (dayOfWeek < 0) ? (Calendar::SUNDAY+7) : Calendar::SUNDAY;
  370.  
  371.     era = GregorianCalendar::AD;
  372.     year = rawYear;
  373.     if (year < 1) {
  374.         era = GregorianCalendar::BC;
  375.         year = 1 - year;
  376.     }
  377.  
  378.     //internalSet(ERA, era);
  379.     //internalSet(YEAR, year);
  380.     //internalSet(MONTH, month + JANUARY); // 0-based
  381.     //internalSet(DATE, date);
  382.     //internalSet(DAY_OF_WEEK, dayOfWeek);
  383.     //internalSet(DAY_OF_YEAR, ++dayOfYear); // Convert from 0-based to 1-based
  384.     
  385.     fields[YEAR] = year;
  386.     month += Calendar::JANUARY;
  387.     fields[MONTH] = month;
  388.     fields[DATE] = date;
  389.     // month: 0 <= m <= 11
  390.     bool_t monthLegal = (    (month - Calendar::JANUARY) >= 0 &&
  391.                             (month - Calendar::JANUARY) <= 11 );
  392.  
  393.     bool_t dateLegal = (    date >= 1 && 
  394.                             date <= (isLeap ? kLeapMonthLength[month - Calendar::JANUARY] 
  395.                                             : kMonthLength[month - Calendar::JANUARY]));
  396.     
  397.     bool_t yearLegal = (year >= 0);
  398.     
  399.     return monthLegal && dateLegal && yearLegal;
  400. }
  401.  
  402. // eof
  403.