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