From dec131d44fcafbbdedb8ef86585eb6e26e586fc4 Mon Sep 17 00:00:00 2001 From: Frank Tang Date: Fri, 13 Sep 2024 00:21:49 -0700 Subject: [PATCH 1/4] ICU-22736 Add Persian test --- icu4c/source/test/intltest/incaltst.cpp | 410 ++++++++++++++++++++++++ icu4c/source/test/intltest/incaltst.h | 1 + 2 files changed, 411 insertions(+) diff --git a/icu4c/source/test/intltest/incaltst.cpp b/icu4c/source/test/intltest/incaltst.cpp index d7d052bde24f..3d67f54932b2 100644 --- a/icu4c/source/test/intltest/incaltst.cpp +++ b/icu4c/source/test/intltest/incaltst.cpp @@ -86,6 +86,7 @@ void IntlCalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &n TESTCASE_AUTO(TestJapanese3860); TESTCASE_AUTO(TestForceGannenNumbering); TESTCASE_AUTO(TestPersian); + TESTCASE_AUTO(TestPersianData); TESTCASE_AUTO(TestPersianFormat); TESTCASE_AUTO(TestTaiwan); TESTCASE_AUTO(TestConsistencyGregorian); @@ -895,6 +896,415 @@ void IntlCalendarTest::TestPersian() { delete cal; delete grego; } +void IntlCalendarTest::TestPersianData() { + UErrorCode status = U_ZERO_ERROR; + std::unique_ptr gcal(Calendar::createInstance(TimeZone::createTimeZone("Asia/Tehran"), "en", status)); + std::unique_ptr cal(Calendar::createInstance(TimeZone::createTimeZone("Asia/Tehran"), "fa_IR@calendar=persian", status)); + + struct TestCase1 { + int32_t rd; + int32_t year; + int32_t month; + int32_t day; + } testCases1[]{ + {656786, 1178, 1, 1}, + {664224, 1198, 5, 10}, + {671401, 1218, 1, 7}, + {694799, 1282, 1, 29}, + {702806, 1304, 1, 1}, + {704424, 1308, 6, 3}, + {708842, 1320, 7, 7}, + {709409, 1322, 1, 29}, + {709580, 1322, 7, 14}, + {727274, 1370, 12, 27}, + {728714, 1374, 12, 6}, + {739330, 1403, 12, 30}, + {739331, 1404, 1, 1}, + {744313, 1417, 8, 19}, + {763436, 1469, 12, 30}, + {763437, 1470, 1, 1}, + {764652, 1473, 4, 28}, + {775123, 1501, 12, 29}, + {775488, 1502, 12, 29}, + {775489, 1503, 1, 1}, + {1317874, 2988, 1, 1}, + }; + std::string utf8; + UnicodeString id; + printf("%s\n", cal->getTimeZone().getID(id).toUTF8String(utf8).c_str()); + for (const auto &testCase : testCases1) { + status = U_ZERO_ERROR; + int32_t jday = testCase.rd + 1721424; + cal->clear(); + cal->set(UCAL_JULIAN_DAY, jday); + int32_t actualYear = cal->get(UCAL_YEAR, status); + int32_t actualMonth = cal->get(UCAL_MONTH, status)+1; + int32_t actualDay = cal->get(UCAL_DAY_OF_MONTH, status)+1; + if (actualYear != testCase.year || actualMonth != testCase.month || + actualDay != testCase.day) { + printf("rd %d = jday %d-> expect Persian(%d, %d, %d) actual Persian(%d, %d, %d)\n", + testCase.rd, + jday, + testCase.year, + testCase.month, + testCase.day, + actualYear, + actualMonth, + actualDay); + } + + cal->clear(); + cal->set(UCAL_YEAR, testCase.year); + cal->set(UCAL_MONTH, testCase.month-1); + cal->set(UCAL_DAY_OF_MONTH, testCase.day-1); + int32_t actualJday = cal->get(UCAL_JULIAN_DAY, status); + int32_t actualRD = actualJday - 1721424; + if (actualRD != testCase.rd) { + printf("Persian(%d, %d, %d) => expect rd %d but actual jd: %d rd: %d\n", + testCase.year, + testCase.month, + testCase.day, + testCase.rd, + actualJday, + actualRD); + } + } + + // From https://calendar.ut.ac.ir/Fa/News/Data/Doc/KabiseShamsi1206-1498-new.pdf + // Plain text version at https://github.com/roozbehp/persiancalendar/blob/main/kabise.txt + struct TestCase2 { + int32_t pYear; + bool pLeap; + int32_t year; + int32_t month; + int32_t day; + } testCases2[]{ + {1206, false, 1827, 3, 22}, + {1207, false, 1828, 3, 21}, + {1208, false, 1829, 3, 21}, + {1209, false, 1830, 3, 21}, + {1210, true, 1831, 3, 21}, + {1211, false, 1832, 3, 21}, + {1212, false, 1833, 3, 21}, + {1213, false, 1834, 3, 21}, + {1214, true, 1835, 3, 21}, + {1215, false, 1836, 3, 21}, + {1216, false, 1837, 3, 21}, + {1217, false, 1838, 3, 21}, + {1218, true, 1839, 3, 21}, + {1219, false, 1840, 3, 21}, + {1220, false, 1841, 3, 21}, + {1221, false, 1842, 3, 21}, + {1222, true, 1843, 3, 21}, + {1223, false, 1844, 3, 21}, + {1224, false, 1845, 3, 21}, + {1225, false, 1846, 3, 21}, + {1226, true, 1847, 3, 21}, + {1227, false, 1848, 3, 21}, + {1228, false, 1849, 3, 21}, + {1229, false, 1850, 3, 21}, + {1230, true, 1851, 3, 21}, + {1231, false, 1852, 3, 21}, + {1232, false, 1853, 3, 21}, + {1233, false, 1854, 3, 21}, + {1234, true, 1855, 3, 21}, + {1235, false, 1856, 3, 21}, + {1236, false, 1857, 3, 21}, + {1237, false, 1858, 3, 21}, + {1238, true, 1859, 3, 21}, + {1239, false, 1860, 3, 21}, + {1240, false, 1861, 3, 21}, + {1241, false, 1862, 3, 21}, + {1242, false, 1863, 3, 21}, + {1243, true, 1864, 3, 20}, + {1244, false, 1865, 3, 21}, + {1245, false, 1866, 3, 21}, + {1246, false, 1867, 3, 21}, + {1247, true, 1868, 3, 20}, + {1248, false, 1869, 3, 21}, + {1249, false, 1870, 3, 21}, + {1250, false, 1871, 3, 21}, + {1251, true, 1872, 3, 20}, + {1252, false, 1873, 3, 21}, + {1253, false, 1874, 3, 21}, + {1254, false, 1875, 3, 21}, + {1255, true, 1876, 3, 20}, + {1256, false, 1877, 3, 21}, + {1257, false, 1878, 3, 21}, + {1258, false, 1879, 3, 21}, + {1259, true, 1880, 3, 20}, + {1260, false, 1881, 3, 21}, + {1261, false, 1882, 3, 21}, + {1262, false, 1883, 3, 21}, + {1263, true, 1884, 3, 20}, + {1264, false, 1885, 3, 21}, + {1265, false, 1886, 3, 21}, + {1266, false, 1887, 3, 21}, + {1267, true, 1888, 3, 20}, + {1268, false, 1889, 3, 21}, + {1269, false, 1890, 3, 21}, + {1270, false, 1891, 3, 21}, + {1271, true, 1892, 3, 20}, + {1272, false, 1893, 3, 21}, + {1273, false, 1894, 3, 21}, + {1274, false, 1895, 3, 21}, + {1275, false, 1896, 3, 20}, + {1276, true, 1897, 3, 20}, + {1277, false, 1898, 3, 21}, + {1278, false, 1899, 3, 21}, + {1279, false, 1900, 3, 21}, + {1280, true, 1901, 3, 21}, + {1281, false, 1902, 3, 22}, + {1282, false, 1903, 3, 22}, + {1283, false, 1904, 3, 21}, + {1284, true, 1905, 3, 21}, + {1285, false, 1906, 3, 22}, + {1286, false, 1907, 3, 22}, + {1287, false, 1908, 3, 21}, + {1288, true, 1909, 3, 21}, + {1289, false, 1910, 3, 22}, + {1290, false, 1911, 3, 22}, + {1291, false, 1912, 3, 21}, + {1292, true, 1913, 3, 21}, + {1293, false, 1914, 3, 22}, + {1294, false, 1915, 3, 22}, + {1295, false, 1916, 3, 21}, + {1296, true, 1917, 3, 21}, + {1297, false, 1918, 3, 22}, + {1298, false, 1919, 3, 22}, + {1299, false, 1920, 3, 21}, + {1300, true, 1921, 3, 21}, + {1301, false, 1922, 3, 22}, + {1302, false, 1923, 3, 22}, + {1303, false, 1924, 3, 21}, + {1304, true, 1925, 3, 21}, + {1305, false, 1926, 3, 22}, + {1306, false, 1927, 3, 22}, + {1307, false, 1928, 3, 21}, + {1308, false, 1929, 3, 21}, + {1309, true, 1930, 3, 21}, + {1310, false, 1931, 3, 22}, + {1311, false, 1932, 3, 21}, + {1312, false, 1933, 3, 21}, + {1313, true, 1934, 3, 21}, + {1314, false, 1935, 3, 22}, + {1315, false, 1936, 3, 21}, + {1316, false, 1937, 3, 21}, + {1317, true, 1938, 3, 21}, + {1318, false, 1939, 3, 22}, + {1319, false, 1940, 3, 21}, + {1320, false, 1941, 3, 21}, + {1321, true, 1942, 3, 21}, + {1322, false, 1943, 3, 22}, + {1323, false, 1944, 3, 21}, + {1324, false, 1945, 3, 21}, + {1325, true, 1946, 3, 21}, + {1326, false, 1947, 3, 22}, + {1327, false, 1948, 3, 21}, + {1328, false, 1949, 3, 21}, + {1329, true, 1950, 3, 21}, + {1330, false, 1951, 3, 22}, + {1331, false, 1952, 3, 21}, + {1332, false, 1953, 3, 21}, + {1333, true, 1954, 3, 21}, + {1334, false, 1955, 3, 22}, + {1335, false, 1956, 3, 21}, + {1336, false, 1957, 3, 21}, + {1337, true, 1958, 3, 21}, + {1338, false, 1959, 3, 22}, + {1339, false, 1960, 3, 21}, + {1340, false, 1961, 3, 21}, + {1341, false, 1962, 3, 21}, + {1342, true, 1963, 3, 21}, + {1343, false, 1964, 3, 21}, + {1344, false, 1965, 3, 21}, + {1345, false, 1966, 3, 21}, + {1346, true, 1967, 3, 21}, + {1347, false, 1968, 3, 21}, + {1348, false, 1969, 3, 21}, + {1349, false, 1970, 3, 21}, + {1350, true, 1971, 3, 21}, + {1351, false, 1972, 3, 21}, + {1352, false, 1973, 3, 21}, + {1353, false, 1974, 3, 21}, + {1354, true, 1975, 3, 21}, + {1355, false, 1976, 3, 21}, + {1356, false, 1977, 3, 21}, + {1357, false, 1978, 3, 21}, + {1358, true, 1979, 3, 21}, + {1359, false, 1980, 3, 21}, + {1360, false, 1981, 3, 21}, + {1361, false, 1982, 3, 21}, + {1362, true, 1983, 3, 21}, + {1363, false, 1984, 3, 21}, + {1364, false, 1985, 3, 21}, + {1365, false, 1986, 3, 21}, + {1366, true, 1987, 3, 21}, + {1367, false, 1988, 3, 21}, + {1368, false, 1989, 3, 21}, + {1369, false, 1990, 3, 21}, + {1370, true, 1991, 3, 21}, + {1371, false, 1992, 3, 21}, + {1372, false, 1993, 3, 21}, + {1373, false, 1994, 3, 21}, + {1374, false, 1995, 3, 21}, + {1375, true, 1996, 3, 20}, + {1376, false, 1997, 3, 21}, + {1377, false, 1998, 3, 21}, + {1378, false, 1999, 3, 21}, + {1379, true, 2000, 3, 20}, + {1380, false, 2001, 3, 21}, + {1381, false, 2002, 3, 21}, + {1382, false, 2003, 3, 21}, + {1383, true, 2004, 3, 20}, + {1384, false, 2005, 3, 21}, + {1385, false, 2006, 3, 21}, + {1386, false, 2007, 3, 21}, + {1387, true, 2008, 3, 20}, + {1388, false, 2009, 3, 21}, + {1389, false, 2010, 3, 21}, + {1390, false, 2011, 3, 21}, + {1391, true, 2012, 3, 20}, + {1392, false, 2013, 3, 21}, + {1393, false, 2014, 3, 21}, + {1394, false, 2015, 3, 21}, + {1395, true, 2016, 3, 20}, + {1396, false, 2017, 3, 21}, + {1397, false, 2018, 3, 21}, + {1398, false, 2019, 3, 21}, + {1399, true, 2020, 3, 20}, + {1400, false, 2021, 3, 21}, + {1401, false, 2022, 3, 21}, + {1402, false, 2023, 3, 21}, + {1403, true, 2024, 3, 20}, + {1404, false, 2025, 3, 21}, + {1405, false, 2026, 3, 21}, + {1406, false, 2027, 3, 21}, + {1407, false, 2028, 3, 20}, + {1408, true, 2029, 3, 20}, + {1409, false, 2030, 3, 21}, + {1410, false, 2031, 3, 21}, + {1411, false, 2032, 3, 20}, + {1412, true, 2033, 3, 20}, + {1413, false, 2034, 3, 21}, + {1414, false, 2035, 3, 21}, + {1415, false, 2036, 3, 20}, + {1416, true, 2037, 3, 20}, + {1417, false, 2038, 3, 21}, + {1418, false, 2039, 3, 21}, + {1419, false, 2040, 3, 20}, + {1420, true, 2041, 3, 20}, + {1421, false, 2042, 3, 21}, + {1422, false, 2043, 3, 21}, + {1423, false, 2044, 3, 20}, + {1424, true, 2045, 3, 20}, + {1425, false, 2046, 3, 21}, + {1426, false, 2047, 3, 21}, + {1427, false, 2048, 3, 20}, + {1428, true, 2049, 3, 20}, + {1429, false, 2050, 3, 21}, + {1430, false, 2051, 3, 21}, + {1431, false, 2052, 3, 20}, + {1432, true, 2053, 3, 20}, + {1433, false, 2054, 3, 21}, + {1434, false, 2055, 3, 21}, + {1435, false, 2056, 3, 20}, + {1436, true, 2057, 3, 20}, + {1437, false, 2058, 3, 21}, + {1438, false, 2059, 3, 21}, + {1439, false, 2060, 3, 20}, + {1440, false, 2061, 3, 20}, + {1441, true, 2062, 3, 20}, + {1442, false, 2063, 3, 21}, + {1443, false, 2064, 3, 20}, + {1444, false, 2065, 3, 20}, + {1445, true, 2066, 3, 20}, + {1446, false, 2067, 3, 21}, + {1447, false, 2068, 3, 20}, + {1448, false, 2069, 3, 20}, + {1449, true, 2070, 3, 20}, + {1450, false, 2071, 3, 21}, + {1451, false, 2072, 3, 20}, + {1452, false, 2073, 3, 20}, + {1453, true, 2074, 3, 20}, + {1454, false, 2075, 3, 21}, + {1455, false, 2076, 3, 20}, + {1456, false, 2077, 3, 20}, + {1457, true, 2078, 3, 20}, + {1458, false, 2079, 3, 21}, + {1459, false, 2080, 3, 20}, + {1460, false, 2081, 3, 20}, + {1461, true, 2082, 3, 20}, + {1462, false, 2083, 3, 21}, + {1463, false, 2084, 3, 20}, + {1464, false, 2085, 3, 20}, + {1465, true, 2086, 3, 20}, + {1466, false, 2087, 3, 21}, + {1467, false, 2088, 3, 20}, + {1468, false, 2089, 3, 20}, + {1469, true, 2090, 3, 20}, + {1470, false, 2091, 3, 21}, + {1471, false, 2092, 3, 20}, + {1472, false, 2093, 3, 20}, + {1473, false, 2094, 3, 20}, + {1474, true, 2095, 3, 20}, + {1475, false, 2096, 3, 20}, + {1476, false, 2097, 3, 20}, + {1477, false, 2098, 3, 20}, + {1478, true, 2099, 3, 20}, + {1479, false, 2100, 3, 21}, + {1480, false, 2101, 3, 21}, + {1481, false, 2102, 3, 21}, + {1482, true, 2103, 3, 21}, + {1483, false, 2104, 3, 21}, + {1484, false, 2105, 3, 21}, + {1485, false, 2106, 3, 21}, + {1486, true, 2107, 3, 21}, + {1487, false, 2108, 3, 21}, + {1488, false, 2109, 3, 21}, + {1489, false, 2110, 3, 21}, + {1490, true, 2111, 3, 21}, + {1491, false, 2112, 3, 21}, + {1492, false, 2113, 3, 21}, + {1493, false, 2114, 3, 21}, + {1494, true, 2115, 3, 21}, + {1495, false, 2116, 3, 21}, + {1496, false, 2117, 3, 21}, + {1497, false, 2118, 3, 21}, + {1498, true, 2119, 3, 21}, + }; + for (const auto &testCase : testCases2) { + cal->clear(); + cal->set(UCAL_YEAR, testCase.pYear); + cal->set(UCAL_MONTH, 0); + cal->set(UCAL_DAY_OF_MONTH, 1); + gcal->setTime(cal->getTime(status), status); + int32_t actualYear = gcal->get(UCAL_YEAR, status); + int32_t actualMonth = gcal->get(UCAL_MONTH, status)+1; + int32_t actualDay = gcal->get(UCAL_DAY_OF_MONTH, status); + if (actualYear != testCase.year || actualMonth != testCase.month || actualDay != testCase.day) { + printf("Persian(%d, 1, 1) => expect Gregorian(%d, %d, %d) actual Gregorian(%d, %d, %d)\n", + testCase.pYear, testCase.year, testCase.month, testCase.day, + actualYear, actualMonth, actualDay); + } + + gcal->clear(); + gcal->set(UCAL_YEAR, testCase.year); + gcal->set(UCAL_MONTH, testCase.month-1); + gcal->set(UCAL_DAY_OF_MONTH, testCase.day); + cal->setTime(gcal->getTime(status), status); + int32_t persianYear = cal->get(UCAL_YEAR, status); + int32_t persianMonth = cal->get(UCAL_MONTH, status)+1; + int32_t persianDay = cal->get(UCAL_DAY_OF_MONTH, status); + if (persianYear != testCase.pYear || persianMonth != 1 || persianDay != 1) { + printf("Gregorian(%d, %d, %d) => expect Persian(%d 1 1) actual Persian(%d, %d %d)\n", + testCase.year, testCase.month, testCase.day, + testCase.pYear, + persianYear, persianMonth, persianDay); + } + } +} + void IntlCalendarTest::TestPersianFormat() { UErrorCode status = U_ZERO_ERROR; diff --git a/icu4c/source/test/intltest/incaltst.h b/icu4c/source/test/intltest/incaltst.h index 8f8ca4300968..f7c59302ac6c 100644 --- a/icu4c/source/test/intltest/incaltst.h +++ b/icu4c/source/test/intltest/incaltst.h @@ -41,6 +41,7 @@ class IntlCalendarTest: public CalendarTimeZoneTest { void TestForceGannenNumbering(); void TestPersian(); + void TestPersianData(); void TestPersianFormat(); void TestConsistencyGregorian(); From af9334a15e3138e652dbbb4d47f9a2eae1a372ac Mon Sep 17 00:00:00 2001 From: Frank Tang Date: Sat, 14 Sep 2024 00:57:45 -0700 Subject: [PATCH 2/4] ICU-22736 fix --- icu4c/source/i18n/persncal.cpp | 59 +++++++++++++++++++++++++ icu4c/source/i18n/ucln_in.h | 1 + icu4c/source/test/intltest/incaltst.cpp | 40 +++++------------ 3 files changed, 72 insertions(+), 28 deletions(-) diff --git a/icu4c/source/i18n/persncal.cpp b/icu4c/source/i18n/persncal.cpp index c97a32197aca..8685f346dd15 100644 --- a/icu4c/source/i18n/persncal.cpp +++ b/icu4c/source/i18n/persncal.cpp @@ -25,6 +25,9 @@ #include "umutex.h" #include "gregoimp.h" // Math #include +#include "cmemory.h" +#include "ucln_in.h" +#include "unicode/uniset.h" static const int16_t kPersianNumDays[] = {0,31,62,93,124,155,186,216,246,276,306,336}; // 0-based, for day-in-year @@ -62,6 +65,45 @@ static const int32_t kPersianCalendarLimits[UCAL_FIELD_COUNT][4] = { { 0, 0, 11, 11}, // ORDINAL_MONTH }; +namespace { // anonymous + +static icu::UnicodeSet *gLeapCorrection = nullptr; +static icu::UInitOnce gCorrectionInitOnce {}; +static int32_t gMinCorrection; +} // namespace +U_CDECL_BEGIN +static UBool calendar_persian_cleanup() { + if (gLeapCorrection) { + delete gLeapCorrection; + gLeapCorrection = nullptr; + } + gCorrectionInitOnce.reset(); + return true; +} +U_CDECL_END + +namespace { // anonymous +static void U_CALLCONV initLeapCorrection() { + static int16_t nonLeapYears[] = { + 1502, 1601, 1634, 1667, 1700, 1733, 1766, 1799, 1832, 1865, 1898, 1931, 1964, 1997, 2030, 2059, + 2063, 2096, 2129, 2158, 2162, 2191, 2195, 2224, 2228, 2257, 2261, 2290, 2294, 2323, 2327, 2356, + 2360, 2389, 2393, 2422, 2426, 2455, 2459, 2488, 2492, 2521, 2525, 2554, 2558, 2587, 2591, 2620, + 2624, 2653, 2657, 2686, 2690, 2719, 2723, 2748, 2752, 2756, 2781, 2785, 2789, 2818, 2822, 2847, + 2851, 2855, 2880, 2884, 2888, 2913, 2917, 2921, 2946, 2950, 2954, 2979, 2983, 2987, + }; + gMinCorrection = nonLeapYears[0]; + icu::UnicodeSet prefab; + for (int32_t i = 0; i < UPRV_LENGTHOF(nonLeapYears); i++) { + prefab.add(nonLeapYears[i]); + } + gLeapCorrection = prefab.cloneAsThawed(); + ucln_i18n_registerCleanup(UCLN_I18N_PERSIAN_CALENDAR, calendar_persian_cleanup); +} +const icu::UnicodeSet* getLeapCorrection() { + umtx_initOnce(gCorrectionInitOnce, &initLeapCorrection); + return gLeapCorrection; +} +} // namespace anonymous U_NAMESPACE_BEGIN static const int32_t PERSIAN_EPOCH = 1948320; @@ -111,6 +153,12 @@ int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType li */ UBool PersianCalendar::isLeapYear(int32_t year) { + if (year >= gMinCorrection && getLeapCorrection()->contains(year)) { + return false; + } + if (year > gMinCorrection && getLeapCorrection()->contains(year-1)) { + return true; + } int64_t y = static_cast(year) * 25LL + 11LL; return (y % 33L < 8); } @@ -181,6 +229,10 @@ int64_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U int64_t julianDay = PERSIAN_EPOCH - 1LL + 365LL * (eyear - 1LL) + ClockMath::floorDivide(8LL * eyear + 21, 33); + if (eyear > gMinCorrection && getLeapCorrection()->contains(eyear-1)) { + julianDay--; + } + if (month != 0) { julianDay += kPersianNumDays[month]; } @@ -226,7 +278,14 @@ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) } int64_t farvardin1 = 365LL * (year - 1) + ClockMath::floorDivide(8LL * year + 21, 33); + if (year > gMinCorrection && getLeapCorrection()->contains(year-1)) { + farvardin1--; + } int32_t dayOfYear = daysSinceEpoch - farvardin1; // 0-based + if (dayOfYear == 365 && year >= gMinCorrection && getLeapCorrection()->contains(year)) { + year++; + dayOfYear = 0; + } U_ASSERT(dayOfYear >= 0); U_ASSERT(dayOfYear < 366); // diff --git a/icu4c/source/i18n/ucln_in.h b/icu4c/source/i18n/ucln_in.h index 765cdd559fb4..b3b19628f4c6 100644 --- a/icu4c/source/i18n/ucln_in.h +++ b/icu4c/source/i18n/ucln_in.h @@ -39,6 +39,7 @@ typedef enum ECleanupI18NType { UCLN_I18N_HEBREW_CALENDAR, UCLN_I18N_ASTRO_CALENDAR, UCLN_I18N_DANGI_CALENDAR, + UCLN_I18N_PERSIAN_CALENDAR, UCLN_I18N_CALENDAR, UCLN_I18N_TIMEZONEFORMAT, UCLN_I18N_TZDBTIMEZONENAMES, diff --git a/icu4c/source/test/intltest/incaltst.cpp b/icu4c/source/test/intltest/incaltst.cpp index 3d67f54932b2..86ecc2f0c771 100644 --- a/icu4c/source/test/intltest/incaltst.cpp +++ b/icu4c/source/test/intltest/incaltst.cpp @@ -929,9 +929,6 @@ void IntlCalendarTest::TestPersianData() { {775489, 1503, 1, 1}, {1317874, 2988, 1, 1}, }; - std::string utf8; - UnicodeString id; - printf("%s\n", cal->getTimeZone().getID(id).toUTF8String(utf8).c_str()); for (const auto &testCase : testCases1) { status = U_ZERO_ERROR; int32_t jday = testCase.rd + 1721424; @@ -940,17 +937,10 @@ void IntlCalendarTest::TestPersianData() { int32_t actualYear = cal->get(UCAL_YEAR, status); int32_t actualMonth = cal->get(UCAL_MONTH, status)+1; int32_t actualDay = cal->get(UCAL_DAY_OF_MONTH, status)+1; - if (actualYear != testCase.year || actualMonth != testCase.month || - actualDay != testCase.day) { - printf("rd %d = jday %d-> expect Persian(%d, %d, %d) actual Persian(%d, %d, %d)\n", - testCase.rd, - jday, - testCase.year, - testCase.month, - testCase.day, - actualYear, - actualMonth, - actualDay); + if (actualYear != testCase.year || actualMonth != testCase.month || actualDay != testCase.day) { + errln(UnicodeString("rd ") + testCase.rd + " = jday " + jday + " -> expect Persian(" + + testCase.year + "/" + testCase.month + "/" + testCase.day + ") " + + "actual Persian(" + actualYear + "/" + actualMonth + "/" + actualDay + ")"); } cal->clear(); @@ -960,13 +950,8 @@ void IntlCalendarTest::TestPersianData() { int32_t actualJday = cal->get(UCAL_JULIAN_DAY, status); int32_t actualRD = actualJday - 1721424; if (actualRD != testCase.rd) { - printf("Persian(%d, %d, %d) => expect rd %d but actual jd: %d rd: %d\n", - testCase.year, - testCase.month, - testCase.day, - testCase.rd, - actualJday, - actualRD); + errln(UnicodeString("Persian(") + testCase.year + "/" + testCase.month + "/" + testCase.day + ") => "+ + "expect rd " + testCase.rd + " but actual jd: " + actualJday + " = rd " + actualRD); } } @@ -1283,9 +1268,9 @@ void IntlCalendarTest::TestPersianData() { int32_t actualMonth = gcal->get(UCAL_MONTH, status)+1; int32_t actualDay = gcal->get(UCAL_DAY_OF_MONTH, status); if (actualYear != testCase.year || actualMonth != testCase.month || actualDay != testCase.day) { - printf("Persian(%d, 1, 1) => expect Gregorian(%d, %d, %d) actual Gregorian(%d, %d, %d)\n", - testCase.pYear, testCase.year, testCase.month, testCase.day, - actualYear, actualMonth, actualDay); + errln(UnicodeString("Persian(") + testCase.pYear + ", 1, 1) => " + + "expect Gregorian(" + testCase.year + "/" + testCase.month + "/" + testCase.day + ") " + + "actual Gregorian(" + actualYear + "/" + actualMonth + "/" + actualDay + ")"); } gcal->clear(); @@ -1297,10 +1282,9 @@ void IntlCalendarTest::TestPersianData() { int32_t persianMonth = cal->get(UCAL_MONTH, status)+1; int32_t persianDay = cal->get(UCAL_DAY_OF_MONTH, status); if (persianYear != testCase.pYear || persianMonth != 1 || persianDay != 1) { - printf("Gregorian(%d, %d, %d) => expect Persian(%d 1 1) actual Persian(%d, %d %d)\n", - testCase.year, testCase.month, testCase.day, - testCase.pYear, - persianYear, persianMonth, persianDay); + errln(UnicodeString("Gregorian(") + testCase.year + "/" + testCase.month + "/" + testCase.day + ") "+ + " => expect Persian(" + testCase.pYear + "/1/1) actual " + + "Persian(" + persianYear + "/" + persianMonth + "/" + persianDay + ")"); } } } From 5d1c5ced2bfcb752f264aebcba2236804cb26294 Mon Sep 17 00:00:00 2001 From: Frank Tang Date: Wed, 18 Sep 2024 11:10:46 -0700 Subject: [PATCH 3/4] park work --- icu4c/source/i18n/persncal.cpp | 46 +++++++++++++++++-------- icu4c/source/test/intltest/incaltst.cpp | 10 +++++- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/icu4c/source/i18n/persncal.cpp b/icu4c/source/i18n/persncal.cpp index 8685f346dd15..6e50c2d8a264 100644 --- a/icu4c/source/i18n/persncal.cpp +++ b/icu4c/source/i18n/persncal.cpp @@ -154,13 +154,17 @@ int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType li UBool PersianCalendar::isLeapYear(int32_t year) { if (year >= gMinCorrection && getLeapCorrection()->contains(year)) { + printf("%d is not leap year\n", year); return false; } if (year > gMinCorrection && getLeapCorrection()->contains(year-1)) { + printf("%d is leap year\n", year); return true; } int64_t y = static_cast(year) * 25LL + 11LL; - return (y % 33L < 8); + bool res = (y % 33L < 8); + printf("%d is %s leap year\n", year, res ? "INDEED" : "NOT"); + return res; } /** @@ -213,8 +217,18 @@ int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const { // Functions for converting from field values to milliseconds.... //------------------------------------------------------------------------- +static int64_t firstJulianOfYear(int64_t year) { + int64_t julianDay = 365LL * (year - 1LL) + ClockMath::floorDivide(8LL * year + 21, 33); + if (year > gMinCorrection && getLeapCorrection()->contains(year-1)) { + julianDay--; + } + return julianDay; +} + + // Return JD of start of given month/year int64_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/, UErrorCode& status) const { + printf("handleComputeMonthStart y=%d m=%d\n", eyear, month); if (U_FAILURE(status)) { return 0; } @@ -227,14 +241,12 @@ int64_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U } } - int64_t julianDay = PERSIAN_EPOCH - 1LL + 365LL * (eyear - 1LL) + ClockMath::floorDivide(8LL * eyear + 21, 33); - - if (eyear > gMinCorrection && getLeapCorrection()->contains(eyear-1)) { - julianDay--; - } + int64_t julianDay = PERSIAN_EPOCH - 1LL + firstJulianOfYear(eyear); + printf("julianDay correct to = %d\n", julianDay); if (month != 0) { julianDay += kPersianNumDays[month]; + printf("julianDay add month to = %d\n", julianDay); } return julianDay; @@ -269,6 +281,7 @@ int32_t PersianCalendar::handleGetExtendedYear(UErrorCode& status) { * method is called. */ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) { + printf("handleComputeFields jd=%d\n", julianDay); int64_t daysSinceEpoch = julianDay - PERSIAN_EPOCH; int64_t year = ClockMath::floorDivideInt64( 33LL * daysSinceEpoch + 3LL, 12053LL) + 1LL; @@ -277,18 +290,19 @@ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) return; } - int64_t farvardin1 = 365LL * (year - 1) + ClockMath::floorDivide(8LL * year + 21, 33); - if (year > gMinCorrection && getLeapCorrection()->contains(year-1)) { - farvardin1--; - } + int64_t farvardin1 = firstJulianOfYear(year); + int32_t dayOfYear = daysSinceEpoch - farvardin1; // 0-based + U_ASSERT(dayOfYear >= 0); + U_ASSERT(dayOfYear < 366); + + printf("a dayOfYear = %d\n", dayOfYear); + // if (dayOfYear == 365 && year >= gMinCorrection && getLeapCorrection()->contains(year)) { year++; dayOfYear = 0; } - U_ASSERT(dayOfYear >= 0); - U_ASSERT(dayOfYear < 366); - // + printf("b dayOfYear = %d\n", dayOfYear); int32_t month; if (dayOfYear < 216) { // Compute 0-based month month = dayOfYear / 31; @@ -298,11 +312,13 @@ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) U_ASSERT(month >= 0); U_ASSERT(month < 12); - int32_t dayOfMonth = dayOfYear - kPersianNumDays[month] + 1; + ++dayOfYear; // Make it 1-based now + printf("c dayOfYear = %d\n", dayOfYear); + int32_t dayOfMonth = dayOfYear - kPersianNumDays[month]; + printf("d dayOfMonth = %d\n", dayOfMonth); U_ASSERT(dayOfMonth > 0); U_ASSERT(dayOfMonth <= 31); - ++dayOfYear; // Make it 1-based now internalSet(UCAL_ERA, 0); internalSet(UCAL_YEAR, year); diff --git a/icu4c/source/test/intltest/incaltst.cpp b/icu4c/source/test/intltest/incaltst.cpp index 86ecc2f0c771..8d7056838954 100644 --- a/icu4c/source/test/intltest/incaltst.cpp +++ b/icu4c/source/test/intltest/incaltst.cpp @@ -907,6 +907,7 @@ void IntlCalendarTest::TestPersianData() { int32_t month; int32_t day; } testCases1[]{ +#if 0 {656786, 1178, 1, 1}, {664224, 1198, 5, 10}, {671401, 1218, 1, 7}, @@ -926,7 +927,11 @@ void IntlCalendarTest::TestPersianData() { {764652, 1473, 4, 28}, {775123, 1501, 12, 29}, {775488, 1502, 12, 29}, - {775489, 1503, 1, 1}, +#endif + {775487, 1502, 12, 28}, + {775488, 1502, 12, 29}, + {775489, 1503, 1, 1}, // real + {775490, 1503, 1, 2}, {1317874, 2988, 1, 1}, }; for (const auto &testCase : testCases1) { @@ -934,6 +939,7 @@ void IntlCalendarTest::TestPersianData() { int32_t jday = testCase.rd + 1721424; cal->clear(); cal->set(UCAL_JULIAN_DAY, jday); + printf("jday = %d\n", jday); int32_t actualYear = cal->get(UCAL_YEAR, status); int32_t actualMonth = cal->get(UCAL_MONTH, status)+1; int32_t actualDay = cal->get(UCAL_DAY_OF_MONTH, status)+1; @@ -964,6 +970,7 @@ void IntlCalendarTest::TestPersianData() { int32_t month; int32_t day; } testCases2[]{ +#if 0 {1206, false, 1827, 3, 22}, {1207, false, 1828, 3, 21}, {1208, false, 1829, 3, 21}, @@ -1256,6 +1263,7 @@ void IntlCalendarTest::TestPersianData() { {1495, false, 2116, 3, 21}, {1496, false, 2117, 3, 21}, {1497, false, 2118, 3, 21}, +#endif {1498, true, 2119, 3, 21}, }; for (const auto &testCase : testCases2) { From eddd7d89c5e7bd06b9ebf1f1691341124fd65f0f Mon Sep 17 00:00:00 2001 From: Frank Tang Date: Thu, 19 Sep 2024 00:13:15 -0700 Subject: [PATCH 4/4] Fix most bug --- icu4c/source/i18n/persncal.cpp | 12 ------------ icu4c/source/test/intltest/incaltst.cpp | 9 +++------ 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/icu4c/source/i18n/persncal.cpp b/icu4c/source/i18n/persncal.cpp index 6e50c2d8a264..6dfc3cb4c9a7 100644 --- a/icu4c/source/i18n/persncal.cpp +++ b/icu4c/source/i18n/persncal.cpp @@ -154,16 +154,13 @@ int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType li UBool PersianCalendar::isLeapYear(int32_t year) { if (year >= gMinCorrection && getLeapCorrection()->contains(year)) { - printf("%d is not leap year\n", year); return false; } if (year > gMinCorrection && getLeapCorrection()->contains(year-1)) { - printf("%d is leap year\n", year); return true; } int64_t y = static_cast(year) * 25LL + 11LL; bool res = (y % 33L < 8); - printf("%d is %s leap year\n", year, res ? "INDEED" : "NOT"); return res; } @@ -228,7 +225,6 @@ static int64_t firstJulianOfYear(int64_t year) { // Return JD of start of given month/year int64_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/, UErrorCode& status) const { - printf("handleComputeMonthStart y=%d m=%d\n", eyear, month); if (U_FAILURE(status)) { return 0; } @@ -242,11 +238,9 @@ int64_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U } int64_t julianDay = PERSIAN_EPOCH - 1LL + firstJulianOfYear(eyear); - printf("julianDay correct to = %d\n", julianDay); if (month != 0) { julianDay += kPersianNumDays[month]; - printf("julianDay add month to = %d\n", julianDay); } return julianDay; @@ -281,7 +275,6 @@ int32_t PersianCalendar::handleGetExtendedYear(UErrorCode& status) { * method is called. */ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) { - printf("handleComputeFields jd=%d\n", julianDay); int64_t daysSinceEpoch = julianDay - PERSIAN_EPOCH; int64_t year = ClockMath::floorDivideInt64( 33LL * daysSinceEpoch + 3LL, 12053LL) + 1LL; @@ -296,13 +289,10 @@ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) U_ASSERT(dayOfYear >= 0); U_ASSERT(dayOfYear < 366); - printf("a dayOfYear = %d\n", dayOfYear); - // if (dayOfYear == 365 && year >= gMinCorrection && getLeapCorrection()->contains(year)) { year++; dayOfYear = 0; } - printf("b dayOfYear = %d\n", dayOfYear); int32_t month; if (dayOfYear < 216) { // Compute 0-based month month = dayOfYear / 31; @@ -313,9 +303,7 @@ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) U_ASSERT(month < 12); ++dayOfYear; // Make it 1-based now - printf("c dayOfYear = %d\n", dayOfYear); int32_t dayOfMonth = dayOfYear - kPersianNumDays[month]; - printf("d dayOfMonth = %d\n", dayOfMonth); U_ASSERT(dayOfMonth > 0); U_ASSERT(dayOfMonth <= 31); diff --git a/icu4c/source/test/intltest/incaltst.cpp b/icu4c/source/test/intltest/incaltst.cpp index 8d7056838954..df0d525ca512 100644 --- a/icu4c/source/test/intltest/incaltst.cpp +++ b/icu4c/source/test/intltest/incaltst.cpp @@ -907,7 +907,6 @@ void IntlCalendarTest::TestPersianData() { int32_t month; int32_t day; } testCases1[]{ -#if 0 {656786, 1178, 1, 1}, {664224, 1198, 5, 10}, {671401, 1218, 1, 7}, @@ -927,22 +926,22 @@ void IntlCalendarTest::TestPersianData() { {764652, 1473, 4, 28}, {775123, 1501, 12, 29}, {775488, 1502, 12, 29}, -#endif {775487, 1502, 12, 28}, {775488, 1502, 12, 29}, {775489, 1503, 1, 1}, // real {775490, 1503, 1, 2}, + {1317873, 2987, 12, 29}, {1317874, 2988, 1, 1}, + {1317875, 2988, 1, 2}, }; for (const auto &testCase : testCases1) { status = U_ZERO_ERROR; int32_t jday = testCase.rd + 1721424; cal->clear(); cal->set(UCAL_JULIAN_DAY, jday); - printf("jday = %d\n", jday); int32_t actualYear = cal->get(UCAL_YEAR, status); int32_t actualMonth = cal->get(UCAL_MONTH, status)+1; - int32_t actualDay = cal->get(UCAL_DAY_OF_MONTH, status)+1; + int32_t actualDay = cal->get(UCAL_DAY_OF_MONTH, status); if (actualYear != testCase.year || actualMonth != testCase.month || actualDay != testCase.day) { errln(UnicodeString("rd ") + testCase.rd + " = jday " + jday + " -> expect Persian(" + testCase.year + "/" + testCase.month + "/" + testCase.day + ") " + @@ -970,7 +969,6 @@ void IntlCalendarTest::TestPersianData() { int32_t month; int32_t day; } testCases2[]{ -#if 0 {1206, false, 1827, 3, 22}, {1207, false, 1828, 3, 21}, {1208, false, 1829, 3, 21}, @@ -1263,7 +1261,6 @@ void IntlCalendarTest::TestPersianData() { {1495, false, 2116, 3, 21}, {1496, false, 2117, 3, 21}, {1497, false, 2118, 3, 21}, -#endif {1498, true, 2119, 3, 21}, }; for (const auto &testCase : testCases2) {