home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Programming / ICU / src / icu / source / test / intltest / tztest.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-19  |  38.7 KB  |  1,109 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 <math.h>
  15. #include "tztest.h"
  16. #include "timezone.h"
  17. #include "simpletz.h"
  18. #include "calendar.h"
  19. #include "gregocal.h"
  20. #include "resbund.h"
  21.  
  22. #define CHECK_HEAP 0
  23.  
  24. #if defined(_WIN32) && !defined(__WINDOWS__)
  25. #define _CRTDBG_MAP_ALLOC
  26. #include <crtdbg.h>
  27. #endif
  28.  
  29. // *****************************************************************************
  30. // class TimeZoneTest
  31. // *****************************************************************************
  32.  
  33. void TimeZoneTest::runIndexedTest( int32_t index, bool_t exec, char* &name, char* par )
  34. {
  35.     if (exec) logln("TestSuite TestTimeZone");
  36.     switch (index) {
  37.         case 0:
  38.             name = "TestPRTOffset";
  39.             if (exec) {
  40.                 logln("TestPRTOffset---"); logln("");
  41.                 TestPRTOffset();
  42.             }
  43.             break;
  44.         case 1:
  45.             name = "TestVariousAPI518";
  46.             if (exec) {
  47.                 logln("TestVariousAPI518---"); logln("");
  48.                 TestVariousAPI518();
  49.             }
  50.             break;
  51.         case 2:
  52.             name = "TestGetAvailableIDs913";
  53.             if (exec) {
  54.                 logln("TestGetAvailableIDs913---"); logln("");
  55.                 TestGetAvailableIDs913();
  56.             }
  57.             break;
  58.         case 3:
  59.             name = "TestGenericAPI";
  60.             if (exec) {
  61.                 logln("TestGenericAPI---"); logln("");
  62.                 TestGenericAPI();
  63.             }
  64.             break;
  65.         case 4:
  66.             name = "TestRuleAPI";
  67.             if (exec) {
  68.                 logln("TestRuleAPI---"); logln("");
  69.                 TestRuleAPI();
  70.             }
  71.             break;
  72.         case 5:
  73.             name = "TestShortZoneIDs";
  74.             if (exec) {
  75.                 logln("TestShortZoneIDs---"); logln("");
  76.                 TestShortZoneIDs();
  77.             }
  78.             break;
  79.         case 6:
  80.             name = "TestCustomParse";
  81.             if (exec) {
  82.                 logln("TestCustomParse---"); logln("");
  83.                 TestCustomParse();
  84.             }
  85.             break;
  86.         case 7:
  87.             name = "TestDisplayName";
  88.             if (exec) {
  89.                 logln("TestDisplayName---"); logln("");
  90.                 TestDisplayName();
  91.             }
  92.             break;
  93.  
  94.         case 8:
  95.             name = "TestDSTSavings";
  96.             if (exec) {
  97.                 logln("TestDSTSavings---"); logln("");
  98.                 TestDSTSavings();
  99.             }
  100.             break;
  101.  
  102.         case 9:
  103.             name = "TestAlternateRules";
  104.             if (exec) {
  105.                 logln("TestAlternateRules---"); logln("");
  106.                 TestAlternateRules();
  107.             }
  108.             break;
  109.  
  110.         default: name = ""; break;
  111.     }
  112. }
  113.  
  114. const int32_t TimeZoneTest::millisPerHour = 3600000;
  115.  
  116. // ---------------------------------------------------------------------------------
  117.  
  118. /**
  119.  * Generic API testing for API coverage.
  120.  */
  121. void
  122. TimeZoneTest::TestGenericAPI()
  123. {
  124.     UnicodeString id("NewGMT");
  125.     int32_t offset = 12345;
  126.  
  127.     SimpleTimeZone *zone = new SimpleTimeZone(offset, id);
  128.     if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE");
  129.  
  130.     TimeZone* zoneclone = zone->clone();
  131.     if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
  132.     zoneclone->setID("abc");
  133.     if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
  134.     delete zoneclone;
  135.  
  136.     zoneclone = zone->clone();
  137.     if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
  138.     zoneclone->setRawOffset(45678);
  139.     if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
  140.  
  141.     SimpleTimeZone copy(*zone);
  142.     if (!(copy == *zone)) errln("FAIL: copy constructor or operator== failed");
  143.     copy = *(SimpleTimeZone*)zoneclone;
  144.     if (!(copy == *zoneclone)) errln("FAIL: assignment operator or operator== failed");
  145.  
  146.     TimeZone* saveDefault = TimeZone::createDefault();
  147.         TimeZone* pstZone = TimeZone::createTimeZone("PST");
  148.  
  149.     logln("call u_t_timezone() which uses the host");
  150.         logln("to get the difference in seconds between coordinated universal");
  151.         logln("time and local time. E.g., -28,800 for PST (GMT-8hrs)");
  152.  
  153.     int32_t tzoffset = icu_timezone();
  154.     logln(UnicodeString("Value returned from t_timezone = ") + tzoffset);
  155.     // Invert sign because UNIX semantics are backwards
  156.     if (tzoffset < 0) tzoffset = -tzoffset;
  157.         UErrorCode status = U_ZERO_ERROR;
  158.     // --- The following test would fail outside PST now that
  159.     // --- PST is generally set to be default timezone in format tests
  160.     //if ((*saveDefault == *pstZone) && (tzoffset != 28800)) {
  161.     //  errln("FAIL: t_timezone may be incorrect.  It is not 28800");
  162.     //}
  163.  
  164.     if (tzoffset != 28800) {
  165.         logln("***** WARNING: If testing in the PST timezone, t_timezone should return 28800! *****");
  166.     }
  167.     if ((tzoffset % 1800 != 0)) {
  168.       errln("FAIL: t_timezone may be incorrect. It is not a multiple of 30min.");
  169.     }
  170.  
  171.     TimeZone::adoptDefault(zone);
  172.     TimeZone* defaultzone = TimeZone::createDefault();
  173.     if (defaultzone == zone ||
  174.         !(*defaultzone == *zone))
  175.         errln("FAIL: createDefault failed");
  176.     TimeZone::adoptDefault(saveDefault);
  177.     delete defaultzone;
  178.     delete zoneclone;
  179.     delete pstZone;
  180. }
  181.  
  182. // ---------------------------------------------------------------------------------
  183.  
  184. /**
  185.  * Test the setStartRule/setEndRule API calls.
  186.  */
  187. void
  188. TimeZoneTest::TestRuleAPI()
  189. {
  190.     UErrorCode status = U_ZERO_ERROR;
  191.  
  192.     UDate offset = 60*60*1000*1.75; // Pick a weird offset
  193.     SimpleTimeZone *zone = new SimpleTimeZone((int32_t)offset, "TestZone");
  194.     if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE");
  195.  
  196.     // Establish our expected transition times.  Do this with a non-DST
  197.     // calendar with the (above) declared local offset.
  198.     GregorianCalendar *gc = new GregorianCalendar(*zone, status);
  199.     if (failure(status, "new GregorianCalendar")) return;
  200.     gc->clear();
  201.     gc->set(1990, Calendar::MARCH, 1);
  202.     UDate marchOneStd = gc->getTime(status); // Local Std time midnight
  203.     gc->clear();
  204.     gc->set(1990, Calendar::JULY, 1);
  205.     UDate julyOneStd = gc->getTime(status); // Local Std time midnight
  206.     if (failure(status, "GregorianCalendar::getTime")) return;
  207.  
  208.     // Starting and ending hours, WALL TIME
  209.     int32_t startHour = (int32_t)(2.25 * 3600000);
  210.     int32_t endHour   = (int32_t)(3.5  * 3600000);
  211.  
  212.     zone->setStartRule(Calendar::MARCH, 1, 0, startHour, status);
  213.     zone->setEndRule  (Calendar::JULY,  1, 0, endHour, status);
  214.  
  215.     delete gc;
  216.     gc = new GregorianCalendar(*zone, status);
  217.     if (failure(status, "new GregorianCalendar")) return;
  218.  
  219.     UDate marchOne = marchOneStd + startHour;
  220.     UDate julyOne = julyOneStd + endHour - 3600000; // Adjust from wall to Std time
  221.  
  222.     UDate expMarchOne = 636251400000.0;
  223.     if (marchOne != expMarchOne)
  224.     {
  225.         errln((UnicodeString)"FAIL: Expected start computed as " + marchOne +
  226.           " = " + dateToString(marchOne));
  227.         logln((UnicodeString)"      Should be                  " + expMarchOne +
  228.           " = " + dateToString(expMarchOne));
  229.     }
  230.  
  231.     UDate expJulyOne = 646793100000.0;
  232.     if (julyOne != expJulyOne)
  233.     {
  234.         errln((UnicodeString)"FAIL: Expected start computed as " + julyOne +
  235.           " = " + dateToString(julyOne));
  236.         logln((UnicodeString)"      Should be                  " + expJulyOne +
  237.           " = " + dateToString(expJulyOne));
  238.     }
  239.  
  240.     testUsingBinarySearch(zone, date(90, Calendar::JANUARY, 1), date(90, Calendar::JUNE, 15), marchOne);
  241.     testUsingBinarySearch(zone, date(90, Calendar::JUNE, 1), date(90, Calendar::DECEMBER, 31), julyOne);
  242.  
  243.     if (zone->inDaylightTime(marchOne - 1000, status) ||
  244.         !zone->inDaylightTime(marchOne, status))
  245.         errln("FAIL: Start rule broken");
  246.     if (!zone->inDaylightTime(julyOne - 1000, status) ||
  247.         zone->inDaylightTime(julyOne, status))
  248.         errln("FAIL: End rule broken");
  249.  
  250.     zone->setStartYear(1991);
  251.     if (zone->inDaylightTime(marchOne, status) ||
  252.         zone->inDaylightTime(julyOne - 1000, status))
  253.         errln("FAIL: Start year broken");
  254.  
  255.     failure(status, "TestRuleAPI");
  256.     delete gc;
  257.     delete zone;
  258. }
  259.  
  260. void
  261. TimeZoneTest::testUsingBinarySearch(SimpleTimeZone* tz, UDate min, UDate max, UDate expectedBoundary)
  262. {
  263.     UErrorCode status = U_ZERO_ERROR;
  264.     bool_t startsInDST = tz->inDaylightTime(min, status);
  265.     if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
  266.     if (tz->inDaylightTime(max, status) == startsInDST) {
  267.         logln("Error: inDaylightTime(" + dateToString(max) + ") != " + ((!startsInDST)?"TRUE":"FALSE"));
  268.         return;
  269.     }
  270.     if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
  271.     while ((max - min) > INTERVAL) {
  272.         UDate mid = (min + max) / 2;
  273.         if (tz->inDaylightTime(mid, status) == startsInDST) {
  274.             min = mid;
  275.         }
  276.         else {
  277.             max = mid;
  278.         }
  279.         if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
  280.     }
  281.     logln(UnicodeString("Binary Search Before: ") + icu_floor(0.5 + min) + " = " + dateToString(min));
  282.     logln(UnicodeString("Binary Search After:  ") + icu_floor(0.5 + max) + " = " + dateToString(max));
  283.     UDate mindelta = expectedBoundary - min;
  284.     UDate maxdelta = max - expectedBoundary;
  285.     if (mindelta >= 0 &&
  286.         mindelta <= INTERVAL &&
  287.         mindelta >= 0 &&
  288.         mindelta <= INTERVAL)
  289.         logln(UnicodeString("PASS: Expected bdry:  ") + expectedBoundary + " = " + dateToString(expectedBoundary));
  290.     else
  291.         errln(UnicodeString("FAIL: Expected bdry:  ") + expectedBoundary + " = " + dateToString(expectedBoundary));
  292. }
  293.  
  294. const UDate TimeZoneTest::INTERVAL = 100;
  295.  
  296. // ---------------------------------------------------------------------------------
  297.  
  298. // -------------------------------------
  299.  
  300. /**
  301.  * Test the offset of the PRT timezone.
  302.  */
  303. void
  304. TimeZoneTest::TestPRTOffset()
  305. {
  306.     TimeZone* tz = TimeZone::createTimeZone("PRT");
  307.     if (tz == 0) {
  308.         errln("FAIL: TimeZone(PRT) is null");
  309.     }
  310.     else {
  311.         if (tz->getRawOffset() != (- 4 * millisPerHour)) errln("FAIL: Offset for PRT should be -4");
  312.     }
  313.     delete tz;
  314. }
  315.  
  316. // -------------------------------------
  317.  
  318. /**
  319.  * Regress a specific bug with a sequence of API calls.
  320.  */
  321. void
  322. TimeZoneTest::TestVariousAPI518()
  323. {
  324.     UErrorCode status = U_ZERO_ERROR;
  325.     TimeZone* time_zone = TimeZone::createTimeZone("PST");
  326.     UDate d = date(97, Calendar::APRIL, 30);
  327.     UnicodeString str;
  328.     logln("The timezone is " + time_zone->getID(str));
  329.     if (!time_zone->inDaylightTime(d, status)) errln("FAIL: inDaylightTime returned FALSE");
  330.     if (U_FAILURE(status)) { errln("FAIL: TimeZone::inDaylightTime failed"); return; }
  331.     if (!time_zone->useDaylightTime()) errln("FAIL: useDaylightTime returned FALSE");
  332.     if (time_zone->getRawOffset() != - 8 * millisPerHour) errln("FAIL: getRawOffset returned wrong value");
  333.     GregorianCalendar *gc = new GregorianCalendar(status);
  334.     if (U_FAILURE(status)) { errln("FAIL: Couldn't create GregorianCalendar"); return; }
  335.     gc->setTime(d, status);
  336.     if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::setTime failed"); return; }
  337.     if (time_zone->getOffset(gc->AD, gc->get(gc->YEAR, status), gc->get(gc->MONTH, status),
  338.         gc->get(gc->DAY_OF_MONTH, status), (uint8_t)gc->get(gc->DAY_OF_WEEK, status), 0) != - 7 * millisPerHour)
  339.         errln("FAIL: getOffset returned wrong value");
  340.     if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::set failed"); return; }
  341.     delete gc;
  342.     delete time_zone;
  343. }
  344.  
  345. // -------------------------------------
  346.  
  347. /**
  348.  * Test the call which retrieves the available IDs.
  349.  */
  350. void
  351. TimeZoneTest::TestGetAvailableIDs913()
  352. {
  353. #if defined(_WIN32) && !defined(__WINDOWS__)
  354. #if defined(WIN32) && defined(_DEBUG) && CHECK_HEAP
  355.     /*
  356.      * Set the debug-heap flag to keep freed blocks in the
  357.      * heap's linked list - This will allow us to catch any
  358.      * inadvertent use of freed memory
  359.      */
  360.     int32_t tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
  361.     tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF;
  362.     tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF;
  363.     tmpDbgFlag |= _CRTDBG_CHECK_ALWAYS_DF;
  364.     _CrtSetDbgFlag(tmpDbgFlag);
  365.  
  366.     _CrtMemState memstate;
  367.     _CrtMemCheckpoint(&memstate);
  368.     {
  369. #endif
  370. #endif
  371.  
  372.     UnicodeString str;
  373.     UnicodeString *buf = new UnicodeString("TimeZone.getAvailableIDs() = { ");
  374.     int32_t s_length;
  375.     const UnicodeString** s = TimeZone::createAvailableIDs(s_length);
  376.     int32_t i;
  377.     for (i = 0; i < s_length;++i) {
  378.         if (i > 0) *buf += ", ";
  379.         *buf += *s[i];
  380.     }
  381.     *buf += " };";
  382.     logln(*buf);
  383.     buf->truncate(0);
  384.     *buf += "TimeZone.getAvailableIDs(GMT+02:00) = { ";
  385.     delete [] s;
  386.  
  387.     s = TimeZone::createAvailableIDs(+ 2 * 60 * 60 * 1000, s_length);
  388.     for (i = 0; i < s_length;++i) {
  389.         if (i > 0) *buf += ", ";
  390.         *buf += *s[i];
  391.     }
  392.     *buf += " };";
  393.     logln(*buf);
  394.     TimeZone *tz = TimeZone::createTimeZone("PST");
  395.     if (tz != 0) logln("getTimeZone(PST) = " + tz->getID(str));
  396.     else errln("FAIL: getTimeZone(PST) = null");
  397.     delete tz;
  398.     tz = TimeZone::createTimeZone("America/Los_Angeles");
  399.     if (tz != 0) logln("getTimeZone(America/Los_Angeles) = " + tz->getID(str));
  400.     else errln("FAIL: getTimeZone(PST) = null");
  401.     delete tz;
  402.  
  403.     // @bug 4096694
  404.     tz = TimeZone::createTimeZone("NON_EXISTENT");
  405.     UnicodeString temp;
  406.     if (tz == 0)
  407.         errln("FAIL: getTimeZone(NON_EXISTENT) = null");
  408.     else if (tz->getID(temp) != "GMT")
  409.         errln("FAIL: getTimeZone(NON_EXISTENT) = " + temp);
  410.     delete tz;
  411.  
  412.     delete buf;
  413.     delete [] s;
  414.  /*
  415. #if defined(WIN32) && defined(_DEBUG) && CHECK_HEAP
  416.     }
  417.     _CrtMemDumpAllObjectsSince(&memstate);
  418.  
  419.     /*
  420.      * Set the debug-heap flag to keep freed blocks in the
  421.      * heap's linked list - This will allow us to catch any
  422.      * inadvertent use of freed memory
  423.      */ /*
  424.     tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
  425.     tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF;
  426.     tmpDbgFlag &= ~_CRTDBG_LEAK_CHECK_DF;
  427.     tmpDbgFlag &= ~_CRTDBG_CHECK_ALWAYS_DF;
  428.     _CrtSetDbgFlag(tmpDbgFlag);
  429. #endif*/
  430. }
  431.  
  432.  
  433.     /**
  434.      * [srl - from java - 7/5/1998]
  435.      * @bug 4130885
  436.      * Certain short zone IDs, used since 1.1.x, are incorrect.
  437.      *
  438.      * The worst of these is:
  439.      *
  440.      * "CAT" (Central African Time) should be GMT+2:00, but instead returns a
  441.      * zone at GMT-1:00. The zone at GMT-1:00 should be called EGT, CVT, EGST,
  442.      * or AZOST, depending on which zone is meant, but in no case is it CAT.
  443.      *
  444.      * Other wrong zone IDs:
  445.      *
  446.      * ECT (European Central Time) GMT+1:00: ECT is Ecuador Time,
  447.      * GMT-5:00. European Central time is abbreviated CEST.
  448.      *
  449.      * SST (Solomon Island Time) GMT+11:00. SST is actually Samoa Standard Time,
  450.      * GMT-11:00. Solomon Island time is SBT.
  451.      *
  452.      * NST (New Zealand Time) GMT+12:00. NST is the abbreviation for
  453.      * Newfoundland Standard Time, GMT-3:30. New Zealanders use NZST.
  454.      *
  455.      * AST (Alaska Standard Time) GMT-9:00. [This has already been noted in
  456.      * another bug.] It should be "AKST". AST is Atlantic Standard Time,
  457.      * GMT-4:00.
  458.      *
  459.      * PNT (Phoenix Time) GMT-7:00. PNT usually means Pitcairn Time,
  460.      * GMT-8:30. There is no standard abbreviation for Phoenix time, as distinct
  461.      * from MST with daylight savings.
  462.      *
  463.      * In addition to these problems, a number of zones are FAKE. That is, they
  464.      * don't match what people use in the real world.
  465.      *
  466.      * FAKE zones:
  467.      *
  468.      * EET (should be EEST)
  469.      * ART (should be EEST)
  470.      * MET (should be IRST)
  471.      * NET (should be AMST)
  472.      * PLT (should be PKT)
  473.      * BST (should be BDT)
  474.      * VST (should be ICT)
  475.      * CTT (should be CST) +
  476.      * ACT (should be CST) +
  477.      * AET (should be EST) +
  478.      * MIT (should be WST) +
  479.      * IET (should be EST) +
  480.      * PRT (should be AST) +
  481.      * CNT (should be NST)
  482.      * AGT (should be ARST)
  483.      * BET (should be EST) +
  484.      *
  485.      * + A zone with the correct name already exists and means something
  486.      * else. E.g., EST usually indicates the US Eastern zone, so it cannot be
  487.      * used for Brazil (BET).
  488.      */
  489. void TimeZoneTest::TestShortZoneIDs()
  490. {
  491.      int32_t i;
  492.      // Create a small struct to hold the array
  493.      struct
  494.      {
  495.           char      id[20];
  496.           int32_t   offset;
  497.           bool_t    daylight;
  498.      }
  499.      kReferenceList [] =
  500.      {
  501.         "MIT", -660, FALSE,
  502.         "HST", -600, FALSE,
  503.         "AST", -540, TRUE,
  504.         "PST", -480, TRUE,
  505.         "PNT", -420, FALSE,
  506.         "MST", -420, TRUE,
  507.         "CST", -360, TRUE,
  508.         "IET", -300, FALSE,
  509.         "EST", -300, TRUE,
  510.         "PRT", -240, FALSE,
  511.         "CNT", -210, TRUE,
  512.         "AGT", -180, FALSE,
  513.         "BET", -180, TRUE,
  514.         // "CAT", -60, FALSE, // Wrong:
  515.         // As of bug 4130885, fix CAT (Central Africa)
  516.         "CAT", 120, FALSE, // Africa/Harare
  517.         "GMT", 0, FALSE,
  518.         "UTC", 0, FALSE, // ** srl: seems broken in C++
  519.         "ECT", 60, TRUE,
  520.         "ART", 120, TRUE,
  521.         "EET", 120, TRUE,
  522.         "EAT", 180, FALSE,
  523.         "MET", 210, TRUE,
  524.         "NET", 240, FALSE,
  525.         "PLT", 300, FALSE,
  526.         "IST", 330, FALSE,
  527.         "BST", 360, FALSE,
  528.         "VST", 420, FALSE,
  529.         "CTT", 480, FALSE,
  530.         "JST", 540, FALSE,
  531.         "ACT", 570, FALSE,
  532.         "AET", 600, TRUE,
  533.         "SST", 660, FALSE,
  534.         // "NST", 720, FALSE,
  535.         // As of bug 4130885, fix NST (New Zealand)
  536.         "NST", 720, TRUE, // Pacific/Auckland
  537.         "",0,FALSE
  538.      };
  539.  
  540.  
  541.      for(i=0;kReferenceList[i].id[0];i++)
  542.      {
  543.           UnicodeString itsID(kReferenceList[i].id);
  544.  
  545.  
  546.           // Check existence.
  547.  
  548.           TimeZone *tz = TimeZone::createTimeZone(itsID);
  549.  
  550.           if(!tz)
  551.           {
  552.               errln("FAIL: Time Zone " + itsID + " does not exist!");
  553.               continue;
  554.           }
  555.  
  556.  
  557.           // Check daylight usage.
  558.  
  559.           bool_t usesDaylight = tz->useDaylightTime();
  560.  
  561.           if(usesDaylight != kReferenceList[i].daylight)
  562.           {
  563.               errln("FAIL: Time Zone " + itsID + " 's daylight is " + (usesDaylight?"TRUE":"FALSE") +
  564.                     " but it should be " + ((kReferenceList[i].daylight)?"TRUE":"FALSE"));
  565.           }
  566.  
  567.  
  568.           // Check offset
  569.  
  570.           int32_t offsetInMinutes = tz->getRawOffset()/60000;
  571.  
  572.           if(offsetInMinutes != kReferenceList[i].offset)
  573.           {
  574.               errln("FAIL: Time Zone " + itsID + " 's offset is " + offsetInMinutes +
  575.                     " but it should be " + kReferenceList[i].offset);
  576.           }
  577.  
  578.           logln(" Time Zone " + itsID + " is OK. ");
  579.           delete tz;
  580.      }
  581.  
  582.  
  583.      // OK now test compat
  584.      logln("Testing for compatibility zones");
  585.  
  586.      const char* compatibilityMap[] =
  587.     {
  588.         // UTC is Universal Time, Coordinated.  Synonym for GMT.
  589.         /*GMT+0*/ "UTC", "GMT",
  590.         // ECT is the ID for European Central Time time zone.
  591.         /*GMT+1*/ "ECT", "Europe/Paris",
  592.         // EET is the ID for Eastern European Time time zone.
  593.         /*GMT+2*/ "EET", "Europe/Istanbul",
  594.         // ART is the ID for (Arabic) Egypt Standard Time timezone.
  595.         /*GMT+2*/ "ART", "Africa/Cairo",
  596.         // CAT is the ID for Central African Time time zone.
  597.         /*GMT+2*/ "CAT", "Africa/Johannesburg",
  598.         // EAT is the ID for Eastern African Time time zone.
  599.         /*GMT+3*/ "EAT", "Asia/Riyadh",
  600.         // MET is the ID for Middle East Time time zone.
  601.         /*GMT+0330*/ "MET", "Asia/Tehran",
  602.         // NET is the ID for Near East Time time zone.
  603.         /*GMT+4*/ "NET", "Asia/Yerevan",
  604.         // PLT is the ID for Pakistan Lahore Time time zone.
  605.         /*GMT+5*/ "PLT", "Asia/Karachi",
  606.         // IST is the ID for India Standard Time time zone.
  607.         /*GMT+0550*/ "IST", "Asia/Calcutta",
  608.         // BST is the ID for Bangladesh Standard Time time zone.
  609.         /*GMT+6*/ "BST", "Asia/Dacca",
  610.         // VST is the ID for Vietnam Standard Time time zone.
  611.         /*GMT+7*/ "VST", "Asia/Bangkok",
  612.         // CTT is the ID for China Taiwan Time time zone.
  613.         /*GMT+8*/ "CTT", "Asia/Shanghai",
  614.         // JST is the ID for Japan Standard Time time zone.
  615.         /*GMT+9*/ "JST", "Asia/Tokyo",
  616.         // ACT is the ID for Australia Central Time time zone.
  617.         /*GMT+0930*/ "ACT", "Australia/Darwin",
  618.         // AET is the ID for Australia Eastern Time time zone.
  619.         /*GMT+10*/ "AET", "Australia/Sydney",
  620.         // SST is the ID for Solomon Standard Time time zone.
  621.         /*GMT+11*/ "SST", "Pacific/Guadalcanal",
  622.         // NST is the ID for New Zealand Standard Time time zone.
  623.         /*GMT+12*/ "NST", "Pacific/Auckland",
  624.         // MIT is the ID for Midway Islands Time time zone.
  625.         /*GMT-11*/ "MIT", "Pacific/Apia",
  626.         // HST is the ID for Hawaii Standard Time time zone.
  627.         /*GMT-10*/ "HST", "Pacific/Honolulu",
  628.         // AST is the ID for Alaska Standard Time time zone.
  629.         /*GMT-9*/ "AST", "America/Anchorage",
  630.         // PST is the ID for Pacific Standard Time time zone.
  631.         /*GMT-8*/ "PST", "America/Los_Angeles",
  632.         // PNT is the ID for Phoenix Standard Time time zone.
  633.         /*GMT-7*/ "PNT", "America/Phoenix",
  634.         // MST is the ID for Mountain Standard Time time zone.
  635.         /*GMT-7*/ "MST", "America/Denver",
  636.         // CST is the ID for Central Standard Time time zone.
  637.         /*GMT-6*/ "CST", "America/Chicago",
  638.         // EST is the ID for Eastern Standard Time time zone.
  639.         /*GMT-5*/ "EST", "America/New_York",
  640.         // IET is the ID for Indiana Eastern Standard Time time zone.
  641.         /*GMT-5*/ "IET", "America/Indianapolis",
  642.         // PRT is the ID for Puerto Rico and US Virgin Islands Time time zone.
  643.         /*GMT-4*/ "PRT", "America/Caracas",
  644.         // CNT is the ID for Canada Newfoundland Time time zone.
  645.         /*GMT-0330*/ "CNT", "America/St_Johns",
  646.         // AGT is the ID for Argentina Standard Time time zone.
  647.         /*GMT-3*/ "AGT", "America/Buenos_Aires",
  648.         // BET is the ID for Brazil Eastern Time time zone.
  649.         /*GMT-3*/ "BET", "America/Sao_Paulo",
  650.          "\0","\0","\0",
  651.     };
  652.  
  653.     for(i=0;*compatibilityMap[i];i+=2)
  654.     {
  655.         UnicodeString itsID;
  656.  
  657.         const char *zone1 = compatibilityMap[i];
  658.         const char *zone2 = compatibilityMap[i+1];
  659.  
  660.         TimeZone *tz1 = TimeZone::createTimeZone(zone1);
  661.         TimeZone *tz2 = TimeZone::createTimeZone(zone2);
  662.  
  663.         // ok what did we get?
  664.  
  665.         // test #1
  666.         {
  667.             UnicodeString id(zone1);
  668.  
  669.             if(!tz1)
  670.             {
  671.                 errln("FAIL: Could not find left hand zone " + id);
  672.             }
  673.             else
  674.             {
  675.                 tz1->getID(itsID);
  676.                 int ioffset = tz1->getRawOffset()/60000;
  677.                 UnicodeString offset;
  678.                 formatMinutes(ioffset, offset);
  679.                 logln(id + " -> " + itsID + " GMT" + offset + " (left hand)");
  680.             }
  681.         }
  682.  
  683.         // test #2
  684.         {
  685.             UnicodeString id(zone2);
  686.  
  687.             if(!tz2)
  688.             {
  689.                 errln("FAIL: Could not find right hand zone " + id + " (right hand)");
  690.             }
  691.             else
  692.             {
  693.                 tz2->getID(itsID);
  694.                 int ioffset = tz2->getRawOffset()/60000;
  695.                 UnicodeString offset;
  696.                 formatMinutes(ioffset, offset);
  697.                 logln(id + " -> " + itsID + " GMT" + offset +" (right hand)");
  698.             }
  699.         }
  700.  
  701.         if(tz1&&tz2)
  702.         {
  703.             tz2->setID(tz1->getID(itsID)); // make the NAME the same so that comparison will only look at the rest of it.
  704.  
  705.             if(*tz1 != *tz2)
  706.             {
  707.                 errln(" FAIL: time zones " + UnicodeString(zone1) + " and " + UnicodeString(zone2) + " do NOT match.");
  708.             }
  709.         }
  710.  
  711.         delete tz1;
  712.         delete tz2;
  713.     }
  714.  
  715. }
  716.  
  717. /**
  718.  * Utility function for TestCustomParse
  719.  */
  720. UnicodeString& TimeZoneTest::formatMinutes(int32_t min, UnicodeString& rv)
  721. {
  722.         rv.remove();
  723.  
  724.         char sign = '+';
  725.         if (min < 0) { sign = '-'; min = -min; }
  726.         int h = min/60;
  727.         min = min%60;
  728.  
  729.         rv += UChar(sign);
  730.         if(h > 10)
  731.             rv += UChar('0' + (h/10));
  732.         rv += UChar('0' + (h%10));
  733.  
  734.         rv += ":";
  735.  
  736.         if(min > 10)
  737.             rv += UChar('0' + (min/10));
  738.         else
  739.             rv += "0";
  740.  
  741.         rv += UChar('0' + (min%10));
  742.  
  743.         return rv;
  744. }
  745.  
  746.  
  747. /**
  748.  * As part of the VM fix (see CCC approved RFE 4028006, bug
  749.  * 4044013), TimeZone.getTimeZone() has been modified to recognize
  750.  * generic IDs of the form GMT[+-]hh:mm, GMT[+-]hhmm, and
  751.  * GMT[+-]hh.  Test this behavior here.
  752.  *
  753.  * @bug 4044013
  754.  */
  755. void TimeZoneTest::TestCustomParse()
  756. {
  757.     int32_t i;
  758.     const int32_t kUnparseable = 604800; // the number of seconds in a week. More than any offset should be.
  759.     const UnicodeString kExpectedCustomID = "Custom";
  760.  
  761.     struct
  762.     {
  763.         char customId[20];
  764.         int32_t expectedOffset;
  765.     }
  766.     kData[] =
  767.     {
  768.         // ID        Expected offset in minutes
  769.         //"GMT",       kUnparseable,   Isn't custom. Can't test it here. [returns normal GMT]
  770.         "GMT-YOUR.AD.HERE",     kUnparseable,
  771.         "GMT0",      kUnparseable,
  772.         "GMT+0",     (0),
  773.         "GMT+1",     (60),
  774.         "GMT-0030",  (-30),
  775.         "GMT+15:99", (15*60+99),
  776.         "GMT+",      kUnparseable,
  777.         "GMT-",      kUnparseable,
  778.         "GMT+0:",    kUnparseable,
  779.         "GMT-:",     kUnparseable,
  780.         "GMT-YOUR.AD.HERE",     kUnparseable,
  781.         "GMT+0010",  (10), // Interpret this as 00:10
  782.         "GMT-10",    (-10*60),
  783.         "GMT+30",    (30),
  784.         "GMT-3:30",  (-(3*60+30)),
  785.         "GMT-230",   (-(2*60+30)),
  786.         0,           0
  787.     };
  788.  
  789.     for (i=0; kData[i].customId[0] != '\0'; i++)
  790.     {
  791.         UnicodeString id(kData[i].customId);
  792.         int32_t exp = kData[i].expectedOffset;
  793.  
  794.         TimeZone *zone = TimeZone::createTimeZone(id);
  795.         UnicodeString   itsID, temp;
  796.  
  797.         logln();
  798.         logln("testing # " + formatMinutes(i, temp) + id);
  799.  
  800.         /*
  801.         if(zone == NULL)
  802.         {
  803.             errln("FAIL: Could not createTimeZone(" + id + "). Returned NULL.");
  804.             continue;
  805.         }
  806.         */
  807.  
  808.  
  809.         if (! zone->getID(itsID).compare("GMT"))
  810.         //if(zone == NULL)
  811.         {
  812.             logln(id + " -> generic GMT");
  813.             // When TimeZone.getTimeZone() can't parse the id, it
  814.             // returns GMT -- a dubious practice, but required for
  815.             // backward compatibility.
  816.             if (exp != kUnparseable) {
  817.                 errln("FAIL: Expected offset of " + formatMinutes(exp,temp) +
  818.                                     " for " + id + ", got parse failure");
  819.             }
  820.         }
  821.         else
  822.         {
  823.             zone->getID(itsID);
  824.             int32_t ioffset = zone->getRawOffset()/60000;
  825.             UnicodeString offset;
  826.             formatMinutes(ioffset, offset);
  827.             logln(id + " -> " + itsID + " GMT" + offset);
  828.             if (exp == kUnparseable)
  829.             {
  830.                 errln("FAIL: Expected parse failure for " + id +
  831.                                     ", got offset of " + offset +
  832.                                     ", id " + itsID);
  833.             }
  834.             else if (ioffset != exp ||
  835.                      (itsID.compare(kExpectedCustomID) != 0))
  836.             {
  837.                 errln("Expected offset of " + formatMinutes(exp,temp) +
  838.                                     ", id Custom, for " + id +
  839.                                     ", got offset of " + offset +
  840.                                     ", id " + itsID);
  841.             }
  842.         }
  843.         delete zone;
  844.     }
  845. }
  846.  
  847. /**
  848.  * Test the basic functionality of the getDisplayName() API.
  849.  *
  850.  * @bug 4112869
  851.  * @bug 4028006
  852.  *
  853.  * See also API change request A41.
  854.  *
  855.  * 4/21/98 - make smarter, so the test works if the ext resources
  856.  * are present or not.
  857.  */
  858. void
  859. TimeZoneTest::TestDisplayName()
  860. {
  861.     UErrorCode status = U_ZERO_ERROR;
  862.     int32_t i;
  863.     TimeZone *zone = TimeZone::createTimeZone("PST");
  864.     UnicodeString name;
  865.     zone->getDisplayName(Locale::ENGLISH, name);
  866.     logln("PST->" + name);
  867.     if (name.compare("Pacific Standard Time") != 0)
  868.         errln("Fail: Expected \"Pacific Standard Time\" but got " + name);
  869.  
  870.     //*****************************************************************
  871.     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
  872.     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
  873.     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
  874.     //*****************************************************************
  875.     struct
  876.     {
  877.         bool_t useDst;
  878.         TimeZone::EDisplayType style;
  879.         char expect[100];
  880.     } kData[] = {
  881.         FALSE, TimeZone::SHORT, "PST",
  882.         TRUE,  TimeZone::SHORT, "PDT",
  883.         FALSE, TimeZone::LONG,  "Pacific Standard Time",
  884.         TRUE,  TimeZone::LONG,  "Pacific Daylight Time",
  885.  
  886.         FALSE, TimeZone::LONG, ""
  887.     };
  888.  
  889.     for (i=0; kData[i].expect[0] != '\0'; i++)
  890.     {
  891.         name.remove();
  892.         name = zone->getDisplayName(kData[i].useDst,
  893.                                    kData[i].style,
  894.                                    Locale::ENGLISH, name);
  895.         if (name.compare(kData[i].expect) != 0)
  896.             errln("Fail: Expected " + UnicodeString(kData[i].expect) + "; got " + name);
  897.         logln("PST [with options]->" + name);
  898.     }
  899.  
  900.     // Make sure that we don't display the DST name by constructing a fake
  901.     // PST zone that has DST all year long.
  902.     SimpleTimeZone *zone2 = new SimpleTimeZone(0, "PST");
  903.  
  904.     zone2->setStartRule(Calendar::JANUARY, 1, 0, 0, status);
  905.     zone2->setEndRule(Calendar::DECEMBER, 31, 0, 0, status);
  906.  
  907.     UnicodeString inDaylight = (zone2->inDaylightTime(UDate(), status)? UnicodeString("TRUE"):UnicodeString("FALSE"));
  908.     logln(UnicodeString("Modified PST inDaylightTime->") + inDaylight );
  909.     if(U_FAILURE(status))
  910.     {
  911.         errln("Some sort of error..."); // REVISIT
  912.     }
  913.     name.remove();
  914.     name = zone2->getDisplayName(Locale::ENGLISH,name);
  915.     logln("Modified PST->" + name);
  916.     if (name.compare("Pacific Standard Time") != 0)
  917.         errln("Fail: Expected \"Pacific Standard Time\"");
  918.  
  919.     // Make sure we get the default display format for Locales
  920.     // with no display name data.
  921.     Locale zh_CN = Locale::SIMPLIFIED_CHINESE;
  922.     name.remove();
  923.     name = zone->getDisplayName(zh_CN,name);
  924.     //*****************************************************************
  925.     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
  926.     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
  927.     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
  928.     //*****************************************************************
  929.     logln("PST(zh_CN)->" + name);
  930.  
  931.     // *** REVISIT SRL how in the world do I check this? looks java specific.
  932.     // Now be smart -- check to see if zh resource is even present.
  933.     // If not, we expect the en fallback behavior.
  934.     ResourceBundle enRB(Locale::getDataDirectory(),
  935.                             Locale::ENGLISH, status);
  936.     if(U_FAILURE(status))
  937.         errln("Couldn't get ResourceBundle for en");
  938.  
  939.     ResourceBundle zhRB(Locale::getDataDirectory(),
  940.                          zh_CN, status);
  941.     //if(U_FAILURE(status))
  942.     //    errln("Couldn't get ResourceBundle for zh_CN");
  943.  
  944.     bool_t noZH = U_FAILURE(status);
  945.  
  946.     if (noZH) {
  947.         logln("Warning: Not testing the zh_CN behavior because resource is absent");
  948.         if (name != "Pacific Standard Time")
  949.             errln("Fail: Expected Pacific Standard Time");
  950.     }
  951.  
  952.  
  953.     if      (name.compare("GMT-08:00") &&
  954.              name.compare("GMT-8:00") &&
  955.              name.compare("GMT-0800") &&
  956.              name.compare("GMT-800")) {
  957.         errln("Fail: Expected GMT-08:00 or something similar");
  958.         errln("************************************************************");
  959.         errln("THE ABOVE FAILURE MAY JUST MEAN THE LOCALE DATA HAS CHANGED");
  960.         errln("************************************************************");
  961.     }
  962.  
  963.     // Now try a non-existent zone
  964.     delete zone2;
  965.     zone2 = new SimpleTimeZone(90*60*1000, "xyzzy");
  966.     name.remove();
  967.     name = zone2->getDisplayName(Locale::ENGLISH,name);
  968.     logln("GMT+90min->" + name);
  969.     if (name.compare("GMT+01:30") &&
  970.         name.compare("GMT+1:30") &&
  971.         name.compare("GMT+0130") &&
  972.         name.compare("GMT+130"))
  973.         errln("Fail: Expected GMT+01:30 or something similar");
  974.  
  975.     // clean up
  976.     delete zone;
  977.     delete zone2;
  978. }
  979.  
  980. /**
  981.  * @bug 4107276
  982.  */
  983. void
  984. TimeZoneTest::TestDSTSavings()
  985. {
  986.     UErrorCode status = U_ZERO_ERROR;
  987.     // It might be better to find a way to integrate this test into the main TimeZone
  988.     // tests above, but I don't have time to figure out how to do this (or if it's
  989.     // even really a good idea).  Let's consider that a future.  --rtg 1/27/98
  990.     SimpleTimeZone *tz = new SimpleTimeZone(-5 * U_MILLIS_PER_HOUR, "dstSavingsTest",
  991.                                            Calendar::MARCH, 1, 0, 0, Calendar::SEPTEMBER, 1, 0, 0,
  992.                                            (int32_t)(0.5 * U_MILLIS_PER_HOUR), status);
  993.     if(U_FAILURE(status))
  994.         errln("couldn't create TimeZone");
  995.  
  996.     if (tz->getRawOffset() != -5 * U_MILLIS_PER_HOUR)
  997.         errln(UnicodeString("Got back a raw offset of ") + (tz->getRawOffset() / U_MILLIS_PER_HOUR) +
  998.               " hours instead of -5 hours.");
  999.     if (!tz->useDaylightTime())
  1000.         errln("Test time zone should use DST but claims it doesn't.");
  1001.     if (tz->getDSTSavings() != 0.5 * U_MILLIS_PER_HOUR)
  1002.         errln(UnicodeString("Set DST offset to 0.5 hour, but got back ") + (tz->getDSTSavings() /
  1003.                                                              U_MILLIS_PER_HOUR) + " hours instead.");
  1004.  
  1005.     int32_t offset = tz->getOffset(GregorianCalendar::AD, 1998, Calendar::JANUARY, 1,
  1006.                               Calendar::THURSDAY, 10 * U_MILLIS_PER_HOUR);
  1007.     if (offset != -5 * U_MILLIS_PER_HOUR)
  1008.         errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ")
  1009.               + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1010.  
  1011.     offset = tz->getOffset(GregorianCalendar::AD, 1998, Calendar::JUNE, 1, Calendar::MONDAY,
  1012.                           10 * U_MILLIS_PER_HOUR);
  1013.     if (offset != -4.5 * U_MILLIS_PER_HOUR)
  1014.         errln(UnicodeString("The offset for 10 AM, 6/1/98 should have been -4.5 hours, but we got ")
  1015.               + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1016.  
  1017.     tz->setDSTSavings(U_MILLIS_PER_HOUR);
  1018.     offset = tz->getOffset(GregorianCalendar::AD, 1998, Calendar::JANUARY, 1,
  1019.                           Calendar::THURSDAY, 10 * U_MILLIS_PER_HOUR);
  1020.     if (offset != -5 * U_MILLIS_PER_HOUR)
  1021.         errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ")
  1022.               + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1023.  
  1024.     offset = tz->getOffset(GregorianCalendar::AD, 1998, Calendar::JUNE, 1, Calendar::MONDAY,
  1025.                           10 * U_MILLIS_PER_HOUR);
  1026.     if (offset != -4 * U_MILLIS_PER_HOUR)
  1027.         errln(UnicodeString("The offset for 10 AM, 6/1/98 (with a 1-hour DST offset) should have been -4 hours, but we got ")
  1028.               + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1029.  
  1030.     delete tz;
  1031. }
  1032.  
  1033. /**
  1034.  * @bug 4107570
  1035.  */
  1036. void
  1037. TimeZoneTest::TestAlternateRules()
  1038. {
  1039.     // Like TestDSTSavings, this test should probably be integrated somehow with the main
  1040.     // test at the top of this class, but I didn't have time to figure out how to do that.
  1041.     //                      --rtg 1/28/98
  1042.  
  1043.     SimpleTimeZone tz(-5 * U_MILLIS_PER_HOUR, "alternateRuleTest");
  1044.  
  1045.     // test the day-of-month API
  1046.     UErrorCode status = U_ZERO_ERROR;
  1047.     tz.setStartRule(Calendar::MARCH, 10, 12 * U_MILLIS_PER_HOUR, status);
  1048.     if(U_FAILURE(status))
  1049.         errln("tz.setStartRule failed");
  1050.     tz.setEndRule(Calendar::OCTOBER, 20, 12 * U_MILLIS_PER_HOUR, status);
  1051.     if(U_FAILURE(status))
  1052.         errln("tz.setStartRule failed");
  1053.  
  1054.     int32_t offset = tz.getOffset(GregorianCalendar::AD, 1998, Calendar::MARCH, 5,
  1055.                               Calendar::THURSDAY, 10 * U_MILLIS_PER_HOUR);
  1056.     if (offset != -5 * U_MILLIS_PER_HOUR)
  1057.         errln(UnicodeString("The offset for 10AM, 3/5/98 should have been -5 hours, but we got ")
  1058.               + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1059.  
  1060.     offset = tz.getOffset(GregorianCalendar::AD, 1998, Calendar::MARCH, 15,
  1061.                           Calendar::SUNDAY, 10 * millisPerHour);
  1062.     if (offset != -4 * U_MILLIS_PER_HOUR)
  1063.         errln(UnicodeString("The offset for 10AM, 3/15/98 should have been -4 hours, but we got ")
  1064.               + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1065.  
  1066.     offset = tz.getOffset(GregorianCalendar::AD, 1998, Calendar::OCTOBER, 15,
  1067.                           Calendar::THURSDAY, 10 * millisPerHour);
  1068.     if (offset != -4 * U_MILLIS_PER_HOUR)
  1069.         errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ")              + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1070.  
  1071.     offset = tz.getOffset(GregorianCalendar::AD, 1998, Calendar::OCTOBER, 25,
  1072.                           Calendar::SUNDAY, 10 * millisPerHour);
  1073.     if (offset != -5 * U_MILLIS_PER_HOUR)
  1074.         errln(UnicodeString("The offset for 10AM, 10/25/98 should have been -5 hours, but we got ")
  1075.               + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1076.  
  1077.     // test the day-of-week-after-day-in-month API
  1078.     tz.setStartRule(Calendar::MARCH, 10, Calendar::FRIDAY, 12 * millisPerHour, TRUE, status);
  1079.     if(U_FAILURE(status))
  1080.         errln("tz.setStartRule failed");
  1081.     tz.setEndRule(Calendar::OCTOBER, 20, Calendar::FRIDAY, 12 * millisPerHour, FALSE, status);
  1082.     if(U_FAILURE(status))
  1083.         errln("tz.setStartRule failed");
  1084.  
  1085.     offset = tz.getOffset(GregorianCalendar::AD, 1998, Calendar::MARCH, 11,
  1086.                           Calendar::WEDNESDAY, 10 * millisPerHour);
  1087.     if (offset != -5 * U_MILLIS_PER_HOUR)
  1088.         errln(UnicodeString("The offset for 10AM, 3/11/98 should have been -5 hours, but we got ")
  1089.               + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1090.  
  1091.     offset = tz.getOffset(GregorianCalendar::AD, 1998, Calendar::MARCH, 14,
  1092.                           Calendar::SATURDAY, 10 * millisPerHour);
  1093.     if (offset != -4 * U_MILLIS_PER_HOUR)
  1094.         errln(UnicodeString("The offset for 10AM, 3/14/98 should have been -4 hours, but we got ")
  1095.               + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1096.  
  1097.     offset = tz.getOffset(GregorianCalendar::AD, 1998, Calendar::OCTOBER, 15,
  1098.                           Calendar::THURSDAY, 10 * millisPerHour);
  1099.     if (offset != -4 * U_MILLIS_PER_HOUR)
  1100.         errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ")
  1101.               + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1102.  
  1103.     offset = tz.getOffset(GregorianCalendar::AD, 1998, Calendar::OCTOBER, 17,
  1104.                           Calendar::SATURDAY, 10 * millisPerHour);
  1105.     if (offset != -5 * U_MILLIS_PER_HOUR)
  1106.         errln(UnicodeString("The offset for 10AM, 10/17/98 should have been -5 hours, but we got ")
  1107.               + (offset / U_MILLIS_PER_HOUR) + " hours.");
  1108. }
  1109.