mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-14 17:24:01 +00:00
ICU-12973 CLDR Japanese Era data and tentative Japanese new era support (#111)
* Updated era data format in supplementalData. * Include tentative era names in data. Implemented Japanese era loaded from CLDR data in ICU4J. * ICU4C implementation, ICU4C refactoring. WIP. * VS project updates and some bug fixes Also added API comments. * Review feedback and bug fixes - NULL to nullptr - use of LocalUResourceBundlePointer - TYPO "name" to "named" - env var checking stricmp() == 0 * API comment correction based on feedback * Duplicate the comment in ucal.h to calendar.h * Fixed spelling errors in API comment
This commit is contained in:
parent
ddf8ff2b7d
commit
45cdda61a6
52 changed files with 2089 additions and 1247 deletions
|
@ -1635,6 +1635,7 @@ ar{
|
|||
"تيشو",
|
||||
"شووا",
|
||||
"هيسي",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -1035,6 +1035,7 @@ bs_Cyrl{
|
|||
"Таишо",
|
||||
"Шова",
|
||||
"Хаисеи",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -1601,6 +1601,7 @@ de{
|
|||
"Taishō",
|
||||
"Shōwa",
|
||||
"Heisei",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1837,6 +1837,7 @@ he{
|
|||
"Taishō",
|
||||
"Shōwa",
|
||||
"Heisei",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1239,6 +1239,7 @@ hi{
|
|||
"ताईशो",
|
||||
"शोवा",
|
||||
"हेईसेई",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1577,6 +1577,7 @@ hr{
|
|||
"Taishō",
|
||||
"Shōwa",
|
||||
"Heisei",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2217,6 +2217,7 @@ id{
|
|||
"Taishō",
|
||||
"Shōwa",
|
||||
"Heisei",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2464,6 +2464,7 @@ ja{
|
|||
"大正",
|
||||
"昭和",
|
||||
"平成",
|
||||
"QQ",
|
||||
}
|
||||
narrow{
|
||||
"大化",
|
||||
|
@ -2702,6 +2703,7 @@ ja{
|
|||
"T",
|
||||
"S",
|
||||
"H",
|
||||
"Q",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1909,6 +1909,7 @@ ko{
|
|||
"다이쇼",
|
||||
"쇼와",
|
||||
"헤이세이",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1845,6 +1845,7 @@ lo{
|
|||
"ໄຕໂຊ",
|
||||
"ໂຊວາ",
|
||||
"ຮີຊີ",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2466,6 +2466,7 @@ lt{
|
|||
"Taišo",
|
||||
"Šova",
|
||||
"Heisei",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"}
|
||||
}
|
||||
|
|
|
@ -1837,6 +1837,7 @@ ru{
|
|||
"Эпоха Тайсьо",
|
||||
"Сьова",
|
||||
"Эпоха Хэйсэй",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1256,6 +1256,7 @@ sr{
|
|||
"Таишо",
|
||||
"Шова",
|
||||
"Хаисеи",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1257,6 +1257,7 @@ sr_Latn{
|
|||
"Taišo",
|
||||
"Šova",
|
||||
"Haisei",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1630,6 +1630,7 @@ sv{
|
|||
"Taishō",
|
||||
"Shōwa",
|
||||
"Heisei",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2332,6 +2332,7 @@ th{
|
|||
"ทะอิโช",
|
||||
"โชวะ",
|
||||
"เฮเซ",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2394,6 +2394,7 @@ yue{
|
|||
"大正",
|
||||
"昭和",
|
||||
"平成",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2392,6 +2392,7 @@ yue_Hans{
|
|||
"大正",
|
||||
"昭和",
|
||||
"平成",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5737,6 +5737,7 @@ zh_Hant{
|
|||
"大正",
|
||||
"昭和",
|
||||
"平成",
|
||||
"Qqqq",
|
||||
}
|
||||
narrow{
|
||||
"大化",
|
||||
|
@ -5975,6 +5976,7 @@ zh_Hant{
|
|||
"大正",
|
||||
"昭和",
|
||||
"平成",
|
||||
"Q",
|
||||
}
|
||||
wide{
|
||||
"大化",
|
||||
|
@ -6213,6 +6215,7 @@ zh_Hant{
|
|||
"大正",
|
||||
"昭和",
|
||||
"平成",
|
||||
"Qqqq",
|
||||
}
|
||||
}
|
||||
intervalFormats{
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
|
313
icu4c/source/i18n/erarules.cpp
Normal file
313
icu4c/source/i18n/erarules.cpp
Normal file
|
@ -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 <stdlib.h>
|
||||
#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 */
|
||||
|
||||
|
80
icu4c/source/i18n/erarules.h
Normal file
80
icu4c/source/i18n/erarules.h
Normal file
|
@ -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_ */
|
|
@ -160,6 +160,7 @@
|
|||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="erarules.cpp" />
|
||||
<ClCompile Include="region.cpp" />
|
||||
<ClCompile Include="uregion.cpp" />
|
||||
<ClCompile Include="alphaindex.cpp" />
|
||||
|
@ -410,6 +411,7 @@
|
|||
<ClInclude Include="collationtailoring.h" />
|
||||
<ClInclude Include="collationweights.h" />
|
||||
<ClInclude Include="dayperiodrules.h" />
|
||||
<ClInclude Include="erarules.h" />
|
||||
<ClInclude Include="numsys_impl.h" />
|
||||
<ClInclude Include="region_impl.h" />
|
||||
<ClInclude Include="selfmtimpl.h" />
|
||||
|
|
|
@ -627,6 +627,9 @@
|
|||
<ClCompile Include="dayperiodrules.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="erarules.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="bocsu.cpp">
|
||||
|
@ -1184,10 +1187,13 @@
|
|||
<ClInclude Include="utf8collationiterator.h">
|
||||
<Filter>collation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="erarules.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="i18n.rc">
|
||||
<Filter>misc</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -87,7 +87,7 @@
|
|||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<ResourceCompile>
|
||||
<Culture>0x0409</Culture>
|
||||
<AdditionalIncludeDirectories>../common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
|
@ -267,6 +267,7 @@
|
|||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="erarules.cpp" />
|
||||
<ClCompile Include="region.cpp" />
|
||||
<ClCompile Include="uregion.cpp" />
|
||||
<ClCompile Include="alphaindex.cpp" />
|
||||
|
@ -515,6 +516,7 @@
|
|||
<ClInclude Include="collationtailoring.h" />
|
||||
<ClInclude Include="collationweights.h" />
|
||||
<ClInclude Include="dayperiodrules.h" />
|
||||
<ClInclude Include="erarules.h" />
|
||||
<ClInclude Include="numsys_impl.h" />
|
||||
<ClInclude Include="region_impl.h" />
|
||||
<ClInclude Include="selfmtimpl.h" />
|
||||
|
@ -662,4 +664,4 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -16,286 +16,80 @@
|
|||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if U_PLATFORM_HAS_WINUWP_API == 0
|
||||
#include <stdlib.h> // 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"
|
||||
#include "ucln_in.h"
|
||||
#include "cstring.h"
|
||||
|
||||
//#define U_DEBUG_JCAL
|
||||
static icu::EraRules * gJapaneseEraRules = nullptr;
|
||||
static icu::UInitOnce gJapaneseEraRulesInitOnce = U_INITONCE_INITIALIZER;
|
||||
static int32_t gCurrentEra = 0;
|
||||
|
||||
#ifdef U_DEBUG_JCAL
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
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 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 maxYear = nextEraYear - kEraInfo[era].year + 1; // 1-base
|
||||
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--;
|
||||
|
|
|
@ -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 <code>UCAL_ERA</code> field, are provided
|
||||
* in this class. Note that the <em>number</em> 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.
|
||||
* <p>
|
||||
* 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):
|
||||
* <ol>
|
||||
* <li>Environment variable <code>ICU_ENABLE_TENTATIVE_ERA=true</code>.</li>
|
||||
* </nl>
|
||||
* @internal
|
||||
*/
|
||||
class JapaneseCalendar : public GregorianCalendar {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* 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:
|
||||
* <ul>
|
||||
* <li>Environment variable <code>ICU_ENABLE_TENTATIVE_ERA=true</code>.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
class U_I18N_API Calendar : public UObject {
|
||||
|
|
|
@ -139,6 +139,19 @@
|
|||
* For example, subtracting 5 days from the date <code>September 12, 1996</code>
|
||||
* results in <code>September 7, 1996</code>.
|
||||
*
|
||||
* <p>
|
||||
* 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:
|
||||
* <ul>
|
||||
* <li>Environment variable <code>ICU_ENABLE_TENTATIVE_ERA=true</code>.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
130
icu4c/source/test/intltest/erarulestest.cpp
Normal file
130
icu4c/source/test/intltest/erarulestest.cpp
Normal file
|
@ -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<EraRules> rules1(EraRules::createInstance(calId, FALSE, status));
|
||||
if (U_FAILURE(status)) {
|
||||
errln(UnicodeString("Era rules for ") + calId + " is not available.");
|
||||
continue;
|
||||
}
|
||||
|
||||
LocalPointer<EraRules> 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<Calendar> 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<EraRules> 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 */
|
||||
|
23
icu4c/source/test/intltest/erarulestest.h
Normal file
23
icu4c/source/test/intltest/erarulestest.h
Normal file
|
@ -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_ */
|
|
@ -2,7 +2,6 @@
|
|||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- The following import will include the 'default' configuration options for VS projects. -->
|
||||
<Import Project="..\..\allinone\Build.Windows.ProjectConfiguration.props" />
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{73632960-B3A6-464D-83A3-4B43365F19B8}</ProjectGuid>
|
||||
<RootNamespace>intltest</RootNamespace>
|
||||
|
@ -160,6 +159,7 @@
|
|||
<ClCompile Include="colldata.cpp">
|
||||
<DisableLanguageExtensions>false</DisableLanguageExtensions>
|
||||
</ClCompile>
|
||||
<ClCompile Include="erarulestest.cpp" />
|
||||
<ClCompile Include="numfmtspectest.cpp" />
|
||||
<ClCompile Include="regiontst.cpp" />
|
||||
<ClCompile Include="ucharstrietest.cpp" />
|
||||
|
@ -365,6 +365,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="colldata.h" />
|
||||
<ClInclude Include="erarulestest.h" />
|
||||
<ClInclude Include="itrbbi.h" />
|
||||
<ClInclude Include="rbbiapts.h" />
|
||||
<ClInclude Include="rbbitst.h" />
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="rbbimonkeytest.cpp">
|
||||
<Filter>break iteration</Filter>
|
||||
</ClCompile>
|
||||
</ClCompile>
|
||||
<ClCompile Include="itspoof.cpp">
|
||||
<Filter>spoof detection</Filter>
|
||||
</ClCompile>
|
||||
|
@ -532,6 +532,9 @@
|
|||
<ClCompile Include="ucharstrietest.cpp">
|
||||
<Filter>data & memory</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="erarulestest.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="itrbbi.h">
|
||||
|
@ -915,5 +918,8 @@
|
|||
<ClInclude Include="listformattertest.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="erarulestest.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -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) {
|
||||
|
|
3
icu4c/source/test/testdata/structLocale.txt
vendored
3
icu4c/source/test/testdata/structLocale.txt
vendored
|
@ -25805,6 +25805,7 @@ structLocale:table(nofallback){
|
|||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
}
|
||||
wide{
|
||||
"",
|
||||
|
@ -26043,6 +26044,7 @@ structLocale:table(nofallback){
|
|||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
}
|
||||
narrow{
|
||||
"",
|
||||
|
@ -26281,6 +26283,7 @@ structLocale:table(nofallback){
|
|||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
}
|
||||
}
|
||||
intervalFormats{
|
||||
|
|
42
icu4j/main/classes/core/src/com/ibm/icu/impl/CalType.java
Normal file
42
icu4j/main/classes/core/src/com/ibm/icu/impl/CalType.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
309
icu4j/main/classes/core/src/com/ibm/icu/impl/EraRules.java
Normal file
309
icu4j/main/classes/core/src/com/ibm/icu/impl/EraRules.java
Normal file
|
@ -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;
|
||||
|
||||
/**
|
||||
* <code>EraRules</code> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ import java.util.Date;
|
|||
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 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
|||
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 abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
|||
String prefRegion = ULocale.getRegionForSupplementalData(locale, true);
|
||||
|
||||
// Read preferred calendar values from supplementalData calendarPreferences
|
||||
ArrayList<String> values = new ArrayList<String>();
|
||||
ArrayList<String> values = new ArrayList<>();
|
||||
|
||||
UResourceBundle rb = UResourceBundle.getBundleInstance(
|
||||
ICUData.ICU_BASE_NAME,
|
||||
|
@ -1965,8 +1936,8 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
|||
}
|
||||
// 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 abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
|||
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 abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
|||
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 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
|||
|
||||
// date format pattern cache
|
||||
private static final ICUCache<String, PatternData> PATTERN_CACHE =
|
||||
new SimpleCache<String, PatternData>();
|
||||
new SimpleCache<>();
|
||||
// final fallback patterns
|
||||
private static final String[] DEFAULT_PATTERNS = {
|
||||
"HH:mm:ss z",
|
||||
|
|
|
@ -10,6 +10,9 @@ package com.ibm.icu.util;
|
|||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.ibm.icu.impl.CalType;
|
||||
import com.ibm.icu.impl.EraRules;
|
||||
|
||||
/**
|
||||
* <code>JapaneseCalendar</code> is a subclass of <code>GregorianCalendar</code>
|
||||
* that numbers years and eras based on the reigns of the Japanese emperors.
|
||||
|
@ -24,19 +27,34 @@ import java.util.Locale;
|
|||
* 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.
|
||||
* <p>
|
||||
* 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 <code>ERA</code> field, are provided
|
||||
* in this class. Note that the <em>number</em> 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.
|
||||
* <p>
|
||||
* 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):
|
||||
* <ol>
|
||||
* <li>Java system property <code>ICU_ENABLE_TENTATIVE_ERA=true</code>.</li>
|
||||
* <li>Environment variable <code>ICU_ENABLE_TENTATIVE_ERA=true</code>.</li>
|
||||
* <li>Java system property <code>jdk.calendar.japanese.supplemental.era=xxx</code>.
|
||||
* (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.)</li>
|
||||
* </nl>
|
||||
* <p>
|
||||
* This class should not be subclassed.</p>
|
||||
* <p>
|
||||
* JapaneseCalendar usually should be instantiated using
|
||||
* JapaneseCalendar usually should be instantiated using
|
||||
* {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a <code>ULocale</code>
|
||||
* with the tag <code>"@calendar=japanese"</code>.</p>
|
||||
*
|
||||
|
@ -210,9 +228,44 @@ public class JapaneseCalendar extends GregorianCalendar {
|
|||
// 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 @@ public class JapaneseCalendar extends GregorianCalendar {
|
|||
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 @@ public class JapaneseCalendar extends GregorianCalendar {
|
|||
* @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.
|
||||
@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
|
||||
if(extendedYear == ERAS[era*3]) {
|
||||
return ERAS[(era*3)+1] // month..
|
||||
-1; // return 0-based month
|
||||
} else {
|
||||
return super.getDefaultMonthInYear(extendedYear);
|
||||
}
|
||||
// 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 @@ public class JapaneseCalendar extends GregorianCalendar {
|
|||
* @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);
|
||||
|
||||
return super.getDefaultDayInMonth(extendedYear, month);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 @@ public class JapaneseCalendar extends GregorianCalendar {
|
|||
/**
|
||||
* @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 SHOWA = CURRENT_ERA - 1;
|
||||
static public final int TAISHO = 233;
|
||||
|
||||
/**
|
||||
* Constant for the era starting on Jan. 7, 1989 AD.
|
||||
* @stable ICU 2.8
|
||||
/**
|
||||
* Constant for the era starting on Dec. 25, 1926 AD.
|
||||
* @stable ICU 2.8
|
||||
*/
|
||||
static public final int HEISEI = CURRENT_ERA;
|
||||
static public final int SHOWA = 234;
|
||||
|
||||
/**
|
||||
* Constant for the era starting on Jan. 7, 1989 AD.
|
||||
* @stable ICU 2.8
|
||||
*/
|
||||
static public final int HEISEI = 235;
|
||||
|
||||
/**
|
||||
* Override GregorianCalendar. We should really handle YEAR_WOY and
|
||||
|
@ -604,6 +383,7 @@ public class JapaneseCalendar extends GregorianCalendar {
|
|||
* not critical.
|
||||
* @stable ICU 2.8
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("fallthrough")
|
||||
protected int handleGetLimit(int field, int limitType) {
|
||||
switch (field) {
|
||||
|
@ -621,7 +401,7 @@ public class JapaneseCalendar extends GregorianCalendar {
|
|||
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 @@ public class JapaneseCalendar extends GregorianCalendar {
|
|||
* {@inheritDoc}
|
||||
* @stable ICU 3.8
|
||||
*/
|
||||
@Override
|
||||
public String getType() {
|
||||
return "japanese";
|
||||
}
|
||||
|
@ -643,6 +424,7 @@ public class JapaneseCalendar extends GregorianCalendar {
|
|||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean haveDefaultCentury() {
|
||||
return false;
|
||||
|
@ -652,6 +434,7 @@ public class JapaneseCalendar extends GregorianCalendar {
|
|||
* {@inheritDoc}
|
||||
* @stable ICU 4.0
|
||||
*/
|
||||
@Override
|
||||
public int getActualMaximum(int field) {
|
||||
if (field == YEAR) {
|
||||
int era = get(Calendar.ERA);
|
||||
|
@ -659,11 +442,12 @@ public class JapaneseCalendar extends GregorianCalendar {
|
|||
// 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--;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:36d0ec0c543d1dccafcc6985a7c18285b255afb98bc2bdb16a867a22600bfddb
|
||||
size 12487287
|
||||
oid sha256:92dc0a5ca71ac54537a6c7c42c2f80ccbd3298d9ebf69c3d732199230d5100c6
|
||||
size 12487439
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:469f76e391dced8e9ae4a9543513dddd6d4d2026ad6cbc0ab79d9553da803e6a
|
||||
oid sha256:d2308b3498ce1c2b869b60b5a0f7cea6f08aff2fac046ae260e10688c15c60b2
|
||||
size 92857
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue