JapaneseCalendar.Win32.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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. using System.Diagnostics;
  5. using Internal.Win32;
  6. namespace System.Globalization
  7. {
  8. public partial class JapaneseCalendar : Calendar
  9. {
  10. private const string JapaneseErasHive = @"System\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras";
  11. // We know about 4 built-in eras, however users may add additional era(s) from the
  12. // registry, by adding values to HKLM\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras
  13. //
  14. // Registry values look like:
  15. // yyyy.mm.dd=era_abbrev_english_englishabbrev
  16. //
  17. // Where yyyy.mm.dd is the registry value name, and also the date of the era start.
  18. // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long)
  19. // era is the Japanese Era name
  20. // abbrev is the Abbreviated Japanese Era Name
  21. // english is the English name for the Era (unused)
  22. // englishabbrev is the Abbreviated English name for the era.
  23. // . is a delimiter, but the value of . doesn't matter.
  24. // '_' marks the space between the japanese era name, japanese abbreviated era name
  25. // english name, and abbreviated english names.
  26. private static EraInfo[] GetJapaneseEras()
  27. {
  28. // Look in the registry key and see if we can find any ranges
  29. int iFoundEras = 0;
  30. EraInfo[] registryEraRanges = null;
  31. try
  32. {
  33. // Need to access registry
  34. using (RegistryKey key = Registry.LocalMachine.OpenSubKey(JapaneseErasHive))
  35. {
  36. // Abort if we didn't find anything
  37. if (key == null) return null;
  38. // Look up the values in our reg key
  39. string[] valueNames = key.GetValueNames();
  40. if (valueNames != null && valueNames.Length > 0)
  41. {
  42. registryEraRanges = new EraInfo[valueNames.Length];
  43. // Loop through the registry and read in all the values
  44. for (int i = 0; i < valueNames.Length; i++)
  45. {
  46. // See if the era is a valid date
  47. EraInfo era = GetEraFromValue(valueNames[i], key.GetValue(valueNames[i]).ToString());
  48. // continue if not valid
  49. if (era == null) continue;
  50. // Remember we found one.
  51. registryEraRanges[iFoundEras] = era;
  52. iFoundEras++;
  53. }
  54. }
  55. }
  56. }
  57. catch (System.Security.SecurityException)
  58. {
  59. // If we weren't allowed to read, then just ignore the error
  60. return null;
  61. }
  62. catch (System.IO.IOException)
  63. {
  64. // If key is being deleted just ignore the error
  65. return null;
  66. }
  67. catch (System.UnauthorizedAccessException)
  68. {
  69. // Registry access rights permissions, just ignore the error
  70. return null;
  71. }
  72. //
  73. // If we didn't have valid eras, then fail
  74. // should have at least 4 eras
  75. //
  76. if (iFoundEras < 4) return null;
  77. //
  78. // Now we have eras, clean them up.
  79. //
  80. // Clean up array length
  81. Array.Resize(ref registryEraRanges, iFoundEras);
  82. // Sort them
  83. Array.Sort(registryEraRanges, CompareEraRanges);
  84. // Clean up era information
  85. for (int i = 0; i < registryEraRanges.Length; i++)
  86. {
  87. // eras count backwards from length to 1 (and are 1 based indexes into string arrays)
  88. registryEraRanges[i].era = registryEraRanges.Length - i;
  89. // update max era year
  90. if (i == 0)
  91. {
  92. // First range is 'til the end of the calendar
  93. registryEraRanges[0].maxEraYear = GregorianCalendar.MaxYear - registryEraRanges[0].yearOffset;
  94. }
  95. else
  96. {
  97. // Rest are until the next era (remember most recent era is first in array)
  98. registryEraRanges[i].maxEraYear = registryEraRanges[i - 1].yearOffset + 1 - registryEraRanges[i].yearOffset;
  99. }
  100. }
  101. // Return our ranges
  102. return registryEraRanges;
  103. }
  104. //
  105. // Compare two era ranges, eg just the ticks
  106. // Remember the era array is supposed to be in reverse chronological order
  107. //
  108. private static int CompareEraRanges(EraInfo a, EraInfo b)
  109. {
  110. return b.ticks.CompareTo(a.ticks);
  111. }
  112. //
  113. // GetEraFromValue
  114. //
  115. // Parse the registry value name/data pair into an era
  116. //
  117. // Registry values look like:
  118. // yyyy.mm.dd=era_abbrev_english_englishabbrev
  119. //
  120. // Where yyyy.mm.dd is the registry value name, and also the date of the era start.
  121. // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long)
  122. // era is the Japanese Era name
  123. // abbrev is the Abbreviated Japanese Era Name
  124. // english is the English name for the Era (unused)
  125. // englishabbrev is the Abbreviated English name for the era.
  126. // . is a delimiter, but the value of . doesn't matter.
  127. // '_' marks the space between the japanese era name, japanese abbreviated era name
  128. // english name, and abbreviated english names.
  129. private static EraInfo GetEraFromValue(string value, string data)
  130. {
  131. // Need inputs
  132. if (value == null || data == null) return null;
  133. //
  134. // Get Date
  135. //
  136. // Need exactly 10 characters in name for date
  137. // yyyy.mm.dd although the . can be any character
  138. if (value.Length != 10) return null;
  139. int year;
  140. int month;
  141. int day;
  142. ReadOnlySpan<char> valueSpan = value.AsSpan();
  143. if (!int.TryParse(valueSpan.Slice(0, 4), NumberStyles.None, NumberFormatInfo.InvariantInfo, out year) ||
  144. !int.TryParse(valueSpan.Slice(5, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out month) ||
  145. !int.TryParse(valueSpan.Slice(8, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out day))
  146. {
  147. // Couldn't convert integer, fail
  148. return null;
  149. }
  150. //
  151. // Get Strings
  152. //
  153. // Needs to be a certain length e_a_E_A at least (7 chars, exactly 4 groups)
  154. string[] names = data.Split('_');
  155. // Should have exactly 4 parts
  156. // 0 - Era Name
  157. // 1 - Abbreviated Era Name
  158. // 2 - English Era Name
  159. // 3 - Abbreviated English Era Name
  160. if (names.Length != 4) return null;
  161. // Each part should have data in it
  162. if (names[0].Length == 0 ||
  163. names[1].Length == 0 ||
  164. names[2].Length == 0 ||
  165. names[3].Length == 0)
  166. return null;
  167. //
  168. // Now we have an era we can build
  169. // Note that the era # and max era year need cleaned up after sorting
  170. // Don't use the full English Era Name (names[2])
  171. //
  172. return new EraInfo(0, year, month, day, year - 1, 1, 0,
  173. names[0], names[1], names[3]);
  174. }
  175. // PAL Layer ends here
  176. private static readonly string[] s_japaneseErasEnglishNames = new string[] { "M", "T", "S", "H" };
  177. private static string GetJapaneseEnglishEraName(int era)
  178. {
  179. Debug.Assert(era > 0);
  180. return era <= s_japaneseErasEnglishNames.Length ? s_japaneseErasEnglishNames[era - 1] : " ";
  181. }
  182. }
  183. }