< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 157
Coverable lines: 157
Total lines: 674
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 92
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.cs

#LineLine coverage
 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
 4namespace System.Globalization
 5{
 6    /// <summary>
 7    /// JapaneseCalendar is based on Gregorian calendar.  The month and day values are the same as
 8    /// Gregorian calendar. However, the year value is an offset to the Gregorian
 9    /// year based on the era.
 10    ///
 11    /// This system is adopted by Emperor Meiji in 1868. The year value is counted based on the reign of an emperor,
 12    /// and the era begins on the day an emperor ascends the throne and continues until his death.
 13    /// The era changes at 12:00AM.
 14    ///
 15    /// For example, the current era is Reiwa. It started on 2019/5/1 A.D.  Therefore, Gregorian year 2019 is also Reiwa
 16    /// 2019/5/1 A.D. is also Reiwa 1st 5/1.
 17    ///
 18    /// Any date in the year during which era is changed can be reckoned in either era. For example,
 19    /// 2019/1/1 can be 1/1 Reiwa 1st year or 1/1 Heisei 31st year.
 20    ///
 21    /// Note:
 22    ///  The DateTime can be represented by the JapaneseCalendar are limited to two factors:
 23    ///      1. The min value and max value of DateTime class.
 24    ///      2. The available era information.
 25    /// </summary>
 26    /// <remarks>
 27    /// Calendar support range:
 28    ///     Calendar    Minimum     Maximum
 29    ///     ==========  ==========  ==========
 30    ///     Gregorian   1868/10/23  9999/12/31
 31    ///     Japanese    Meiji 01/01 Reiwa 7981/12/31
 32    /// </remarks>
 33    public partial class JapaneseCalendar : Calendar
 34    {
 035        private static readonly DateTime s_calendarMinValue = new DateTime(1868, 10, 23);
 36
 037        public override DateTime MinSupportedDateTime => s_calendarMinValue;
 38
 039        public override DateTime MaxSupportedDateTime => DateTime.MaxValue;
 40
 041        public override CalendarAlgorithmType AlgorithmType => CalendarAlgorithmType.SolarCalendar;
 42
 43        // Using a field initializer rather than a static constructor so that the whole class can be lazy
 44        // init.
 45        private static EraInfo[]? s_japaneseEraInfo;
 46
 47        // m_EraInfo must be listed in reverse chronological order.  The most recent era
 48        // should be the first element.
 49        // That is, m_EraInfo[0] contains the most recent era.
 50        //
 51        // We know about 4 built-in eras, however users may add additional era(s) from the
 52        // registry, by adding values to HKLM\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras
 53        // we don't read the registry and instead we call WinRT to get the needed information
 54        //
 55        // Registry values look like:
 56        //      yyyy.mm.dd=era_abbrev_english_englishabbrev
 57        //
 58        // Where yyyy.mm.dd is the registry value name, and also the date of the era start.
 59        // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long)
 60        // era is the Japanese Era name
 61        // abbrev is the Abbreviated Japanese Era Name
 62        // english is the English name for the Era (unused)
 63        // englishabbrev is the Abbreviated English name for the era.
 64        // . is a delimiter, but the value of . doesn't matter.
 65        // '_' marks the space between the japanese era name, japanese abbreviated era name
 66        //     english name, and abbreviated english names.
 67        internal static EraInfo[] GetEraInfo()
 68        {
 69            // See if we need to build it
 070            return s_japaneseEraInfo ??=
 071                (GlobalizationMode.UseNls ? NlsGetJapaneseEras() : IcuGetJapaneseEras()) ??
 072                // See if we have to use the built-in eras
 073                [
 074                    new EraInfo(5, 2019, 5, 1, 2018, 1, GregorianCalendar.MaxYear - 2018, "\x4ee4\x548c", "\x4ee4", "R")
 075                    new EraInfo(4, 1989, 1, 8, 1988, 1, 2019 - 1988, "\x5e73\x6210", "\x5e73", "H"),
 076                    new EraInfo(3, 1926, 12, 25, 1925, 1, 1989 - 1925, "\x662d\x548c", "\x662d", "S"),
 077                    new EraInfo(2, 1912, 7, 30, 1911, 1, 1926 - 1911, "\x5927\x6b63", "\x5927", "T"),
 078                    new EraInfo(1, 1868, 1, 1, 1867, 1, 1912 - 1867, "\x660e\x6cbb", "\x660e", "M")
 079                ];
 80        }
 81
 82        internal static Calendar? s_defaultInstance;
 83        internal GregorianCalendarHelper _helper;
 84
 085        internal static Calendar GetDefaultInstance() => s_defaultInstance ??= new JapaneseCalendar();
 86
 087        public JapaneseCalendar()
 88        {
 89            try
 90            {
 091                new CultureInfo("ja-JP");
 092            }
 093            catch (ArgumentException e)
 94            {
 095                throw new TypeInitializationException(this.GetType().ToString(), e);
 96            }
 97
 098            _helper = new GregorianCalendarHelper(this, GetEraInfo());
 099        }
 100
 0101        internal override CalendarId ID => CalendarId.JAPAN;
 102
 103        public override DateTime AddMonths(DateTime time, int months)
 104        {
 0105            return _helper.AddMonths(time, months);
 106        }
 107
 108        public override DateTime AddYears(DateTime time, int years)
 109        {
 0110            return _helper.AddYears(time, years);
 111        }
 112
 113        public override int GetDaysInMonth(int year, int month, int era)
 114        {
 0115            return _helper.GetDaysInMonth(year, month, era);
 116        }
 117
 118        public override int GetDaysInYear(int year, int era)
 119        {
 0120            return _helper.GetDaysInYear(year, era);
 121        }
 122
 123        public override int GetDayOfMonth(DateTime time)
 124        {
 0125            return _helper.GetDayOfMonth(time);
 126        }
 127
 128        public override DayOfWeek GetDayOfWeek(DateTime time)
 129        {
 0130            return _helper.GetDayOfWeek(time);
 131        }
 132
 133        public override int GetDayOfYear(DateTime time)
 134        {
 0135            return _helper.GetDayOfYear(time);
 136        }
 137
 138        public override int GetMonthsInYear(int year, int era)
 139        {
 0140            return _helper.GetMonthsInYear(year, era);
 141        }
 142
 143        public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek)
 144        {
 0145            return _helper.GetWeekOfYear(time, rule, firstDayOfWeek);
 146        }
 147
 148        public override int GetEra(DateTime time)
 149        {
 0150            return _helper.GetEra(time);
 151        }
 152
 153        public override int GetMonth(DateTime time)
 154        {
 0155            return _helper.GetMonth(time);
 156        }
 157
 158        public override int GetYear(DateTime time)
 159        {
 0160            return _helper.GetYear(time);
 161        }
 162
 163        public override bool IsLeapDay(int year, int month, int day, int era)
 164        {
 0165            return _helper.IsLeapDay(year, month, day, era);
 166        }
 167
 168        public override bool IsLeapYear(int year, int era)
 169        {
 0170            return _helper.IsLeapYear(year, era);
 171        }
 172
 173        public override int GetLeapMonth(int year, int era)
 174        {
 0175            return _helper.GetLeapMonth(year, era);
 176        }
 177
 178        public override bool IsLeapMonth(int year, int month, int era)
 179        {
 0180            return _helper.IsLeapMonth(year, month, era);
 181        }
 182
 183
 184        public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisec
 185        {
 0186            return _helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era);
 187        }
 188
 189        /// <summary>
 190        /// For Japanese calendar, four digit year is not used. Few emperors will live for more than one hundred years.
 191        /// Therefore, for any two digit number, we just return the original number.
 192        /// </summary>
 193        public override int ToFourDigitYear(int year)
 194        {
 0195            ArgumentOutOfRangeException.ThrowIfNegativeOrZero(year);
 0196            ArgumentOutOfRangeException.ThrowIfGreaterThan(year, _helper.MaxYear);
 197
 0198            return year;
 199        }
 200
 201
 0202        public override int[] Eras => _helper.Eras;
 203
 204        /// <summary>
 205        /// Return the various era strings
 206        /// Note: The arrays are backwards of the eras
 207        /// </summary>
 208        internal static string[] EraNames()
 209        {
 0210            EraInfo[] eras = GetEraInfo();
 0211            string[] eraNames = new string[eras.Length];
 212
 0213            for (int i = 0; i < eras.Length; i++)
 214            {
 215                // Strings are in chronological order, eras are backwards order.
 0216                eraNames[i] = eras[eras.Length - i - 1].eraName!;
 217            }
 218
 0219            return eraNames;
 220        }
 221
 222        internal static string[] AbbrevEraNames()
 223        {
 0224            EraInfo[] eras = GetEraInfo();
 0225            string[] erasAbbrev = new string[eras.Length];
 226
 0227            for (int i = 0; i < eras.Length; i++)
 228            {
 229                // Strings are in chronological order, eras are backwards order.
 0230                erasAbbrev[i] = eras[eras.Length - i - 1].abbrevEraName!;
 231            }
 232
 0233            return erasAbbrev;
 234        }
 235
 236        internal static string[] EnglishEraNames()
 237        {
 0238            EraInfo[] eras = GetEraInfo();
 0239            string[] erasEnglish = new string[eras.Length];
 240
 0241            for (int i = 0; i < eras.Length; i++)
 242            {
 243                // Strings are in chronological order, eras are backwards order.
 0244                erasEnglish[i] = eras[eras.Length - i - 1].englishEraName!;
 245            }
 246
 0247            return erasEnglish;
 248        }
 249
 250        private const int DefaultTwoDigitYearMax = 99;
 251
 252        internal override bool IsValidYear(int year, int era)
 253        {
 0254            return _helper.IsValidYear(year, era);
 255        }
 256
 257        public override int TwoDigitYearMax
 258        {
 259            get
 260            {
 0261                if (_twoDigitYearMax == -1)
 262                {
 0263                    _twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DefaultTwoDigitYearMax);
 264                }
 265
 0266                return _twoDigitYearMax;
 267            }
 268            set
 269            {
 0270                VerifyWritable();
 0271                if (value < 99 || value > _helper.MaxYear)
 272                {
 0273                    throw new ArgumentOutOfRangeException(
 0274                        nameof(value),
 0275                        value,
 0276                        SR.Format(SR.ArgumentOutOfRange_Range, 99, _helper.MaxYear));
 277                }
 278
 0279                _twoDigitYearMax = value;
 0280            }
 281        }
 282    }
 283}
 284

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Icu.cs

