Home | History | Annotate | Download | only in intltest
      1 /***********************************************************************
      2  * COPYRIGHT:
      3  * Copyright (c) 1997-2010, 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 "tzbdtest.h"
     12 #include "unicode/timezone.h"
     13 #include "unicode/simpletz.h"
     14 #include "unicode/gregocal.h"
     15 #include "putilimp.h"
     16 
     17 void TimeZoneBoundaryTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
     18 {
     19     if (exec) logln("TestSuite TestTimeZoneBoundary");
     20     switch (index) {
     21         case 0:
     22             name = "TestBoundaries";
     23             if (exec) {
     24                 logln("TestBoundaries---"); logln("");
     25                 TestBoundaries();
     26             }
     27             break;
     28         case 1:
     29             name = "TestNewRules";
     30             if (exec) {
     31                 logln("TestNewRules---"); logln("");
     32                 TestNewRules();
     33             }
     34             break;
     35         case 2:
     36             name = "TestStepwise";
     37             if (exec) {
     38                 logln("TestStepwise---"); logln("");
     39                 TestStepwise();
     40             }
     41             break;
     42         default: name = ""; break;
     43     }
     44 }
     45 
     46 // *****************************************************************************
     47 // class TimeZoneBoundaryTest
     48 // *****************************************************************************
     49 
     50 TimeZoneBoundaryTest::TimeZoneBoundaryTest()
     51 :
     52 ONE_SECOND(1000),
     53 ONE_MINUTE(60 * ONE_SECOND),
     54 ONE_HOUR(60 * ONE_MINUTE),
     55 ONE_DAY(24 * ONE_HOUR),
     56 ONE_YEAR(uprv_floor(365.25 * ONE_DAY)),
     57 SIX_MONTHS(ONE_YEAR / 2)
     58 {
     59 }
     60 
     61 const int32_t TimeZoneBoundaryTest::MONTH_LENGTH[] = {
     62     31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
     63 };
     64 
     65 const UDate TimeZoneBoundaryTest::PST_1997_BEG = 860320800000.0;
     66 
     67 const UDate TimeZoneBoundaryTest::PST_1997_END = 877856400000.0;
     68 
     69 const UDate TimeZoneBoundaryTest::INTERVAL = 10;
     70 
     71 // -------------------------------------
     72 
     73 void
     74 TimeZoneBoundaryTest::findDaylightBoundaryUsingDate(UDate d, const char* startMode, UDate expectedBoundary)
     75 {
     76     UnicodeString str;
     77     if (dateToString(d, str).indexOf(startMode) == - 1) {
     78         logln(UnicodeString("Error: ") + startMode + " not present in " + str);
     79     }
     80     UDate min = d;
     81     UDate max = min + SIX_MONTHS;
     82     while ((max - min) > INTERVAL) {
     83         UDate mid = (min + max) / 2;
     84         UnicodeString* s = &dateToString(mid, str);
     85         if (s->indexOf(startMode) != - 1) {
     86             min = mid;
     87         }
     88         else {
     89             max = mid;
     90         }
     91     }
     92     logln("Date Before: " + showDate(min));
     93     logln("Date After:  " + showDate(max));
     94     UDate mindelta = expectedBoundary - min;
     95     UDate maxdelta = max - expectedBoundary;
     96     if (mindelta >= 0 &&
     97         mindelta <= INTERVAL &&
     98         maxdelta >= 0 &&
     99         maxdelta <= INTERVAL) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary);
    100     else dataerrln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary);
    101 }
    102 
    103 // -------------------------------------
    104 
    105 void
    106 TimeZoneBoundaryTest::findDaylightBoundaryUsingTimeZone(UDate d, UBool startsInDST, UDate expectedBoundary)
    107 {
    108     TimeZone *zone = TimeZone::createDefault();
    109     findDaylightBoundaryUsingTimeZone(d, startsInDST, expectedBoundary, zone);
    110     delete zone;
    111 }
    112 
    113 // -------------------------------------
    114 
    115 void
    116 TimeZoneBoundaryTest::findDaylightBoundaryUsingTimeZone(UDate d, UBool startsInDST, UDate expectedBoundary, TimeZone* tz)
    117 {
    118     UErrorCode status = U_ZERO_ERROR;
    119     UnicodeString str;
    120     UDate min = d;
    121     UDate max = min + SIX_MONTHS;
    122     if (tz->inDaylightTime(d, status) != startsInDST) {
    123         dataerrln("FAIL: " + tz->getID(str) + " inDaylightTime(" + dateToString(d) + ") != " + (startsInDST ? "true" : "false"));
    124         startsInDST = !startsInDST;
    125     }
    126     if (failure(status, "TimeZone::inDaylightTime", TRUE)) return;
    127     if (tz->inDaylightTime(max, status) == startsInDST) {
    128         dataerrln("FAIL: " + tz->getID(str) + " inDaylightTime(" + dateToString(max) + ") != " + (startsInDST ? "false" : "true"));
    129         return;
    130     }
    131     if (failure(status, "TimeZone::inDaylightTime")) return;
    132     while ((max - min) > INTERVAL) {
    133         UDate mid = (min + max) / 2;
    134         UBool isIn = tz->inDaylightTime(mid, status);
    135         if (failure(status, "TimeZone::inDaylightTime")) return;
    136         if (isIn == startsInDST) {
    137             min = mid;
    138         }
    139         else {
    140             max = mid;
    141         }
    142     }
    143     logln(tz->getID(str) + " Before: " + showDate(min));
    144     logln(tz->getID(str) + " After:  " + showDate(max));
    145     UDate mindelta = expectedBoundary - min;
    146     UDate maxdelta = max - expectedBoundary;
    147     if (mindelta >= 0 &&
    148         mindelta <= INTERVAL &&
    149         maxdelta >= 0 &&
    150         maxdelta <= INTERVAL) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary);
    151     else errln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary);
    152 }
    153 
    154 // -------------------------------------
    155 /*
    156 UnicodeString*
    157 TimeZoneBoundaryTest::showDate(int32_t l)
    158 {
    159     return showDate(new Date(l));
    160 }
    161 */
    162 // -------------------------------------
    163 
    164 UnicodeString
    165 TimeZoneBoundaryTest::showDate(UDate d)
    166 {
    167     int32_t y, m, day, h, min, sec;
    168     dateToFields(d, y, m, day, h, min, sec);
    169     return UnicodeString("") + y + "/" + showNN(m + 1) + "/" +
    170         showNN(day) + " " + showNN(h) + ":" + showNN(min) +
    171         " \"" + dateToString(d) + "\" = " + uprv_floor(d+0.5);
    172 }
    173 
    174 // -------------------------------------
    175 
    176 UnicodeString
    177 TimeZoneBoundaryTest::showNN(int32_t n)
    178 {
    179     UnicodeString nStr;
    180     if (n < 10) {
    181         nStr += UnicodeString("0", "");
    182     }
    183     return nStr + n;
    184 }
    185 
    186 // -------------------------------------
    187 
    188 void
    189 TimeZoneBoundaryTest::verifyDST(UDate d, TimeZone* time_zone, UBool expUseDaylightTime, UBool expInDaylightTime, UDate expZoneOffset, UDate expDSTOffset)
    190 {
    191     UnicodeString str;
    192     UErrorCode status = U_ZERO_ERROR;
    193     logln("-- Verifying time " + dateToString(d) + " in zone " + time_zone->getID(str));
    194     if (time_zone->inDaylightTime(d, status) == expInDaylightTime)
    195         logln(UnicodeString("PASS: inDaylightTime = ") + (time_zone->inDaylightTime(d, status)?"true":"false"));
    196     else
    197         dataerrln(UnicodeString("FAIL: inDaylightTime = ") + (time_zone->inDaylightTime(d, status)?"true":"false"));
    198     if (failure(status, "TimeZone::inDaylightTime", TRUE))
    199         return;
    200     if (time_zone->useDaylightTime() == expUseDaylightTime)
    201         logln(UnicodeString("PASS: useDaylightTime = ") + (time_zone->useDaylightTime()?"true":"false"));
    202     else
    203         dataerrln(UnicodeString("FAIL: useDaylightTime = ") + (time_zone->useDaylightTime()?"true":"false"));
    204     if (time_zone->getRawOffset() == expZoneOffset)
    205         logln(UnicodeString("PASS: getRawOffset() = ") + (expZoneOffset / ONE_HOUR));
    206     else
    207         dataerrln(UnicodeString("FAIL: getRawOffset() = ") + (time_zone->getRawOffset() / ONE_HOUR) + ";  expected " + (expZoneOffset / ONE_HOUR));
    208 
    209     GregorianCalendar *gc = new GregorianCalendar(time_zone->clone(), status);
    210     gc->setTime(d, status);
    211     if (failure(status, "GregorianCalendar::setTime")) return;
    212     int32_t offset = time_zone->getOffset((uint8_t)gc->get(UCAL_ERA, status),
    213         gc->get(UCAL_YEAR, status), gc->get(UCAL_MONTH, status),
    214         gc->get(UCAL_DATE, status), (uint8_t)gc->get(UCAL_DAY_OF_WEEK, status),
    215         ((gc->get(UCAL_HOUR_OF_DAY, status) * 60 + gc->get(UCAL_MINUTE, status)) * 60 + gc->get(UCAL_SECOND, status)) * 1000 + gc->get(UCAL_MILLISECOND, status),
    216         status);
    217     if (failure(status, "GregorianCalendar::get")) return;
    218     if (offset == expDSTOffset) logln(UnicodeString("PASS: getOffset() = ") + (offset / ONE_HOUR));
    219     else dataerrln(UnicodeString("FAIL: getOffset() = ") + (offset / ONE_HOUR) + "; expected " + (expDSTOffset / ONE_HOUR));
    220     delete gc;
    221 }
    222 
    223 // -------------------------------------
    224 /**
    225     * Check that the given year/month/dom/hour maps to and from the
    226     * given epochHours.  This verifies the functioning of the
    227     * calendar and time zone in conjunction with one another,
    228     * including the calendar time->fields and fields->time and
    229     * the time zone getOffset method.
    230     *
    231     * @param epochHours hours after Jan 1 1970 0:00 GMT.
    232     */
    233 void TimeZoneBoundaryTest::verifyMapping(Calendar& cal, int year, int month, int dom, int hour,
    234                     double epochHours) {
    235     double H = 3600000.0;
    236     UErrorCode status = U_ZERO_ERROR;
    237     cal.clear();
    238     cal.set(year, month, dom, hour, 0, 0);
    239     UDate e = cal.getTime(status)/ H;
    240     UDate ed = (epochHours * H);
    241     if (e == epochHours) {
    242         logln(UnicodeString("Ok: ") + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " +
    243                 e + " (" + ed + ")");
    244     } else {
    245         dataerrln(UnicodeString("FAIL: ") + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " +
    246                 e + " (" + (e * H) + ")" +
    247                 ", expected " + epochHours + " (" + ed + ")");
    248     }
    249     cal.setTime(ed, status);
    250     if (cal.get(UCAL_YEAR, status) == year &&
    251         cal.get(UCAL_MONTH, status) == month &&
    252         cal.get(UCAL_DATE, status) == dom &&
    253         cal.get(UCAL_MILLISECONDS_IN_DAY, status) == hour * 3600000) {
    254         logln(UnicodeString("Ok: ") + epochHours + " (" + ed + ") => " +
    255                 cal.get(UCAL_YEAR, status) + "/" +
    256                 (cal.get(UCAL_MONTH, status)+1) + "/" +
    257                 cal.get(UCAL_DATE, status) + " " +
    258                 cal.get(UCAL_MILLISECOND, status)/H);
    259     } else {
    260         dataerrln(UnicodeString("FAIL: ") + epochHours + " (" + ed + ") => " +
    261                 cal.get(UCAL_YEAR, status) + "/" +
    262                 (cal.get(UCAL_MONTH, status)+1) + "/" +
    263                 cal.get(UCAL_DATE, status) + " " +
    264                 cal.get(UCAL_MILLISECOND, status)/H +
    265                 ", expected " + year + "/" + (month+1) + "/" + dom +
    266                 " " + hour);
    267     }
    268 }
    269 
    270 /**
    271  * Test the behavior of SimpleTimeZone at the transition into and out of DST.
    272  * Use a binary search to find boundaries.
    273  */
    274 void
    275 TimeZoneBoundaryTest::TestBoundaries()
    276 {
    277     UErrorCode status = U_ZERO_ERROR;
    278     TimeZone* pst = TimeZone::createTimeZone("PST");
    279     Calendar* tempcal = Calendar::createInstance(pst, status);
    280     if(U_SUCCESS(status)){
    281         verifyMapping(*tempcal, 1997, Calendar::APRIL, 3,  0, 238904.0);
    282         verifyMapping(*tempcal, 1997, Calendar::APRIL, 4,  0, 238928.0);
    283         verifyMapping(*tempcal, 1997, Calendar::APRIL, 5,  0, 238952.0);
    284         verifyMapping(*tempcal, 1997, Calendar::APRIL, 5, 23, 238975.0);
    285         verifyMapping(*tempcal, 1997, Calendar::APRIL, 6,  0, 238976.0);
    286         verifyMapping(*tempcal, 1997, Calendar::APRIL, 6,  1, 238977.0);
    287         verifyMapping(*tempcal, 1997, Calendar::APRIL, 6,  3, 238978.0);
    288     }else{
    289         dataerrln("Could not create calendar. Error: %s", u_errorName(status));
    290     }
    291     TimeZone* utc = TimeZone::createTimeZone("UTC");
    292     Calendar* utccal =  Calendar::createInstance(utc, status);
    293     if(U_SUCCESS(status)){
    294         verifyMapping(*utccal, 1997, Calendar::APRIL, 6, 0, 238968.0);
    295     }else{
    296         dataerrln("Could not create calendar. Error: %s", u_errorName(status));
    297     }
    298     TimeZone* save = TimeZone::createDefault();
    299     TimeZone::setDefault(*pst);
    300 
    301     if (tempcal != NULL) {
    302         // DST changeover for PST is 4/6/1997 at 2 hours past midnight
    303         // at 238978.0 epoch hours.
    304         tempcal->clear();
    305         tempcal->set(1997, Calendar::APRIL, 6);
    306         UDate d = tempcal->getTime(status);
    307 
    308         // i is minutes past midnight standard time
    309         for (int i=-120; i<=180; i+=60)
    310         {
    311             UBool inDST = (i >= 120);
    312             tempcal->setTime(d + i*60*1000, status);
    313             verifyDST(tempcal->getTime(status),pst, TRUE, inDST, -8*ONE_HOUR,inDST ? -7*ONE_HOUR : -8*ONE_HOUR);
    314         }
    315     }
    316     TimeZone::setDefault(*save);
    317     delete save;
    318     delete utccal;
    319     delete tempcal;
    320 
    321 #if 1
    322     {
    323         logln("--- Test a ---");
    324         UDate d = date(97, UCAL_APRIL, 6);
    325         TimeZone *z = TimeZone::createTimeZone("PST");
    326         for (int32_t i = 60; i <= 180; i += 15) {
    327             UBool inDST = (i >= 120);
    328             UDate e = d + i * 60 * 1000;
    329             verifyDST(e, z, TRUE, inDST, - 8 * ONE_HOUR, inDST ? - 7 * ONE_HOUR: - 8 * ONE_HOUR);
    330         }
    331         delete z;
    332     }
    333 #endif
    334 #if 1
    335     {
    336         logln("--- Test b ---");
    337         TimeZone *tz;
    338         TimeZone::setDefault(*(tz = TimeZone::createTimeZone("PST")));
    339         delete tz;
    340         logln("========================================");
    341         findDaylightBoundaryUsingDate(date(97, 0, 1), "PST", PST_1997_BEG);
    342         logln("========================================");
    343         findDaylightBoundaryUsingDate(date(97, 6, 1), "PDT", PST_1997_END);
    344     }
    345 #endif
    346 #if 1
    347     {
    348         logln("--- Test c ---");
    349         logln("========================================");
    350         TimeZone* z = TimeZone::createTimeZone("Australia/Adelaide");
    351         findDaylightBoundaryUsingTimeZone(date(97, 0, 1), TRUE, 859653000000.0, z);
    352         logln("========================================");
    353         findDaylightBoundaryUsingTimeZone(date(97, 6, 1), FALSE, 877797000000.0, z);
    354         delete z;
    355     }
    356 #endif
    357 #if 1
    358     {
    359         logln("--- Test d ---");
    360         logln("========================================");
    361         findDaylightBoundaryUsingTimeZone(date(97, 0, 1), FALSE, PST_1997_BEG);
    362         logln("========================================");
    363         findDaylightBoundaryUsingTimeZone(date(97, 6, 1), TRUE, PST_1997_END);
    364     }
    365 #endif
    366 #if 0
    367     {
    368         logln("--- Test e ---");
    369         TimeZone *z = TimeZone::createDefault();
    370         logln(UnicodeString("") + z->getOffset(1, 97, 3, 4, 6, 0) + " " + date(97, 3, 4));
    371         logln(UnicodeString("") + z->getOffset(1, 97, 3, 5, 7, 0) + " " + date(97, 3, 5));
    372         logln(UnicodeString("") + z->getOffset(1, 97, 3, 6, 1, 0) + " " + date(97, 3, 6));
    373         logln(UnicodeString("") + z->getOffset(1, 97, 3, 7, 2, 0) + " " + date(97, 3, 7));
    374         delete z;
    375     }
    376 #endif
    377 }
    378 
    379 // -------------------------------------
    380 
    381 void
    382 TimeZoneBoundaryTest::testUsingBinarySearch(SimpleTimeZone* tz, UDate d, UDate expectedBoundary)
    383 {
    384     UErrorCode status = U_ZERO_ERROR;
    385     UDate min = d;
    386     UDate max = min + SIX_MONTHS;
    387     UBool startsInDST = tz->inDaylightTime(d, status);
    388     if (failure(status, "SimpleTimeZone::inDaylightTime", TRUE)) return;
    389     if (tz->inDaylightTime(max, status) == startsInDST) {
    390         errln("Error: inDaylightTime(" + dateToString(max) + ") != " + ((!startsInDST)?"true":"false"));
    391     }
    392     if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
    393     while ((max - min) > INTERVAL) {
    394         UDate mid = (min + max) / 2;
    395         if (tz->inDaylightTime(mid, status) == startsInDST) {
    396             min = mid;
    397         }
    398         else {
    399             max = mid;
    400         }
    401         if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
    402     }
    403     logln("Binary Search Before: " + showDate(min));
    404     logln("Binary Search After:  " + showDate(max));
    405     UDate mindelta = expectedBoundary - min;
    406     UDate maxdelta = max - expectedBoundary;
    407     if (mindelta >= 0 &&
    408         mindelta <= INTERVAL &&
    409         maxdelta >= 0 &&
    410         maxdelta <= INTERVAL) logln(UnicodeString("PASS: Expected boundary at ") + expectedBoundary);
    411     else errln(UnicodeString("FAIL: Expected boundary at ") + expectedBoundary);
    412 }
    413 
    414 // -------------------------------------
    415 
    416 /**
    417  * Test the handling of the "new" rules; that is, rules other than nth Day of week.
    418  */
    419 void
    420 TimeZoneBoundaryTest::TestNewRules()
    421 {
    422 #if 1
    423     {
    424         UErrorCode status = U_ZERO_ERROR;
    425         SimpleTimeZone *tz;
    426         logln("-----------------------------------------------------------------");
    427         logln("Aug 2ndTues .. Mar 15");
    428         tz = new SimpleTimeZone(- 8 * (int32_t)ONE_HOUR, "Test_1", UCAL_AUGUST, 2, UCAL_TUESDAY, 2 * (int32_t)ONE_HOUR, UCAL_MARCH, 15, 0, 2 * (int32_t)ONE_HOUR, status);
    429         logln("========================================");
    430         testUsingBinarySearch(tz, date(97, 0, 1), 858416400000.0);
    431         logln("========================================");
    432         testUsingBinarySearch(tz, date(97, 6, 1), 871380000000.0);
    433         delete tz;
    434         logln("-----------------------------------------------------------------");
    435         logln("Apr Wed>=14 .. Sep Sun<=20");
    436         tz = new SimpleTimeZone(- 8 * (int32_t)ONE_HOUR, "Test_2", UCAL_APRIL, 14, - UCAL_WEDNESDAY, 2 *(int32_t)ONE_HOUR, UCAL_SEPTEMBER, - 20, - UCAL_SUNDAY, 2 * (int32_t)ONE_HOUR, status);
    437         logln("========================================");
    438         testUsingBinarySearch(tz, date(97, 0, 1), 861184800000.0);
    439         logln("========================================");
    440         testUsingBinarySearch(tz, date(97, 6, 1), 874227600000.0);
    441         delete tz;
    442     }
    443 #endif
    444 }
    445 
    446 // -------------------------------------
    447 
    448 void
    449 TimeZoneBoundaryTest::findBoundariesStepwise(int32_t year, UDate interval, TimeZone* z, int32_t expectedChanges)
    450 {
    451     UErrorCode status = U_ZERO_ERROR;
    452     UnicodeString str;
    453     UDate d = date(year - 1900, UCAL_JANUARY, 1);
    454     UDate time = d;
    455     UDate limit = time + ONE_YEAR + ONE_DAY;
    456     UBool lastState = z->inDaylightTime(d, status);
    457     if (failure(status, "TimeZone::inDaylightTime", TRUE)) return;
    458     int32_t changes = 0;
    459     logln(UnicodeString("-- Zone ") + z->getID(str) + " starts in " + year + " with DST = " + (lastState?"true":"false"));
    460     logln(UnicodeString("useDaylightTime = ") + (z->useDaylightTime()?"true":"false"));
    461     while (time < limit) {
    462         d = time;
    463         UBool state = z->inDaylightTime(d, status);
    464         if (failure(status, "TimeZone::inDaylightTime")) return;
    465         if (state != lastState) {
    466             logln(UnicodeString(state ? "Entry ": "Exit ") + "at " + d);
    467             lastState = state;++changes;
    468         }
    469         time += interval;
    470     }
    471     if (changes == 0) {
    472         if (!lastState &&
    473             !z->useDaylightTime()) logln("No DST");
    474         else errln("FAIL: DST all year, or no DST with true useDaylightTime");
    475     }
    476     else if (changes != 2) {
    477         errln(UnicodeString("FAIL: ") + changes + " changes seen; should see 0 or 2");
    478     }
    479     else if (!z->useDaylightTime()) {
    480         errln("FAIL: useDaylightTime false but 2 changes seen");
    481     }
    482     if (changes != expectedChanges) {
    483         dataerrln(UnicodeString("FAIL: ") + changes + " changes seen; expected " + expectedChanges);
    484     }
    485 }
    486 
    487 // -------------------------------------
    488 
    489 /**
    490  * This test is problematic. It makes assumptions about the behavior
    491  * of specific zones. Since ICU's zone table is based on the Olson
    492  * zones (the UNIX zones), and those change from time to time, this
    493  * test can fail after a zone table update. If that happens, the
    494  * selected zones need to be updated to have the behavior
    495  * expected. That is, they should have DST, not have DST, and have DST
    496  * -- other than that this test isn't picky. 12/3/99 aliu
    497  *
    498  * Test the behavior of SimpleTimeZone at the transition into and out of DST.
    499  * Use a stepwise march to find boundaries.
    500  */
    501 void
    502 TimeZoneBoundaryTest::TestStepwise()
    503 {
    504     TimeZone *zone =  TimeZone::createTimeZone("America/New_York");
    505     findBoundariesStepwise(1997, ONE_DAY, zone, 2);
    506     delete zone;
    507     zone = TimeZone::createTimeZone("UTC"); // updated 12/3/99 aliu
    508     findBoundariesStepwise(1997, ONE_DAY, zone, 0);
    509     delete zone;
    510     zone = TimeZone::createTimeZone("Australia/Adelaide");
    511     findBoundariesStepwise(1997, ONE_DAY, zone, 2);
    512     delete zone;
    513 }
    514 
    515 #endif /* #if !UCONFIG_NO_FORMATTING */
    516