| | | 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 | | |
| | | 4 | | using System.Collections.Generic; |
| | | 5 | | using System.Diagnostics; |
| | | 6 | | using System.Runtime.CompilerServices; |
| | | 7 | | |
| | | 8 | | namespace System.Globalization |
| | | 9 | | { |
| | | 10 | | internal sealed partial class CalendarData |
| | | 11 | | { |
| | | 12 | | private const uint CAL_ICALINTVALUE = 0x00000001; |
| | | 13 | | private const uint CAL_RETURN_GENITIVE_NAMES = 0x10000000; |
| | | 14 | | private const uint CAL_NOUSEROVERRIDE = 0x80000000; |
| | | 15 | | private const uint CAL_SMONTHDAY = 0x00000038; |
| | | 16 | | private const uint CAL_SSHORTDATE = 0x00000005; |
| | | 17 | | private const uint CAL_SLONGDATE = 0x00000006; |
| | | 18 | | private const uint CAL_SYEARMONTH = 0x0000002f; |
| | | 19 | | private const uint CAL_SDAYNAME7 = 0x0000000d; |
| | | 20 | | private const uint CAL_SABBREVDAYNAME7 = 0x00000014; |
| | | 21 | | private const uint CAL_SMONTHNAME1 = 0x00000015; |
| | | 22 | | private const uint CAL_SABBREVMONTHNAME1 = 0x00000022; |
| | | 23 | | private const uint CAL_SSHORTESTDAYNAME7 = 0x00000037; |
| | | 24 | | private const uint CAL_SERASTRING = 0x00000004; |
| | | 25 | | private const uint CAL_SABBREVERASTRING = 0x00000039; |
| | | 26 | | private const uint CAL_ITWODIGITYEARMAX = 0x00000030; |
| | | 27 | | |
| | | 28 | | private const uint ENUM_ALL_CALENDARS = 0xffffffff; |
| | | 29 | | |
| | | 30 | | private const uint LOCALE_ICALENDARTYPE = 0x00001009; |
| | | 31 | | private const uint LOCALE_SSHORTDATE = 0x0000001F; |
| | | 32 | | private const uint LOCALE_SLONGDATE = 0x00000020; |
| | | 33 | | private const uint LOCALE_SYEARMONTH = 0x00001006; |
| | | 34 | | |
| | | 35 | | private bool LoadCalendarDataFromSystemCore(string localeName, CalendarId calendarId) |
| | | 36 | | { |
| | 0 | 37 | | Debug.Assert(!GlobalizationMode.Invariant); |
| | | 38 | | |
| | 0 | 39 | | if (GlobalizationMode.UseNls) |
| | | 40 | | { |
| | 0 | 41 | | return NlsLoadCalendarDataFromSystem(localeName, calendarId); |
| | | 42 | | } |
| | | 43 | | |
| | | 44 | | // If running using ICU on Windows we should honor user overrides using NLS and the rest from ICU |
| | 0 | 45 | | bool result = IcuLoadCalendarDataFromSystem(localeName, calendarId); |
| | | 46 | | |
| | 0 | 47 | | if (result && bUseUserOverrides) |
| | | 48 | | { |
| | 0 | 49 | | NormalizeCalendarId(ref calendarId, ref localeName); |
| | 0 | 50 | | result &= CallGetCalendarInfoEx(localeName, calendarId, CAL_ITWODIGITYEARMAX, out this.iTwoDigitYearMax) |
| | | 51 | | |
| | | 52 | | // They want user overrides, see if the user calendar matches the input calendar |
| | 0 | 53 | | CalendarId userCalendar = (CalendarId)CultureData.GetLocaleInfoExInt(localeName, LOCALE_ICALENDARTYPE); |
| | | 54 | | |
| | | 55 | | // If the calendars were the same, see if the locales were the same |
| | 0 | 56 | | if (userCalendar == calendarId) |
| | | 57 | | { |
| | 0 | 58 | | string? shortDateOverride = CultureData.ReescapeWin32String(CultureData.GetLocaleInfoEx(localeName, |
| | 0 | 59 | | string? longDateOverride = CultureData.ReescapeWin32String(CultureData.GetLocaleInfoEx(localeName, L |
| | 0 | 60 | | InsertOrSwapOverride(shortDateOverride, ref this.saShortDates); |
| | 0 | 61 | | InsertOrSwapOverride(longDateOverride, ref this.saLongDates); |
| | | 62 | | } |
| | | 63 | | } |
| | | 64 | | |
| | 0 | 65 | | return result; |
| | | 66 | | } |
| | | 67 | | |
| | | 68 | | private static void InsertOrSwapOverride(string? value, ref string[] destination) |
| | | 69 | | { |
| | 0 | 70 | | if (value == null) |
| | 0 | 71 | | return; |
| | | 72 | | |
| | 0 | 73 | | int i = Array.IndexOf(destination, value); |
| | 0 | 74 | | if (i >= 0) |
| | | 75 | | { |
| | 0 | 76 | | if (i > 0) |
| | | 77 | | { |
| | 0 | 78 | | string tmp = destination[0]; |
| | 0 | 79 | | destination[0] = value; |
| | 0 | 80 | | destination[i] = tmp; |
| | | 81 | | } |
| | | 82 | | |
| | 0 | 83 | | return; |
| | | 84 | | } |
| | | 85 | | |
| | 0 | 86 | | string[] newArray = new string[destination.Length + 1]; |
| | 0 | 87 | | newArray[0] = value; |
| | 0 | 88 | | Array.Copy(destination, 0, newArray, 1, destination.Length); |
| | 0 | 89 | | destination = newArray; |
| | 0 | 90 | | } |
| | | 91 | | |
| | | 92 | | private bool NlsLoadCalendarDataFromSystem(string localeName, CalendarId calendarId) |
| | | 93 | | { |
| | 0 | 94 | | bool ret = true; |
| | | 95 | | |
| | 0 | 96 | | uint useOverrides = bUseUserOverrides ? 0 : CAL_NOUSEROVERRIDE; |
| | | 97 | | |
| | 0 | 98 | | NormalizeCalendarId(ref calendarId, ref localeName); |
| | | 99 | | |
| | | 100 | | // Numbers |
| | 0 | 101 | | ret &= CallGetCalendarInfoEx(localeName, calendarId, CAL_ITWODIGITYEARMAX | useOverrides, out this.iTwoDigit |
| | | 102 | | |
| | | 103 | | // Strings |
| | 0 | 104 | | ret &= CallGetCalendarInfoEx(localeName, calendarId, CAL_SCALNAME, out this.sNativeName); |
| | 0 | 105 | | ret &= CallGetCalendarInfoEx(localeName, calendarId, CAL_SMONTHDAY, out this.sMonthDay); |
| | | 106 | | |
| | | 107 | | // String Arrays |
| | | 108 | | // Formats |
| | 0 | 109 | | ret &= CallEnumCalendarInfo(localeName, calendarId, CAL_SSHORTDATE, LOCALE_SSHORTDATE | useOverrides, out th |
| | 0 | 110 | | ret &= CallEnumCalendarInfo(localeName, calendarId, CAL_SLONGDATE, LOCALE_SLONGDATE | useOverrides, out this |
| | | 111 | | |
| | | 112 | | // Get the YearMonth pattern. |
| | 0 | 113 | | ret &= CallEnumCalendarInfo(localeName, calendarId, CAL_SYEARMONTH, LOCALE_SYEARMONTH, out this.saYearMonths |
| | | 114 | | |
| | | 115 | | // Day & Month Names |
| | | 116 | | // These are all single calType entries, 1 per day, so we have to make 7 or 13 calls to collect all the name |
| | | 117 | | |
| | | 118 | | // Day |
| | | 119 | | // Note that we're off-by-one since managed starts on sunday and windows starts on monday |
| | 0 | 120 | | ret &= GetCalendarDayInfo(localeName, calendarId, CAL_SDAYNAME7, out this.saDayNames); |
| | 0 | 121 | | ret &= GetCalendarDayInfo(localeName, calendarId, CAL_SABBREVDAYNAME7, out this.saAbbrevDayNames); |
| | | 122 | | |
| | | 123 | | // Month names |
| | 0 | 124 | | ret &= GetCalendarMonthInfo(localeName, calendarId, CAL_SMONTHNAME1, out this.saMonthNames); |
| | 0 | 125 | | ret &= GetCalendarMonthInfo(localeName, calendarId, CAL_SABBREVMONTHNAME1, out this.saAbbrevMonthNames); |
| | | 126 | | |
| | | 127 | | // |
| | | 128 | | // The following LCTYPE are not supported in some platforms. If the call fails, |
| | | 129 | | // don't return a failure. |
| | | 130 | | // |
| | 0 | 131 | | GetCalendarDayInfo(localeName, calendarId, CAL_SSHORTESTDAYNAME7, out this.saSuperShortDayNames); |
| | | 132 | | |
| | | 133 | | // Gregorian may have genitive month names |
| | 0 | 134 | | if (calendarId == CalendarId.GREGORIAN) |
| | | 135 | | { |
| | 0 | 136 | | GetCalendarMonthInfo(localeName, calendarId, CAL_SMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, out this.saMon |
| | 0 | 137 | | GetCalendarMonthInfo(localeName, calendarId, CAL_SABBREVMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, out this |
| | | 138 | | } |
| | | 139 | | |
| | | 140 | | // Calendar Parts Names |
| | | 141 | | // This doesn't get always get localized names for gregorian (not available in windows < 7) |
| | | 142 | | // so: eg: coreclr on win < 7 won't get these |
| | 0 | 143 | | CallEnumCalendarInfo(localeName, calendarId, CAL_SERASTRING, 0, out this.saEraNames!); |
| | 0 | 144 | | CallEnumCalendarInfo(localeName, calendarId, CAL_SABBREVERASTRING, 0, out this.saAbbrevEraNames!); |
| | | 145 | | |
| | | 146 | | // |
| | | 147 | | // Calendar Era Info |
| | | 148 | | // Note that calendar era data (offsets, etc) is hard coded for each calendar since this |
| | | 149 | | // data is implementation specific and not dynamic (except perhaps Japanese) |
| | | 150 | | // |
| | | 151 | | |
| | | 152 | | // Clean up the escaping of the formats |
| | 0 | 153 | | this.saShortDates = CultureData.ReescapeWin32Strings(this.saShortDates)!; |
| | 0 | 154 | | this.saLongDates = CultureData.ReescapeWin32Strings(this.saLongDates)!; |
| | 0 | 155 | | this.saYearMonths = CultureData.ReescapeWin32Strings(this.saYearMonths)!; |
| | 0 | 156 | | this.sMonthDay = CultureData.ReescapeWin32String(this.sMonthDay)!; |
| | | 157 | | |
| | 0 | 158 | | return ret; |
| | | 159 | | } |
| | | 160 | | |
| | | 161 | | private static void NormalizeCalendarId(ref CalendarId calendarId, ref string localeName) |
| | | 162 | | { |
| | | 163 | | // |
| | | 164 | | // Windows doesn't support some calendars right now, so remap those. |
| | | 165 | | // |
| | 0 | 166 | | switch (calendarId) |
| | | 167 | | { |
| | | 168 | | case CalendarId.JAPANESELUNISOLAR: // Data looks like Japanese |
| | 0 | 169 | | calendarId = CalendarId.JAPAN; |
| | 0 | 170 | | break; |
| | | 171 | | case CalendarId.JULIAN: // Data looks like gregorian US |
| | | 172 | | case CalendarId.CHINESELUNISOLAR: // Algorithmic, so actual data is irrelevant |
| | | 173 | | case CalendarId.SAKA: // reserved to match Office but not implemented in our code, so da |
| | | 174 | | case CalendarId.LUNAR_ETO_CHN: // reserved to match Office but not implemented in our code, so da |
| | | 175 | | case CalendarId.LUNAR_ETO_KOR: // reserved to match Office but not implemented in our code, so da |
| | | 176 | | case CalendarId.LUNAR_ETO_ROKUYOU: // reserved to match Office but not implemented in our code, so da |
| | | 177 | | case CalendarId.KOREANLUNISOLAR: // Algorithmic, so actual data is irrelevant |
| | | 178 | | case CalendarId.TAIWANLUNISOLAR: // Algorithmic, so actual data is irrelevant |
| | 0 | 179 | | calendarId = CalendarId.GREGORIAN_US; |
| | | 180 | | break; |
| | | 181 | | } |
| | | 182 | | |
| | | 183 | | // |
| | | 184 | | // Special handling for some special calendar due to OS limitation. |
| | | 185 | | // This includes calendar like Taiwan calendar, UmAlQura calendar, etc. |
| | | 186 | | // |
| | 0 | 187 | | CheckSpecialCalendar(ref calendarId, ref localeName); |
| | 0 | 188 | | } |
| | | 189 | | |
| | | 190 | | //////////////////////////////////////////////////////////////////////// |
| | | 191 | | // |
| | | 192 | | // For calendars like Gregorain US/Taiwan/UmAlQura, they are not available |
| | | 193 | | // in all OS or all localized versions of OS. |
| | | 194 | | // If OS does not support these calendars, we will fallback by using the |
| | | 195 | | // appropriate fallback calendar and locale combination to retrieve data from OS. |
| | | 196 | | // |
| | | 197 | | // Parameters: |
| | | 198 | | // __deref_inout pCalendarInt: |
| | | 199 | | // Pointer to the calendar ID. This will be updated to new fallback calendar ID if needed. |
| | | 200 | | // __in_out pLocaleNameStackBuffer |
| | | 201 | | // Pointer to the StackSString object which holds the locale name to be checked. |
| | | 202 | | // This will be updated to new fallback locale name if needed. |
| | | 203 | | // |
| | | 204 | | //////////////////////////////////////////////////////////////////////// |
| | | 205 | | private static void CheckSpecialCalendar(ref CalendarId calendar, ref string localeName) |
| | | 206 | | { |
| | | 207 | | // Gregorian-US isn't always available in the OS, however it is the same for all locales |
| | 0 | 208 | | switch (calendar) |
| | | 209 | | { |
| | | 210 | | case CalendarId.GREGORIAN_US: |
| | | 211 | | // See if this works |
| | 0 | 212 | | if (!CallGetCalendarInfoEx(localeName, calendar, CAL_SCALNAME, out string _)) |
| | | 213 | | { |
| | | 214 | | // Failed, set it to a locale (fa-IR) that always has Gregorian US available in the OS |
| | 0 | 215 | | localeName = "fa-IR"; |
| | | 216 | | |
| | | 217 | | // See if that works |
| | 0 | 218 | | if (!CallGetCalendarInfoEx(localeName, calendar, CAL_SCALNAME, out string _)) |
| | | 219 | | { |
| | | 220 | | // Failed again, just use en-US with the gregorian calendar |
| | 0 | 221 | | localeName = "en-US"; |
| | 0 | 222 | | calendar = CalendarId.GREGORIAN; |
| | | 223 | | } |
| | | 224 | | } |
| | 0 | 225 | | break; |
| | | 226 | | case CalendarId.TAIWAN: |
| | | 227 | | // Taiwan calendar data is not always in all language version of OS due to Geopolical reasons. |
| | | 228 | | // It is only available in zh-TW localized versions of Windows. |
| | | 229 | | // Let's check if OS supports it. If not, fallback to Greogrian localized for Taiwan calendar. |
| | 0 | 230 | | if (!NlsSystemSupportsTaiwaneseCalendar()) |
| | | 231 | | { |
| | 0 | 232 | | calendar = CalendarId.GREGORIAN; |
| | | 233 | | } |
| | | 234 | | break; |
| | | 235 | | } |
| | 0 | 236 | | } |
| | | 237 | | |
| | | 238 | | private static unsafe bool CallEnumCalendarInfo(string localeName, CalendarId calendar, uint calType, uint lcTyp |
| | | 239 | | { |
| | 0 | 240 | | EnumData context = default; |
| | 0 | 241 | | context.userOverride = null; |
| | 0 | 242 | | context.strings = new List<string>(); |
| | | 243 | | // First call GetLocaleInfo if necessary |
| | 0 | 244 | | if ((lcType != 0) && ((lcType & CAL_NOUSEROVERRIDE) == 0)) |
| | | 245 | | { |
| | | 246 | | // They want user overrides, see if the user calendar matches the input calendar |
| | 0 | 247 | | CalendarId userCalendar = (CalendarId)CultureData.GetLocaleInfoExInt(localeName, LOCALE_ICALENDARTYPE); |
| | | 248 | | |
| | | 249 | | // If the calendars were the same, see if the locales were the same |
| | 0 | 250 | | if (userCalendar == calendar) |
| | | 251 | | { |
| | | 252 | | // They matched, get the user override since locale & calendar match |
| | 0 | 253 | | string? res = CultureData.GetLocaleInfoEx(localeName, lcType); |
| | | 254 | | |
| | | 255 | | // if it succeeded remember the override for the later callers |
| | 0 | 256 | | if (res != null) |
| | | 257 | | { |
| | | 258 | | // Remember this was the override (so we can look for duplicates later in the enum function) |
| | 0 | 259 | | context.userOverride = res; |
| | | 260 | | |
| | | 261 | | // Add to the result strings. |
| | 0 | 262 | | context.strings.Add(res); |
| | | 263 | | } |
| | | 264 | | } |
| | | 265 | | } |
| | | 266 | | |
| | | 267 | | // Now call the enumeration API. Work is done by our callback function |
| | 0 | 268 | | Interop.Kernel32.EnumCalendarInfoExEx(&EnumCalendarInfoCallback, localeName, (uint)calendar, null, calType, |
| | | 269 | | |
| | | 270 | | // Now we have a list of data, fail if we didn't find anything. |
| | 0 | 271 | | Debug.Assert(context.strings != null); |
| | 0 | 272 | | if (context.strings.Count == 0) |
| | | 273 | | { |
| | 0 | 274 | | data = null; |
| | 0 | 275 | | return false; |
| | | 276 | | } |
| | | 277 | | |
| | 0 | 278 | | string[] output = context.strings.ToArray(); |
| | | 279 | | |
| | 0 | 280 | | if (calType == CAL_SABBREVERASTRING || calType == CAL_SERASTRING) |
| | | 281 | | { |
| | | 282 | | // Eras are enumerated backwards. (oldest era name first, but |
| | | 283 | | // Japanese calendar has newest era first in array, and is only |
| | | 284 | | // calendar with multiple eras) |
| | 0 | 285 | | Array.Reverse(output, 0, output.Length); |
| | | 286 | | } |
| | | 287 | | |
| | 0 | 288 | | data = output; |
| | | 289 | | |
| | 0 | 290 | | return true; |
| | | 291 | | } |
| | | 292 | | |
| | | 293 | | //////////////////////////////////////////////////////////////////////// |
| | | 294 | | // |
| | | 295 | | // Get the native day names |
| | | 296 | | // |
| | | 297 | | // NOTE: There's a disparity between .NET & windows day orders, the input day should |
| | | 298 | | // start with Sunday |
| | | 299 | | // |
| | | 300 | | // Parameters: |
| | | 301 | | // OUT pOutputStrings The output string[] value. |
| | | 302 | | // |
| | | 303 | | //////////////////////////////////////////////////////////////////////// |
| | | 304 | | private static bool GetCalendarDayInfo(string localeName, CalendarId calendar, uint calType, out string[] output |
| | | 305 | | { |
| | 0 | 306 | | bool result = true; |
| | | 307 | | |
| | | 308 | | // |
| | | 309 | | // We'll need a new array of 7 items |
| | | 310 | | // |
| | 0 | 311 | | string[] results = new string[7]; |
| | | 312 | | |
| | | 313 | | // Get each one of them |
| | 0 | 314 | | for (int i = 0; i < 7; i++, calType++) |
| | | 315 | | { |
| | 0 | 316 | | result &= CallGetCalendarInfoEx(localeName, calendar, calType, out results[i]); |
| | | 317 | | |
| | | 318 | | // On the first iteration we need to go from CAL_SDAYNAME7 to CAL_SDAYNAME1, so subtract 7 before the ++ |
| | | 319 | | // This is because the framework starts on sunday and windows starts on monday when counting days |
| | 0 | 320 | | if (i == 0) |
| | 0 | 321 | | calType -= 7; |
| | | 322 | | } |
| | | 323 | | |
| | 0 | 324 | | outputStrings = results; |
| | | 325 | | |
| | 0 | 326 | | return result; |
| | | 327 | | } |
| | | 328 | | |
| | | 329 | | //////////////////////////////////////////////////////////////////////// |
| | | 330 | | // |
| | | 331 | | // Get the native month names |
| | | 332 | | // |
| | | 333 | | // Parameters: |
| | | 334 | | // OUT pOutputStrings The output string[] value. |
| | | 335 | | // |
| | | 336 | | //////////////////////////////////////////////////////////////////////// |
| | | 337 | | private static bool GetCalendarMonthInfo(string localeName, CalendarId calendar, uint calType, out string[] outp |
| | | 338 | | { |
| | | 339 | | // |
| | | 340 | | // We'll need a new array of 13 items |
| | | 341 | | // |
| | 0 | 342 | | string[] results = new string[13]; |
| | | 343 | | |
| | | 344 | | // Get each one of them |
| | 0 | 345 | | for (int i = 0; i < 13; i++, calType++) |
| | | 346 | | { |
| | 0 | 347 | | if (!CallGetCalendarInfoEx(localeName, calendar, calType, out results[i])) |
| | 0 | 348 | | results[i] = ""; |
| | | 349 | | } |
| | | 350 | | |
| | 0 | 351 | | outputStrings = results; |
| | | 352 | | |
| | 0 | 353 | | return true; |
| | | 354 | | } |
| | | 355 | | |
| | | 356 | | internal static unsafe int GetCalendarsCore(string localeName, bool useUserOverride, CalendarId[] calendars) |
| | | 357 | | { |
| | 0 | 358 | | Debug.Assert(!GlobalizationMode.Invariant); |
| | | 359 | | |
| | 0 | 360 | | if (GlobalizationMode.UseNls) |
| | | 361 | | { |
| | 0 | 362 | | return NlsGetCalendars(localeName, useUserOverride, calendars); |
| | | 363 | | } |
| | | 364 | | |
| | 0 | 365 | | int count = IcuGetCalendars(localeName, calendars); |
| | | 366 | | |
| | 0 | 367 | | if (useUserOverride) |
| | | 368 | | { |
| | | 369 | | // They want user overrides, see if the user calendar matches the input calendar |
| | 0 | 370 | | int userCalendar = CultureData.GetLocaleInfoExInt(localeName, LOCALE_ICALENDARTYPE); |
| | | 371 | | |
| | 0 | 372 | | if (userCalendar != 0 && (CalendarId)userCalendar != calendars[0]) |
| | | 373 | | { |
| | 0 | 374 | | CalendarId userOverride = (CalendarId)userCalendar; |
| | 0 | 375 | | for (int i = 1; i < calendars.Length; i++) |
| | | 376 | | { |
| | 0 | 377 | | if (calendars[i] == userOverride) |
| | | 378 | | { |
| | 0 | 379 | | CalendarId tmp = calendars[0]; |
| | 0 | 380 | | calendars[0] = userOverride; |
| | 0 | 381 | | calendars[i] = tmp; |
| | 0 | 382 | | return count; |
| | | 383 | | } |
| | | 384 | | } |
| | | 385 | | |
| | | 386 | | // We didn't find it, we insert it at the beginning of the array. If calendar's array is full, we dr |
| | 0 | 387 | | count = count < calendars.Length ? count + 1 : count; |
| | 0 | 388 | | Span<CalendarId> tmpSpan = stackalloc CalendarId[count]; // should be 23 max. |
| | 0 | 389 | | tmpSpan[0] = userOverride; |
| | 0 | 390 | | calendars.AsSpan(0, count - 1).CopyTo(tmpSpan.Slice(1)); |
| | 0 | 391 | | tmpSpan.CopyTo(calendars); |
| | | 392 | | } |
| | | 393 | | } |
| | | 394 | | |
| | 0 | 395 | | return count; |
| | | 396 | | } |
| | | 397 | | |
| | | 398 | | private static int NlsGetCalendars(string localeName, bool useUserOverride, CalendarId[] calendars) |
| | | 399 | | { |
| | 0 | 400 | | NlsEnumCalendarsData data = default; |
| | 0 | 401 | | data.userOverride = 0; |
| | 0 | 402 | | data.calendars = new List<int>(); |
| | | 403 | | |
| | | 404 | | // First call GetLocaleInfo if necessary |
| | 0 | 405 | | if (useUserOverride) |
| | | 406 | | { |
| | | 407 | | // They want user overrides, see if the user calendar matches the input calendar |
| | 0 | 408 | | int userCalendar = CultureData.GetLocaleInfoExInt(localeName, LOCALE_ICALENDARTYPE); |
| | | 409 | | |
| | | 410 | | // If we got a default, then use it as the first calendar |
| | 0 | 411 | | if (userCalendar != 0) |
| | | 412 | | { |
| | 0 | 413 | | data.userOverride = userCalendar; |
| | 0 | 414 | | data.calendars.Add(userCalendar); |
| | | 415 | | } |
| | | 416 | | } |
| | | 417 | | |
| | | 418 | | unsafe |
| | | 419 | | { |
| | 0 | 420 | | Interop.Kernel32.EnumCalendarInfoExEx(&EnumCalendarsCallback, localeName, ENUM_ALL_CALENDARS, null, CAL_ |
| | | 421 | | } |
| | | 422 | | |
| | | 423 | | // Copy to the output array |
| | 0 | 424 | | for (int i = 0; i < Math.Min(calendars.Length, data.calendars.Count); i++) |
| | 0 | 425 | | calendars[i] = (CalendarId)data.calendars[i]; |
| | | 426 | | |
| | | 427 | | // Now we have a list of data, return the count |
| | 0 | 428 | | return data.calendars.Count; |
| | | 429 | | } |
| | | 430 | | |
| | | 431 | | // Get native two digit year max |
| | | 432 | | internal static int GetTwoDigitYearMax(CalendarId calendarId) |
| | | 433 | | { |
| | 0 | 434 | | return GlobalizationMode.Invariant ? Invariant.iTwoDigitYearMax : |
| | 0 | 435 | | CallGetCalendarInfoEx(null, calendarId, CAL_ITWODIGITYEARMAX, out int twoDigitYearMax) ? |
| | 0 | 436 | | twoDigitYearMax : |
| | 0 | 437 | | -1; |
| | | 438 | | } |
| | | 439 | | } |
| | | 440 | | } |
| | | 441 | | |