// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; namespace System.Globalization { public partial class JapaneseCalendar : Calendar { private static readonly string[] s_abbreviatedEnglishEraNames = ["M", "T", "S", "H", "R"]; private static EraInfo[]? IcuGetJapaneseEras() { if (GlobalizationMode.Invariant) { return null; } 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; for (int i = latestEra; i >= 0; i--) { DateTime dt; if (!GetJapaneseEraStartDate(i, out dt)) { return null; } if (dt.Year == s_calendarMinValue.Year) { // adjust the start date to the minimum supported date time // date 1868/9/8 is actually Meiji 1/1/1 but the actual Gregorian date is 1868/10/23. The 1868/9/8 is used before as a Gregorian era start date // for JapaneseCalendar but now we are ensuring using the correct 1868/10/23 stored in s_calendarMinValue. dt = s_calendarMinValue; } if (dt < s_calendarMinValue) { // only populate the Eras that are valid JapaneseCalendar date times break; } eras.Add(new EraInfo(i, dt.Year, dt.Month, dt.Day, dt.Year - 1, 1, lastMaxYear - dt.Year + 1, eraNames![i], GetAbbreviatedEraName(eraNames, i), "")); lastMaxYear = dt.Year; } 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. // We have the following check as older ICU versions doesn't carry the English names (e.g. ICU version 50). if (abbrevEnglishEraNames[^1] is { Length: 0 } or [> '\u007F', ..]) { // Couldn't get English names. abbrevEnglishEraNames = s_abbreviatedEnglishEraNames; } int startIndex = abbrevEnglishEraNames == s_abbreviatedEnglishEraNames ? eras.Count - 1 : abbrevEnglishEraNames.Length - 1; Debug.Assert(abbrevEnglishEraNames == s_abbreviatedEnglishEraNames || eras.Count <= abbrevEnglishEraNames.Length); // remap the Era numbers, now that we know how many there will be for (int i = 0; i < eras.Count; i++) { eras[i].era = eras.Count - i; if (startIndex < abbrevEnglishEraNames.Length) { eras[i].englishEraName = abbrevEnglishEraNames[startIndex]; } startIndex--; } return eras.ToArray(); } // PAL Layer ends here private static string GetAbbreviatedEraName(string[] eraNames, int eraIndex) { // This matches the behavior on Win32 - only returning the first character of the era name. // See Calendar.EraAsString(Int32) - https://msdn.microsoft.com/en-us/library/windows/apps/br206751.aspx return eraNames[eraIndex].Substring(0, 1); } private static bool GetJapaneseEraStartDate(int era, out DateTime dateTime) { Debug.Assert(!GlobalizationMode.Invariant); dateTime = default; int startYear; int startMonth; int 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); } return result; } } }