diff --git a/icu4c/source/data/locales/ar.txt b/icu4c/source/data/locales/ar.txt index 8fb84fa6141d..7c809887c3b5 100644 --- a/icu4c/source/data/locales/ar.txt +++ b/icu4c/source/data/locales/ar.txt @@ -1635,6 +1635,7 @@ ar{ "تيشو", "شووا", "هيسي", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/ast.txt b/icu4c/source/data/locales/ast.txt index c2f8950a09c4..ce9a6857812a 100644 --- a/icu4c/source/data/locales/ast.txt +++ b/icu4c/source/data/locales/ast.txt @@ -2304,6 +2304,7 @@ ast{ "Taishō", "e. Shōwa", "Heisei", + "Qqqq", } narrow{ "Taika", @@ -2542,6 +2543,7 @@ ast{ "T", "S", "H", + "Q", } wide{ "Taika (645–650)", @@ -2780,6 +2782,7 @@ ast{ "Taishō", "era Shōwa", "Heisei", + "Qqqq", } } intervalFormats{ diff --git a/icu4c/source/data/locales/br.txt b/icu4c/source/data/locales/br.txt index 41100c2de9a4..f060ea61b512 100644 --- a/icu4c/source/data/locales/br.txt +++ b/icu4c/source/data/locales/br.txt @@ -4105,6 +4105,7 @@ br{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } narrow{ "Taika (645–650)", @@ -4343,6 +4344,7 @@ br{ "T", "S", "H", + "Q", } wide{ "Taika (645–650)", @@ -4581,6 +4583,7 @@ br{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } } intervalFormats{ diff --git a/icu4c/source/data/locales/bs_Cyrl.txt b/icu4c/source/data/locales/bs_Cyrl.txt index dca25e65ad55..06765fbd71c6 100644 --- a/icu4c/source/data/locales/bs_Cyrl.txt +++ b/icu4c/source/data/locales/bs_Cyrl.txt @@ -1035,6 +1035,7 @@ bs_Cyrl{ "Таишо", "Шова", "Хаисеи", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/cs.txt b/icu4c/source/data/locales/cs.txt index 7299b8ef365b..186a9ae66a40 100644 --- a/icu4c/source/data/locales/cs.txt +++ b/icu4c/source/data/locales/cs.txt @@ -3697,6 +3697,7 @@ cs{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } narrow{ "Taika (645–650)", @@ -3935,6 +3936,7 @@ cs{ "T", "S", "H", + "Q", } wide{ "Taika (645–650)", @@ -4173,6 +4175,7 @@ cs{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } } intervalFormats{ diff --git a/icu4c/source/data/locales/de.txt b/icu4c/source/data/locales/de.txt index 20d7fdeaa201..dafc31df0738 100644 --- a/icu4c/source/data/locales/de.txt +++ b/icu4c/source/data/locales/de.txt @@ -1601,6 +1601,7 @@ de{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/he.txt b/icu4c/source/data/locales/he.txt index 28259660cf44..88a9233eb4b5 100644 --- a/icu4c/source/data/locales/he.txt +++ b/icu4c/source/data/locales/he.txt @@ -1837,6 +1837,7 @@ he{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/hi.txt b/icu4c/source/data/locales/hi.txt index 19d1409f67ee..7a34f79dbac6 100644 --- a/icu4c/source/data/locales/hi.txt +++ b/icu4c/source/data/locales/hi.txt @@ -1239,6 +1239,7 @@ hi{ "ताईशो", "शोवा", "हेईसेई", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/hr.txt b/icu4c/source/data/locales/hr.txt index c10b26711ec1..abace4851570 100644 --- a/icu4c/source/data/locales/hr.txt +++ b/icu4c/source/data/locales/hr.txt @@ -1577,6 +1577,7 @@ hr{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/id.txt b/icu4c/source/data/locales/id.txt index 59360effa8a2..e969669bb041 100644 --- a/icu4c/source/data/locales/id.txt +++ b/icu4c/source/data/locales/id.txt @@ -2217,6 +2217,7 @@ id{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/ja.txt b/icu4c/source/data/locales/ja.txt index 0a98cbd98b21..cd2258f645b4 100644 --- a/icu4c/source/data/locales/ja.txt +++ b/icu4c/source/data/locales/ja.txt @@ -2464,6 +2464,7 @@ ja{ "大正", "昭和", "平成", + "QQ", } narrow{ "大化", @@ -2702,6 +2703,7 @@ ja{ "T", "S", "H", + "Q", } } } diff --git a/icu4c/source/data/locales/ko.txt b/icu4c/source/data/locales/ko.txt index 8096579bc2b9..57a36e88a9a8 100644 --- a/icu4c/source/data/locales/ko.txt +++ b/icu4c/source/data/locales/ko.txt @@ -1909,6 +1909,7 @@ ko{ "다이쇼", "쇼와", "헤이세이", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/lo.txt b/icu4c/source/data/locales/lo.txt index 6f40fbee80cb..5f06344c1c10 100644 --- a/icu4c/source/data/locales/lo.txt +++ b/icu4c/source/data/locales/lo.txt @@ -1845,6 +1845,7 @@ lo{ "ໄຕໂຊ", "ໂຊວາ", "ຮີຊີ", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/lt.txt b/icu4c/source/data/locales/lt.txt index 457dc018e62f..27e07acadc85 100644 --- a/icu4c/source/data/locales/lt.txt +++ b/icu4c/source/data/locales/lt.txt @@ -2466,6 +2466,7 @@ lt{ "Taišo", "Šova", "Heisei", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/nb.txt b/icu4c/source/data/locales/nb.txt index 661655e93cf3..a27810c98cec 100644 --- a/icu4c/source/data/locales/nb.txt +++ b/icu4c/source/data/locales/nb.txt @@ -5483,6 +5483,7 @@ nb{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } narrow{ "Taika (645–650)", @@ -5721,6 +5722,7 @@ nb{ "T", "S", "H", + "Q", } wide{ "Taika (645–650)", @@ -5959,6 +5961,7 @@ nb{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } } intervalFormats{ diff --git a/icu4c/source/data/locales/nl.txt b/icu4c/source/data/locales/nl.txt index 90893ff3f3f8..cc582f203fb3 100644 --- a/icu4c/source/data/locales/nl.txt +++ b/icu4c/source/data/locales/nl.txt @@ -4894,6 +4894,7 @@ nl{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } narrow{ "Taika (645–650)", @@ -5132,6 +5133,7 @@ nl{ "T", "S", "H", + "Q", } wide{ "Taika (645–650)", @@ -5370,6 +5372,7 @@ nl{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/root.txt b/icu4c/source/data/locales/root.txt index 343bd4cdea19..cb0f7e65b6f4 100644 --- a/icu4c/source/data/locales/root.txt +++ b/icu4c/source/data/locales/root.txt @@ -1713,6 +1713,7 @@ root{ "Taishō", "Shōwa", "Heisei", + "QQ", } narrow{ "Taika (645–650)", @@ -1951,6 +1952,7 @@ root{ "T", "S", "H", + "Q", } wide:alias{"/LOCALE/calendar/japanese/eras/abbreviated"} } diff --git a/icu4c/source/data/locales/ru.txt b/icu4c/source/data/locales/ru.txt index 931b225a8dca..7f3618e72e73 100644 --- a/icu4c/source/data/locales/ru.txt +++ b/icu4c/source/data/locales/ru.txt @@ -1837,6 +1837,7 @@ ru{ "Эпоха Тайсьо", "Сьова", "Эпоха Хэйсэй", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/sr.txt b/icu4c/source/data/locales/sr.txt index a3dc6b1853cb..26d40e52a589 100644 --- a/icu4c/source/data/locales/sr.txt +++ b/icu4c/source/data/locales/sr.txt @@ -1256,6 +1256,7 @@ sr{ "Таишо", "Шова", "Хаисеи", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/sr_Latn.txt b/icu4c/source/data/locales/sr_Latn.txt index 57ceb03c99ef..1d8d305333b6 100644 --- a/icu4c/source/data/locales/sr_Latn.txt +++ b/icu4c/source/data/locales/sr_Latn.txt @@ -1257,6 +1257,7 @@ sr_Latn{ "Taišo", "Šova", "Haisei", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/sv.txt b/icu4c/source/data/locales/sv.txt index de38b740f23f..d23eeda63db5 100644 --- a/icu4c/source/data/locales/sv.txt +++ b/icu4c/source/data/locales/sv.txt @@ -1630,6 +1630,7 @@ sv{ "Taishō", "Shōwa", "Heisei", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/th.txt b/icu4c/source/data/locales/th.txt index 457973b4473e..c1a65f4483f8 100644 --- a/icu4c/source/data/locales/th.txt +++ b/icu4c/source/data/locales/th.txt @@ -2332,6 +2332,7 @@ th{ "ทะอิโช", "โชวะ", "เฮเซ", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/yue.txt b/icu4c/source/data/locales/yue.txt index c06fa48292ff..4d7b9d4412e0 100644 --- a/icu4c/source/data/locales/yue.txt +++ b/icu4c/source/data/locales/yue.txt @@ -2394,6 +2394,7 @@ yue{ "大正", "昭和", "平成", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/yue_Hans.txt b/icu4c/source/data/locales/yue_Hans.txt index 07d8219d27f0..4c1f2718a416 100644 --- a/icu4c/source/data/locales/yue_Hans.txt +++ b/icu4c/source/data/locales/yue_Hans.txt @@ -2392,6 +2392,7 @@ yue_Hans{ "大正", "昭和", "平成", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/zh.txt b/icu4c/source/data/locales/zh.txt index 66cc28ba229e..ce678deaa636 100644 --- a/icu4c/source/data/locales/zh.txt +++ b/icu4c/source/data/locales/zh.txt @@ -3403,6 +3403,7 @@ zh{ "大正", "昭和", "平成", + "Qqqq", } narrow{ "大化(645–650)", @@ -3641,6 +3642,7 @@ zh{ "T", "S", "H", + "Q", } wide{ "大化 (645–650)", @@ -3879,6 +3881,7 @@ zh{ "大正", "昭和", "平成", + "Qqqq", } } } diff --git a/icu4c/source/data/locales/zh_Hant.txt b/icu4c/source/data/locales/zh_Hant.txt index 678093757545..ce44c806c9d9 100644 --- a/icu4c/source/data/locales/zh_Hant.txt +++ b/icu4c/source/data/locales/zh_Hant.txt @@ -5737,6 +5737,7 @@ zh_Hant{ "大正", "昭和", "平成", + "Qqqq", } narrow{ "大化", @@ -5975,6 +5976,7 @@ zh_Hant{ "大正", "昭和", "平成", + "Q", } wide{ "大化", @@ -6213,6 +6215,7 @@ zh_Hant{ "大正", "昭和", "平成", + "Qqqq", } } intervalFormats{ diff --git a/icu4c/source/data/misc/supplementalData.txt b/icu4c/source/data/misc/supplementalData.txt index 086b8f8b36f5..63a145dee7a3 100644 --- a/icu4c/source/data/misc/supplementalData.txt +++ b/icu4c/source/data/misc/supplementalData.txt @@ -6,8 +6,9 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -18457, - 643382272, + -542, + 1, + 1, } } } @@ -17,8 +18,9 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -33843, - 1352598528, + -2636, + 1, + 1, } } } @@ -28,14 +30,16 @@ supplementalData:table(nofallback){ eras{ 0{ end:intvector{ - -12383, - 368826367, + 284, + 8, + 28, } } 1{ start:intvector{ - -12383, - 368826368, + 284, + 8, + 29, } } } @@ -45,8 +49,9 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -31610, - -93940736, + -2332, + 1, + 1, } } } @@ -56,14 +61,16 @@ supplementalData:table(nofallback){ eras{ 0{ end:intvector{ - -14411, - 664902655, + 8, + 8, + 28, } } 1{ start:intvector{ - -14411, - 664902656, + 8, + 8, + 29, } } } @@ -73,8 +80,9 @@ supplementalData:table(nofallback){ eras{ 0{ end:intvector{ - -54823, - -2125298689, + -5492, + 8, + 29, } } } @@ -83,14 +91,16 @@ supplementalData:table(nofallback){ eras{ 0{ end:intvector{ - -14468, - -477728769, + 0, + 12, + 31, } } 1{ start:intvector{ - -14468, - -477728768, + 1, + 1, + 1, } } } @@ -100,8 +110,9 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -42096, - 1167292416, + -3760, + 10, + 7, } } } @@ -111,8 +122,9 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -13895, - -44389376, + 79, + 1, + 1, } } } @@ -121,8 +133,9 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -9901, - -1497369600, + 622, + 7, + 15, } } } @@ -132,8 +145,9 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -9901, - -1410969600, + 622, + 7, + 16, } } } @@ -143,8 +157,9 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -9901, - -1497369600, + 622, + 7, + 15, } } } @@ -154,8 +169,9 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -9901, - -1497369600, + 622, + 7, + 15, } } } @@ -165,8 +181,9 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -9901, - -1497369600, + 622, + 7, + 15, } } } @@ -176,1418 +193,1662 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -9732, - 548124672, + 645, + 6, + 19, } } 1{ start:intvector{ - -9698, - 1572036608, + 650, + 2, + 15, } } 10{ start:intvector{ - -9114, - 1157535744, + 729, + 8, + 5, } } 100{ start:intvector{ - -5884, - -819397632, + 1169, + 4, + 8, } } 101{ start:intvector{ - -5869, - -1048707072, + 1171, + 4, + 21, } } 102{ start:intvector{ - -5837, - 504906752, + 1175, + 7, + 28, } } 103{ start:intvector{ - -5823, - -156402688, + 1177, + 8, + 4, } } 104{ start:intvector{ - -5794, - -294454272, + 1181, + 7, + 14, } } 105{ start:intvector{ - -5787, - 1324541952, + 1182, + 5, + 27, } } 106{ start:intvector{ - -5773, - 810999808, + 1184, + 4, + 16, } } 107{ start:intvector{ - -5764, - -234673152, + 1185, + 8, + 14, } } 108{ start:intvector{ - -5729, - 702838784, + 1190, + 4, + 11, } } 109{ start:intvector{ - -5663, - -1680770048, + 1199, + 4, + 27, } } 11{ start:intvector{ - -8970, - -223922176, + 749, + 4, + 14, } } 110{ start:intvector{ - -5650, - -664144896, + 1201, + 2, + 13, } } 111{ start:intvector{ - -5627, - 59374592, + 1204, + 2, + 20, } } 112{ start:intvector{ - -5611, - 200697856, + 1206, + 4, + 27, } } 113{ start:intvector{ - -5600, - 130457600, + 1207, + 10, + 25, } } 114{ start:intvector{ - -5576, - -885324800, + 1211, + 3, + 9, } } 115{ start:intvector{ - -5556, - -125470720, + 1213, + 12, + 6, } } 116{ start:intvector{ - -5516, - 1110004736, + 1219, + 4, + 12, } } 117{ start:intvector{ - -5494, - 1401524224, + 1222, + 4, + 13, } } 118{ start:intvector{ - -5475, - 2049945600, + 1224, + 11, + 20, } } 119{ start:intvector{ - -5472, - -2083523584, + 1225, + 4, + 20, } } 12{ start:intvector{ - -8968, - -1988256768, + 749, + 7, + 2, } } 120{ start:intvector{ - -5453, - -398302208, + 1227, + 12, + 10, } } 121{ start:intvector{ - -5444, - -86607872, + 1229, + 3, + 5, } } 122{ start:intvector{ - -5421, - -1757255680, + 1232, + 4, + 2, } } 123{ start:intvector{ - -5413, - 837173248, + 1233, + 4, + 15, } } 124{ start:intvector{ - -5402, - -1540834304, + 1234, + 11, + 5, } } 125{ start:intvector{ - -5395, - 164561920, + 1235, + 9, + 19, } } 126{ start:intvector{ - -5372, - 1690714112, + 1238, + 11, + 23, } } 127{ start:intvector{ - -5371, - -332820480, + 1239, + 2, + 7, } } 128{ start:intvector{ - -5360, - 2077506560, + 1240, + 7, + 16, } } 129{ start:intvector{ - -5341, - -1309839360, + 1243, + 2, + 26, } } 13{ start:intvector{ - -8908, - 1130272768, + 757, + 8, + 18, } } 130{ start:intvector{ - -5311, - 539309056, + 1247, + 2, + 28, } } 131{ start:intvector{ - -5296, - 828399616, + 1249, + 3, + 18, } } 132{ start:intvector{ - -5241, - -1398568960, + 1256, + 10, + 5, } } 133{ start:intvector{ - -5238, - -459470848, + 1257, + 3, + 14, } } 134{ start:intvector{ - -5223, - -775180288, + 1259, + 3, + 26, } } 135{ start:intvector{ - -5215, - -1957318656, + 1260, + 4, + 13, } } 136{ start:intvector{ - -5209, - -683922432, + 1261, + 2, + 20, } } 137{ start:intvector{ - -5186, - 125997056, + 1264, + 2, + 28, } } 138{ start:intvector{ - -5105, - -67721216, + 1275, + 4, + 25, } } 139{ start:intvector{ - -5084, - -319634432, + 1278, + 2, + 29, } } 14{ start:intvector{ - -8854, - -1899328512, + 765, + 1, + 7, } } 140{ start:intvector{ - -5009, - -1811781632, + 1288, + 4, + 28, } } 141{ start:intvector{ - -4969, - 1324493824, + 1293, + 8, + 5, } } 142{ start:intvector{ - -4928, - 1400434688, + 1299, + 4, + 25, } } 143{ start:intvector{ - -4902, - -1725282304, + 1302, + 11, + 21, } } 144{ start:intvector{ - -4897, - -995318784, + 1303, + 8, + 5, } } 145{ start:intvector{ - -4872, - 1938266112, + 1306, + 12, + 14, } } 146{ start:intvector{ - -4859, - -735276032, + 1308, + 10, + 9, } } 147{ start:intvector{ - -4840, - -1901254656, + 1311, + 4, + 28, } } 148{ start:intvector{ - -4833, - 581741568, + 1312, + 3, + 20, } } 149{ start:intvector{ - -4798, - -158681088, + 1317, + 2, + 3, } } 15{ start:intvector{ - -8835, - -1337307136, + 767, + 8, + 16, } } 150{ start:intvector{ - -4781, - 1451442176, + 1319, + 4, + 28, } } 151{ start:intvector{ - -4768, - -1049299968, + 1321, + 2, + 23, } } 152{ start:intvector{ - -4740, - -1644384256, + 1324, + 12, + 9, } } 153{ start:intvector{ - -4730, - -1134857216, + 1326, + 4, + 26, } } 154{ start:intvector{ - -4705, - 1280327680, + 1329, + 8, + 29, } } 155{ start:intvector{ - -4691, - -1800181760, + 1331, + 8, + 9, } } 156{ start:intvector{ - -4673, - -1003993088, + 1334, + 1, + 29, } } 157{ start:intvector{ - -4657, - 321897472, + 1336, + 2, + 29, } } 158{ start:intvector{ - -4627, - -1494088704, + 1340, + 4, + 28, } } 159{ start:intvector{ - -4578, - 1003481088, + 1346, + 12, + 8, } } 16{ start:intvector{ - -8812, - -1452754944, + 770, + 10, + 1, } } 160{ start:intvector{ - -4405, - -775228416, + 1370, + 7, + 24, } } 161{ start:intvector{ - -4392, - 993964032, + 1372, + 4, + 1, } } 162{ start:intvector{ - -4369, - 1656116224, + 1375, + 5, + 27, } } 163{ start:intvector{ - -4341, - 1925031936, + 1379, + 3, + 22, } } 164{ start:intvector{ - -4327, - 1497889792, + 1381, + 2, + 10, } } 165{ start:intvector{ - -4304, - -234125312, + 1384, + 4, + 28, } } 166{ start:intvector{ - -4305, - -1209558016, + 1387, + 8, + 22, } } 167{ start:intvector{ - -4279, - 1403459584, + 1387, + 8, + 23, } } 168{ start:intvector{ - -4268, - 469219328, + 1389, + 2, + 9, } } 169{ start:intvector{ - -4260, - 1533480960, + 1390, + 3, + 26, } } 17{ start:intvector{ - -8737, - -7302144, + 781, + 1, + 1, } } 170{ start:intvector{ - -4229, - -948672512, + 1394, + 7, + 5, } } 171{ start:intvector{ - -3980, - 939438080, + 1428, + 4, + 27, } } 172{ start:intvector{ - -3970, - 844165120, + 1429, + 9, + 5, } } 173{ start:intvector{ - -3886, - 1478112256, + 1441, + 2, + 17, } } 174{ start:intvector{ - -3864, - 560031744, + 1444, + 2, + 5, } } 175{ start:intvector{ - -3824, - 1561339904, + 1449, + 7, + 28, } } 176{ start:intvector{ - -3802, - 1507259392, + 1452, + 7, + 25, } } 177{ start:intvector{ - -3780, - 1625978880, + 1455, + 7, + 25, } } 178{ start:intvector{ - -3764, - 1680902144, + 1457, + 9, + 28, } } 179{ start:intvector{ - -3740, - 553687040, + 1460, + 12, + 21, } } 18{ start:intvector{ - -8725, - -138909696, + 782, + 8, + 19, } } 180{ start:intvector{ - -3702, - 1072929792, + 1466, + 2, + 28, } } 181{ start:intvector{ - -3695, - -1491608576, + 1467, + 3, + 3, } } 182{ start:intvector{ - -3679, - 2080681984, + 1469, + 4, + 28, } } 183{ start:intvector{ - -3545, - -1797502976, + 1487, + 7, + 29, } } 184{ start:intvector{ - -3530, - -1076412416, + 1489, + 8, + 21, } } 185{ start:intvector{ - -3508, - 572474368, + 1492, + 7, + 19, } } 186{ start:intvector{ - -3445, - 1890334720, + 1501, + 2, + 29, } } 187{ start:intvector{ - -3423, - 2095454208, + 1504, + 2, + 30, } } 188{ start:intvector{ - -3295, - -377726976, + 1521, + 8, + 23, } } 189{ start:intvector{ - -3243, - 1244540928, + 1528, + 8, + 20, } } 19{ start:intvector{ - -8550, - 1883980800, + 806, + 5, + 18, } } 190{ start:intvector{ - -3214, - 1020089344, + 1532, + 7, + 29, } } 191{ start:intvector{ - -3044, - -228918272, + 1555, + 10, + 23, } } 192{ start:intvector{ - -3026, - 974237696, + 1558, + 2, + 28, } } 193{ start:intvector{ - -2937, - 2078948352, + 1570, + 4, + 23, } } 194{ start:intvector{ - -2913, - 1988533248, + 1573, + 7, + 28, } } 195{ start:intvector{ - -2771, - -1948590080, + 1592, + 12, + 8, } } 196{ start:intvector{ - -2742, - 393925632, + 1596, + 10, + 27, } } 197{ start:intvector{ - -2605, - -1940361216, + 1615, + 7, + 13, } } 198{ start:intvector{ - -2542, - -17700864, + 1624, + 2, + 30, } } 199{ start:intvector{ - -2389, - -939697152, + 1644, + 12, + 16, } } 2{ start:intvector{ - -9537, - 418301952, + 672, + 1, + 1, } } 20{ start:intvector{ - -8518, - 1389027328, + 810, + 9, + 19, } } 200{ start:intvector{ - -2365, - 154455040, + 1648, + 2, + 15, } } 201{ start:intvector{ - -2332, - -981633024, + 1652, + 9, + 18, } } 202{ start:intvector{ - -2313, - -1629211648, + 1655, + 4, + 13, } } 203{ start:intvector{ - -2289, - -1287626752, + 1658, + 7, + 23, } } 204{ start:intvector{ - -2269, - -182172672, + 1661, + 4, + 25, } } 205{ start:intvector{ - -2177, - 540603392, + 1673, + 9, + 21, } } 206{ start:intvector{ - -2118, - 289532928, + 1681, + 9, + 29, } } 207{ start:intvector{ - -2101, - -1419878400, + 1684, + 2, + 21, } } 208{ start:intvector{ - -2067, - -2037566464, + 1688, + 9, + 30, } } 209{ start:intvector{ - -1953, - 99929088, + 1704, + 3, + 13, } } 21{ start:intvector{ - -8420, - 40632320, + 824, + 1, + 5, } } 210{ start:intvector{ - -1901, - 1315229696, + 1711, + 4, + 25, } } 211{ start:intvector{ - -1863, - 970472448, + 1716, + 6, + 22, } } 212{ start:intvector{ - -1717, - 305247232, + 1736, + 4, + 28, } } 213{ start:intvector{ - -1682, - -1731175424, + 1741, + 2, + 27, } } 214{ start:intvector{ - -1660, - -2130855936, + 1744, + 2, + 21, } } 215{ start:intvector{ - -1628, - -1070609408, + 1748, + 7, + 12, } } 216{ start:intvector{ - -1604, - -297024512, + 1751, + 10, + 27, } } 217{ start:intvector{ - -1511, - -2116183040, + 1764, + 6, + 2, } } 218{ start:intvector{ - -1449, - -1514555392, + 1772, + 11, + 16, } } 219{ start:intvector{ - -1387, - 790039552, + 1781, + 4, + 2, } } 22{ start:intvector{ - -8347, - 1954419712, + 834, + 1, + 3, } } 220{ start:intvector{ - -1330, - -1646063616, + 1789, + 1, + 25, } } 221{ start:intvector{ - -1242, - -47985664, + 1801, + 2, + 5, } } 222{ start:intvector{ - -1219, - 589133824, + 1804, + 2, + 11, } } 223{ start:intvector{ - -1115, - 1810135040, + 1818, + 4, + 22, } } 224{ start:intvector{ - -1022, - 1114176512, + 1830, + 12, + 10, } } 225{ start:intvector{ - -920, - -109054976, + 1844, + 12, + 2, } } 226{ start:intvector{ - -896, - -977070080, + 1848, + 2, + 28, } } 227{ start:intvector{ - -846, - 1459132416, + 1854, + 11, + 27, } } 228{ start:intvector{ - -807, - 1398607872, + 1860, + 3, + 18, } } 229{ start:intvector{ - -800, - 537036800, + 1861, + 2, + 19, } } 23{ start:intvector{ - -8241, - -1847080960, + 848, + 6, + 13, } } 230{ start:intvector{ - -778, - 742156288, + 1864, + 2, + 20, } } 231{ start:intvector{ - -770, - 1979217920, + 1865, + 4, + 7, } } 232{ start:intvector{ - -745, - -1689931776, + 1868, + 9, + 8, } } 233{ start:intvector{ - -422, - 322598912, + 1912, + 7, + 30, } } 234{ start:intvector{ - -317, - -393534464, + 1926, + 12, + 25, } } 235{ start:intvector{ - 139, - -1074621440, + 1989, + 1, + 8, + } + } + 236{ + named{"false"} + start:intvector{ + 2019, + 5, + 1, } } 24{ start:intvector{ - -8220, - -1407794176, + 851, + 4, + 28, } } 25{ start:intvector{ - -8193, - 279856128, + 854, + 11, + 30, } } 26{ start:intvector{ - -8177, - 1889979392, + 857, + 2, + 21, } } 27{ start:intvector{ - -8161, - 821702656, + 859, + 4, + 15, } } 28{ start:intvector{ - -8029, - 2052419584, + 877, + 4, + 16, } } 29{ start:intvector{ - -7971, - 739516416, + 885, + 2, + 21, } } 3{ start:intvector{ - -9431, - -13598720, + 686, + 7, + 20, } } 30{ start:intvector{ - -7941, - -558069760, + 889, + 4, + 27, } } 31{ start:intvector{ - -7875, - -115511296, + 898, + 4, + 26, } } 32{ start:intvector{ - -7851, - -1588326400, + 901, + 7, + 15, } } 33{ start:intvector{ - -7691, - 1527873536, + 923, + 4, + 11, } } 34{ start:intvector{ - -7632, - 1881603072, + 931, + 4, + 26, } } 35{ start:intvector{ - -7580, - 1714503680, + 938, + 5, + 22, } } 36{ start:intvector{ - -7515, - -348537856, + 947, + 4, + 22, } } 37{ start:intvector{ - -7437, - 801380352, + 957, + 10, + 27, } } 38{ start:intvector{ - -7413, - 2093365248, + 961, + 2, + 16, } } 39{ start:intvector{ - -7388, - 1855182848, + 964, + 7, + 10, } } 4{ start:intvector{ - -9323, - -938866688, + 701, + 3, + 21, } } 40{ start:intvector{ - -7358, - -2120803328, + 968, + 8, + 13, } } 41{ start:intvector{ - -7346, - 1524156416, + 970, + 3, + 25, } } 42{ start:intvector{ - -7319, - -712527872, + 973, + 12, + 20, } } 43{ start:intvector{ - -7300, - -1446506496, + 976, + 7, + 13, } } 44{ start:intvector{ - -7282, - 620649472, + 978, + 11, + 29, } } 45{ start:intvector{ - -7250, - 1248896000, + 983, + 4, + 15, } } 46{ start:intvector{ - -7235, - 1019586560, + 985, + 4, + 27, } } 47{ start:intvector{ - -7221, - 2061244416, + 987, + 4, + 5, } } 48{ start:intvector{ - -7204, - -1289766912, + 989, + 8, + 8, } } 49{ start:intvector{ - -7195, - -546072576, + 990, + 11, + 7, } } 5{ start:intvector{ - -9300, - -708714496, + 704, + 5, + 10, } } 50{ start:intvector{ - -7163, - 1785141248, + 995, + 2, + 22, } } 51{ start:intvector{ - -7134, - 5489664, + 999, + 1, + 13, } } 52{ start:intvector{ - -7094, - -1992169472, + 1004, + 7, + 20, } } 53{ start:intvector{ - -7032, - 2126825472, + 1012, + 12, + 25, } } 54{ start:intvector{ - -7000, - 1199872000, + 1017, + 4, + 23, } } 55{ start:intvector{ - -6972, - 259187712, + 1021, + 2, + 2, } } 56{ start:intvector{ - -6947, - 1489805312, + 1024, + 7, + 13, } } 57{ start:intvector{ - -6918, - -92013568, + 1028, + 7, + 25, } } 58{ start:intvector{ - -6853, - 818879488, + 1037, + 4, + 21, } } 59{ start:intvector{ - -6827, - 1383329792, + 1040, + 11, + 10, } } 6{ start:intvector{ - -9273, - -810431488, + 708, + 1, + 11, } } 60{ start:intvector{ - -6798, - -25689088, + 1044, + 11, + 24, } } 61{ start:intvector{ - -6787, - 743037952, + 1046, + 4, + 14, } } 62{ start:intvector{ - -6738, - -1115726848, + 1053, + 1, + 11, } } 63{ start:intvector{ - -6696, - 429014016, + 1058, + 8, + 29, } } 64{ start:intvector{ - -6646, - -22318080, + 1065, + 8, + 2, } } 65{ start:intvector{ - -6618, - 653564928, + 1069, + 4, + 13, } } 66{ start:intvector{ - -6579, - -1973926912, + 1074, + 8, + 23, } } 67{ start:intvector{ - -6555, - 1366625280, + 1077, + 11, + 17, } } 68{ start:intvector{ - -6531, - 325810176, + 1081, + 2, + 10, } } 69{ start:intvector{ - -6509, - 185329664, + 1084, + 2, + 7, } } 7{ start:intvector{ - -9217, - -186200064, + 715, + 9, + 2, } } 70{ start:intvector{ - -6486, - 1193081856, + 1087, + 4, + 7, } } 71{ start:intvector{ - -6430, - -922454016, + 1094, + 12, + 15, } } 72{ start:intvector{ - -6415, - -2015763456, + 1096, + 12, + 17, } } 73{ start:intvector{ - -6408, - 1504032768, + 1097, + 11, + 21, } } 74{ start:intvector{ - -6395, - 1397457920, + 1099, + 8, + 28, } } 75{ start:intvector{ - -6362, - 236337152, + 1104, + 2, + 10, } } 76{ start:intvector{ - -6347, - -313539584, + 1106, + 4, + 9, } } 77{ start:intvector{ - -6330, - -147183616, + 1108, + 8, + 3, } } 78{ start:intvector{ - -6315, - 980874240, + 1110, + 7, + 13, } } 79{ start:intvector{ - -6293, - 1185993728, + 1113, + 7, + 13, } } 8{ start:intvector{ - -9200, - 819123200, + 717, + 11, + 17, } } 80{ start:intvector{ - -6259, - -97861632, + 1118, + 4, + 3, } } 81{ start:intvector{ - -6244, - -759171072, + 1120, + 4, + 10, } } 82{ start:intvector{ - -6214, - 312377344, + 1124, + 4, + 3, } } 83{ start:intvector{ - -6201, - 1415402496, + 1126, + 1, + 22, } } 84{ start:intvector{ - -6164, - 872812544, + 1131, + 1, + 29, } } 85{ start:intvector{ - -6153, - 2012172288, + 1132, + 8, + 11, } } 86{ start:intvector{ - -6133, - 1562426368, + 1135, + 4, + 27, } } 87{ start:intvector{ - -6088, - -223669248, + 1141, + 7, + 10, } } 88{ start:intvector{ - -6082, - -764673024, + 1142, + 4, + 28, } } 89{ start:intvector{ - -6068, - 943152128, + 1144, + 2, + 23, } } 9{ start:intvector{ - -9155, - -621372416, + 724, + 2, + 4, } } 90{ start:intvector{ - -6058, - -1805488128, + 1145, + 7, + 22, } } 91{ start:intvector{ - -6017, - 405420032, + 1151, + 1, + 26, } } 92{ start:intvector{ - -5990, - -1399264256, + 1154, + 10, + 28, } } 93{ start:intvector{ - -5979, - -1383104512, + 1156, + 4, + 27, } } 94{ start:intvector{ - -5957, - -1869185024, + 1159, + 4, + 20, } } 95{ start:intvector{ - -5952, - -448021504, + 1160, + 1, + 10, } } 96{ start:intvector{ - -5939, - 111570944, + 1161, + 9, + 4, } } 97{ start:intvector{ - -5928, - -2093636608, + 1163, + 3, + 29, } } 98{ start:intvector{ - -5912, - -1779513344, + 1165, + 6, + 5, } } 99{ start:intvector{ - -5903, - -1727019008, + 1166, + 8, + 27, } } } @@ -1597,8 +1858,9 @@ supplementalData:table(nofallback){ eras{ 0{ start:intvector{ - -9905, - -1165500416, + 622, + 1, + 1, } } } @@ -1608,14 +1870,16 @@ supplementalData:table(nofallback){ eras{ 0{ end:intvector{ - -427, - -727931905, + 1911, + 12, + 31, } } 1{ start:intvector{ - -427, - -727931904, + 1912, + 1, + 1, } } } diff --git a/icu4c/source/i18n/Makefile.in b/icu4c/source/i18n/Makefile.in index 6d9496ce2374..98b36b4c9177 100644 --- a/icu4c/source/i18n/Makefile.in +++ b/icu4c/source/i18n/Makefile.in @@ -111,6 +111,7 @@ double-conversion-fast-dtoa.o double-conversion-strtod.o \ numparse_stringsegment.o numparse_parsednumber.o numparse_impl.o \ numparse_symbols.o numparse_decimal.o numparse_scientific.o numparse_currency.o \ numparse_affixes.o numparse_compositions.o numparse_validators.o \ +erarules.o ## Header files to install diff --git a/icu4c/source/i18n/erarules.cpp b/icu4c/source/i18n/erarules.cpp new file mode 100644 index 000000000000..3ea385046d2c --- /dev/null +++ b/icu4c/source/i18n/erarules.cpp @@ -0,0 +1,313 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include +#include "unicode/ucal.h" +#include "unicode/ures.h" +#include "unicode/ustring.h" +#include "cmemory.h" +#include "cstring.h" +#include "erarules.h" +#include "gregoimp.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +static const int32_t MAX_ENCODED_START_YEAR = 32767; +static const int32_t MIN_ENCODED_START_YEAR = -32768; +static const int32_t MIN_ENCODED_START = -2147483391; // encodeDate(MIN_ENCODED_START_YEAR, 1, 1, ...); + +static const int32_t YEAR_MASK = 0xFFFF0000; +static const int32_t MONTH_MASK = 0x0000FF00; +static const int32_t DAY_MASK = 0x000000FF; + +static const int32_t MAX_INT32 = 0x7FFFFFFF; +static const int32_t MIN_INT32 = 0xFFFFFFFF; + +static const UChar VAL_FALSE[] = {0x66, 0x61, 0x6c, 0x73, 0x65}; // "false" +static const UChar VAL_FALSE_LEN = 5; + +static UBool isSet(int startDate) { + return startDate != 0; +} + +static UBool isValidRuleStartDate(int32_t year, int32_t month, int32_t day) { + return year >= MIN_ENCODED_START_YEAR && year <= MAX_ENCODED_START_YEAR + && month >= 1 && month <= 12 && day >=1 && day <= 31; +} + +/** + * Encode year/month/date to a single integer. + * year is high 16 bits (-32768 to 32767), month is + * next 8 bits and day of month is last 8 bits. + * + * @param year year + * @param month month (1-base) + * @param day day of month + * @return an encoded date. + */ +static int32_t encodeDate(int32_t year, int32_t month, int32_t day) { + return year << 16 | month << 8 | day; +} + +static void decodeDate(int32_t encodedDate, int32_t (&fields)[3]) { + if (encodedDate == MIN_ENCODED_START) { + fields[0] = MIN_INT32; + fields[1] = 1; + fields[2] = 1; + } else { + fields[0] = (encodedDate & YEAR_MASK) >> 16; + fields[1] = (encodedDate & MONTH_MASK) >> 8; + fields[2] = encodedDate & DAY_MASK; + } +} + +/** + * Compare an encoded date with another date specified by year/month/day. + * @param encoded An encoded date + * @param year Year of another date + * @param month Month of another date + * @param day Day of another date + * @return -1 when encoded date is earlier, 0 when two dates are same, + * and 1 when encoded date is later. + */ +static int32_t compareEncodedDateWithYMD(int encoded, int year, int month, int day) { + if (year < MIN_ENCODED_START_YEAR) { + if (encoded == MIN_ENCODED_START) { + if (year > MIN_INT32 || month > 1 || day > 1) { + return -1; + } + return 0; + } else { + return 1; + } + } else if (year > MAX_ENCODED_START_YEAR) { + return -1; + } else { + int tmp = encodeDate(year, month, day); + if (encoded < tmp) { + return -1; + } else if (encoded == tmp) { + return 0; + } else { + return 1; + } + } +} + +EraRules::EraRules(int32_t *startDates, int32_t numEras) + : startDates(startDates), numEras(numEras) { + initCurrentEra(); +} + +EraRules::~EraRules() { + uprv_free(startDates); +} + +EraRules* EraRules::createInstance(const char *calType, UBool includeTentativeEra, UErrorCode& status) { + if(U_FAILURE(status)) { + return nullptr; + } + LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "supplementalData", &status)); + ures_getByKey(rb.getAlias(), "calendarData", rb.getAlias(), &status); + ures_getByKey(rb.getAlias(), calType, rb.getAlias(), &status); + ures_getByKey(rb.getAlias(), "eras", rb.getAlias(), &status); + + if (U_FAILURE(status)) { + return nullptr; + } + + int32_t numEras = ures_getSize(rb.getAlias()); + int32_t firstTentativeIdx = MAX_INT32; + + int32_t *startDates = (int32_t*)uprv_malloc(numEras * sizeof(int32_t)); + if (startDates == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memset(startDates, 0, numEras * sizeof(int32_t)); + + while (ures_hasNext(rb.getAlias())) { + LocalUResourceBundlePointer eraRuleRes(ures_getNextResource(rb.getAlias(), nullptr, &status)); + if (U_FAILURE(status)) { + goto error; + } + const char *eraIdxStr = ures_getKey(eraRuleRes.getAlias()); + char *endp; + int32_t eraIdx = (int32_t)strtol(eraIdxStr, &endp, 10); + if ((size_t)(endp - eraIdxStr) != uprv_strlen(eraIdxStr)) { + status = U_INVALID_FORMAT_ERROR; + goto error; + } + if (eraIdx < 0 || eraIdx >= numEras) { + status = U_INVALID_FORMAT_ERROR; + goto error; + } + if (isSet(startDates[eraIdx])) { + // start date of the index was already set + status = U_INVALID_FORMAT_ERROR; + goto error; + } + + UBool hasName = TRUE; + UBool hasEnd = TRUE; + int32_t len; + while (ures_hasNext(eraRuleRes.getAlias())) { + LocalUResourceBundlePointer res(ures_getNextResource(eraRuleRes.getAlias(), nullptr, &status)); + if (U_FAILURE(status)) { + goto error; + } + const char *key = ures_getKey(res.getAlias()); + if (uprv_strcmp(key, "start") == 0) { + const int32_t *fields = ures_getIntVector(res.getAlias(), &len, &status); + if (U_FAILURE(status)) { + goto error; + } + if (len != 3 || !isValidRuleStartDate(fields[0], fields[1], fields[2])) { + status = U_INVALID_FORMAT_ERROR; + goto error; + } + startDates[eraIdx] = encodeDate(fields[0], fields[1], fields[2]); + } else if (uprv_strcmp(key, "named") == 0) { + const UChar *val = ures_getString(res.getAlias(), &len, &status); + if (u_strncmp(val, VAL_FALSE, VAL_FALSE_LEN) == 0) { + hasName = FALSE; + } + } else if (uprv_strcmp(key, "end") == 0) { + hasEnd = TRUE; + } + } + + if (isSet(startDates[eraIdx])) { + if (hasEnd) { + // This implementation assumes either start or end is available, not both. + // For now, just ignore the end rule. + } + } else { + if (hasEnd) { + if (eraIdx != 0) { + // This implementation does not support end only rule for eras other than + // the first one. + status = U_INVALID_FORMAT_ERROR; + goto error; + } + U_ASSERT(eraIdx == 0); + startDates[eraIdx] = MIN_ENCODED_START; + } else { + status = U_INVALID_FORMAT_ERROR; + goto error; + } + } + + if (hasName) { + if (eraIdx >= firstTentativeIdx) { + status = U_INVALID_FORMAT_ERROR; + goto error; + } + } else { + if (eraIdx < firstTentativeIdx) { + firstTentativeIdx = eraIdx; + } + } + } + + EraRules *result; + if (firstTentativeIdx < MAX_INT32 && !includeTentativeEra) { + result = new EraRules(startDates, firstTentativeIdx); + } else { + result = new EraRules(startDates, numEras); + } + + if (result == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return result; + +error: + uprv_free(startDates); + return nullptr; +} + +void EraRules::getStartDate(int32_t eraIdx, int32_t (&fields)[3], UErrorCode& status) const { + if(U_FAILURE(status)) { + return; + } + if (eraIdx < 0 || eraIdx >= numEras) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + decodeDate(startDates[eraIdx], fields); +} + +int32_t EraRules::getStartYear(int32_t eraIdx, UErrorCode& status) const { + int year = MAX_INT32; // bogus value + if(U_FAILURE(status)) { + return year; + } + if (eraIdx < 0 || eraIdx >= numEras) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return year; + } + int fields[3]; + decodeDate(startDates[eraIdx], fields); + year = fields[0]; + + return year; +} + +int32_t EraRules::getEraIndex(int32_t year, int32_t month, int32_t day, UErrorCode& status) const { + if(U_FAILURE(status)) { + return -1; + } + + if (month < 1 || month > 12 || day < 1 || day > 31) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return -1; + } + int32_t high = numEras; // last index + 1 + int32_t low; + + // Short circuit for recent years. Most modern computations will + // occur in the last few eras. + if (compareEncodedDateWithYMD(startDates[getCurrentEraIndex()], year, month, day) <= 0) { + low = getCurrentEraIndex(); + } else { + low = 0; + } + + // Do binary search + while (low < high - 1) { + int i = (low + high) / 2; + if (compareEncodedDateWithYMD(startDates[i], year, month, day) <= 0) { + low = i; + } else { + high = i; + } + } + return low; +} + +void EraRules::initCurrentEra() { + UDate now = ucal_getNow(); + int year, month0, dom, dow, doy, mid; + Grego::timeToFields(now, year, month0, dom, dow, doy, mid); + int currentEncodedDate = encodeDate(year, month0 + 1 /* changes to 1-base */, dom); + int eraIdx = numEras - 1; + while (eraIdx > 0) { + if (currentEncodedDate >= startDates[eraIdx]) { + break; + } + eraIdx--; + } + // Note: current era could be before the first era. + // In this case, this implementation returns the first era index (0). + currentEra = eraIdx;} + +U_NAMESPACE_END +#endif /* #if !UCONFIG_NO_FORMATTING */ + + diff --git a/icu4c/source/i18n/erarules.h b/icu4c/source/i18n/erarules.h new file mode 100644 index 000000000000..83a8a82cc376 --- /dev/null +++ b/icu4c/source/i18n/erarules.h @@ -0,0 +1,80 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef ERARULES_H_ +#define ERARULES_H_ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/uobject.h" + +U_NAMESPACE_BEGIN + +class U_I18N_API EraRules : public UMemory { +public: + ~EraRules(); + + static EraRules* createInstance(const char *calType, UBool includeTentativeEra, UErrorCode& status); + + /** + * Gets number of effective eras + * @return number of effective eras + */ + inline int32_t getNumberOfEras() const { + return numEras; + } + + /** + * Gets start date of an era + * @param eraIdx Era index + * @param fields Receives date fields. The result includes values of year, month, + * day of month in this order. When an era has no start date, the result + * will be January 1st in year whose value is minimum integer. + * @param status Receives status. + */ + void getStartDate(int32_t eraIdx, int32_t (&fields)[3], UErrorCode& status) const; + + /** + * Gets start year of an era + * @param eraIdx Era index + * @param status Receives status. + * @return The first year of an era. When a era has no start date, minimum int32 + * value is returned. + */ + int32_t getStartYear(int32_t eraIdx, UErrorCode& status) const; + + /** + * Returns era index for the specified year/month/day. + * @param year Year + * @param month Month (1-base) + * @param day Day of month + * @param status Receives status + * @return era index (or 0, when the specified date is before the first era) + */ + int32_t getEraIndex(int32_t year, int32_t month, int32_t day, UErrorCode& status) const; + + /** + * Gets the current era index. This is calculated only once for an instance of + * EraRules. + * + * @return era index of current era (or 0, when current date is before the first era) + */ + inline int32_t getCurrentEraIndex() const { + return currentEra; + } + +private: + EraRules(int32_t *startDates, int32_t numEra); + + void initCurrentEra(); + + int32_t *startDates; + int32_t numEras; + int32_t currentEra; +}; + +U_NAMESPACE_END +#endif /* #if !UCONFIG_NO_FORMATTING */ +#endif /* ERARULES_H_ */ diff --git a/icu4c/source/i18n/i18n.vcxproj b/icu4c/source/i18n/i18n.vcxproj index b0c57ce62ae3..18f48c6584ea 100644 --- a/icu4c/source/i18n/i18n.vcxproj +++ b/icu4c/source/i18n/i18n.vcxproj @@ -160,6 +160,7 @@ + @@ -410,6 +411,7 @@ + diff --git a/icu4c/source/i18n/i18n.vcxproj.filters b/icu4c/source/i18n/i18n.vcxproj.filters index 752e4ece46a3..db3470f83b20 100644 --- a/icu4c/source/i18n/i18n.vcxproj.filters +++ b/icu4c/source/i18n/i18n.vcxproj.filters @@ -627,6 +627,9 @@ formatting + + formatting + @@ -1184,10 +1187,13 @@ collation + + formatting + misc - + \ No newline at end of file diff --git a/icu4c/source/i18n/i18n_uwp.vcxproj b/icu4c/source/i18n/i18n_uwp.vcxproj index d4dbfe645304..e85c27766ab0 100644 --- a/icu4c/source/i18n/i18n_uwp.vcxproj +++ b/icu4c/source/i18n/i18n_uwp.vcxproj @@ -87,7 +87,7 @@ false /utf-8 %(AdditionalOptions) - + 0x0409 ../common;%(AdditionalIncludeDirectories) @@ -267,6 +267,7 @@ + @@ -515,6 +516,7 @@ + @@ -662,4 +664,4 @@ - + \ No newline at end of file diff --git a/icu4c/source/i18n/japancal.cpp b/icu4c/source/i18n/japancal.cpp index 17b4e0764ddc..35e1834098d9 100644 --- a/icu4c/source/i18n/japancal.cpp +++ b/icu4c/source/i18n/japancal.cpp @@ -16,286 +16,80 @@ #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING - +#if U_PLATFORM_HAS_WINUWP_API == 0 +#include // getenv() is not available in UWP env +#endif #include "cmemory.h" +#include "erarules.h" #include "japancal.h" #include "unicode/gregocal.h" #include "umutex.h" #include "uassert.h" - -//#define U_DEBUG_JCAL - -#ifdef U_DEBUG_JCAL -#include -#endif +#include "ucln_in.h" +#include "cstring.h" + +static icu::EraRules * gJapaneseEraRules = nullptr; +static icu::UInitOnce gJapaneseEraRulesInitOnce = U_INITONCE_INITIALIZER; +static int32_t gCurrentEra = 0; + +U_CDECL_BEGIN +static UBool japanese_calendar_cleanup(void) { + if (gJapaneseEraRules) { + delete gJapaneseEraRules; + gJapaneseEraRules = nullptr; + } + gCurrentEra = 0; + gJapaneseEraRulesInitOnce.reset(); + return TRUE; +} +U_CDECL_END U_NAMESPACE_BEGIN UOBJECT_DEFINE_RTTI_IMPLEMENTATION(JapaneseCalendar) -// Gregorian date of each emperor's ascension -// Years are AD, months are 1-based. -static const struct { - int16_t year; - int8_t month; - int8_t day; -} kEraInfo[] = { - // Year Month Day - { 645, 6, 19 }, // Taika 0 - { 650, 2, 15 }, // Hakuchi 1 - { 672, 1, 1 }, // Hakuho 2 - { 686, 7, 20 }, // Shucho 3 - { 701, 3, 21 }, // Taiho 4 - { 704, 5, 10 }, // Keiun 5 - { 708, 1, 11 }, // Wado 6 - { 715, 9, 2 }, // Reiki 7 - { 717, 11, 17 }, // Yoro 8 - { 724, 2, 4 }, // Jinki 9 - { 729, 8, 5 }, // Tempyo 10 - { 749, 4, 14 }, // Tempyo-kampo 11 - { 749, 7, 2 }, // Tempyo-shoho 12 - { 757, 8, 18 }, // Tempyo-hoji 13 - { 765, 1, 7 }, // Tempho-jingo 14 - { 767, 8, 16 }, // Jingo-keiun 15 - { 770, 10, 1 }, // Hoki 16 - { 781, 1, 1 }, // Ten-o 17 - { 782, 8, 19 }, // Enryaku 18 - { 806, 5, 18 }, // Daido 19 - { 810, 9, 19 }, // Konin 20 - { 824, 1, 5 }, // Tencho - { 834, 1, 3 }, // Showa - { 848, 6, 13 }, // Kajo - { 851, 4, 28 }, // Ninju - { 854, 11, 30 }, // Saiko - { 857, 2, 21 }, // Tennan - { 859, 4, 15 }, // Jogan - { 877, 4, 16 }, // Genkei - { 885, 2, 21 }, // Ninna - { 889, 4, 27 }, // Kampyo 30 - { 898, 4, 26 }, // Shotai - { 901, 7, 15 }, // Engi - { 923, 4, 11 }, // Encho - { 931, 4, 26 }, // Shohei - { 938, 5, 22 }, // Tengyo - { 947, 4, 22 }, // Tenryaku - { 957, 10, 27 }, // Tentoku - { 961, 2, 16 }, // Owa - { 964, 7, 10 }, // Koho - { 968, 8, 13 }, // Anna 40 - { 970, 3, 25 }, // Tenroku - { 973, 12, 20 }, // Ten-en - { 976, 7, 13 }, // Jogen - { 978, 11, 29 }, // Tengen - { 983, 4, 15 }, // Eikan - { 985, 4, 27 }, // Kanna - { 987, 4, 5 }, // Ei-en - { 989, 8, 8 }, // Eiso - { 990, 11, 7 }, // Shoryaku - { 995, 2, 22 }, // Chotoku 50 - { 999, 1, 13 }, // Choho - { 1004, 7, 20 }, // Kanko - { 1012, 12, 25 }, // Chowa - { 1017, 4, 23 }, // Kannin - { 1021, 2, 2 }, // Jian - { 1024, 7, 13 }, // Manju - { 1028, 7, 25 }, // Chogen - { 1037, 4, 21 }, // Choryaku - { 1040, 11, 10 }, // Chokyu - { 1044, 11, 24 }, // Kantoku 60 - { 1046, 4, 14 }, // Eisho - { 1053, 1, 11 }, // Tengi - { 1058, 8, 29 }, // Kohei - { 1065, 8, 2 }, // Jiryaku - { 1069, 4, 13 }, // Enkyu - { 1074, 8, 23 }, // Shoho - { 1077, 11, 17 }, // Shoryaku - { 1081, 2, 10 }, // Eiho - { 1084, 2, 7 }, // Otoku - { 1087, 4, 7 }, // Kanji 70 - { 1094, 12, 15 }, // Kaho - { 1096, 12, 17 }, // Eicho - { 1097, 11, 21 }, // Shotoku - { 1099, 8, 28 }, // Kowa - { 1104, 2, 10 }, // Choji - { 1106, 4, 9 }, // Kasho - { 1108, 8, 3 }, // Tennin - { 1110, 7, 13 }, // Ten-ei - { 1113, 7, 13 }, // Eikyu - { 1118, 4, 3 }, // Gen-ei 80 - { 1120, 4, 10 }, // Hoan - { 1124, 4, 3 }, // Tenji - { 1126, 1, 22 }, // Daiji - { 1131, 1, 29 }, // Tensho - { 1132, 8, 11 }, // Chosho - { 1135, 4, 27 }, // Hoen - { 1141, 7, 10 }, // Eiji - { 1142, 4, 28 }, // Koji - { 1144, 2, 23 }, // Tenyo - { 1145, 7, 22 }, // Kyuan 90 - { 1151, 1, 26 }, // Ninpei - { 1154, 10, 28 }, // Kyuju - { 1156, 4, 27 }, // Hogen - { 1159, 4, 20 }, // Heiji - { 1160, 1, 10 }, // Eiryaku - { 1161, 9, 4 }, // Oho - { 1163, 3, 29 }, // Chokan - { 1165, 6, 5 }, // Eiman - { 1166, 8, 27 }, // Nin-an - { 1169, 4, 8 }, // Kao 100 - { 1171, 4, 21 }, // Shoan - { 1175, 7, 28 }, // Angen - { 1177, 8, 4 }, // Jisho - { 1181, 7, 14 }, // Yowa - { 1182, 5, 27 }, // Juei - { 1184, 4, 16 }, // Genryuku - { 1185, 8, 14 }, // Bunji - { 1190, 4, 11 }, // Kenkyu - { 1199, 4, 27 }, // Shoji - { 1201, 2, 13 }, // Kennin 110 - { 1204, 2, 20 }, // Genkyu - { 1206, 4, 27 }, // Ken-ei - { 1207, 10, 25 }, // Shogen - { 1211, 3, 9 }, // Kenryaku - { 1213, 12, 6 }, // Kenpo - { 1219, 4, 12 }, // Shokyu - { 1222, 4, 13 }, // Joo - { 1224, 11, 20 }, // Gennin - { 1225, 4, 20 }, // Karoku - { 1227, 12, 10 }, // Antei 120 - { 1229, 3, 5 }, // Kanki - { 1232, 4, 2 }, // Joei - { 1233, 4, 15 }, // Tempuku - { 1234, 11, 5 }, // Bunryaku - { 1235, 9, 19 }, // Katei - { 1238, 11, 23 }, // Ryakunin - { 1239, 2, 7 }, // En-o - { 1240, 7, 16 }, // Ninji - { 1243, 2, 26 }, // Kangen - { 1247, 2, 28 }, // Hoji 130 - { 1249, 3, 18 }, // Kencho - { 1256, 10, 5 }, // Kogen - { 1257, 3, 14 }, // Shoka - { 1259, 3, 26 }, // Shogen - { 1260, 4, 13 }, // Bun-o - { 1261, 2, 20 }, // Kocho - { 1264, 2, 28 }, // Bun-ei - { 1275, 4, 25 }, // Kenji - { 1278, 2, 29 }, // Koan - { 1288, 4, 28 }, // Shoo 140 - { 1293, 8, 55 }, // Einin - { 1299, 4, 25 }, // Shoan - { 1302, 11, 21 }, // Kengen - { 1303, 8, 5 }, // Kagen - { 1306, 12, 14 }, // Tokuji - { 1308, 10, 9 }, // Enkei - { 1311, 4, 28 }, // Ocho - { 1312, 3, 20 }, // Showa - { 1317, 2, 3 }, // Bunpo - { 1319, 4, 28 }, // Geno 150 - { 1321, 2, 23 }, // Genkyo - { 1324, 12, 9 }, // Shochu - { 1326, 4, 26 }, // Kareki - { 1329, 8, 29 }, // Gentoku - { 1331, 8, 9 }, // Genko - { 1334, 1, 29 }, // Kemmu - { 1336, 2, 29 }, // Engen - { 1340, 4, 28 }, // Kokoku - { 1346, 12, 8 }, // Shohei - { 1370, 7, 24 }, // Kentoku 160 - { 1372, 4, 1 }, // Bunch\u0169 - { 1375, 5, 27 }, // Tenju - { 1379, 3, 22 }, // Koryaku - { 1381, 2, 10 }, // Kowa - { 1384, 4, 28 }, // Gench\u0169 - { 1384, 2, 27 }, // Meitoku - { 1387, 8, 23 }, // Kakei - { 1389, 2, 9 }, // Koo - { 1390, 3, 26 }, // Meitoku - { 1394, 7, 5 }, // Oei 170 - { 1428, 4, 27 }, // Shocho - { 1429, 9, 5 }, // Eikyo - { 1441, 2, 17 }, // Kakitsu - { 1444, 2, 5 }, // Bun-an - { 1449, 7, 28 }, // Hotoku - { 1452, 7, 25 }, // Kyotoku - { 1455, 7, 25 }, // Kosho - { 1457, 9, 28 }, // Choroku - { 1460, 12, 21 }, // Kansho - { 1466, 2, 28 }, // Bunsho 180 - { 1467, 3, 3 }, // Onin - { 1469, 4, 28 }, // Bunmei - { 1487, 7, 29 }, // Chokyo - { 1489, 8, 21 }, // Entoku - { 1492, 7, 19 }, // Meio - { 1501, 2, 29 }, // Bunki - { 1504, 2, 30 }, // Eisho - { 1521, 8, 23 }, // Taiei - { 1528, 8, 20 }, // Kyoroku - { 1532, 7, 29 }, // Tenmon 190 - { 1555, 10, 23 }, // Koji - { 1558, 2, 28 }, // Eiroku - { 1570, 4, 23 }, // Genki - { 1573, 7, 28 }, // Tensho - { 1592, 12, 8 }, // Bunroku - { 1596, 10, 27 }, // Keicho - { 1615, 7, 13 }, // Genwa - { 1624, 2, 30 }, // Kan-ei - { 1644, 12, 16 }, // Shoho - { 1648, 2, 15 }, // Keian 200 - { 1652, 9, 18 }, // Shoo - { 1655, 4, 13 }, // Meiryaku - { 1658, 7, 23 }, // Manji - { 1661, 4, 25 }, // Kanbun - { 1673, 9, 21 }, // Enpo - { 1681, 9, 29 }, // Tenwa - { 1684, 2, 21 }, // Jokyo - { 1688, 9, 30 }, // Genroku - { 1704, 3, 13 }, // Hoei - { 1711, 4, 25 }, // Shotoku 210 - { 1716, 6, 22 }, // Kyoho - { 1736, 4, 28 }, // Genbun - { 1741, 2, 27 }, // Kanpo - { 1744, 2, 21 }, // Enkyo - { 1748, 7, 12 }, // Kan-en - { 1751, 10, 27 }, // Horyaku - { 1764, 6, 2 }, // Meiwa - { 1772, 11, 16 }, // An-ei - { 1781, 4, 2 }, // Tenmei - { 1789, 1, 25 }, // Kansei 220 - { 1801, 2, 5 }, // Kyowa - { 1804, 2, 11 }, // Bunka - { 1818, 4, 22 }, // Bunsei - { 1830, 12, 10 }, // Tenpo - { 1844, 12, 2 }, // Koka - { 1848, 2, 28 }, // Kaei - { 1854, 11, 27 }, // Ansei - { 1860, 3, 18 }, // Man-en - { 1861, 2, 19 }, // Bunkyu - { 1864, 2, 20 }, // Genji 230 - { 1865, 4, 7 }, // Keio 231 - { 1868, 9, 8 }, // Meiji 232 - { 1912, 7, 30 }, // Taisho 233 - { 1926, 12, 25 }, // Showa 234 - { 1989, 1, 8 } // Heisei 235 -}; - -#define kEraCount UPRV_LENGTHOF(kEraInfo) - -/** - * The current era, for reference. - */ -static const int32_t kCurrentEra = (kEraCount-1); // int32_t to match the calendar field type - static const int32_t kGregorianEpoch = 1970; // used as the default value of EXTENDED_YEAR +static const char* TENTATIVE_ERA_VAR_NAME = "ICU_ENABLE_TENTATIVE_ERA"; + +// Initialize global Japanese era data +static void U_CALLCONV initializeEras(UErrorCode &status) { + // Although start date of next Japanese era is planned ahead, a name of + // new era might not be available. This implementation allows tester to + // check a new era without era names by settings below (in priority order). + // By default, such tentative era is disabled. + + // 1. Environment variable ICU_ENABLE_TENTATIVE_ERA=true or false + // 2. Windows registry (TBD) + + UBool includeTentativeEra = FALSE; + +#if U_PLATFORM_HAS_WINUWP_API == 0 + char *envVarVal = getenv(TENTATIVE_ERA_VAR_NAME); + if (envVarVal != NULL && uprv_stricmp(envVarVal, "true") == 0) { + includeTentativeEra = TRUE; + } +#endif + gJapaneseEraRules = EraRules::createInstance("japanese", includeTentativeEra, status); + if (U_FAILURE(status)) { + return; + } + gCurrentEra = gJapaneseEraRules->getCurrentEraIndex(); +} + +static void init(UErrorCode &status) { + umtx_initOnce(gJapaneseEraRulesInitOnce, &initializeEras, status); + ucln_i18n_registerCleanup(UCLN_I18N_JAPANESE_CALENDAR, japanese_calendar_cleanup); +} /* Some platforms don't like to export constants, like old Palm OS and some z/OS configurations. */ uint32_t JapaneseCalendar::getCurrentEra() { - return kCurrentEra; + return gCurrentEra; } JapaneseCalendar::JapaneseCalendar(const Locale& aLocale, UErrorCode& success) : GregorianCalendar(aLocale, success) { + init(success); setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. } @@ -306,6 +100,9 @@ JapaneseCalendar::~JapaneseCalendar() JapaneseCalendar::JapaneseCalendar(const JapaneseCalendar& source) : GregorianCalendar(source) { + UErrorCode status = U_ZERO_ERROR; + init(status); + U_ASSERT(U_SUCCESS(status)); } JapaneseCalendar& JapaneseCalendar::operator= ( const JapaneseCalendar& right) @@ -332,10 +129,14 @@ int32_t JapaneseCalendar::getDefaultMonthInYear(int32_t eyear) int32_t month = 0; // Find out if we are at the edge of an era - - if(eyear == kEraInfo[era].year) { + int32_t eraStart[3] = { 0,0,0 }; + UErrorCode status = U_ZERO_ERROR; + gJapaneseEraRules->getStartDate(era, eraStart, status); + U_ASSERT(U_SUCCESS(status)); + if(eyear == eraStart[0]) { // Yes, we're in the first year of this era. - return kEraInfo[era].month-1; + return eraStart[1] // month + -1; // return 0-based month } return month; @@ -346,9 +147,13 @@ int32_t JapaneseCalendar::getDefaultDayInMonth(int32_t eyear, int32_t month) int32_t era = internalGetEra(); int32_t day = 1; - if(eyear == kEraInfo[era].year) { - if(month == (kEraInfo[era].month-1)) { - return kEraInfo[era].day; + int32_t eraStart[3] = { 0,0,0 }; + UErrorCode status = U_ZERO_ERROR; + gJapaneseEraRules->getStartDate(era, eraStart, status); + U_ASSERT(U_SUCCESS(status)); + if(eyear == eraStart[0]) { + if(month == eraStart[1] - 1) { + return eraStart[2]; } } @@ -358,7 +163,7 @@ int32_t JapaneseCalendar::getDefaultDayInMonth(int32_t eyear, int32_t month) int32_t JapaneseCalendar::internalGetEra() const { - return internalGet(UCAL_ERA, kCurrentEra); + return internalGet(UCAL_ERA, gCurrentEra); } int32_t JapaneseCalendar::handleGetExtendedYear() @@ -369,12 +174,18 @@ int32_t JapaneseCalendar::handleGetExtendedYear() if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR && newerField(UCAL_EXTENDED_YEAR, UCAL_ERA) == UCAL_EXTENDED_YEAR) { - year = internalGet(UCAL_EXTENDED_YEAR, kGregorianEpoch); - } else { - // Subtract one because year starts at 1 - year = internalGet(UCAL_YEAR) + kEraInfo[internalGetEra()].year - 1; - } - return year; + year = internalGet(UCAL_EXTENDED_YEAR, kGregorianEpoch); + } else { + UErrorCode status = U_ZERO_ERROR; + int32_t eraStartYear = gJapaneseEraRules->getStartYear(internalGet(UCAL_ERA, gCurrentEra), status); + U_ASSERT(U_SUCCESS(status)); + + // extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc + year = internalGet(UCAL_YEAR, 1) // pin to minimum of year 1 (first year) + + eraStartYear // add gregorian starting year + - 1; // Subtract one because year starts at 1 + } + return year; } @@ -383,79 +194,10 @@ void JapaneseCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status //Calendar::timeToFields(theTime, quick, status); GregorianCalendar::handleComputeFields(julianDay, status); int32_t year = internalGet(UCAL_EXTENDED_YEAR); // Gregorian year + int32_t eraIdx = gJapaneseEraRules->getEraIndex(year, internalGet(UCAL_MONTH) + 1, internalGet(UCAL_DAY_OF_MONTH), status); - int32_t low = 0; - - // Short circuit for recent years. Most modern computations will - // occur in the current era and won't require the binary search. - // Note that if the year is == the current era year, then we use - // the binary search to handle the month/dom comparison. -#ifdef U_DEBUG_JCAL - fprintf(stderr, "== %d \n", year); -#endif - - if (year > kEraInfo[kCurrentEra].year) { - low = kCurrentEra; -#ifdef U_DEBUG_JCAL - fprintf(stderr, " low=%d (special)\n", low); -#endif - } else { - // Binary search - int32_t high = kEraCount; - -#ifdef U_DEBUG_JCAL - fprintf(stderr, " high=%d\n", high); -#endif - while (low < high - 1) { - int32_t i = (low + high) / 2; - int32_t diff = year - kEraInfo[i].year; - -#ifdef U_DEBUG_JCAL - fprintf(stderr, " d=%d low=%d, high=%d. Considering %d:M%d D%d Y%d. { we are ?:M%d D%d Y%d }\n", - diff,low, high, i, kEraInfo[i].month-1, kEraInfo[i].day, kEraInfo[i].year, internalGet(UCAL_MONTH), internalGet(UCAL_DATE),year); -#endif - - // If years are the same, then compare the months, and if those - // are the same, compare days of month. In the ERAS array - // months are 1-based for easier maintenance. - if (diff == 0) { - diff = internalGet(UCAL_MONTH) - (kEraInfo[i].month - 1); -#ifdef U_DEBUG_JCAL - fprintf(stderr, "diff now %d (M) = %d - %d - 1\n", diff, internalGet(UCAL_MONTH), kEraInfo[i].month); -#endif - if (diff == 0) { - diff = internalGet(UCAL_DATE) - kEraInfo[i].day; -#ifdef U_DEBUG_JCAL - fprintf(stderr, "diff now %d (D)\n", diff); -#endif - } - } - if (diff >= 0) { - low = i; - } else { - high = i; - } -#ifdef U_DEBUG_JCAL - fprintf(stderr, ". low=%d, high=%d, i=%d, diff=%d.. %d\n", low, high, i, diff, year); -#endif - - } - } - -#ifdef U_DEBUG_JCAL - fprintf(stderr, " low[era]=%d,.. %d\n", low, year); -#endif - // Now we've found the last era that starts before this date, so - // adjust the year to count from the start of that era. Note that - // all dates before the first era will fall into the first era by - // the algorithm. - - internalSet(UCAL_ERA, low); - internalSet(UCAL_YEAR, year - kEraInfo[low].year + 1); -#ifdef U_DEBUG_JCAL - fprintf(stderr, " Set ERA=%d, year=%d\n", low, year-kEraInfo[low].year+1); -#endif - + internalSet(UCAL_ERA, eraIdx); + internalSet(UCAL_YEAR, year - gJapaneseEraRules->getStartYear(eraIdx, status) + 1); } /* @@ -483,7 +225,7 @@ int32_t JapaneseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType l if (limitType == UCAL_LIMIT_MINIMUM || limitType == UCAL_LIMIT_GREATEST_MINIMUM) { return 0; } - return kCurrentEra; + return gCurrentEra; case UCAL_YEAR: { switch (limitType) { @@ -494,7 +236,12 @@ int32_t JapaneseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType l return 1; case UCAL_LIMIT_COUNT: //added to avoid warning case UCAL_LIMIT_MAXIMUM: - return GregorianCalendar::handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM) - kEraInfo[kCurrentEra].year; + { + UErrorCode status = U_ZERO_ERROR; + int32_t eraStartYear = gJapaneseEraRules->getStartYear(gCurrentEra, status); + U_ASSERT(U_SUCCESS(status)); + return GregorianCalendar::handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM) - eraStartYear; + } default: return 1; // Error condition, invalid limitType } @@ -510,15 +257,18 @@ int32_t JapaneseCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode if (U_FAILURE(status)) { return 0; // error case... any value } - if (era == kCurrentEra) { + if (era == gCurrentEra) { // TODO: Investigate what value should be used here - revisit after 4.0. return handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM); } else { - int32_t nextEraYear = kEraInfo[era + 1].year; - int32_t nextEraMonth = kEraInfo[era + 1].month; - int32_t nextEraDate = kEraInfo[era + 1].day; - - int32_t maxYear = nextEraYear - kEraInfo[era].year + 1; // 1-base + int32_t nextEraStart[3] = { 0,0,0 }; + gJapaneseEraRules->getStartDate(era + 1, nextEraStart, status); + int32_t nextEraYear = nextEraStart[0]; + int32_t nextEraMonth = nextEraStart[1]; // 1-base + int32_t nextEraDate = nextEraStart[2]; + + int32_t eraStartYear = gJapaneseEraRules->getStartYear(era, status); + int32_t maxYear = nextEraYear - eraStartYear + 1; // 1-base if (nextEraMonth == 1 && nextEraDate == 1) { // Subtract 1, because the next era starts at Jan 1 maxYear--; diff --git a/icu4c/source/i18n/japancal.h b/icu4c/source/i18n/japancal.h index f05b2ac9b779..7a6d13228b1e 100644 --- a/icu4c/source/i18n/japancal.h +++ b/icu4c/source/i18n/japancal.h @@ -49,10 +49,18 @@ U_NAMESPACE_BEGIN * July 30, 1912 (Taisho), December 25, 1926 (Showa), and January 7, 1989 (Heisei). Constants * for these eras, suitable for use in the UCAL_ERA field, are provided * in this class. Note that the number used for each era is more or - * less arbitrary. Currently, the era starting in 1053 AD is era #0; however this - * may change in the future as we add more historical data. Use the predefined - * constants rather than using actual, absolute numbers. + * less arbitrary. Currently, the era starting in 645 AD is era #0; however this + * may change in the future. Use the predefined constants rather than using actual, + * absolute numbers. *

+ * Since ICU4C 63, start date of each era is imported from CLDR. CLDR era data + * may contain tentative era in near future with placeholder names. By default, + * such era data is not enabled. ICU4C users who want to test the behavior of + * the future era can enable this one of following settings (in the priority + * order): + *

    + *
  1. Environment variable ICU_ENABLE_TENTATIVE_ERA=true.
  2. + * * @internal */ class JapaneseCalendar : public GregorianCalendar { diff --git a/icu4c/source/i18n/ucln_in.h b/icu4c/source/i18n/ucln_in.h index 337621f4abc4..4c13b9ffcb53 100644 --- a/icu4c/source/i18n/ucln_in.h +++ b/icu4c/source/i18n/ucln_in.h @@ -32,6 +32,7 @@ typedef enum ECleanupI18NType { UCLN_I18N_SPOOFDATA, UCLN_I18N_TRANSLITERATOR, UCLN_I18N_REGEX, + UCLN_I18N_JAPANESE_CALENDAR, UCLN_I18N_ISLAMIC_CALENDAR, UCLN_I18N_CHINESE_CALENDAR, UCLN_I18N_HEBREW_CALENDAR, diff --git a/icu4c/source/i18n/unicode/calendar.h b/icu4c/source/i18n/unicode/calendar.h index bf72edfc0bf2..e961c1c64948 100644 --- a/icu4c/source/i18n/unicode/calendar.h +++ b/icu4c/source/i18n/unicode/calendar.h @@ -167,6 +167,19 @@ class BasicTimeZone; * to ~5,800,000 CE. Programmers should use the protected constants in `Calendar` to * specify an extremely early or extremely late date. * + *

    + * The Japanese calendar uses a combination of era name and year number. + * When an emperor of Japan abdicates and a new emperor ascends the throne, + * a new era is declared and year number is reset to 1. Even if the date of + * abdication is scheduled ahead of time, the new era name might not be + * announced until just before the date. In such case, ICU4C may include + * a start date of future era without actual era name, but not enabled + * by default. ICU4C users who want to test the behavior of the future era + * can enable the tentative era by: + *

      + *
    • Environment variable ICU_ENABLE_TENTATIVE_ERA=true.
    • + *
    + * * @stable ICU 2.0 */ class U_I18N_API Calendar : public UObject { diff --git a/icu4c/source/i18n/unicode/ucal.h b/icu4c/source/i18n/unicode/ucal.h index c765e7859f59..fb7c387c2d7a 100644 --- a/icu4c/source/i18n/unicode/ucal.h +++ b/icu4c/source/i18n/unicode/ucal.h @@ -139,6 +139,19 @@ * For example, subtracting 5 days from the date September 12, 1996 * results in September 7, 1996. * + *

    + * The Japanese calendar uses a combination of era name and year number. + * When an emperor of Japan abdicates and a new emperor ascends the throne, + * a new era is declared and year number is reset to 1. Even if the date of + * abdication is scheduled ahead of time, the new era name might not be + * announced until just before the date. In such case, ICU4C may include + * a start date of future era without actual era name, but not enabled + * by default. ICU4C users who want to test the behavior of the future era + * can enable the tentative era by: + *

      + *
    • Environment variable ICU_ENABLE_TENTATIVE_ERA=true.
    • + *
    + * * @stable ICU 2.0 */ diff --git a/icu4c/source/test/intltest/Makefile.in b/icu4c/source/test/intltest/Makefile.in index c2b38c688f76..4d3e17435ea5 100644 --- a/icu4c/source/test/intltest/Makefile.in +++ b/icu4c/source/test/intltest/Makefile.in @@ -66,7 +66,7 @@ numbertest_affixutils.o numbertest_api.o numbertest_decimalquantity.o \ numbertest_modifiers.o numbertest_patternmodifier.o numbertest_patternstring.o \ numbertest_stringbuilder.o numbertest_stringsegment.o \ numbertest_parse.o numbertest_doubleconversion.o numbertest_skeletons.o \ -static_unisets_test.o numfmtdatadriventest.o +static_unisets_test.o numfmtdatadriventest.o erarulestest.o DEPS = $(OBJECTS:.o=.d) diff --git a/icu4c/source/test/intltest/erarulestest.cpp b/icu4c/source/test/intltest/erarulestest.cpp new file mode 100644 index 000000000000..b550361b271f --- /dev/null +++ b/icu4c/source/test/intltest/erarulestest.cpp @@ -0,0 +1,130 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" +#include "unicode/localpointer.h" +#include "unicode/unistr.h" +#include "erarules.h" +#include "erarulestest.h" + +void EraRulesTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) +{ + if (exec) { + logln("TestSuite EraRulesTest"); + } + TESTCASE_AUTO_BEGIN; + TESTCASE_AUTO(testAPIs); + TESTCASE_AUTO(testJapanese); + TESTCASE_AUTO_END; +} + +void EraRulesTest::testAPIs() { + const char * calTypes[] = { + "gregorian", + //"iso8601", + "buddhist", + "chinese", + "coptic", + "dangi", + "ethiopic", + "ethiopic-amete-alem", + "hebrew", + "indian", + "islamic", + "islamic-civil", + "islamic-rgsa", + "islamic-tbla", + "islamic-umalqura", + "japanese", + "persian", + "roc", + //"unknown", + NULL + }; + + for (int32_t i = 0; calTypes[i] != NULL; i++) { + UErrorCode status = U_ZERO_ERROR; + const char *calId = calTypes[i]; + + LocalPointer rules1(EraRules::createInstance(calId, FALSE, status)); + if (U_FAILURE(status)) { + errln(UnicodeString("Era rules for ") + calId + " is not available."); + continue; + } + + LocalPointer rules2(EraRules::createInstance(calId, TRUE, status)); + if (U_FAILURE(status)) { + errln(UnicodeString("Era rules for ") + calId + " (including tentative eras) is not available."); + continue; + } + + int32_t numEras1 = rules1->getNumberOfEras(); + if (numEras1 <= 0) { + errln(UnicodeString("Number of era rules for ") + calId + " is " + numEras1); + } + + int32_t numEras2 = rules2->getNumberOfEras(); + if (numEras2 < numEras1) { + errln(UnicodeString("Number of era including tentative eras is fewer than one without tentative eras in calendar: ") + + calId); + } + + LocalPointer cal(Calendar::createInstance("en", status)); + if (U_FAILURE(status)) { + errln("Failed to create a Calendar instance."); + continue; + } + int32_t currentIdx = rules1->getCurrentEraIndex(); + int32_t currentYear = cal->get(UCAL_YEAR, status); + int32_t idx = rules1->getEraIndex( + currentYear, cal->get(UCAL_MONTH, status) + 1, + cal->get(UCAL_DATE, status), status); + if (U_FAILURE(status)) { + errln("Error while getting index of era."); + continue; + } + if (idx != currentIdx) { + errln(UnicodeString("Current era index:") + currentIdx + " is different from era index of now:" + idx + + " in calendar:" + calId); + } + + int32_t eraStartYear = rules1->getStartYear(currentIdx, status); + if (U_FAILURE(status)) { + errln(UnicodeString("Failed to get the start year of era index: ") + currentIdx + " in calendar: " + calId); + } + if (currentYear < eraStartYear) { + errln(UnicodeString("Current era's start year is after the current year in calendar:") + calId); + } + } +} + +void EraRulesTest::testJapanese() { + const int32_t HEISEI = 235; // ICU4C does not define constants for eras + + UErrorCode status = U_ZERO_ERROR; + LocalPointer rules(EraRules::createInstance("japanese", TRUE, status)); + if (U_FAILURE(status)) { + errln("Failed to get era rules for Japanese calendar."); + return; + } + // Rules should have an era after Heisei + int32_t numRules = rules->getNumberOfEras(); + if (numRules <= HEISEI) { + errln("Era after Heisei is not available."); + return; + } + int postHeiseiStartYear = rules->getStartYear(HEISEI + 1, status); + if (U_FAILURE(status)) { + errln("Failed to get the start year of era after Heisei."); + } + if (postHeiseiStartYear != 2019) { + errln(UnicodeString("Era after Heisei should start in 2019, but got ") + postHeiseiStartYear); + } +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ + diff --git a/icu4c/source/test/intltest/erarulestest.h b/icu4c/source/test/intltest/erarulestest.h new file mode 100644 index 000000000000..28116af198df --- /dev/null +++ b/icu4c/source/test/intltest/erarulestest.h @@ -0,0 +1,23 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef ERARULESTEST_H_ +#define ERARULESTEST_H_ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "intltest.h" + +class EraRulesTest : public IntlTest { +public: + void runIndexedTest(int32_t index, UBool exec, const char* &name, char* par = NULL); + +private: + void testAPIs(); + void testJapanese(); +}; + +#endif /* #if !UCONFIG_NO_FORMATTING */ +#endif /* ERARULESTEST_H_ */ diff --git a/icu4c/source/test/intltest/intltest.vcxproj b/icu4c/source/test/intltest/intltest.vcxproj index 0d7bae442ba4..60d34a0c9571 100644 --- a/icu4c/source/test/intltest/intltest.vcxproj +++ b/icu4c/source/test/intltest/intltest.vcxproj @@ -2,7 +2,6 @@ - {73632960-B3A6-464D-83A3-4B43365F19B8} intltest @@ -160,6 +159,7 @@ false + @@ -365,6 +365,7 @@ + diff --git a/icu4c/source/test/intltest/intltest.vcxproj.filters b/icu4c/source/test/intltest/intltest.vcxproj.filters index 4fea4428844f..fc2ecb53050d 100644 --- a/icu4c/source/test/intltest/intltest.vcxproj.filters +++ b/icu4c/source/test/intltest/intltest.vcxproj.filters @@ -72,7 +72,7 @@ break iteration - + spoof detection @@ -532,6 +532,9 @@ data & memory + + formatting + @@ -915,5 +918,8 @@ formatting + + formatting + - + \ No newline at end of file diff --git a/icu4c/source/test/intltest/itformat.cpp b/icu4c/source/test/intltest/itformat.cpp index cea3249a4487..d450922eb6ed 100644 --- a/icu4c/source/test/intltest/itformat.cpp +++ b/icu4c/source/test/intltest/itformat.cpp @@ -60,6 +60,7 @@ #include "listformattertest.h" // ListFormatterTest #include "regiontst.h" // RegionTest #include "numbertest.h" // NumberTest +#include "erarulestest.h" // EraRulesTest extern IntlTest *createCompactDecimalFormatTest(); extern IntlTest *createGenderInfoTest(); @@ -215,6 +216,7 @@ void IntlTestFormat::runIndexedTest( int32_t index, UBool exec, const char* &nam break; TESTCLASS(50,NumberFormatDataDrivenTest); TESTCLASS(51,NumberTest); + TESTCLASS(52,EraRulesTest); default: name = ""; break; //needed to end loop } if (exec) { diff --git a/icu4c/source/test/testdata/structLocale.txt b/icu4c/source/test/testdata/structLocale.txt index a49de07741dd..c0c2012aa780 100644 --- a/icu4c/source/test/testdata/structLocale.txt +++ b/icu4c/source/test/testdata/structLocale.txt @@ -25805,6 +25805,7 @@ structLocale:table(nofallback){ "", "", "", + "", } wide{ "", @@ -26043,6 +26044,7 @@ structLocale:table(nofallback){ "", "", "", + "", } narrow{ "", @@ -26281,6 +26283,7 @@ structLocale:table(nofallback){ "", "", "", + "", } } intervalFormats{ diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/CalType.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/CalType.java new file mode 100644 index 000000000000..ad9c3bb982e1 --- /dev/null +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/CalType.java @@ -0,0 +1,42 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html#License +package com.ibm.icu.impl; + +/** + * Calendar type enum, moved from com.ibm.icu.util.Calendar. + * + * @author Yoshito Umaoka + */ +public enum CalType { + GREGORIAN("gregorian"), + ISO8601("iso8601"), + + BUDDHIST("buddhist"), + CHINESE("chinese"), + COPTIC("coptic"), + DANGI("dangi"), + ETHIOPIC("ethiopic"), + ETHIOPIC_AMETE_ALEM("ethiopic-amete-alem"), + HEBREW("hebrew"), + INDIAN("indian"), + ISLAMIC("islamic"), + ISLAMIC_CIVIL("islamic-civil"), + ISLAMIC_RGSA("islamic-rgsa"), + ISLAMIC_TBLA("islamic-tbla"), + ISLAMIC_UMALQURA("islamic-umalqura"), + JAPANESE("japanese"), + PERSIAN("persian"), + ROC("roc"), + + UNKNOWN("unknown"); + + String id; + + CalType(String id) { + this.id = id; + } + + public String getId() { + return id; + } +} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/EraRules.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/EraRules.java new file mode 100644 index 000000000000..9b82396fc867 --- /dev/null +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/EraRules.java @@ -0,0 +1,309 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html#License +package com.ibm.icu.impl; + +import java.util.Arrays; + +import com.ibm.icu.util.ICUException; +import com.ibm.icu.util.UResourceBundle; +import com.ibm.icu.util.UResourceBundleIterator; + +/** + * EraRules represents calendar era rules specified + * in supplementalData/calendarData. + * + * @author Yoshito Umaoka + */ +public class EraRules { + private static final int MAX_ENCODED_START_YEAR = 32767; + private static final int MIN_ENCODED_START_YEAR = -32768; + + public static final int MIN_ENCODED_START = encodeDate(MIN_ENCODED_START_YEAR, 1, 1); + + private static final int YEAR_MASK = 0xFFFF0000; + private static final int MONTH_MASK = 0x0000FF00; + private static final int DAY_MASK = 0x000000FF; + + private int[] startDates; + private int numEras; + private int currentEra; + + private EraRules(int[] startDates, int numEras) { + this.startDates = startDates; + this.numEras = numEras; + initCurrentEra(); + } + + public static EraRules getInstance(CalType calType, boolean includeTentativeEra) { + UResourceBundle supplementalDataRes = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, + "supplementalData", ICUResourceBundle.ICU_DATA_CLASS_LOADER); + UResourceBundle calendarDataRes = supplementalDataRes.get("calendarData"); + UResourceBundle calendarTypeRes = calendarDataRes.get(calType.getId()); + UResourceBundle erasRes = calendarTypeRes.get("eras"); + + int numEras = erasRes.getSize(); + int firstTentativeIdx = Integer.MAX_VALUE; // first tentative era index + int[] startDates = new int[numEras]; + + UResourceBundleIterator itr = erasRes.getIterator(); + while (itr.hasNext()) { + UResourceBundle eraRuleRes = itr.next(); + String eraIdxStr = eraRuleRes.getKey(); + int eraIdx = -1; + try { + eraIdx = Integer.parseInt(eraIdxStr); + } catch (NumberFormatException e) { + throw new ICUException("Invald era rule key:" + eraIdxStr + " in era rule data for " + calType.getId()); + } + if (eraIdx < 0 || eraIdx >= numEras) { + throw new ICUException("Era rule key:" + eraIdxStr + " in era rule data for " + calType.getId() + + " must be in range [0, " + (numEras - 1) + "]"); + } + if (isSet(startDates[eraIdx])) { + throw new ICUException( + "Dupulicated era rule for rule key:" + eraIdxStr + " in era rule data for " + calType.getId()); + } + + boolean hasName = true; + boolean hasEnd = false; + UResourceBundleIterator ruleItr = eraRuleRes.getIterator(); + while (ruleItr.hasNext()) { + UResourceBundle res = ruleItr.next(); + String key = res.getKey(); + if (key.equals("start")) { + int[] fields = res.getIntVector(); + if (fields.length != 3 || !isValidRuleStartDate(fields[0], fields[1], fields[2])) { + throw new ICUException( + "Invalid era rule date data:" + Arrays.toString(fields) + " in era rule data for " + + calType.getId()); + } + startDates[eraIdx] = encodeDate(fields[0], fields[1], fields[2]); + } else if (key.equals("named")) { + String val = res.getString(); + if (val.equals("false")) { + hasName = false; + } + } else if (key.equals("end")) { + hasEnd = true; + } + } + if (isSet(startDates[eraIdx])) { + if (hasEnd) { + // This implementation assumes either start or end is available, not both. + // For now, just ignore the end rule. + } + } else { + if (hasEnd) { + if (eraIdx != 0) { + // This implementation does not support end only rule for eras other than + // the first one. + throw new ICUException( + "Era data for " + eraIdxStr + " in era rule data for " + calType.getId() + + " has only end rule."); + } + startDates[eraIdx] = MIN_ENCODED_START; + } else { + throw new ICUException("Missing era start/end rule date for key:" + eraIdxStr + " in era rule data for " + + calType.getId()); + } + } + + if (hasName) { + if (eraIdx >= firstTentativeIdx) { + throw new ICUException( + "Non-tentative era(" + eraIdx + ") must be placed before the first tentative era"); + } + } else { + if (eraIdx < firstTentativeIdx) { + firstTentativeIdx = eraIdx; + } + } + } + + if (firstTentativeIdx < Integer.MAX_VALUE && !includeTentativeEra) { + return new EraRules(startDates, firstTentativeIdx); + } + + return new EraRules(startDates, numEras); + } + + /** + * Gets number of effective eras + * @return number of effective eras + */ + public int getNumberOfEras() { + return numEras; + } + + /** + * Gets start date of an era + * @param eraIdx Era index + * @param fillIn Receives date fields if supplied. If null, or size of array + * is less than 3, then a new int[] will be newly allocated. + * @return An int array including values of year, month, day of month in this order. + * When an era has no start date, the result will be January 1st in year + * whose value is minimum integer. + */ + public int[] getStartDate(int eraIdx, int[] fillIn) { + if (eraIdx < 0 || eraIdx >= numEras) { + throw new IllegalArgumentException("eraIdx is out of range"); + } + return decodeDate(startDates[eraIdx], fillIn); + } + + /** + * Gets start year of an era + * @param eraIdx Era index + * @return The first year of an era. When a era has no start date, minimum integer + * value is returned. + */ + public int getStartYear(int eraIdx) { + if (eraIdx < 0 || eraIdx >= numEras) { + throw new IllegalArgumentException("eraIdx is out of range"); + } + int[] fields = decodeDate(startDates[eraIdx], null); + return fields[0]; + } + + /** + * Returns era index for the specified year/month/day. + * @param year Year + * @param month Month (1-base) + * @param day Day of month + * @return era index (or 0, when the specified date is before the first era) + */ + public int getEraIndex(int year, int month, int day) { + if (month < 1 || month > 12 || day < 1 || day > 31) { + throw new IllegalArgumentException("Illegal date - year:" + year + "month:" + month + "day:" + day); + } + int high = numEras; // last index + 1 + int low; + + // Short circuit for recent years. Most modern computations will + // occur in the last few eras. + if (compareEncodedDateWithYMD(startDates[getCurrentEraIndex()], year, month, day) <= 0) { + low = getCurrentEraIndex(); + } else { + low = 0; + } + + // Do binary search + while (low < high - 1) { + int i = (low + high) / 2; + if (compareEncodedDateWithYMD(startDates[i], year, month, day) <= 0) { + low = i; + } else { + high = i; + } + } + return low; + } + + /** + * Gets the current era index. This is calculated only once for an instance of + * EraRules. + * + * @return era index of current era (or 0, when current date is before the first era) + */ + public int getCurrentEraIndex() { + return currentEra; + } + + private void initCurrentEra() { + int[] fields = Grego.timeToFields(System.currentTimeMillis(), null); + int currentEncodedDate = encodeDate(fields[0], fields[1] + 1 /* changes to 1-base */, fields[2]); + int eraIdx = numEras - 1; + while (eraIdx > 0) { + if (currentEncodedDate >= startDates[eraIdx]) { + break; + } + eraIdx--; + } + // Note: current era could be before the first era. + // In this case, this implementation returns the first era index (0). + currentEra = eraIdx; + } + + // + // private methods + // + + private static boolean isSet(int startDate) { + return startDate != 0; + } + + private static boolean isValidRuleStartDate(int year, int month, int day) { + return year >= MIN_ENCODED_START_YEAR && year <= MAX_ENCODED_START_YEAR + && month >= 1 && month <= 12 && day >= 1 && day <= 31; + } + + /** + * Encode year/month/date to a single integer. + * year is high 16 bits (-32768 to 32767), month is + * next 8 bits and day of month is last 8 bits. + * + * @param year year + * @param month month (1-base) + * @param day day of month + * @return an encoded date. + */ + private static int encodeDate(int year, int month, int day) { + return year << 16 | month << 8 | day; + } + + private static int[] decodeDate(int encodedDate, int[] fillIn) { + int year, month, day; + if (encodedDate == MIN_ENCODED_START) { + year = Integer.MIN_VALUE; + month = 1; + day = 1; + } else { + year = (encodedDate & YEAR_MASK) >> 16; + month = (encodedDate & MONTH_MASK) >> 8; + day = encodedDate & DAY_MASK; + } + + if (fillIn != null && fillIn.length >= 3) { + fillIn[0] = year; + fillIn[1] = month; + fillIn[2] = day; + return fillIn; + } + + int[] result = {year, month, day}; + return result; + } + + /** + * Compare an encoded date with another date specified by year/month/day. + * @param encoded An encoded date + * @param year Year of another date + * @param month Month of another date + * @param day Day of another date + * @return -1 when encoded date is earlier, 0 when two dates are same, + * and 1 when encoded date is later. + */ + private static int compareEncodedDateWithYMD(int encoded, int year, int month, int day) { + if (year < MIN_ENCODED_START_YEAR) { + if (encoded == MIN_ENCODED_START) { + if (year > Integer.MIN_VALUE || month > 1 || day > 1) { + return -1; + } + return 0; + } else { + return 1; + } + } else if (year > MAX_ENCODED_START_YEAR) { + return -1; + } else { + int tmp = encodeDate(year, month, day); + if (encoded < tmp) { + return -1; + } else if (encoded == tmp) { + return 0; + } else { + return 1; + } + } + } +} \ No newline at end of file diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/Calendar.java b/icu4j/main/classes/core/src/com/ibm/icu/util/Calendar.java index 81d35403ac5c..2c369058655d 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/Calendar.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/Calendar.java @@ -17,6 +17,7 @@ import java.util.Locale; import java.util.MissingResourceException; +import com.ibm.icu.impl.CalType; import com.ibm.icu.impl.CalendarUtil; import com.ibm.icu.impl.ICUCache; import com.ibm.icu.impl.ICUData; @@ -1780,42 +1781,12 @@ private static String getRegionForCalendar(ULocale loc) { return region; } - private enum CalType { - GREGORIAN("gregorian"), - ISO8601("iso8601"), - - BUDDHIST("buddhist"), - CHINESE("chinese"), - COPTIC("coptic"), - DANGI("dangi"), - ETHIOPIC("ethiopic"), - ETHIOPIC_AMETE_ALEM("ethiopic-amete-alem"), - HEBREW("hebrew"), - INDIAN("indian"), - ISLAMIC("islamic"), - ISLAMIC_CIVIL("islamic-civil"), - ISLAMIC_RGSA("islamic-rgsa"), - ISLAMIC_TBLA("islamic-tbla"), - ISLAMIC_UMALQURA("islamic-umalqura"), - JAPANESE("japanese"), - PERSIAN("persian"), - ROC("roc"), - - UNKNOWN("unknown"); - - String id; - - CalType(String id) { - this.id = id; - } - } - private static CalType getCalendarTypeForLocale(ULocale l) { String s = CalendarUtil.getCalendarType(l); if (s != null) { s = s.toLowerCase(Locale.ENGLISH); for (CalType type : CalType.values()) { - if (s.equals(type.id)) { + if (s.equals(type.getId())) { return type; } } @@ -1938,7 +1909,7 @@ public static final String[] getKeywordValuesForLocale(String key, ULocale local String prefRegion = ULocale.getRegionForSupplementalData(locale, true); // Read preferred calendar values from supplementalData calendarPreferences - ArrayList values = new ArrayList(); + ArrayList values = new ArrayList<>(); UResourceBundle rb = UResourceBundle.getBundleInstance( ICUData.ICU_BASE_NAME, @@ -1965,8 +1936,8 @@ public static final String[] getKeywordValuesForLocale(String key, ULocale local } // then, add other available clanedars for (CalType t : CalType.values()) { - if (!values.contains(t.id)) { - values.add(t.id); + if (!values.contains(t.getId())) { + values.add(t.getId()); } } return values.toArray(new String[values.size()]); @@ -2191,7 +2162,7 @@ public final int getRelatedYear() { CalType type = CalType.GREGORIAN; String typeString = getType(); for (CalType testType : CalType.values()) { - if (typeString.equals(testType.id)) { + if (typeString.equals(testType.getId())) { type = testType; break; } @@ -2265,7 +2236,7 @@ public final void setRelatedYear(int year) { CalType type = CalType.GREGORIAN; String typeString = getType(); for (CalType testType : CalType.values()) { - if (typeString.equals(testType.id)) { + if (typeString.equals(testType.getId())) { type = testType; break; } @@ -3526,7 +3497,7 @@ protected DateFormat handleGetDateFormat(String pattern, String override, ULocal // date format pattern cache private static final ICUCache PATTERN_CACHE = - new SimpleCache(); + new SimpleCache<>(); // final fallback patterns private static final String[] DEFAULT_PATTERNS = { "HH:mm:ss z", diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/JapaneseCalendar.java b/icu4j/main/classes/core/src/com/ibm/icu/util/JapaneseCalendar.java index 5463407eb55a..7ba86605a522 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/JapaneseCalendar.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/JapaneseCalendar.java @@ -10,6 +10,9 @@ import java.util.Date; import java.util.Locale; +import com.ibm.icu.impl.CalType; +import com.ibm.icu.impl.EraRules; + /** * JapaneseCalendar is a subclass of GregorianCalendar * that numbers years and eras based on the reigns of the Japanese emperors. @@ -24,19 +27,34 @@ * of that year were in the Showa era, e.g. "January 6, 64 Showa", while the rest * of the year was in the Heisei era, e.g. "January 7, 1 Heisei". This class * handles this distinction correctly when computing dates. However, in lenient - * mode either form of date is acceptable as input. + * mode either form of date is acceptable as input. *

    * In modern times, eras have started on January 8, 1868 AD, Gregorian (Meiji), * July 30, 1912 (Taisho), December 25, 1926 (Showa), and January 7, 1989 (Heisei). Constants * for these eras, suitable for use in the ERA field, are provided * in this class. Note that the number used for each era is more or - * less arbitrary. Currently, the era starting in 1053 AD is era #0; however this - * may change in the future as we add more historical data. Use the predefined - * constants rather than using actual, absolute numbers. + * less arbitrary. Currently, the era starting in 645 AD is era #0; however this + * may change in the future. Use the predefined constants rather than using actual, + * absolute numbers. + *

    + * Since ICU4J 63, start date of each era is imported from CLDR. CLDR era data + * may contain tentative era in near future with placeholder names. By default, + * such era data is not enabled. ICU4J users who want to test the behavior of + * the future era can enable this by one of following settings (in the priority + * order): + *

      + *
    1. Java system property ICU_ENABLE_TENTATIVE_ERA=true.
    2. + *
    3. Environment variable ICU_ENABLE_TENTATIVE_ERA=true.
    4. + *
    5. Java system property jdk.calendar.japanese.supplemental.era=xxx. + * (Note: This configuration is used for specifying a new era's start date and + * names in OpenJDK. ICU4J implementation enables the CLDR tentative era when + * this property is defined, but it does not use the start date and names specified + * by the property value.)
    6. + * *

      * This class should not be subclassed.

      *

      - * JapaneseCalendar usually should be instantiated using + * JapaneseCalendar usually should be instantiated using * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a ULocale * with the tag "@calendar=japanese".

      * @@ -210,9 +228,44 @@ public JapaneseCalendar(int year, int month, int date, int hour, // Use 1970 as the default value of EXTENDED_YEAR private static final int GREGORIAN_EPOCH = 1970; + private static final EraRules ERA_RULES; + + static { + // Although start date of next Japanese era is planned ahead, a name of + // new era might not be available. This implementation allows tester to + // check a new era without era names by settings below (in priority order). + // By default, such tentative era is disabled. + // + // 1. System property ICU_ENABLE_TENTATIVE_ERA=true or false + // 2. Environment variable ICU_ENABLE_TENTATIVE_ERA=true or false + // 3. Java system property - jdk.calendar.japanese.supplemental.era for Japanese + // + // Note: Java system property specifies the start date of new Japanese era, + // but this implementation always uses the date read from ICU data. + + boolean includeTentativeEra = false; + + final String VAR_NAME = "ICU_ENABLE_TENTATIVE_ERA"; + + String eraConf = System.getProperty(VAR_NAME); + if (eraConf == null) { + eraConf = System.getenv(VAR_NAME); + } + + if (eraConf != null) { + includeTentativeEra = eraConf.equalsIgnoreCase("true"); + } else { + String jdkEraConf = System.getProperty("jdk.calendar.japanese.supplemental.era"); + includeTentativeEra = jdkEraConf != null; + } + + ERA_RULES = EraRules.getInstance(CalType.JAPANESE, includeTentativeEra); + } + /** * @stable ICU 2.8 */ + @Override protected int handleGetExtendedYear() { // EXTENDED_YEAR in JapaneseCalendar is a Gregorian year // The default value of EXTENDED_YEAR is 1970 (Showa 45) @@ -221,14 +274,14 @@ protected int handleGetExtendedYear() { newerField(EXTENDED_YEAR, ERA) == EXTENDED_YEAR) { year = internalGet(EXTENDED_YEAR, GREGORIAN_EPOCH); } else { - // extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc - year = internalGet(YEAR, 1) // pin to minimum of year 1 (first year) - + ERAS[internalGet(ERA, CURRENT_ERA) * 3] // add gregorian starting year - - 1; // Subtract one because year starts at 1 + // extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc + year = internalGet(YEAR, 1) // pin to minimum of year 1 (first year) + + ERA_RULES.getStartYear(internalGet(ERA, CURRENT_ERA)) // add gregorian starting year + - 1; // Subtract one because year starts at 1 } return year; } - + /** * Called by handleComputeJulianDay. Returns the default month (0-based) for the year, * taking year and era into account. Defaults to 0 (JANUARY) for Gregorian. @@ -238,18 +291,19 @@ protected int handleGetExtendedYear() { * @draft ICU 3.6 (retain) * @see #MONTH */ - protected int getDefaultMonthInYear(int extendedYear) - { - int era = internalGet(ERA, CURRENT_ERA); - //computeFields(status); // No need to compute fields here - expect the caller already did so. - - // Find out if we are at the edge of an era - if(extendedYear == ERAS[era*3]) { - return ERAS[(era*3)+1] // month.. - -1; // return 0-based month - } else { - return super.getDefaultMonthInYear(extendedYear); - } + @Override + protected int getDefaultMonthInYear(int extendedYear) { + int era = internalGet(ERA, CURRENT_ERA); + // computeFields(status); // No need to compute fields here - expect the caller already did so. + + // Find out if we are at the edge of an era + int[] eraStart = ERA_RULES.getStartDate(era, null); + if (extendedYear == eraStart[0]) { + return eraStart[1] // month.. + - 1; // return 0-based month + } else { + return super.getDefaultMonthInYear(extendedYear); + } } /** @@ -262,308 +316,33 @@ protected int getDefaultMonthInYear(int extendedYear) * @provisional ICU 3.6 * @see #DAY_OF_MONTH */ + @Override protected int getDefaultDayInMonth(int extendedYear, int month) { - int era = internalGet(ERA, CURRENT_ERA); - - if(extendedYear == ERAS[era*3]) { // if it is year 1.. - if(month == ((ERAS[(era*3)+1])-1)) { // if it is the emperor's first month.. - return ERAS[(era*3)+2]; // return the D_O_M of acession + int era = internalGet(ERA, CURRENT_ERA); + int[] eraStart = ERA_RULES.getStartDate(era, null); + + if (extendedYear == eraStart[0]) { // if it is year 1.. + if (month == (eraStart[1] - 1)) { // if it is the emperor's first month.. + return eraStart[2]; // return the D_O_M of accession + } } - } - return super.getDefaultDayInMonth(extendedYear, month); + return super.getDefaultDayInMonth(extendedYear, month); } /** * @stable ICU 2.8 */ + @Override protected void handleComputeFields(int julianDay) { super.handleComputeFields(julianDay); int year = internalGet(EXTENDED_YEAR); + int eraIdx = ERA_RULES.getEraIndex(year, internalGet(MONTH) + 1 /* 1-base */, internalGet(DAY_OF_MONTH)); - int low = 0; - - // Short circuit for recent years. Most modern computations will - // occur in the current era and won't require the binary search. - // Note that if the year is == the current era year, then we use - // the binary search to handle the month/dom comparison. - if (year > ERAS[ERAS.length - 3]) { - low = CURRENT_ERA; - } else { - // Binary search - int high = ERAS.length / 3; - - while (low < high - 1) { - int i = (low + high) / 2; - int diff = year - ERAS[i*3]; - - // If years are the same, then compare the months, and if those - // are the same, compare days of month. In the ERAS array - // months are 1-based for easier maintenance. - if (diff == 0) { - diff = internalGet(MONTH) - (ERAS[i*3 + 1] - 1); - if (diff == 0) { - diff = internalGet(DAY_OF_MONTH) - ERAS[i*3 + 2]; - } - } - if (diff >= 0) { - low = i; - } else { - high = i; - } - } - } - - // Now we've found the last era that starts before this date, so - // adjust the year to count from the start of that era. Note that - // all dates before the first era will fall into the first era by - // the algorithm. - internalSet(ERA, low); - internalSet(YEAR, year - ERAS[low*3] + 1); + internalSet(ERA, eraIdx); + internalSet(YEAR, year - ERA_RULES.getStartYear(eraIdx) + 1); } - private static final int[] ERAS = { - // Gregorian date of each emperor's ascension - // Years are AD, months are 1-based. - // Year Month Day - 645, 6, 19, // Taika - 650, 2, 15, // Hakuchi - 672, 1, 1, // Hakuho - 686, 7, 20, // Shucho - 701, 3, 21, // Taiho - 704, 5, 10, // Keiun - 708, 1, 11, // Wado - 715, 9, 2, // Reiki - 717, 11, 17, // Yoro - 724, 2, 4, // Jinki - 729, 8, 5, // Tempyo - 749, 4, 14, // Tempyo-kampo - 749, 7, 2, // Tempyo-shoho - 757, 8, 18, // Tempyo-hoji - 765, 1, 7, // Tempho-jingo - 767, 8, 16, // Jingo-keiun - 770, 10, 1, // Hoki - 781, 1, 1, // Ten-o - 782, 8, 19, // Enryaku - 806, 5, 18, // Daido - 810, 9, 19, // Konin - 824, 1, 5, // Tencho - 834, 1, 3, // Showa - 848, 6, 13, // Kajo - 851, 4, 28, // Ninju - 854, 11, 30, // Saiko - 857, 2, 21, // Tennan - 859, 4, 15, // Jogan - 877, 4, 16, // Genkei - 885, 2, 21, // Ninna - 889, 4, 27, // Kampyo - 898, 4, 26, // Shotai - 901, 7, 15, // Engi - 923, 4, 11, // Encho - 931, 4, 26, // Shohei - 938, 5, 22, // Tengyo - 947, 4, 22, // Tenryaku - 957, 10, 27, // Tentoku - 961, 2, 16, // Owa - 964, 7, 10, // Koho - 968, 8, 13, // Anna - 970, 3, 25, // Tenroku - 973, 12, 20, // Ten-en - 976, 7, 13, // Jogen - 978, 11, 29, // Tengen - 983, 4, 15, // Eikan - 985, 4, 27, // Kanna - 987, 4, 5, // Ei-en - 989, 8, 8, // Eiso - 990, 11, 7, // Shoryaku - 995, 2, 22, // Chotoku - 999, 1, 13, // Choho - 1004, 7, 20, // Kanko - 1012, 12, 25, // Chowa - 1017, 4, 23, // Kannin - 1021, 2, 2, // Jian - 1024, 7, 13, // Manju - 1028, 7, 25, // Chogen - 1037, 4, 21, // Choryaku - 1040, 11, 10, // Chokyu - 1044, 11, 24, // Kantoku - 1046, 4, 14, // Eisho - 1053, 1, 11, // Tengi - 1058, 8, 29, // Kohei - 1065, 8, 2, // Jiryaku - 1069, 4, 13, // Enkyu - 1074, 8, 23, // Shoho - 1077, 11, 17, // Shoryaku - 1081, 2, 10, // Eiho - 1084, 2, 7, // Otoku - 1087, 4, 7, // Kanji - 1094, 12, 15, // Kaho - 1096, 12, 17, // Eicho - 1097, 11, 21, // Shotoku - 1099, 8, 28, // Kowa - 1104, 2, 10, // Choji - 1106, 4, 9, // Kasho - 1108, 8, 3, // Tennin - 1110, 7, 13, // Ten-ei - 1113, 7, 13, // Eikyu - 1118, 4, 3, // Gen-ei - 1120, 4, 10, // Hoan - 1124, 4, 3, // Tenji - 1126, 1, 22, // Daiji - 1131, 1, 29, // Tensho - 1132, 8, 11, // Chosho - 1135, 4, 27, // Hoen - 1141, 7, 10, // Eiji - 1142, 4, 28, // Koji - 1144, 2, 23, // Tenyo - 1145, 7, 22, // Kyuan - 1151, 1, 26, // Ninpei - 1154, 10, 28, // Kyuju - 1156, 4, 27, // Hogen - 1159, 4, 20, // Heiji - 1160, 1, 10, // Eiryaku - 1161, 9, 4, // Oho - 1163, 3, 29, // Chokan - 1165, 6, 5, // Eiman - 1166, 8, 27, // Nin-an - 1169, 4, 8, // Kao - 1171, 4, 21, // Shoan - 1175, 7, 28, // Angen - 1177, 8, 4, // Jisho - 1181, 7, 14, // Yowa - 1182, 5, 27, // Juei - 1184, 4, 16, // Genryuku - 1185, 8, 14, // Bunji - 1190, 4, 11, // Kenkyu - 1199, 4, 27, // Shoji - 1201, 2, 13, // Kennin - 1204, 2, 20, // Genkyu - 1206, 4, 27, // Ken-ei - 1207, 10, 25, // Shogen - 1211, 3, 9, // Kenryaku - 1213, 12, 6, // Kenpo - 1219, 4, 12, // Shokyu - 1222, 4, 13, // Joo - 1224, 11, 20, // Gennin - 1225, 4, 20, // Karoku - 1227, 12, 10, // Antei - 1229, 3, 5, // Kanki - 1232, 4, 2, // Joei - 1233, 4, 15, // Tempuku - 1234, 11, 5, // Bunryaku - 1235, 9, 19, // Katei - 1238, 11, 23, // Ryakunin - 1239, 2, 7, // En-o - 1240, 7, 16, // Ninji - 1243, 2, 26, // Kangen - 1247, 2, 28, // Hoji - 1249, 3, 18, // Kencho - 1256, 10, 5, // Kogen - 1257, 3, 14, // Shoka - 1259, 3, 26, // Shogen - 1260, 4, 13, // Bun-o - 1261, 2, 20, // Kocho - 1264, 2, 28, // Bun-ei - 1275, 4, 25, // Kenji - 1278, 2, 29, // Koan - 1288, 4, 28, // Shoo - 1293, 8, 55, // Einin - 1299, 4, 25, // Shoan - 1302, 11, 21, // Kengen - 1303, 8, 5, // Kagen - 1306, 12, 14, // Tokuji - 1308, 10, 9, // Enkei - 1311, 4, 28, // Ocho - 1312, 3, 20, // Showa - 1317, 2, 3, // Bunpo - 1319, 4, 28, // Geno - 1321, 2, 23, // Genkyo - 1324, 12, 9, // Shochu - 1326, 4, 26, // Kareki - 1329, 8, 29, // Gentoku - 1331, 8, 9, // Genko - 1334, 1, 29, // Kemmu - 1336, 2, 29, // Engen - 1340, 4, 28, // Kokoku - 1346, 12, 8, // Shohei - 1370, 7, 24, // Kentoku - 1372, 4, 1, // Bunch\u0169 - 1375, 5, 27, // Tenju - 1379, 3, 22, // Koryaku - 1381, 2, 10, // Kowa - 1384, 4, 28, // Gench\u0169 - 1384, 2, 27, // Meitoku - 1387, 8, 23, // Kakei - 1389, 2, 9, // Koo - 1390, 3, 26, // Meitoku - 1394, 7, 5, // Oei - 1428, 4, 27, // Shocho - 1429, 9, 5, // Eikyo - 1441, 2, 17, // Kakitsu - 1444, 2, 5, // Bun-an - 1449, 7, 28, // Hotoku - 1452, 7, 25, // Kyotoku - 1455, 7, 25, // Kosho - 1457, 9, 28, // Choroku - 1460, 12, 21, // Kansho - 1466, 2, 28, // Bunsho - 1467, 3, 3, // Onin - 1469, 4, 28, // Bunmei - 1487, 7, 29, // Chokyo - 1489, 8, 21, // Entoku - 1492, 7, 19, // Meio - 1501, 2, 29, // Bunki - 1504, 2, 30, // Eisho - 1521, 8, 23, // Taiei - 1528, 8, 20, // Kyoroku - 1532, 7, 29, // Tenmon - 1555, 10, 23, // Koji - 1558, 2, 28, // Eiroku - 1570, 4, 23, // Genki - 1573, 7, 28, // Tensho - 1592, 12, 8, // Bunroku - 1596, 10, 27, // Keicho - 1615, 7, 13, // Genwa - 1624, 2, 30, // Kan-ei - 1644, 12, 16, // Shoho - 1648, 2, 15, // Keian - 1652, 9, 18, // Shoo - 1655, 4, 13, // Meiryaku - 1658, 7, 23, // Manji - 1661, 4, 25, // Kanbun - 1673, 9, 21, // Enpo - 1681, 9, 29, // Tenwa - 1684, 2, 21, // Jokyo - 1688, 9, 30, // Genroku - 1704, 3, 13, // Hoei - 1711, 4, 25, // Shotoku - 1716, 6, 22, // Kyoho - 1736, 4, 28, // Genbun - 1741, 2, 27, // Kanpo - 1744, 2, 21, // Enkyo - 1748, 7, 12, // Kan-en - 1751, 10, 27, // Horyaku - 1764, 6, 2, // Meiwa - 1772, 11, 16, // An-ei - 1781, 4, 2, // Tenmei - 1789, 1, 25, // Kansei - 1801, 2, 5, // Kyowa - 1804, 2, 11, // Bunka - 1818, 4, 22, // Bunsei - 1830, 12, 10, // Tenpo - 1844, 12, 2, // Koka - 1848, 2, 28, // Kaei - 1854, 11, 27, // Ansei - 1860, 3, 18, // Man-en - 1861, 2, 19, // Bunkyu - 1864, 2, 20, // Genji - 1865, 4, 7, // Keio - 1868, 9, 8, // Meiji - 1912, 7, 30, // Taisho - 1926, 12, 25, // Showa - 1989, 1, 8, // Heisei - }; - //------------------------------------------------------------------------- // Public constants for some of the recent eras that folks might use... //------------------------------------------------------------------------- @@ -572,31 +351,31 @@ protected void handleComputeFields(int julianDay) { /** * @stable ICU 2.8 */ - static public final int CURRENT_ERA = (ERAS.length / 3) - 1; - - /** + static public final int CURRENT_ERA = ERA_RULES.getCurrentEraIndex(); + + /** * Constant for the era starting on Sept. 8, 1868 AD. - * @stable ICU 2.8 + * @stable ICU 2.8 */ - static public final int MEIJI = CURRENT_ERA - 3; + static public final int MEIJI = 232; - /** - * Constant for the era starting on July 30, 1912 AD. - * @stable ICU 2.8 + /** + * Constant for the era starting on July 30, 1912 AD. + * @stable ICU 2.8 */ - static public final int TAISHO = CURRENT_ERA - 2; - - /** - * Constant for the era starting on Dec. 25, 1926 AD. - * @stable ICU 2.8 + static public final int TAISHO = 233; + + /** + * Constant for the era starting on Dec. 25, 1926 AD. + * @stable ICU 2.8 */ - static public final int SHOWA = CURRENT_ERA - 1; + static public final int SHOWA = 234; - /** - * Constant for the era starting on Jan. 7, 1989 AD. - * @stable ICU 2.8 + /** + * Constant for the era starting on Jan. 7, 1989 AD. + * @stable ICU 2.8 */ - static public final int HEISEI = CURRENT_ERA; + static public final int HEISEI = 235; /** * Override GregorianCalendar. We should really handle YEAR_WOY and @@ -604,6 +383,7 @@ protected void handleComputeFields(int julianDay) { * not critical. * @stable ICU 2.8 */ + @Override @SuppressWarnings("fallthrough") protected int handleGetLimit(int field, int limitType) { switch (field) { @@ -621,7 +401,7 @@ protected int handleGetLimit(int field, int limitType) { case LEAST_MAXIMUM: return 1; case MAXIMUM: - return super.handleGetLimit(field, MAXIMUM) - ERAS[CURRENT_ERA*3]; + return super.handleGetLimit(field, MAXIMUM) - ERA_RULES.getStartYear(CURRENT_ERA); } //Fall through to the default if not handled above } @@ -634,6 +414,7 @@ protected int handleGetLimit(int field, int limitType) { * {@inheritDoc} * @stable ICU 3.8 */ + @Override public String getType() { return "japanese"; } @@ -643,6 +424,7 @@ public String getType() { * @internal * @deprecated This API is ICU internal only. */ + @Override @Deprecated public boolean haveDefaultCentury() { return false; @@ -652,6 +434,7 @@ public boolean haveDefaultCentury() { * {@inheritDoc} * @stable ICU 4.0 */ + @Override public int getActualMaximum(int field) { if (field == YEAR) { int era = get(Calendar.ERA); @@ -659,11 +442,12 @@ public int getActualMaximum(int field) { // TODO: Investigate what value should be used here - revisit after 4.0. return handleGetLimit(YEAR, MAXIMUM); } else { - int nextEraYear = ERAS[(era+1)*3]; - int nextEraMonth = ERAS[(era+1)*3 + 1]; - int nextEraDate = ERAS[(era+1)*3 + 2]; + int[] nextEraStart = ERA_RULES.getStartDate(era + 1, null); + int nextEraYear = nextEraStart[0]; + int nextEraMonth = nextEraStart[1]; // 1-base + int nextEraDate = nextEraStart[2]; - int maxYear = nextEraYear - ERAS[era*3] + 1; // 1-base + int maxYear = nextEraYear - ERA_RULES.getStartYear(era) + 1; // 1-base if (nextEraMonth == 1 && nextEraDate == 1) { // Substract 1, because the next era starts at Jan 1 maxYear--; diff --git a/icu4j/main/shared/data/icudata.jar b/icu4j/main/shared/data/icudata.jar index 198adf2fc9e5..e2be2f06c93b 100644 --- a/icu4j/main/shared/data/icudata.jar +++ b/icu4j/main/shared/data/icudata.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36d0ec0c543d1dccafcc6985a7c18285b255afb98bc2bdb16a867a22600bfddb -size 12487287 +oid sha256:92dc0a5ca71ac54537a6c7c42c2f80ccbd3298d9ebf69c3d732199230d5100c6 +size 12487439 diff --git a/icu4j/main/shared/data/icutzdata.jar b/icu4j/main/shared/data/icutzdata.jar index afa08eb95e77..6d6bf299cc0b 100755 --- a/icu4j/main/shared/data/icutzdata.jar +++ b/icu4j/main/shared/data/icutzdata.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:469f76e391dced8e9ae4a9543513dddd6d4d2026ad6cbc0ab79d9553da803e6a +oid sha256:d2308b3498ce1c2b869b60b5a0f7cea6f08aff2fac046ae260e10688c15c60b2 size 92857 diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/EraRulesTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/EraRulesTest.java new file mode 100644 index 000000000000..c2cf6e11debf --- /dev/null +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/EraRulesTest.java @@ -0,0 +1,76 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html#License +package com.ibm.icu.dev.test.calendar; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import com.ibm.icu.dev.test.TestFmwk; +import com.ibm.icu.impl.CalType; +import com.ibm.icu.impl.EraRules; +import com.ibm.icu.util.Calendar; +import com.ibm.icu.util.JapaneseCalendar; +import com.ibm.icu.util.ULocale; + +/** + * Tests for EraRules class + */ +@RunWith(JUnit4.class) +public class EraRulesTest extends TestFmwk { + @Test + public void testAPIs() { + for (CalType calType : CalType.values()) { + String calId = calType.getId(); + if (calId.equals("iso8601") || calId.equals("unknown")) { + continue; + } + EraRules rules1 = EraRules.getInstance(calType, false); + if (rules1 == null) { + errln("Era rules for " + calId + " is not available."); + } + + EraRules rules2 = EraRules.getInstance(calType, true); + if (rules2 == null) { + errln("Era rules for " + calId + " (including tentative eras) is not available."); + } + int numEras1 = rules1.getNumberOfEras(); + if (numEras1 <= 0) { + errln("Number of era rules for " + calId + " is " + numEras1); + } + int numEras2 = rules2.getNumberOfEras(); + if (numEras2 < numEras1) { + errln("Number of era including tentative eras is fewer than one without tentative eras in calendar: " + + calId); + } + + Calendar cal = Calendar.getInstance(new ULocale("en")); + int currentIdx = rules1.getCurrentEraIndex(); + int currentYear = cal.get(Calendar.YEAR); + int idx = rules1.getEraIndex(currentYear, cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DATE)); + if (idx != currentIdx) { + errln("Current era index:" + currentIdx + " is different from era index of now:" + idx + + " in calendar:" + calId); + } + + int eraStartYear = rules1.getStartYear(currentIdx); + if (currentYear < eraStartYear) { + errln("Current era's start year is after the current year in calendar:" + calId); + } + } + } + + @Test + public void testJapanese() { + EraRules rules = EraRules.getInstance(CalType.JAPANESE, true); + // Rules should have an era after Heisei + int numRules = rules.getNumberOfEras(); + if (numRules <= JapaneseCalendar.HEISEI) { + errln("Era after Heisei is not available."); + } + int postHeiseiStartYear = rules.getStartYear(JapaneseCalendar.HEISEI + 1); + if (postHeiseiStartYear != 2019) { + errln("Era after Heisei should start in 2019, but got " + postHeiseiStartYear); + } + } +}