1 #if MONO 2 #define __APPLE__ 3 #endif 4 // ==++== 5 // 6 // Copyright (c) Microsoft Corporation. All rights reserved. 7 // 8 // ==--== 9 namespace System.Globalization { 10 11 using System; 12 using System.Diagnostics.CodeAnalysis; 13 using System.Diagnostics.Contracts; 14 using Microsoft.Win32; 15 using PermissionSet = System.Security.PermissionSet; 16 using System.Security.Permissions; 17 18 /*=================================JapaneseCalendar========================== 19 ** 20 ** JapaneseCalendar is based on Gregorian calendar. The month and day values are the same as 21 ** Gregorian calendar. However, the year value is an offset to the Gregorian 22 ** year based on the era. 23 ** 24 ** This system is adopted by Emperor Meiji in 1868. The year value is counted based on the reign of an emperor, 25 ** and the era begins on the day an emperor ascends the throne and continues until his death. 26 ** The era changes at 12:00AM. 27 ** 28 ** For example, the current era is Heisei. It started on 1989/1/8 A.D. Therefore, Gregorian year 1989 is also Heisei 1st. 29 ** 1989/1/8 A.D. is also Heisei 1st 1/8. 30 ** 31 ** Any date in the year during which era is changed can be reckoned in either era. For example, 32 ** 1989/1/1 can be 1/1 Heisei 1st year or 1/1 Showa 64th year. 33 ** 34 ** Note: 35 ** The DateTime can be represented by the JapaneseCalendar are limited to two factors: 36 ** 1. The min value and max value of DateTime class. 37 ** 2. The available era information. 38 ** 39 ** Calendar support range: 40 ** Calendar Minimum Maximum 41 ** ========== ========== ========== 42 ** Gregorian 1868/09/08 9999/12/31 43 ** Japanese Meiji 01/01 Heisei 8011/12/31 44 ============================================================================*/ 45 46 47 [Serializable] 48 [System.Runtime.InteropServices.ComVisible(true)] 49 public class JapaneseCalendar : Calendar 50 { 51 internal static readonly DateTime calendarMinValue = new DateTime(1868, 9, 8); 52 53 54 [System.Runtime.InteropServices.ComVisible(false)] 55 public override DateTime MinSupportedDateTime 56 { 57 get 58 { 59 return (calendarMinValue); 60 } 61 } 62 63 [System.Runtime.InteropServices.ComVisible(false)] 64 public override DateTime MaxSupportedDateTime 65 { 66 get 67 { 68 return (DateTime.MaxValue); 69 } 70 } 71 72 // Return the type of the Japanese calendar. 73 // 74 75 [System.Runtime.InteropServices.ComVisible(false)] 76 public override CalendarAlgorithmType AlgorithmType 77 { 78 get 79 { 80 return CalendarAlgorithmType.SolarCalendar; 81 } 82 } 83 84 // 85 // Using a field initializer rather than a static constructor so that the whole class can be lazy 86 // init. 87 static internal volatile EraInfo[] japaneseEraInfo; 88 89 private const string c_japaneseErasHive = @"System\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras"; 90 private const string c_japaneseErasHivePermissionList = @"HKEY_LOCAL_MACHINE\" + c_japaneseErasHive; 91 92 // 93 // Read our era info 94 // 95 // m_EraInfo must be listed in reverse chronological order. The most recent era 96 // should be the first element. 97 // That is, m_EraInfo[0] contains the most recent era. 98 // 99 // We know about 4 built-in eras, however users may add additional era(s) from the 100 // registry, by adding values to HKLM\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras 101 // 102 // Registry values look like: 103 // yyyy.mm.dd=era_abbrev_english_englishabbrev 104 // 105 // Where yyyy.mm.dd is the registry value name, and also the date of the era start. 106 // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long) 107 // era is the Japanese Era name 108 // abbrev is the Abbreviated Japanese Era Name 109 // english is the English name for the Era (unused) 110 // englishabbrev is the Abbreviated English name for the era. 111 // . is a delimiter, but the value of . doesn't matter. 112 // '_' marks the space between the japanese era name, japanese abbreviated era name 113 // english name, and abbreviated english names. 114 // GetEraInfo()115 internal static EraInfo[] GetEraInfo() 116 { 117 // See if we need to build it 118 if (japaneseEraInfo == null) 119 { 120 // See if we have any eras from the registry 121 japaneseEraInfo = GetErasFromRegistry(); 122 123 // See if we have to use the built-in eras 124 if (japaneseEraInfo == null) 125 { 126 // We know about some built-in ranges 127 EraInfo[] defaultEraRanges = new EraInfo[4]; 128 defaultEraRanges[0] = new EraInfo( 4, 1989, 1, 8, 1988, 1, GregorianCalendar.MaxYear - 1988, 129 "\x5e73\x6210", "\x5e73", "H"); // era #4 start year/month/day, yearOffset, minEraYear 130 defaultEraRanges[1] = new EraInfo( 3, 1926, 12, 25, 1925, 1, 1989-1925, 131 "\x662d\x548c", "\x662d", "S"); // era #3,start year/month/day, yearOffset, minEraYear 132 defaultEraRanges[2] = new EraInfo( 2, 1912, 7, 30, 1911, 1, 1926-1911, 133 "\x5927\x6b63", "\x5927", "T"); // era #2,start year/month/day, yearOffset, minEraYear 134 defaultEraRanges[3] = new EraInfo( 1, 1868, 1, 1, 1867, 1, 1912-1867, 135 "\x660e\x6cbb", "\x660e", "M"); // era #1,start year/month/day, yearOffset, minEraYear 136 137 // Remember the ranges we built 138 japaneseEraInfo = defaultEraRanges; 139 } 140 } 141 142 // return the era we found/made 143 return japaneseEraInfo; 144 } 145 146 // 147 // GetErasFromRegistry() 148 // 149 // We know about 4 built-in eras, however users may add additional era(s) from the 150 // registry, by adding values to HKLM\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras 151 // 152 // Registry values look like: 153 // yyyy.mm.dd=era_abbrev_english_englishabbrev 154 // 155 // Where yyyy.mm.dd is the registry value name, and also the date of the era start. 156 // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long) 157 // era is the Japanese Era name 158 // abbrev is the Abbreviated Japanese Era Name 159 // english is the English name for the Era (unused) 160 // englishabbrev is the Abbreviated English name for the era. 161 // . is a delimiter, but the value of . doesn't matter. 162 // '_' marks the space between the japanese era name, japanese abbreviated era name 163 // english name, and abbreviated english names. 164 [System.Security.SecuritySafeCritical] // auto-generated GetErasFromRegistry()165 private static EraInfo[] GetErasFromRegistry() 166 { 167 #if MONO 168 return null; 169 #else 170 // Look in the registry key and see if we can find any ranges 171 int iFoundEras = 0; 172 EraInfo[] registryEraRanges = null; 173 174 try 175 { 176 // Need to access registry 177 PermissionSet permSet = new PermissionSet(PermissionState.None); 178 permSet.AddPermission(new RegistryPermission(RegistryPermissionAccess.Read, c_japaneseErasHivePermissionList)); 179 permSet.Assert(); 180 RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey(c_japaneseErasHive, false); 181 182 // Abort if we didn't find anything 183 if (key == null) return null; 184 185 // Look up the values in our reg key 186 String[] valueNames = key.GetValueNames(); 187 if (valueNames != null && valueNames.Length > 0) 188 { 189 registryEraRanges = new EraInfo[valueNames.Length]; 190 191 // Loop through the registry and read in all the values 192 for (int i = 0; i < valueNames.Length; i++) 193 { 194 // See if the era is a valid date 195 EraInfo era = GetEraFromValue(valueNames[i], key.GetValue(valueNames[i]).ToString()); 196 197 // continue if not valid 198 if (era == null) continue; 199 200 // Remember we found one. 201 registryEraRanges[iFoundEras] = era; 202 iFoundEras++; 203 } 204 } 205 } 206 catch (System.Security.SecurityException) 207 { 208 // If we weren't allowed to read, then just ignore the error 209 return null; 210 } 211 catch (System.IO.IOException) 212 { 213 // If key is being deleted just ignore the error 214 return null; 215 } 216 catch (System.UnauthorizedAccessException) 217 { 218 // Registry access rights permissions, just ignore the error 219 return null; 220 } 221 222 // 223 // If we didn't have valid eras, then fail 224 // should have at least 4 eras 225 // 226 if (iFoundEras < 4) return null; 227 228 // 229 // Now we have eras, clean them up. 230 // 231 // Clean up array length 232 Array.Resize(ref registryEraRanges, iFoundEras); 233 234 // Sort them 235 Array.Sort(registryEraRanges, CompareEraRanges); 236 237 // Clean up era information 238 for (int i = 0; i < registryEraRanges.Length; i++) 239 { 240 // eras count backwards from length to 1 (and are 1 based indexes into string arrays) 241 registryEraRanges[i].era = registryEraRanges.Length - i; 242 243 // update max era year 244 if (i == 0) 245 { 246 // First range is 'til the end of the calendar 247 registryEraRanges[0].maxEraYear = GregorianCalendar.MaxYear - registryEraRanges[0].yearOffset; 248 } 249 else 250 { 251 // Rest are until the next era (remember most recent era is first in array) 252 registryEraRanges[i].maxEraYear = registryEraRanges[i-1].yearOffset + 1 - registryEraRanges[i].yearOffset; 253 } 254 } 255 256 // Return our ranges 257 return registryEraRanges; 258 #endif 259 } 260 261 // 262 // Compare two era ranges, eg just the ticks 263 // Remember the era array is supposed to be in reverse chronological order 264 // CompareEraRanges(EraInfo a, EraInfo b)265 private static int CompareEraRanges(EraInfo a, EraInfo b) 266 { 267 return b.ticks.CompareTo(a.ticks); 268 } 269 270 // 271 // GetEraFromValue 272 // 273 // Parse the registry value name/data pair into an era 274 // 275 // Registry values look like: 276 // yyyy.mm.dd=era_abbrev_english_englishabbrev 277 // 278 // Where yyyy.mm.dd is the registry value name, and also the date of the era start. 279 // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long) 280 // era is the Japanese Era name 281 // abbrev is the Abbreviated Japanese Era Name 282 // english is the English name for the Era (unused) 283 // englishabbrev is the Abbreviated English name for the era. 284 // . is a delimiter, but the value of . doesn't matter. 285 // '_' marks the space between the japanese era name, japanese abbreviated era name 286 // english name, and abbreviated english names. GetEraFromValue(String value, String data)287 private static EraInfo GetEraFromValue(String value, String data) 288 { 289 // Need inputs 290 if (value == null || data == null) return null; 291 292 // 293 // Get Date 294 // 295 // Need exactly 10 characters in name for date 296 // yyyy.mm.dd although the . can be any character 297 if (value.Length != 10) return null; 298 299 int year; 300 int month; 301 int day; 302 303 if (!Number.TryParseInt32(value.Substring(0,4), NumberStyles.None, NumberFormatInfo.InvariantInfo, out year) || 304 !Number.TryParseInt32(value.Substring(5,2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out month) || 305 !Number.TryParseInt32(value.Substring(8,2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out day)) 306 { 307 // Couldn't convert integer, fail 308 return null; 309 } 310 311 // 312 // Get Strings 313 // 314 // Needs to be a certain length e_a_E_A at least (7 chars, exactly 4 groups) 315 String[] names = data.Split(new char[] {'_'}); 316 317 // Should have exactly 4 parts 318 // 0 - Era Name 319 // 1 - Abbreviated Era Name 320 // 2 - English Era Name 321 // 3 - Abbreviated English Era Name 322 if (names.Length != 4) return null; 323 324 // Each part should have data in it 325 if (names[0].Length == 0 || 326 names[1].Length == 0 || 327 names[2].Length == 0 || 328 names[3].Length == 0) 329 return null; 330 331 // 332 // Now we have an era we can build 333 // Note that the era # and max era year need cleaned up after sorting 334 // Don't use the full English Era Name (names[2]) 335 // 336 return new EraInfo( 0, year, month, day, year - 1, 1, 0, 337 names[0], names[1], names[3]); 338 } 339 340 internal static volatile Calendar s_defaultInstance; 341 internal GregorianCalendarHelper helper; 342 343 /*=================================GetDefaultInstance========================== 344 **Action: Internal method to provide a default intance of JapaneseCalendar. Used by NLS+ implementation 345 ** and other calendars. 346 **Returns: 347 **Arguments: 348 **Exceptions: 349 ============================================================================*/ 350 GetDefaultInstance()351 internal static Calendar GetDefaultInstance() { 352 if (s_defaultInstance == null) { 353 s_defaultInstance = new JapaneseCalendar(); 354 } 355 return (s_defaultInstance); 356 } 357 358 JapaneseCalendar()359 public JapaneseCalendar() { 360 try { 361 new CultureInfo("ja-JP"); 362 } catch (ArgumentException e) { 363 throw new TypeInitializationException(this.GetType().FullName, e); 364 } 365 helper = new GregorianCalendarHelper(this, GetEraInfo()); 366 } 367 368 internal override int ID { 369 get { 370 return (CAL_JAPAN); 371 } 372 } 373 374 AddMonths(DateTime time, int months)375 public override DateTime AddMonths(DateTime time, int months) { 376 return (helper.AddMonths(time, months)); 377 } 378 379 AddYears(DateTime time, int years)380 public override DateTime AddYears(DateTime time, int years) { 381 return (helper.AddYears(time, years)); 382 } 383 384 /*=================================GetDaysInMonth========================== 385 **Action: Returns the number of days in the month given by the year and month arguments. 386 **Returns: The number of days in the given month. 387 **Arguments: 388 ** year The year in Japanese calendar. 389 ** month The month 390 ** era The Japanese era value. 391 **Exceptions 392 ** ArgumentException If month is less than 1 or greater * than 12. 393 ============================================================================*/ 394 395 GetDaysInMonth(int year, int month, int era)396 public override int GetDaysInMonth(int year, int month, int era) { 397 return (helper.GetDaysInMonth(year, month, era)); 398 } 399 400 GetDaysInYear(int year, int era)401 public override int GetDaysInYear(int year, int era) { 402 return (helper.GetDaysInYear(year, era)); 403 } 404 405 GetDayOfMonth(DateTime time)406 public override int GetDayOfMonth(DateTime time) { 407 return (helper.GetDayOfMonth(time)); 408 } 409 410 GetDayOfWeek(DateTime time)411 public override DayOfWeek GetDayOfWeek(DateTime time) { 412 return (helper.GetDayOfWeek(time)); 413 } 414 415 GetDayOfYear(DateTime time)416 public override int GetDayOfYear(DateTime time) 417 { 418 return (helper.GetDayOfYear(time)); 419 } 420 421 GetMonthsInYear(int year, int era)422 public override int GetMonthsInYear(int year, int era) 423 { 424 return (helper.GetMonthsInYear(year, era)); 425 } 426 427 428 [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems. 429 [System.Runtime.InteropServices.ComVisible(false)] GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek)430 public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) 431 { 432 return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); 433 } 434 435 /*=================================GetEra========================== 436 **Action: Get the era value of the specified time. 437 **Returns: The era value for the specified time. 438 **Arguments: 439 ** time the specified date time. 440 **Exceptions: ArgumentOutOfRangeException if time is out of the valid era ranges. 441 ============================================================================*/ 442 443 GetEra(DateTime time)444 public override int GetEra(DateTime time) { 445 return (helper.GetEra(time)); 446 } 447 448 GetMonth(DateTime time)449 public override int GetMonth(DateTime time) { 450 return (helper.GetMonth(time)); 451 } 452 453 GetYear(DateTime time)454 public override int GetYear(DateTime time) { 455 return (helper.GetYear(time)); 456 } 457 458 IsLeapDay(int year, int month, int day, int era)459 public override bool IsLeapDay(int year, int month, int day, int era) 460 { 461 return (helper.IsLeapDay(year, month, day, era)); 462 } 463 464 IsLeapYear(int year, int era)465 public override bool IsLeapYear(int year, int era) { 466 return (helper.IsLeapYear(year, era)); 467 } 468 469 // Returns the leap month in a calendar year of the specified era. This method returns 0 470 // if this calendar does not have leap month, or this year is not a leap year. 471 // 472 473 [System.Runtime.InteropServices.ComVisible(false)] GetLeapMonth(int year, int era)474 public override int GetLeapMonth(int year, int era) 475 { 476 return (helper.GetLeapMonth(year, era)); 477 } 478 479 IsLeapMonth(int year, int month, int era)480 public override bool IsLeapMonth(int year, int month, int era) { 481 return (helper.IsLeapMonth(year, month, era)); 482 } 483 484 ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)485 public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) { 486 return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era)); 487 } 488 489 // For Japanese calendar, four digit year is not used. Few emperors will live for more than one hundred years. 490 // Therefore, for any two digit number, we just return the original number. 491 ToFourDigitYear(int year)492 public override int ToFourDigitYear(int year) { 493 if (year <= 0) { 494 throw new ArgumentOutOfRangeException("year", 495 Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); 496 } 497 Contract.EndContractBlock(); 498 499 if (year > helper.MaxYear) { 500 throw new ArgumentOutOfRangeException( 501 "year", 502 String.Format( 503 CultureInfo.CurrentCulture, 504 Environment.GetResourceString("ArgumentOutOfRange_Range"), 505 1, 506 helper.MaxYear)); 507 } 508 return (year); 509 } 510 511 512 public override int[] Eras { 513 get { 514 return (helper.Eras); 515 } 516 } 517 518 // 519 // Return the various era strings 520 // Note: The arrays are backwards of the eras 521 // EraNames()522 internal static String[] EraNames() 523 { 524 EraInfo[] eras = GetEraInfo(); 525 String[] eraNames = new String[eras.Length]; 526 527 for (int i = 0; i < eras.Length; i++) 528 { 529 // Strings are in chronological order, eras are backwards order. 530 eraNames[i] = eras[eras.Length - i - 1].eraName; 531 } 532 533 return eraNames; 534 } 535 AbbrevEraNames()536 internal static String[] AbbrevEraNames() 537 { 538 EraInfo[] eras = GetEraInfo(); 539 String[] erasAbbrev = new String[eras.Length]; 540 541 for (int i = 0; i < eras.Length; i++) 542 { 543 // Strings are in chronological order, eras are backwards order. 544 erasAbbrev[i] = eras[eras.Length - i - 1].abbrevEraName; 545 } 546 547 return erasAbbrev; 548 } 549 EnglishEraNames()550 internal static String[] EnglishEraNames() 551 { 552 EraInfo[] eras = GetEraInfo(); 553 String[] erasEnglish = new String[eras.Length]; 554 555 for (int i = 0; i < eras.Length; i++) 556 { 557 // Strings are in chronological order, eras are backwards order. 558 erasEnglish[i] = eras[eras.Length - i - 1].englishEraName; 559 } 560 561 return erasEnglish; 562 } 563 564 private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 99; 565 IsValidYear(int year, int era)566 internal override bool IsValidYear(int year, int era) { 567 return helper.IsValidYear(year, era); 568 } 569 570 public override int TwoDigitYearMax { 571 get { 572 if (twoDigitYearMax == -1) { 573 twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); 574 } 575 return (twoDigitYearMax); 576 } 577 578 set { 579 VerifyWritable(); 580 if (value < 99 || value > helper.MaxYear) 581 { 582 throw new ArgumentOutOfRangeException( 583 "year", 584 String.Format( 585 CultureInfo.CurrentCulture, 586 Environment.GetResourceString("ArgumentOutOfRange_Range"), 587 99, 588 helper.MaxYear)); 589 } 590 twoDigitYearMax = value; 591 } 592 } 593 } 594 } 595