#LineLine coverage
 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
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6
 7namespace System.Globalization
 8{
 9    public partial class JapaneseCalendar : Calendar
 10    {
 011        private static readonly string[] s_abbreviatedEnglishEraNames = ["M", "T", "S", "H", "R"];
 12
 13        private static EraInfo[]? IcuGetJapaneseEras()
 14        {
 015            if (GlobalizationMode.Invariant)
 16            {
 017                return null;
 18            }
 19
 020            Debug.Assert(!GlobalizationMode.UseNls);
 21
 22            string[]? eraNames;
 23            int latestEra;
 24#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 25            if (GlobalizationMode.Hybrid)
 26            {
 27                eraNames = Interop.Globalization.GetCalendarInfoNative("ja-JP", CalendarId.JAPAN, CalendarDataType.EraNa
 28                if (eraNames.Length == 0)
 29                {
 30                    return null;
 31                }
 32                latestEra = Interop.Globalization.GetLatestJapaneseEraNative();
 33            }
 34            else
 35            {
 36                if (!CalendarData.EnumCalendarInfo("ja-JP", CalendarId.JAPAN, CalendarDataType.EraNames, out eraNames))
 37                {
 38                    return null;
 39                }
 40                latestEra = Interop.Globalization.GetLatestJapaneseEra();
 41            }
 42#else
 043            if (!CalendarData.EnumCalendarInfo("ja-JP", CalendarId.JAPAN, CalendarDataType.EraNames, out eraNames))
 44            {
 045                return null;
 46            }
 047            latestEra = Interop.Globalization.GetLatestJapaneseEra();
 48#endif
 49
 050            List<EraInfo> eras = new List<EraInfo>();
 051            int lastMaxYear = GregorianCalendar.MaxYear;
 52
 053            for (int i = latestEra; i >= 0; i--)
 54            {
 55                DateTime dt;
 056                if (!GetJapaneseEraStartDate(i, out dt))
 57                {
 058                    return null;
 59                }
 60
 061                if (dt.Year == s_calendarMinValue.Year)
 62                {
 63                    // adjust the start date to the minimum supported date time
 64                    // date 1868/9/8 is actually Meiji 1/1/1 but the actual Gregorian date is 1868/10/23. The 1868/9/8 i
 65                    // for JapaneseCalendar but now we are ensuring using the correct 1868/10/23 stored in s_calendarMin
 066                    dt = s_calendarMinValue;
 67                }
 68
 069                if (dt < s_calendarMinValue)
 70                {
 71                    // only populate the Eras that are valid JapaneseCalendar date times
 72                    break;
 73                }
 74
 075                eras.Add(new EraInfo(i, dt.Year, dt.Month, dt.Day, dt.Year - 1, 1, lastMaxYear - dt.Year + 1, eraNames![
 76
 077                lastMaxYear = dt.Year;
 78            }
 79
 80            string[] abbrevEnglishEraNames;
 81#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 82            if (GlobalizationMode.Hybrid)
 83            {
 84                var abbrevEraNames = Interop.Globalization.GetCalendarInfoNative("ja", CalendarId.JAPAN, CalendarDataTyp
 85                if (abbrevEraNames == null)
 86                {
 87                    // Failed to get English names. fallback to hardcoded data.
 88                    abbrevEnglishEraNames = s_abbreviatedEnglishEraNames;
 89                }
 90                else
 91                {
 92                    abbrevEnglishEraNames = abbrevEraNames.Split("||");
 93                }
 94            }
 95            else
 96            {
 97                if (!CalendarData.EnumCalendarInfo("ja", CalendarId.JAPAN, CalendarDataType.AbbrevEraNames, out abbrevEn
 98                {
 99                    // Failed to get English names. fallback to hardcoded data.
 100                    abbrevEnglishEraNames = s_abbreviatedEnglishEraNames;
 101                }
 102            }
 103#else
 0104            if (!CalendarData.EnumCalendarInfo("ja", CalendarId.JAPAN, CalendarDataType.AbbrevEraNames, out abbrevEnglis
 105            {
 106                // Failed to get English names. fallback to hardcoded data.
 0107                abbrevEnglishEraNames = s_abbreviatedEnglishEraNames;
 108            }
 109#endif
 110
 111            // Check if we are getting the English Name at the end of the returned list.
 112            // ICU usually return long list including all Era names written in Japanese characters except the recent era
 113            // We have the following check as older ICU versions doesn't carry the English names (e.g. ICU version 50).
 0114            if (abbrevEnglishEraNames[^1] is { Length: 0 } or [> '\u007F', ..])
 115            {
 116                // Couldn't get English names.
 0117                abbrevEnglishEraNames = s_abbreviatedEnglishEraNames;
 118            }
 119
 0120            int startIndex = abbrevEnglishEraNames == s_abbreviatedEnglishEraNames ? eras.Count - 1 : abbrevEnglishEraNa
 121
 0122            Debug.Assert(abbrevEnglishEraNames == s_abbreviatedEnglishEraNames || eras.Count <= abbrevEnglishEraNames.Le
 123
 124            // remap the Era numbers, now that we know how many there will be
 0125            for (int i = 0; i < eras.Count; i++)
 126            {
 0127                eras[i].era = eras.Count - i;
 0128                if (startIndex < abbrevEnglishEraNames.Length)
 129                {
 0130                    eras[i].englishEraName = abbrevEnglishEraNames[startIndex];
 131                }
 0132                startIndex--;
 133            }
 134
 0135            return eras.ToArray();
 136        }
 137
 138        // PAL Layer ends here
 139
 140        private static string GetAbbreviatedEraName(string[] eraNames, int eraIndex)
 141        {
 142            // This matches the behavior on Win32 - only returning the first character of the era name.
 143            // See Calendar.EraAsString(Int32) - https://msdn.microsoft.com/en-us/library/windows/apps/br206751.aspx
 0144            return eraNames[eraIndex].Substring(0, 1);
 145        }
 146
 147        private static bool GetJapaneseEraStartDate(int era, out DateTime dateTime)
 148        {
 0149            Debug.Assert(!GlobalizationMode.Invariant);
 150
 0151            dateTime = default;
 152
 153            int startYear;
 154            int startMonth;
 155            int startDay;
 156            bool result;
 157#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
 158            if (GlobalizationMode.Hybrid)
 159                result = Interop.Globalization.GetJapaneseEraStartDateNative(era, out startYear, out startMonth, out sta
 160            else
 161                result = Interop.Globalization.GetJapaneseEraStartDate(era, out startYear, out startMonth, out startDay)
 162#else
 0163            result = Interop.Globalization.GetJapaneseEraStartDate(era, out startYear, out startMonth, out startDay);
 164#endif
 0165            if (result)
 166            {
 0167                dateTime = new DateTime(startYear, startMonth, startDay);
 168            }
 169
 0170            return result;
 171        }
 172    }
 173}
 174

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Nls.cs

#LineLine coverage
 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
 4using System.Diagnostics;
 5
 6#if TARGET_WINDOWS
 7using Internal.Win32;
 8#endif
 9
 10namespace System.Globalization
 11{
 12    public partial class JapaneseCalendar : Calendar
 13    {
 14#if TARGET_WINDOWS
 15        private const string JapaneseErasHive = @"System\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras";
 16
 17        // We know about 4 built-in eras, however users may add additional era(s) from the
 18        // registry, by adding values to HKLM\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras
 19        //
 20        // Registry values look like:
 21        //      yyyy.mm.dd=era_abbrev_english_englishabbrev
 22        //
 23        // Where yyyy.mm.dd is the registry value name, and also the date of the era start.
 24        // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long)
 25        // era is the Japanese Era name
 26        // abbrev is the Abbreviated Japanese Era Name
 27        // english is the English name for the Era (unused)
 28        // englishabbrev is the Abbreviated English name for the era.
 29        // . is a delimiter, but the value of . doesn't matter.
 30        // '_' marks the space between the japanese era name, japanese abbreviated era name
 31        //     english name, and abbreviated english names.
 32        private static EraInfo[]? NlsGetJapaneseEras()
 33        {
 034            Debug.Assert(GlobalizationMode.UseNls);
 35
 36            // Look in the registry key and see if we can find any ranges
 037            int iFoundEras = 0;
 038            EraInfo[]? registryEraRanges = null;
 39
 40            try
 41            {
 42                // Need to access registry
 043                using (RegistryKey? key = Registry.LocalMachine.OpenSubKey(JapaneseErasHive))
 44                {
 45                    // Abort if we didn't find anything
 046                    if (key == null) return null;
 47
 48                    // Look up the values in our reg key
 049                    string[] valueNames = key.GetValueNames();
 050                    if (valueNames != null && valueNames.Length > 0)
 51                    {
 052                        registryEraRanges = new EraInfo[valueNames.Length];
 53
 54                        // Loop through the registry and read in all the values
 055                        for (int i = 0; i < valueNames.Length; i++)
 56                        {
 57                            // See if the era is a valid date
 058                            EraInfo? era = GetEraFromValue(valueNames[i], key.GetValue(valueNames[i])?.ToString());
 59
 60                            // continue if not valid
 061                            if (era == null) continue;
 62
 63                            // Remember we found one.
 064                            registryEraRanges[iFoundEras] = era;
 065                            iFoundEras++;
 66                        }
 67                    }
 068                }
 069            }
 070            catch (Security.SecurityException)
 71            {
 72                // If we weren't allowed to read, then just ignore the error
 073                return null;
 74            }
 075            catch (IO.IOException)
 76            {
 77                // If key is being deleted just ignore the error
 078                return null;
 79            }
 080            catch (UnauthorizedAccessException)
 81            {
 82                // Registry access rights permissions, just ignore the error
 083                return null;
 84            }
 85
 86            //
 87            // If we didn't have valid eras, then fail
 88            // should have at least 4 eras
 89            //
 090            if (iFoundEras < 4) return null;
 91
 92            //
 93            // Now we have eras, clean them up.
 94            //
 95            // Clean up array length
 096            Array.Resize(ref registryEraRanges, iFoundEras);
 97
 98            // Sort them
 099            Array.Sort(registryEraRanges, CompareEraRanges);
 100
 101            // Clean up era information
 0102            for (int i = 0; i < registryEraRanges.Length; i++)
 103            {
 104                // eras count backwards from length to 1 (and are 1 based indexes into string arrays)
 0105                registryEraRanges[i].era = registryEraRanges.Length - i;
 106
 107                // update max era year
 0108                if (i == 0)
 109                {
 110                    // First range is 'til the end of the calendar
 0111                    registryEraRanges[0].maxEraYear = GregorianCalendar.MaxYear - registryEraRanges[0].yearOffset;
 112                }
 113                else
 114                {
 115                    // Rest are until the next era (remember most recent era is first in array)
 0116                    registryEraRanges[i].maxEraYear = registryEraRanges[i - 1].yearOffset + 1 - registryEraRanges[i].yea
 117                }
 118            }
 119
 120            // Return our ranges
 0121            return registryEraRanges;
 0122        }
 123#else
 124        // no-op, in Unix we never call this function.
 125        // the reason to have it is to simplify the build
 126        // this way we avoid having to include RegistryKey
 127        // and all it's windows PInvokes.
 128        private static EraInfo[]? NlsGetJapaneseEras()
 129        {
 130            Debug.Fail("Should never be called non-Windows platforms.");
 131            throw new PlatformNotSupportedException();
 132        }
 133#endif
 134
 135        //
 136        // Compare two era ranges, eg just the ticks
 137        // Remember the era array is supposed to be in reverse chronological order
 138        //
 139        private static int CompareEraRanges(EraInfo a, EraInfo b)
 140        {
 0141            return b.ticks.CompareTo(a.ticks);
 142        }
 143
 144        //
 145        // GetEraFromValue
 146        //
 147        // Parse the registry value name/data pair into an era
 148        //
 149        // Registry values look like:
 150        //      yyyy.mm.dd=era_abbrev_english_englishabbrev
 151        //
 152        // Where yyyy.mm.dd is the registry value name, and also the date of the era start.
 153        // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long)
 154        // era is the Japanese Era name
 155        // abbrev is the Abbreviated Japanese Era Name
 156        // english is the English name for the Era (unused)
 157        // englishabbrev is the Abbreviated English name for the era.
 158        // . is a delimiter, but the value of . doesn't matter.
 159        // '_' marks the space between the japanese era name, japanese abbreviated era name
 160        //     english name, and abbreviated english names.
 161        private static unsafe EraInfo? GetEraFromValue(string? value, string? data)
 162        {
 163            // Need inputs
 0164            if (value == null || data == null) return null;
 165
 166            //
 167            // Get Date
 168            //
 169            // Need exactly 10 characters in name for date
 170            // yyyy.mm.dd although the . can be any character
 0171            if (value.Length != 10) return null;
 172
 173
 0174            ReadOnlySpan<char> valueSpan = value.AsSpan();
 0175            if (!int.TryParse(valueSpan.Slice(0, 4), NumberStyles.None, NumberFormatInfo.InvariantInfo, out int year) ||
 0176                !int.TryParse(valueSpan.Slice(5, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out int month) |
 0177                !int.TryParse(valueSpan.Slice(8, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out int day))
 178            {
 179                // Couldn't convert integer, fail
 0180                return null;
 181            }
 182
 183            //
 184            // Get Strings
 185            //
 186            // Needs to be a certain length e_a_E_A at least (7 chars, exactly 4 groups)
 187            // Should have exactly 4 parts
 188            // 0 - Era Name
 189            // 1 - Abbreviated Era Name
 190            // 2 - English Era Name
 191            // 3 - Abbreviated English Era Name
 0192            Span<Range> names = stackalloc Range[5];
 0193            ReadOnlySpan<char> dataSpan = data;
 0194            if (dataSpan.Split(names, '_') == 4)
 195            {
 0196                ReadOnlySpan<char> eraName = dataSpan[names[0]];
 0197                ReadOnlySpan<char> abbreviatedEraName = dataSpan[names[1]];
 0198                ReadOnlySpan<char> englishEraName = dataSpan[names[2]];
 0199                ReadOnlySpan<char> abbreviatedEnglishEraName = dataSpan[names[3]];
 200
 201                // Each part should have data in it
 0202                if (!eraName.IsEmpty && !abbreviatedEraName.IsEmpty && !englishEraName.IsEmpty && !abbreviatedEnglishEra
 203                {
 204                    // Now we have an era we can build
 205                    // Note that the era # and max era year need cleaned up after sorting
 206                    // Don't use the full English Era Name (names[2])
 0207                    return new EraInfo(0, year, month, day, year - 1, 1, 0,
 0208                                       eraName.ToString(), abbreviatedEraName.ToString(), abbreviatedEnglishEraName.ToSt
 209                }
 210            }
 211
 0212            return null;
 213        }
 214    }
 215}
 216