Skip to content

Commit

Permalink
[iOS][non-icu] HybridGlobalization implement japanese calendar data (d…
Browse files Browse the repository at this point in the history
…otnet#92471)

Add japanese calendar function and GetCalendars for hybrid mode
  • Loading branch information
mkhamoyan authored Oct 9, 2023
1 parent 9f4884c commit a5461cb
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 21 deletions.
11 changes: 11 additions & 0 deletions src/libraries/Common/src/Interop/Interop.Calendar.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,18 @@ internal static partial class Interop
{
internal static partial class Globalization
{
[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetCalendarsNative", StringMarshalling = StringMarshalling.Utf8)]
internal static partial int GetCalendarsNative(string localeName, CalendarId[] calendars, int calendarsCapacity);

[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetCalendarInfoNative", StringMarshalling = StringMarshalling.Utf8)]
internal static partial string GetCalendarInfoNative(string localeName, CalendarId calendarId, CalendarDataType calendarDataType);

[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLatestJapaneseEraNative")]
internal static partial int GetLatestJapaneseEraNative();

[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetJapaneseEraStartDateNative", StringMarshalling = StringMarshalling.Utf8)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool GetJapaneseEraStartDateNative(int era, out int startYear, out int startMonth, out int startDay);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ public void GetEra_Invalid_ThrowsArgumentOutOfRangeException()
Assert.All(DateTime_TestData(calendar), dt =>
{
// JapaneseCalendar throws on ICU, but not on NLS or in HybridGlobalization on Browser
if ((calendar is JapaneseCalendar && (PlatformDetection.IsNlsGlobalization || PlatformDetection.IsHybridGlobalizationOnBrowser || PlatformDetection.IsHybridGlobalizationOnOSX)) || calendar is HebrewCalendar || calendar is TaiwanLunisolarCalendar || calendar is JapaneseLunisolarCalendar)
if ((calendar is JapaneseCalendar && (PlatformDetection.IsNlsGlobalization || PlatformDetection.IsHybridGlobalizationOnBrowser)) || calendar is HebrewCalendar || calendar is TaiwanLunisolarCalendar || calendar is JapaneseLunisolarCalendar)
{
calendar.GetEra(dt);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,15 @@ internal static int IcuGetCalendars(string localeName, CalendarId[] calendars)
Debug.Assert(!GlobalizationMode.UseNls);

// NOTE: there are no 'user overrides' on Linux
int count = Interop.Globalization.GetCalendars(localeName, calendars, calendars.Length);
int count;
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
count = Interop.Globalization.GetCalendarsNative(localeName, calendars, calendars.Length);
else
count = Interop.Globalization.GetCalendars(localeName, calendars, calendars.Length);
#else
count = Interop.Globalization.GetCalendars(localeName, calendars, calendars.Length);
#endif

// ensure there is at least 1 calendar returned
if (count == 0 && calendars.Length > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,36 @@ public partial class JapaneseCalendar : Calendar
Debug.Assert(!GlobalizationMode.UseNls);

string[]? eraNames;
int latestEra;
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
{
eraNames = Interop.Globalization.GetCalendarInfoNative("ja-JP", CalendarId.JAPAN, CalendarDataType.EraNames).Split("||");
if (eraNames.Length == 0)
{
return null;
}
latestEra = Interop.Globalization.GetLatestJapaneseEraNative();
}
else
{
if (!CalendarData.EnumCalendarInfo("ja-JP", CalendarId.JAPAN, CalendarDataType.EraNames, out eraNames))
{
return null;
}
latestEra = Interop.Globalization.GetLatestJapaneseEra();
}
#else
if (!CalendarData.EnumCalendarInfo("ja-JP", CalendarId.JAPAN, CalendarDataType.EraNames, out eraNames))
{
return null;
}
latestEra = Interop.Globalization.GetLatestJapaneseEra();
#endif

List<EraInfo> eras = new List<EraInfo>();
int lastMaxYear = GregorianCalendar.MaxYear;

int latestEra = Interop.Globalization.GetLatestJapaneseEra();

for (int i = latestEra; i >= 0; i--)
{
DateTime dt;
Expand All @@ -50,11 +70,35 @@ public partial class JapaneseCalendar : Calendar
}

string[] abbrevEnglishEraNames;
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
{
var abbrevEraNames = Interop.Globalization.GetCalendarInfoNative("ja", CalendarId.JAPAN, CalendarDataType.AbbrevEraNames);
if (abbrevEraNames == null)
{
// Failed to get English names. fallback to hardcoded data.
abbrevEnglishEraNames = s_abbreviatedEnglishEraNames;
}
else
{
abbrevEnglishEraNames = abbrevEraNames.Split("||");
}
}
else
{
if (!CalendarData.EnumCalendarInfo("ja", CalendarId.JAPAN, CalendarDataType.AbbrevEraNames, out abbrevEnglishEraNames!))
{
// Failed to get English names. fallback to hardcoded data.
abbrevEnglishEraNames = s_abbreviatedEnglishEraNames;
}
}
#else
if (!CalendarData.EnumCalendarInfo("ja", CalendarId.JAPAN, CalendarDataType.AbbrevEraNames, out abbrevEnglishEraNames!))
{
// Failed to get English names. fallback to hardcoded data.
abbrevEnglishEraNames = s_abbreviatedEnglishEraNames;
}
#endif

// Check if we are getting the English Name at the end of the returned list.
// ICU usually return long list including all Era names written in Japanese characters except the recent eras which actually we support will be returned in English.
Expand Down Expand Up @@ -101,12 +145,15 @@ private static bool GetJapaneseEraStartDate(int era, out DateTime dateTime)
int startYear;
int startMonth;
int startDay;
bool result = Interop.Globalization.GetJapaneseEraStartDate(
era,
out startYear,
out startMonth,
out startDay);

bool result;
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
result = Interop.Globalization.GetJapaneseEraStartDateNative(era, out startYear, out startMonth, out startDay);
else
result = Interop.Globalization.GetJapaneseEraStartDate(era, out startYear, out startMonth, out startDay);
#else
result = Interop.Globalization.GetJapaneseEraStartDate(era, out startYear, out startMonth, out startDay);
#endif
if (result)
{
dateTime = new DateTime(startYear, startMonth, startDay);
Expand Down
3 changes: 3 additions & 0 deletions src/native/libs/System.Globalization.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ static const Entry s_globalizationNative[] =
DllImportEntry(GlobalizationNative_CompareStringNative)
DllImportEntry(GlobalizationNative_EndsWithNative)
DllImportEntry(GlobalizationNative_GetCalendarInfoNative)
DllImportEntry(GlobalizationNative_GetCalendarsNative)
DllImportEntry(GlobalizationNative_GetJapaneseEraStartDateNative)
DllImportEntry(GlobalizationNative_GetLatestJapaneseEraNative)
DllImportEntry(GlobalizationNative_GetLocaleInfoIntNative)
DllImportEntry(GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative)
DllImportEntry(GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative)
Expand Down
10 changes: 0 additions & 10 deletions src/native/libs/System.Globalization.Native/pal_calendarData.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,6 @@
#define STRING_COPY(destination, numberOfElements, source) strncpy_s(destination, numberOfElements, source, _TRUNCATE);
#endif

#define GREGORIAN_NAME "gregorian"
#define JAPANESE_NAME "japanese"
#define BUDDHIST_NAME "buddhist"
#define HEBREW_NAME "hebrew"
#define DANGI_NAME "dangi"
#define PERSIAN_NAME "persian"
#define ISLAMIC_NAME "islamic"
#define ISLAMIC_UMALQURA_NAME "islamic-umalqura"
#define ROC_NAME "roc"

#define JAPANESE_LOCALE_AND_CALENDAR "ja_JP@calendar=japanese"

static const UChar UDAT_MONTH_DAY_UCHAR[] = {'M', 'M', 'M', 'M', 'd', '\0'};
Expand Down
21 changes: 21 additions & 0 deletions src/native/libs/System.Globalization.Native/pal_calendarData.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ typedef enum
CalendarData_AbbrevEraNames = 14,
} CalendarDataType;

#define GREGORIAN_NAME "gregorian"
#define JAPANESE_NAME "japanese"
#define BUDDHIST_NAME "buddhist"
#define HEBREW_NAME "hebrew"
#define DANGI_NAME "dangi"
#define PERSIAN_NAME "persian"
#define ISLAMIC_NAME "islamic"
#define ISLAMIC_UMALQURA_NAME "islamic-umalqura"
#define ROC_NAME "roc"

// the function pointer definition for the callback used in EnumCalendarInfo
typedef void (PAL_CALLBACK_CALLTYPE *EnumCalendarInfoCallback)(const UChar*, const void*);

Expand Down Expand Up @@ -96,4 +106,15 @@ PALEXPORT int32_t GlobalizationNative_GetJapaneseEraStartDate(int32_t era,
PALEXPORT const char* GlobalizationNative_GetCalendarInfoNative(const char* localeName,
CalendarId calendarId,
CalendarDataType dataType);

PALEXPORT int32_t GlobalizationNative_GetCalendarsNative(const char* localeName,
CalendarId* calendars,
int32_t calendarsCapacity);

PALEXPORT int32_t GlobalizationNative_GetLatestJapaneseEraNative(void);

PALEXPORT int32_t GlobalizationNative_GetJapaneseEraStartDateNative(int32_t era,
int32_t* startYear,
int32_t* startMonth,
int32_t* startDay);
#endif
147 changes: 146 additions & 1 deletion src/native/libs/System.Globalization.Native/pal_calendarData.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,33 @@
return calendarIdentifier;
}

/*
Function:
GetCalendarId
Gets the associated CalendarId for the calendar name.
*/
static CalendarId GetCalendarId(const char* calendarName)
{
if (strcasecmp(calendarName, GREGORIAN_NAME) == 0)
return GREGORIAN;
else if (strcasecmp(calendarName, JAPANESE_NAME) == 0)
return JAPAN;
else if (strcasecmp(calendarName, BUDDHIST_NAME) == 0)
return THAI;
else if (strcasecmp(calendarName, HEBREW_NAME) == 0)
return HEBREW;
else if (strcasecmp(calendarName, PERSIAN_NAME) == 0)
return PERSIAN;
else if (strcasecmp(calendarName, ISLAMIC_NAME) == 0)
return HIJRI;
else if (strcasecmp(calendarName, ISLAMIC_UMALQURA_NAME) == 0)
return UMALQURA;
else if (strcasecmp(calendarName, ROC_NAME) == 0)
return TAIWAN;
else
return UNINITIALIZED_VALUE;
}
/*
Function:
GlobalizationNative_GetCalendarInfoNative
Expand Down Expand Up @@ -79,7 +106,7 @@
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:calendarIdentifier];

if (dataType == CalendarData_NativeName)
return calendar ? strdup([[calendar calendarIdentifier] UTF8String]) : NULL;
return strdup([[currentLocale localizedStringForCalendarIdentifier:calendarIdentifier] UTF8String]);

NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
dateFormat.locale = currentLocale;
Expand Down Expand Up @@ -141,4 +168,122 @@
return arrayToString ? strdup([arrayToString UTF8String]) : NULL;
}
}

/*
Function:
GetLatestJapaneseEraNative
Gets the latest era in the Japanese calendar.
*/
int32_t GlobalizationNative_GetLatestJapaneseEraNative(void)
{
@autoreleasepool
{
// Create an NSCalendar with the Japanese calendar identifier
NSCalendar *japaneseCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierJapanese];
// Get the latest era
NSDateComponents *latestEraComponents = [japaneseCalendar components:NSCalendarUnitEra fromDate:[NSDate date]];
// Extract the era component
NSInteger latestEra = [latestEraComponents era];
return (int32_t)latestEra;
}
}

/*
Function:
GetJapaneseEraStartDateNative
Gets the starting Gregorian date of the specified Japanese Era.
*/
int32_t GlobalizationNative_GetJapaneseEraStartDateNative(int32_t era, int32_t* startYear, int32_t* startMonth, int32_t* startDay)
{
@autoreleasepool
{
NSCalendar *japaneseCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierJapanese];
NSDateComponents *startDateComponents = [[NSDateComponents alloc] init];
startDateComponents.era = era;
// set the date to Jan 1, 1
startDateComponents.month = 1;
startDateComponents.day = 1;
startDateComponents.year = 1;
NSDate *date = [japaneseCalendar dateFromComponents:startDateComponents];
int32_t currentEra;

for (int month = 0; month <= 12; month++)
{
NSDateComponents *eraComponents = [japaneseCalendar components:NSCalendarUnitEra fromDate:date];
currentEra = [eraComponents era];
if (currentEra == era)
{
for (int day = 0; day < 31; day++)
{
// subtract 1 day at a time until we get out of the specified Era
startDateComponents.day = startDateComponents.day - 1;
date = [japaneseCalendar dateFromComponents:startDateComponents];
eraComponents = [japaneseCalendar components:NSCalendarUnitEra fromDate:date];
currentEra = [eraComponents era];
if (currentEra != era)
{
// add back 1 day to get back into the specified Era
startDateComponents.day = startDateComponents.day + 1;
date = [japaneseCalendar dateFromComponents:startDateComponents];
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *components = [gregorianCalendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date];
*startYear = [components year];
*startMonth = [components month];
*startDay = [components day];
return 1;
}
}
}
// add 1 month at a time until we get into the specified Era
startDateComponents.month = startDateComponents.month + 1;
date = [japaneseCalendar dateFromComponents:startDateComponents];
eraComponents = [japaneseCalendar components:NSCalendarUnitEra fromDate:date];
currentEra = [eraComponents era];
}

return 0;
}
}

/*
Function:
GetCalendarsNative
Returns the list of CalendarIds that are available for the specified locale.
*/
int32_t GlobalizationNative_GetCalendarsNative(const char* localeName, CalendarId* calendars, int32_t calendarsCapacity)
{
@autoreleasepool
{
NSArray *calendarIdentifiers = @[
NSCalendarIdentifierGregorian,
NSCalendarIdentifierBuddhist,
NSCalendarIdentifierHebrew,
NSCalendarIdentifierIslamicUmmAlQura,
NSCalendarIdentifierIslamic,
NSCalendarIdentifierJapanese,
NSCalendarIdentifierPersian,
NSCalendarIdentifierRepublicOfChina,
];

NSString *locName = [NSString stringWithFormat:@"%s", localeName];
NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName];
NSString *defaultCalendarIdentifier = [currentLocale calendarIdentifier];
int32_t calendarCount = MIN(calendarIdentifiers.count, calendarsCapacity);
int32_t calendarIndex = 0;
CalendarId defaultCalendarId = GetCalendarId([defaultCalendarIdentifier UTF8String]);
// If the default calendar is not supported, return the Gregorian calendar as the default.
calendars[calendarIndex++] = defaultCalendarId == UNINITIALIZED_VALUE ? GREGORIAN : defaultCalendarId;
for (int i = 0; i < calendarCount; i++)
{
CalendarId calendarId = GetCalendarId([calendarIdentifiers[i] UTF8String]);
if (calendarId == UNINITIALIZED_VALUE || calendarId == defaultCalendarId)
continue;
calendars[calendarIndex++] = calendarId;
}
return calendarCount;
}
}
#endif

0 comments on commit a5461cb

Please sign in to comment.