1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5#include <assert.h>
6#include <string.h>
7#include <vector>
8
9#include "icushim.h"
10#include "locale.hpp"
11#include "holders.h"
12#include "errors.h"
13
14#define GREGORIAN_NAME "gregorian"
15#define JAPANESE_NAME "japanese"
16#define BUDDHIST_NAME "buddhist"
17#define HEBREW_NAME "hebrew"
18#define DANGI_NAME "dangi"
19#define PERSIAN_NAME "persian"
20#define ISLAMIC_NAME "islamic"
21#define ISLAMIC_UMALQURA_NAME "islamic-umalqura"
22#define ROC_NAME "roc"
23
24#define JAPANESE_LOCALE_AND_CALENDAR "ja_JP@calendar=japanese"
25
26const UChar UDAT_MONTH_DAY_UCHAR[] = {'M', 'M', 'M', 'M', 'd', '\0'};
27const UChar UDAT_YEAR_NUM_MONTH_DAY_UCHAR[] = {'y', 'M', 'd', '\0'};
28const UChar UDAT_YEAR_MONTH_UCHAR[] = {'y', 'M', 'M', 'M', 'M', '\0'};
29
30/*
31* These values should be kept in sync with System.Globalization.CalendarId
32*/
33enum CalendarId : int16_t
34{
35 UNINITIALIZED_VALUE = 0,
36 GREGORIAN = 1, // Gregorian (localized) calendar
37 GREGORIAN_US = 2, // Gregorian (U.S.) calendar
38 JAPAN = 3, // Japanese Emperor Era calendar
39 /* SSS_WARNINGS_OFF */
40 TAIWAN = 4, // Taiwan Era calendar /* SSS_WARNINGS_ON */
41 KOREA = 5, // Korean Tangun Era calendar
42 HIJRI = 6, // Hijri (Arabic Lunar) calendar
43 THAI = 7, // Thai calendar
44 HEBREW = 8, // Hebrew (Lunar) calendar
45 GREGORIAN_ME_FRENCH = 9, // Gregorian Middle East French calendar
46 GREGORIAN_ARABIC = 10, // Gregorian Arabic calendar
47 GREGORIAN_XLIT_ENGLISH = 11, // Gregorian Transliterated English calendar
48 GREGORIAN_XLIT_FRENCH = 12,
49 // Note that all calendars after this point are MANAGED ONLY for now.
50 JULIAN = 13,
51 JAPANESELUNISOLAR = 14,
52 CHINESELUNISOLAR = 15,
53 SAKA = 16, // reserved to match Office but not implemented in our code
54 LUNAR_ETO_CHN = 17, // reserved to match Office but not implemented in our code
55 LUNAR_ETO_KOR = 18, // reserved to match Office but not implemented in our code
56 LUNAR_ETO_ROKUYOU = 19, // reserved to match Office but not implemented in our code
57 KOREANLUNISOLAR = 20,
58 TAIWANLUNISOLAR = 21,
59 PERSIAN = 22,
60 UMALQURA = 23,
61 LAST_CALENDAR = 23 // Last calendar ID
62};
63
64/*
65* These values should be kept in sync with System.Globalization.CalendarDataType
66*/
67enum CalendarDataType : int32_t
68{
69 Uninitialized = 0,
70 NativeName = 1,
71 MonthDay = 2,
72 ShortDates = 3,
73 LongDates = 4,
74 YearMonths = 5,
75 DayNames = 6,
76 AbbrevDayNames = 7,
77 MonthNames = 8,
78 AbbrevMonthNames = 9,
79 SuperShortDayNames = 10,
80 MonthGenitiveNames = 11,
81 AbbrevMonthGenitiveNames = 12,
82 EraNames = 13,
83 AbbrevEraNames = 14,
84};
85
86// the function pointer definition for the callback used in EnumCalendarInfo
87typedef void (*EnumCalendarInfoCallback)(const UChar*, const void*);
88
89/*
90Function:
91GetCalendarName
92
93Gets the associated ICU calendar name for the CalendarId.
94*/
95const char* GetCalendarName(CalendarId calendarId)
96{
97 switch (calendarId)
98 {
99 case JAPAN:
100 return JAPANESE_NAME;
101 case THAI:
102 return BUDDHIST_NAME;
103 case HEBREW:
104 return HEBREW_NAME;
105 case KOREA:
106 return DANGI_NAME;
107 case PERSIAN:
108 return PERSIAN_NAME;
109 case HIJRI:
110 return ISLAMIC_NAME;
111 case UMALQURA:
112 return ISLAMIC_UMALQURA_NAME;
113 case TAIWAN:
114 return ROC_NAME;
115 case GREGORIAN:
116 case GREGORIAN_US:
117 case GREGORIAN_ARABIC:
118 case GREGORIAN_ME_FRENCH:
119 case GREGORIAN_XLIT_ENGLISH:
120 case GREGORIAN_XLIT_FRENCH:
121 case JULIAN:
122 case LUNAR_ETO_CHN:
123 case LUNAR_ETO_KOR:
124 case LUNAR_ETO_ROKUYOU:
125 case SAKA:
126 // don't support the lunisolar calendars until we have a solid understanding
127 // of how they map to the ICU/CLDR calendars
128 case CHINESELUNISOLAR:
129 case KOREANLUNISOLAR:
130 case JAPANESELUNISOLAR:
131 case TAIWANLUNISOLAR:
132 default:
133 return GREGORIAN_NAME;
134 }
135}
136
137/*
138Function:
139GetCalendarId
140
141Gets the associated CalendarId for the ICU calendar name.
142*/
143CalendarId GetCalendarId(const char* calendarName)
144{
145 if (strcasecmp(calendarName, GREGORIAN_NAME) == 0)
146 // TODO: what about the other gregorian types?
147 return GREGORIAN;
148 else if (strcasecmp(calendarName, JAPANESE_NAME) == 0)
149 return JAPAN;
150 else if (strcasecmp(calendarName, BUDDHIST_NAME) == 0)
151 return THAI;
152 else if (strcasecmp(calendarName, HEBREW_NAME) == 0)
153 return HEBREW;
154 else if (strcasecmp(calendarName, DANGI_NAME) == 0)
155 return KOREA;
156 else if (strcasecmp(calendarName, PERSIAN_NAME) == 0)
157 return PERSIAN;
158 else if (strcasecmp(calendarName, ISLAMIC_NAME) == 0)
159 return HIJRI;
160 else if (strcasecmp(calendarName, ISLAMIC_UMALQURA_NAME) == 0)
161 return UMALQURA;
162 else if (strcasecmp(calendarName, ROC_NAME) == 0)
163 return TAIWAN;
164 else
165 return UNINITIALIZED_VALUE;
166}
167
168/*
169Function:
170GetCalendars
171
172Returns the list of CalendarIds that are available for the specified locale.
173*/
174extern "C" int32_t GlobalizationNative_GetCalendars(
175 const UChar* localeName, CalendarId* calendars, int32_t calendarsCapacity)
176{
177 UErrorCode err = U_ZERO_ERROR;
178 char locale[ULOC_FULLNAME_CAPACITY];
179 GetLocale(localeName, locale, ULOC_FULLNAME_CAPACITY, false, &err);
180
181 if (U_FAILURE(err))
182 return 0;
183
184 UEnumeration* pEnum = ucal_getKeywordValuesForLocale("calendar", locale, TRUE, &err);
185 UEnumerationHolder enumHolder(pEnum, err);
186
187 if (U_FAILURE(err))
188 return 0;
189
190 int stringEnumeratorCount = uenum_count(pEnum, &err);
191 if (U_FAILURE(err))
192 return 0;
193
194 int calendarsReturned = 0;
195 for (int i = 0; i < stringEnumeratorCount && calendarsReturned < calendarsCapacity; i++)
196 {
197 int32_t calendarNameLength = 0;
198 const char* calendarName = uenum_next(pEnum, &calendarNameLength, &err);
199 if (U_SUCCESS(err))
200 {
201 CalendarId calendarId = GetCalendarId(calendarName);
202 if (calendarId != UNINITIALIZED_VALUE)
203 {
204 calendars[calendarsReturned] = calendarId;
205 calendarsReturned++;
206 }
207 }
208 }
209
210 return calendarsReturned;
211}
212
213/*
214Function:
215GetMonthDayPattern
216
217Gets the Month-Day DateTime pattern for the specified locale.
218*/
219ResultCode GetMonthDayPattern(const char* locale, UChar* sMonthDay, int32_t stringCapacity)
220{
221 UErrorCode err = U_ZERO_ERROR;
222 UDateTimePatternGenerator* pGenerator = udatpg_open(locale, &err);
223 UDateTimePatternGeneratorHolder generatorHolder(pGenerator, err);
224
225 if (U_FAILURE(err))
226 return GetResultCode(err);
227
228 udatpg_getBestPattern(pGenerator, UDAT_MONTH_DAY_UCHAR, -1, sMonthDay, stringCapacity, &err);
229
230 return GetResultCode(err);
231}
232
233/*
234Function:
235GetNativeCalendarName
236
237Gets the native calendar name.
238*/
239ResultCode GetNativeCalendarName(const char* locale, CalendarId calendarId, UChar* nativeName, int32_t stringCapacity)
240{
241 UErrorCode err = U_ZERO_ERROR;
242 ULocaleDisplayNames* pDisplayNames = uldn_open(locale, ULDN_STANDARD_NAMES, &err);
243 ULocaleDisplayNamesHolder displayNamesHolder(pDisplayNames, err);
244
245 uldn_keyValueDisplayName(pDisplayNames, "calendar", GetCalendarName(calendarId), nativeName, stringCapacity, &err);
246
247 return GetResultCode(err);
248}
249
250/*
251Function:
252GetCalendarInfo
253
254Gets a single string of calendar information by filling the result parameter
255with the requested value.
256*/
257extern "C" ResultCode GlobalizationNative_GetCalendarInfo(
258 const UChar* localeName, CalendarId calendarId, CalendarDataType dataType, UChar* result, int32_t resultCapacity)
259{
260 UErrorCode err = U_ZERO_ERROR;
261 char locale[ULOC_FULLNAME_CAPACITY];
262 GetLocale(localeName, locale, ULOC_FULLNAME_CAPACITY, false, &err);
263
264 if (U_FAILURE(err))
265 return UnknownError;
266
267 switch (dataType)
268 {
269 case NativeName:
270 return GetNativeCalendarName(locale, calendarId, result, resultCapacity);
271 case MonthDay:
272 return GetMonthDayPattern(locale, result, resultCapacity);
273 default:
274 assert(false);
275 return UnknownError;
276 }
277}
278
279/*
280Function:
281InvokeCallbackForDatePattern
282
283Gets the ICU date pattern for the specified locale and EStyle and invokes the
284callback with the result.
285*/
286bool InvokeCallbackForDatePattern(const char* locale,
287 UDateFormatStyle style,
288 EnumCalendarInfoCallback callback,
289 const void* context)
290{
291 UErrorCode err = U_ZERO_ERROR;
292 UDateFormat* pFormat = udat_open(UDAT_NONE, style, locale, nullptr, 0, nullptr, 0, &err);
293 UDateFormatHolder formatHolder(pFormat, err);
294
295 if (U_FAILURE(err))
296 return false;
297
298 UErrorCode ignore = U_ZERO_ERROR;
299 int32_t patternLen = udat_toPattern(pFormat, false, nullptr, 0, &ignore);
300
301 std::vector<UChar> pattern(patternLen + 1, '\0');
302
303 udat_toPattern(pFormat, false, pattern.data(), patternLen + 1, &err);
304
305 if (U_SUCCESS(err))
306 {
307 callback(pattern.data(), context);
308 }
309
310 return U_SUCCESS(err);
311}
312
313/*
314Function:
315InvokeCallbackForDateTimePattern
316
317Gets the DateTime pattern for the specified skeleton and invokes the callback
318with the retrieved value.
319*/
320bool InvokeCallbackForDateTimePattern(const char* locale,
321 const UChar* patternSkeleton,
322 EnumCalendarInfoCallback callback,
323 const void* context)
324{
325 UErrorCode err = U_ZERO_ERROR;
326 UDateTimePatternGenerator* pGenerator = udatpg_open(locale, &err);
327 UDateTimePatternGeneratorHolder generatorHolder(pGenerator, err);
328
329 if (U_FAILURE(err))
330 return false;
331
332 UErrorCode ignore = U_ZERO_ERROR;
333 int32_t patternLen = udatpg_getBestPattern(pGenerator, patternSkeleton, -1, nullptr, 0, &ignore);
334
335 std::vector<UChar> bestPattern(patternLen + 1, '\0');
336
337 udatpg_getBestPattern(pGenerator, patternSkeleton, -1, bestPattern.data(), patternLen + 1, &err);
338
339 if (U_SUCCESS(err))
340 {
341 callback(bestPattern.data(), context);
342 }
343
344 return U_SUCCESS(err);
345}
346
347/*
348Function:
349EnumSymbols
350
351Enumerates all of the symbols of a type for a locale and calendar and invokes a callback
352for each value.
353*/
354bool EnumSymbols(const char* locale,
355 CalendarId calendarId,
356 UDateFormatSymbolType type,
357 int32_t startIndex,
358 EnumCalendarInfoCallback callback,
359 const void* context)
360{
361 UErrorCode err = U_ZERO_ERROR;
362 UDateFormat* pFormat = udat_open(UDAT_DEFAULT, UDAT_DEFAULT, locale, nullptr, 0, nullptr, 0, &err);
363 UDateFormatHolder formatHolder(pFormat, err);
364
365 if (U_FAILURE(err))
366 return false;
367
368 char localeWithCalendarName[ULOC_FULLNAME_CAPACITY];
369 strncpy(localeWithCalendarName, locale, ULOC_FULLNAME_CAPACITY);
370 uloc_setKeywordValue("calendar", GetCalendarName(calendarId), localeWithCalendarName, ULOC_FULLNAME_CAPACITY, &err);
371
372 if (U_FAILURE(err))
373 return false;
374
375 UCalendar* pCalendar = ucal_open(nullptr, 0, localeWithCalendarName, UCAL_DEFAULT, &err);
376 UCalendarHolder calendarHolder(pCalendar, err);
377
378 if (U_FAILURE(err))
379 return false;
380
381 udat_setCalendar(pFormat, pCalendar);
382
383 int32_t symbolCount = udat_countSymbols(pFormat, type);
384
385 for (int32_t i = startIndex; i < symbolCount; i++)
386 {
387 UErrorCode ignore = U_ZERO_ERROR;
388 int symbolLen = udat_getSymbols(pFormat, type, i, nullptr, 0, &ignore);
389
390 std::vector<UChar> symbolBuf(symbolLen + 1, '\0');
391
392 udat_getSymbols(pFormat, type, i, symbolBuf.data(), symbolBuf.size(), &err);
393
394 assert(U_SUCCESS(err));
395
396 if (U_FAILURE(err))
397 return false;
398
399 callback(symbolBuf.data(), context);
400 }
401
402 return true;
403}
404
405bool EnumUResourceBundle(const UResourceBundle* bundle, EnumCalendarInfoCallback callback, const void* context)
406{
407 int32_t eraNameCount = ures_getSize(bundle);
408
409 for (int i = 0; i < eraNameCount; i++)
410 {
411 UErrorCode status = U_ZERO_ERROR;
412 int32_t ignore; // We don't care about the length of the string as it is null terminated.
413 const UChar* eraName = ures_getStringByIndex(bundle, i, &ignore, &status);
414
415 if (U_SUCCESS(status))
416 {
417 callback(eraName, context);
418 }
419 }
420
421 return true;
422}
423
424/*
425Function:
426EnumAbbrevEraNames
427
428Enumerates all the abbreviated era names of the specified locale and calendar, invoking the
429callback function for each era name.
430*/
431bool EnumAbbrevEraNames(const char* locale,
432 CalendarId calendarId,
433 EnumCalendarInfoCallback callback,
434 const void* context)
435{
436 // The C-API for ICU provides no way to get at the abbreviated era names for a calendar (so we can't use EnumSymbols
437 // here). Instead we will try to walk the ICU resource tables directly and fall back to regular era names if can't
438 // find good data.
439 char localeNameBuf[ULOC_FULLNAME_CAPACITY];
440 char parentNameBuf[ULOC_FULLNAME_CAPACITY];
441
442 char* localeNamePtr = localeNameBuf;
443 char* parentNamePtr = parentNameBuf;
444
445 strncpy(localeNamePtr, locale, ULOC_FULLNAME_CAPACITY);
446
447 while (true)
448 {
449 UErrorCode status = U_ZERO_ERROR;
450
451 UResourceBundle* rootResBundle = ures_open(nullptr, localeNamePtr, &status);
452 UResourceBundleHolder rootResBundleHolder(rootResBundle, status);
453
454 UResourceBundle* calResBundle = ures_getByKey(rootResBundle, "calendar", nullptr, &status);
455 UResourceBundleHolder calResBundleHolder(calResBundle, status);
456
457 UResourceBundle* targetCalResBundle =
458 ures_getByKey(calResBundle, GetCalendarName(calendarId), nullptr, &status);
459 UResourceBundleHolder targetCalResBundleHolder(targetCalResBundle, status);
460
461 UResourceBundle* erasColResBundle = ures_getByKey(targetCalResBundle, "eras", nullptr, &status);
462 UResourceBundleHolder erasColResBundleHolder(erasColResBundle, status);
463
464 UResourceBundle* erasResBundle = ures_getByKey(erasColResBundle, "narrow", nullptr, &status);
465 UResourceBundleHolder erasResBundleHolder(erasResBundle, status);
466
467 if (U_SUCCESS(status))
468 {
469 EnumUResourceBundle(erasResBundle, callback, context);
470 return true;
471 }
472
473 // Couldn't find the data we need for this locale, we should fallback.
474 if (localeNameBuf[0] == 0x0)
475 {
476 // We are already at the root locale so there is nothing to fall back to, just use the regular eras.
477 break;
478 }
479
480 uloc_getParent(localeNamePtr, parentNamePtr, ULOC_FULLNAME_CAPACITY, &status);
481
482 if (U_FAILURE(status))
483 {
484 // Something bad happened getting the parent name, bail out.
485 break;
486 }
487
488 // Swap localeNamePtr and parentNamePtr, parentNamePtr is what we want to use on the next iteration
489 // and we can use the current localeName as scratch space if we have to fall back on that
490 // iteration.
491
492 char* temp = localeNamePtr;
493 localeNamePtr = parentNamePtr;
494 parentNamePtr = temp;
495 }
496
497 // Walking the resource bundles didn't work, just use the regular eras.
498 return EnumSymbols(locale, calendarId, UDAT_ERAS, 0, callback, context);
499}
500
501/*
502Function:
503EnumCalendarInfo
504
505Retrieves a collection of calendar string data specified by the locale,
506calendar, and data type.
507Allows for a collection of calendar string data to be retrieved by invoking
508the callback for each value in the collection.
509The context parameter is passed through to the callback along with each string.
510*/
511extern "C" int32_t GlobalizationNative_EnumCalendarInfo(
512 EnumCalendarInfoCallback callback,
513 const UChar* localeName,
514 CalendarId calendarId,
515 CalendarDataType dataType,
516 const void* context)
517{
518 UErrorCode err = U_ZERO_ERROR;
519 char locale[ULOC_FULLNAME_CAPACITY];
520 GetLocale(localeName, locale, ULOC_FULLNAME_CAPACITY, false, &err);
521
522 if (U_FAILURE(err))
523 return false;
524
525 switch (dataType)
526 {
527 case ShortDates:
528 // ShortDates to map kShort and kMedium in ICU, but also adding the "yMd"
529 // skeleton as well, as this closely matches what is used on Windows
530 return InvokeCallbackForDatePattern(locale, UDAT_SHORT, callback, context) &&
531 InvokeCallbackForDatePattern(locale, UDAT_MEDIUM, callback, context) &&
532 InvokeCallbackForDateTimePattern(locale, UDAT_YEAR_NUM_MONTH_DAY_UCHAR, callback, context);
533 case LongDates:
534 // LongDates map to kFull and kLong in ICU.
535 return InvokeCallbackForDatePattern(locale, UDAT_FULL, callback, context) &&
536 InvokeCallbackForDatePattern(locale, UDAT_LONG, callback, context);
537 case YearMonths:
538 return InvokeCallbackForDateTimePattern(locale, UDAT_YEAR_MONTH_UCHAR, callback, context);
539 case DayNames:
540 return EnumSymbols(locale, calendarId, UDAT_STANDALONE_WEEKDAYS, 1, callback, context);
541 case AbbrevDayNames:
542 return EnumSymbols(locale, calendarId, UDAT_STANDALONE_SHORT_WEEKDAYS, 1, callback, context);
543 case MonthNames:
544 return EnumSymbols(locale, calendarId, UDAT_STANDALONE_MONTHS, 0, callback, context);
545 case AbbrevMonthNames:
546 return EnumSymbols(locale, calendarId, UDAT_STANDALONE_SHORT_MONTHS, 0, callback, context);
547 case SuperShortDayNames:
548 // UDAT_STANDALONE_SHORTER_WEEKDAYS was added in ICU 51, and CentOS 7 currently uses ICU 50.
549 // fallback to UDAT_STANDALONE_NARROW_WEEKDAYS in that case.
550#if HAVE_UDAT_STANDALONE_SHORTER_WEEKDAYS
551 return EnumSymbols(locale, calendarId, UDAT_STANDALONE_SHORTER_WEEKDAYS, 1, callback, context);
552#else
553 return EnumSymbols(locale, calendarId, UDAT_STANDALONE_NARROW_WEEKDAYS, 1, callback, context);
554#endif
555 case MonthGenitiveNames:
556 return EnumSymbols(locale, calendarId, UDAT_MONTHS, 0, callback, context);
557 case AbbrevMonthGenitiveNames:
558 return EnumSymbols(locale, calendarId, UDAT_SHORT_MONTHS, 0, callback, context);
559 case EraNames:
560 return EnumSymbols(locale, calendarId, UDAT_ERAS, 0, callback, context);
561 case AbbrevEraNames:
562 return EnumAbbrevEraNames(locale, calendarId, callback, context);
563 default:
564 assert(false);
565 return false;
566 }
567}
568
569/*
570Function:
571GetLatestJapaneseEra
572
573Gets the latest era in the Japanese calendar.
574*/
575extern "C" int32_t GlobalizationNative_GetLatestJapaneseEra()
576{
577 UErrorCode err = U_ZERO_ERROR;
578 UCalendar* pCal = ucal_open(nullptr, 0, JAPANESE_LOCALE_AND_CALENDAR, UCAL_TRADITIONAL, &err);
579 UCalendarHolder calHolder(pCal, err);
580
581 if (U_FAILURE(err))
582 return 0;
583
584 ucal_set(pCal, UCAL_EXTENDED_YEAR, 9999);
585 int32_t ret = ucal_get(pCal, UCAL_ERA, &err);
586
587 return U_SUCCESS(err) ? ret : 0;
588}
589
590/*
591Function:
592GetJapaneseEraInfo
593
594Gets the starting Gregorian date of the specified Japanese Era.
595*/
596extern "C" int32_t GlobalizationNative_GetJapaneseEraStartDate(
597 int32_t era, int32_t* startYear, int32_t* startMonth, int32_t* startDay)
598{
599 *startYear = -1;
600 *startMonth = -1;
601 *startDay = -1;
602
603 UErrorCode err = U_ZERO_ERROR;
604 UCalendar* pCal = ucal_open(nullptr, 0, JAPANESE_LOCALE_AND_CALENDAR, UCAL_TRADITIONAL, &err);
605 UCalendarHolder calHolder(pCal, err);
606
607 if (U_FAILURE(err))
608 return false;
609
610 ucal_set(pCal, UCAL_ERA, era);
611 ucal_set(pCal, UCAL_YEAR, 1);
612
613 // UCAL_EXTENDED_YEAR is the gregorian year for the JapaneseCalendar
614 *startYear = ucal_get(pCal, UCAL_EXTENDED_YEAR, &err);
615 if (U_FAILURE(err))
616 return false;
617
618 // set the date to Jan 1
619 ucal_set(pCal, UCAL_MONTH, 0);
620 ucal_set(pCal, UCAL_DATE, 1);
621
622 int32_t currentEra;
623 for (int i = 0; i <= 12; i++)
624 {
625 currentEra = ucal_get(pCal, UCAL_ERA, &err);
626 if (U_FAILURE(err))
627 return false;
628
629 if (currentEra == era)
630 {
631 for (int i = 0; i < 31; i++)
632 {
633 // subtract 1 day at a time until we get out of the specified Era
634 ucal_add(pCal, UCAL_DATE, -1, &err);
635 if (U_FAILURE(err))
636 return false;
637
638 currentEra = ucal_get(pCal, UCAL_ERA, &err);
639 if (U_FAILURE(err))
640 return false;
641
642 if (currentEra != era)
643 {
644 // add back 1 day to get back into the specified Era
645 ucal_add(pCal, UCAL_DATE, 1, &err);
646 if (U_FAILURE(err))
647 return false;
648
649 *startMonth =
650 ucal_get(pCal, UCAL_MONTH, &err) + 1; // ICU Calendar months are 0-based, but .NET is 1-based
651 if (U_FAILURE(err))
652 return false;
653
654 *startDay = ucal_get(pCal, UCAL_DATE, &err);
655 if (U_FAILURE(err))
656 return false;
657
658 return true;
659 }
660 }
661 }
662
663 // add 1 month at a time until we get into the specified Era
664 ucal_add(pCal, UCAL_MONTH, 1, &err);
665 if (U_FAILURE(err))
666 return false;
667 }
668
669 return false;
670}
671