1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
6 #include "unicode/utypes.h"
8 #if !UCONFIG_NO_FORMATTING
11 #include "unicode/ucal.h"
12 #include "unicode/ures.h"
13 #include "unicode/ustring.h"
22 static const int32_t MAX_ENCODED_START_YEAR
= 32767;
23 static const int32_t MIN_ENCODED_START_YEAR
= -32768;
24 static const int32_t MIN_ENCODED_START
= -2147483391; // encodeDate(MIN_ENCODED_START_YEAR, 1, 1, ...);
26 static const int32_t YEAR_MASK
= 0xFFFF0000;
27 static const int32_t MONTH_MASK
= 0x0000FF00;
28 static const int32_t DAY_MASK
= 0x000000FF;
30 static const int32_t MAX_INT32
= 0x7FFFFFFF;
31 static const int32_t MIN_INT32
= 0xFFFFFFFF;
33 static const UChar VAL_FALSE
[] = {0x66, 0x61, 0x6c, 0x73, 0x65}; // "false"
34 static const UChar VAL_FALSE_LEN
= 5;
36 static UBool
isSet(int startDate
) {
37 return startDate
!= 0;
40 static UBool
isValidRuleStartDate(int32_t year
, int32_t month
, int32_t day
) {
41 return year
>= MIN_ENCODED_START_YEAR
&& year
<= MAX_ENCODED_START_YEAR
42 && month
>= 1 && month
<= 12 && day
>=1 && day
<= 31;
46 * Encode year/month/date to a single integer.
47 * year is high 16 bits (-32768 to 32767), month is
48 * next 8 bits and day of month is last 8 bits.
51 * @param month month (1-base)
52 * @param day day of month
53 * @return an encoded date.
55 static int32_t encodeDate(int32_t year
, int32_t month
, int32_t day
) {
56 return year
<< 16 | month
<< 8 | day
;
59 static void decodeDate(int32_t encodedDate
, int32_t (&fields
)[3]) {
60 if (encodedDate
== MIN_ENCODED_START
) {
61 fields
[0] = MIN_INT32
;
65 fields
[0] = (encodedDate
& YEAR_MASK
) >> 16;
66 fields
[1] = (encodedDate
& MONTH_MASK
) >> 8;
67 fields
[2] = encodedDate
& DAY_MASK
;
72 * Compare an encoded date with another date specified by year/month/day.
73 * @param encoded An encoded date
74 * @param year Year of another date
75 * @param month Month of another date
76 * @param day Day of another date
77 * @return -1 when encoded date is earlier, 0 when two dates are same,
78 * and 1 when encoded date is later.
80 static int32_t compareEncodedDateWithYMD(int encoded
, int year
, int month
, int day
) {
81 if (year
< MIN_ENCODED_START_YEAR
) {
82 if (encoded
== MIN_ENCODED_START
) {
83 if (year
> MIN_INT32
|| month
> 1 || day
> 1) {
90 } else if (year
> MAX_ENCODED_START_YEAR
) {
93 int tmp
= encodeDate(year
, month
, day
);
96 } else if (encoded
== tmp
) {
104 EraRules::EraRules(LocalMemory
<int32_t>& eraStartDates
, int32_t numEras
)
106 startDates
= std::move(eraStartDates
);
110 EraRules::~EraRules() {
113 EraRules
* EraRules::createInstance(const char *calType
, UBool includeTentativeEra
, UErrorCode
& status
) {
114 if(U_FAILURE(status
)) {
117 LocalUResourceBundlePointer
rb(ures_openDirect(nullptr, "supplementalData", &status
));
118 ures_getByKey(rb
.getAlias(), "calendarData", rb
.getAlias(), &status
);
119 ures_getByKey(rb
.getAlias(), calType
, rb
.getAlias(), &status
);
120 ures_getByKey(rb
.getAlias(), "eras", rb
.getAlias(), &status
);
122 if (U_FAILURE(status
)) {
126 int32_t numEras
= ures_getSize(rb
.getAlias());
127 int32_t firstTentativeIdx
= MAX_INT32
;
129 LocalMemory
<int32_t> startDates(static_cast<int32_t *>(uprv_malloc(numEras
* sizeof(int32_t))));
130 if (startDates
.isNull()) {
131 status
= U_MEMORY_ALLOCATION_ERROR
;
134 uprv_memset(startDates
.getAlias(), 0 , numEras
* sizeof(int32_t));
136 while (ures_hasNext(rb
.getAlias())) {
137 LocalUResourceBundlePointer
eraRuleRes(ures_getNextResource(rb
.getAlias(), nullptr, &status
));
138 if (U_FAILURE(status
)) {
141 const char *eraIdxStr
= ures_getKey(eraRuleRes
.getAlias());
143 int32_t eraIdx
= (int32_t)strtol(eraIdxStr
, &endp
, 10);
144 if ((size_t)(endp
- eraIdxStr
) != uprv_strlen(eraIdxStr
)) {
145 status
= U_INVALID_FORMAT_ERROR
;
148 if (eraIdx
< 0 || eraIdx
>= numEras
) {
149 status
= U_INVALID_FORMAT_ERROR
;
152 if (isSet(startDates
[eraIdx
])) {
153 // start date of the index was already set
154 status
= U_INVALID_FORMAT_ERROR
;
158 UBool hasName
= TRUE
;
161 while (ures_hasNext(eraRuleRes
.getAlias())) {
162 LocalUResourceBundlePointer
res(ures_getNextResource(eraRuleRes
.getAlias(), nullptr, &status
));
163 if (U_FAILURE(status
)) {
166 const char *key
= ures_getKey(res
.getAlias());
167 if (uprv_strcmp(key
, "start") == 0) {
168 const int32_t *fields
= ures_getIntVector(res
.getAlias(), &len
, &status
);
169 if (U_FAILURE(status
)) {
172 if (len
!= 3 || !isValidRuleStartDate(fields
[0], fields
[1], fields
[2])) {
173 status
= U_INVALID_FORMAT_ERROR
;
176 startDates
[eraIdx
] = encodeDate(fields
[0], fields
[1], fields
[2]);
177 } else if (uprv_strcmp(key
, "named") == 0) {
178 const UChar
*val
= ures_getString(res
.getAlias(), &len
, &status
);
179 if (u_strncmp(val
, VAL_FALSE
, VAL_FALSE_LEN
) == 0) {
182 } else if (uprv_strcmp(key
, "end") == 0) {
187 if (isSet(startDates
[eraIdx
])) {
189 // This implementation assumes either start or end is available, not both.
190 // For now, just ignore the end rule.
195 // This implementation does not support end only rule for eras other than
197 status
= U_INVALID_FORMAT_ERROR
;
200 U_ASSERT(eraIdx
== 0);
201 startDates
[eraIdx
] = MIN_ENCODED_START
;
203 status
= U_INVALID_FORMAT_ERROR
;
209 if (eraIdx
>= firstTentativeIdx
) {
210 status
= U_INVALID_FORMAT_ERROR
;
214 if (eraIdx
< firstTentativeIdx
) {
215 firstTentativeIdx
= eraIdx
;
221 if (firstTentativeIdx
< MAX_INT32
&& !includeTentativeEra
) {
222 result
= new EraRules(startDates
, firstTentativeIdx
);
224 result
= new EraRules(startDates
, numEras
);
227 if (result
== nullptr) {
228 status
= U_MEMORY_ALLOCATION_ERROR
;
233 void EraRules::getStartDate(int32_t eraIdx
, int32_t (&fields
)[3], UErrorCode
& status
) const {
234 if(U_FAILURE(status
)) {
237 if (eraIdx
< 0 || eraIdx
>= numEras
) {
238 status
= U_ILLEGAL_ARGUMENT_ERROR
;
241 decodeDate(startDates
[eraIdx
], fields
);
244 int32_t EraRules::getStartYear(int32_t eraIdx
, UErrorCode
& status
) const {
245 int year
= MAX_INT32
/2; // bogus value (/2 so don't overflow elsewhere <rdar://problem/49714633>)
246 if(U_FAILURE(status
)) {
249 if (eraIdx
< 0 || eraIdx
>= numEras
) {
250 status
= U_ILLEGAL_ARGUMENT_ERROR
;
254 decodeDate(startDates
[eraIdx
], fields
);
260 int32_t EraRules::getEraIndex(int32_t year
, int32_t month
, int32_t day
, UErrorCode
& status
) const {
261 if(U_FAILURE(status
)) {
265 if (month
< 1 || month
> 12 || day
< 1 || day
> 31) {
266 status
= U_ILLEGAL_ARGUMENT_ERROR
;
269 int32_t high
= numEras
; // last index + 1
272 // Short circuit for recent years. Most modern computations will
273 // occur in the last few eras.
274 if (compareEncodedDateWithYMD(startDates
[getCurrentEraIndex()], year
, month
, day
) <= 0) {
275 low
= getCurrentEraIndex();
281 while (low
< high
- 1) {
282 int i
= (low
+ high
) / 2;
283 if (compareEncodedDateWithYMD(startDates
[i
], year
, month
, day
) <= 0) {
292 void EraRules::initCurrentEra() {
293 UDate now
= ucal_getNow();
294 int year
, month0
, dom
, dow
, doy
, mid
;
295 Grego::timeToFields(now
, year
, month0
, dom
, dow
, doy
, mid
);
296 int currentEncodedDate
= encodeDate(year
, month0
+ 1 /* changes to 1-base */, dom
);
297 int eraIdx
= numEras
- 1;
299 if (currentEncodedDate
>= startDates
[eraIdx
]) {
304 // Note: current era could be before the first era.
305 // In this case, this implementation returns the first era index (0).
306 currentEra
= eraIdx
;}
309 #endif /* #if !UCONFIG_NO_FORMATTING */