Using running process to determine mono exe path on windows
[mono.git] / mcs / class / referencesource / mscorlib / system / globalization / japanesecalendar.cs
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         //
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
165         private static EraInfo[] GetErasFromRegistry()
166         {
167             // Look in the registry key and see if we can find any ranges
168             int iFoundEras = 0;
169             EraInfo[] registryEraRanges = null;
170 #if !MONO            
171             try
172             {
173                 // Need to access registry
174                 PermissionSet permSet = new PermissionSet(PermissionState.None);
175                 permSet.AddPermission(new RegistryPermission(RegistryPermissionAccess.Read, c_japaneseErasHivePermissionList));
176                 permSet.Assert();
177                 RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey(c_japaneseErasHive, false);
178
179                 // Abort if we didn't find anything
180                 if (key == null) return null;
181
182                 // Look up the values in our reg key
183                 String[] valueNames = key.GetValueNames();
184                 if (valueNames != null && valueNames.Length > 0)
185                 {
186                     registryEraRanges = new EraInfo[valueNames.Length];
187
188                     // Loop through the registry and read in all the values
189                     for (int i = 0; i < valueNames.Length; i++)
190                     {
191                         // See if the era is a valid date
192                         EraInfo era = GetEraFromValue(valueNames[i], key.GetValue(valueNames[i]).ToString());
193
194                         // continue if not valid
195                         if (era == null) continue;
196
197                         // Remember we found one.
198                         registryEraRanges[iFoundEras] = era;
199                         iFoundEras++;                        
200                     }
201                 }
202             }
203             catch (System.Security.SecurityException)
204             {
205                 // If we weren't allowed to read, then just ignore the error
206                 return null;
207             }
208             catch (System.IO.IOException)
209             {
210                 // If key is being deleted just ignore the error
211                 return null;
212             }
213             catch (System.UnauthorizedAccessException)
214             {
215                 // Registry access rights permissions, just ignore the error
216                 return null;
217             }
218
219             //
220             // If we didn't have valid eras, then fail
221             // should have at least 4 eras
222             //
223             if (iFoundEras < 4) return null;
224
225             //
226             // Now we have eras, clean them up.
227             //
228             // Clean up array length
229             Array.Resize(ref registryEraRanges, iFoundEras);
230
231             // Sort them
232             Array.Sort(registryEraRanges, CompareEraRanges);
233
234             // Clean up era information
235             for (int i = 0; i < registryEraRanges.Length; i++)
236             {
237                 // eras count backwards from length to 1 (and are 1 based indexes into string arrays)
238                 registryEraRanges[i].era = registryEraRanges.Length - i;
239
240                 // update max era year
241                 if (i == 0)
242                 {
243                     // First range is 'til the end of the calendar
244                     registryEraRanges[0].maxEraYear = GregorianCalendar.MaxYear - registryEraRanges[0].yearOffset;
245                 }
246                 else
247                 {
248                     // Rest are until the next era (remember most recent era is first in array)
249                     registryEraRanges[i].maxEraYear = registryEraRanges[i-1].yearOffset + 1 - registryEraRanges[i].yearOffset;
250                 }
251             }
252 #endif
253             // Return our ranges
254             return registryEraRanges;
255         }
256
257         //
258         // Compare two era ranges, eg just the ticks
259         // Remember the era array is supposed to be in reverse chronological order
260         //
261         private static int CompareEraRanges(EraInfo a, EraInfo b)
262         {
263             return b.ticks.CompareTo(a.ticks);
264         }
265
266         //
267         // GetEraFromValue
268         //
269         // Parse the registry value name/data pair into an era
270         //
271         // Registry values look like:
272         //      yyyy.mm.dd=era_abbrev_english_englishabbrev
273         //
274         // Where yyyy.mm.dd is the registry value name, and also the date of the era start.
275         // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long)
276         // era is the Japanese Era name
277         // abbrev is the Abbreviated Japanese Era Name
278         // english is the English name for the Era (unused)
279         // englishabbrev is the Abbreviated English name for the era.
280         // . is a delimiter, but the value of . doesn't matter.
281         // '_' marks the space between the japanese era name, japanese abbreviated era name
282         //     english name, and abbreviated english names.
283         private static EraInfo GetEraFromValue(String value, String data)
284         {
285             // Need inputs
286             if (value == null || data == null) return null;
287             
288             //
289             // Get Date
290             //
291             // Need exactly 10 characters in name for date
292             // yyyy.mm.dd although the . can be any character
293             if (value.Length != 10) return null;
294
295             int year;
296             int month;
297             int day;
298
299             if (!Number.TryParseInt32(value.Substring(0,4), NumberStyles.None, NumberFormatInfo.InvariantInfo, out year) ||
300                 !Number.TryParseInt32(value.Substring(5,2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out month) ||
301                 !Number.TryParseInt32(value.Substring(8,2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out day))
302             {
303                 // Couldn't convert integer, fail
304                 return null;
305             }
306
307             //
308             // Get Strings
309             //
310             // Needs to be a certain length e_a_E_A at least (7 chars, exactly 4 groups)
311             String[] names = data.Split(new char[] {'_'});
312
313             // Should have exactly 4 parts
314             // 0 - Era Name
315             // 1 - Abbreviated Era Name
316             // 2 - English Era Name
317             // 3 - Abbreviated English Era Name
318             if (names.Length != 4) return null;
319
320             // Each part should have data in it
321             if (names[0].Length == 0 ||
322                 names[1].Length == 0 ||
323                 names[2].Length == 0 ||
324                 names[3].Length == 0)
325                 return null;
326
327             //
328             // Now we have an era we can build
329             // Note that the era # and max era year need cleaned up after sorting
330             // Don't use the full English Era Name (names[2])
331             //
332             return new EraInfo( 0, year, month, day, year - 1, 1, 0, 
333                                 names[0], names[1], names[3]);
334         }
335
336         internal static volatile Calendar s_defaultInstance;
337         internal GregorianCalendarHelper helper;
338
339         /*=================================GetDefaultInstance==========================
340         **Action: Internal method to provide a default intance of JapaneseCalendar.  Used by NLS+ implementation
341         **       and other calendars.
342         **Returns:
343         **Arguments:
344         **Exceptions:
345         ============================================================================*/
346
347         internal static Calendar GetDefaultInstance() {
348             if (s_defaultInstance == null) {
349                 s_defaultInstance = new JapaneseCalendar();
350             }
351             return (s_defaultInstance);
352         }
353
354
355         public JapaneseCalendar() {
356             try {
357                 new CultureInfo("ja-JP");
358             } catch (ArgumentException e) {
359                 throw new TypeInitializationException(this.GetType().FullName, e);
360             }
361             helper = new GregorianCalendarHelper(this, GetEraInfo());
362         }
363
364         internal override int ID {
365             get {
366                 return (CAL_JAPAN);
367             }
368         }
369
370
371         public override DateTime AddMonths(DateTime time, int months) {
372             return (helper.AddMonths(time, months));
373         }
374
375
376         public override DateTime AddYears(DateTime time, int years) {
377             return (helper.AddYears(time, years));
378         }
379
380         /*=================================GetDaysInMonth==========================
381         **Action: Returns the number of days in the month given by the year and month arguments.
382         **Returns: The number of days in the given month.
383         **Arguments:
384         **      year The year in Japanese calendar.
385         **      month The month
386         **      era     The Japanese era value.
387         **Exceptions
388         **  ArgumentException  If month is less than 1 or greater * than 12.
389         ============================================================================*/
390
391
392         public override int GetDaysInMonth(int year, int month, int era) {
393             return (helper.GetDaysInMonth(year, month, era));
394         }
395
396
397         public override int GetDaysInYear(int year, int era) {
398             return (helper.GetDaysInYear(year, era));
399         }
400
401
402         public override int GetDayOfMonth(DateTime time) {
403             return (helper.GetDayOfMonth(time));
404         }
405
406
407         public override DayOfWeek GetDayOfWeek(DateTime time)  {
408             return (helper.GetDayOfWeek(time));
409         }
410
411
412         public override int GetDayOfYear(DateTime time)
413         {
414             return (helper.GetDayOfYear(time));
415         }
416
417
418         public override int GetMonthsInYear(int year, int era)
419         {
420             return (helper.GetMonthsInYear(year, era));
421         }
422
423
424         [SuppressMessage("Microsoft.Contracts", "CC1055")]  // Skip extra error checking to avoid *potential* AppCompat problems.
425         [System.Runtime.InteropServices.ComVisible(false)]
426         public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek)
427         {
428             return (helper.GetWeekOfYear(time, rule, firstDayOfWeek));
429         }
430
431         /*=================================GetEra==========================
432         **Action: Get the era value of the specified time.
433         **Returns: The era value for the specified time.
434         **Arguments:
435         **      time the specified date time.
436         **Exceptions: ArgumentOutOfRangeException if time is out of the valid era ranges.
437         ============================================================================*/
438
439
440         public override int GetEra(DateTime time) {
441             return (helper.GetEra(time));
442         }
443
444
445         public override int GetMonth(DateTime time) {
446             return (helper.GetMonth(time));
447             }
448
449
450         public override int GetYear(DateTime time) {
451             return (helper.GetYear(time));
452         }
453
454
455         public override bool IsLeapDay(int year, int month, int day, int era)
456         {
457             return (helper.IsLeapDay(year, month, day, era));
458         }
459
460
461         public override bool IsLeapYear(int year, int era) {
462             return (helper.IsLeapYear(year, era));
463         }
464
465         // Returns  the leap month in a calendar year of the specified era. This method returns 0
466         // if this calendar does not have leap month, or this year is not a leap year.
467         //
468
469         [System.Runtime.InteropServices.ComVisible(false)]
470         public override int GetLeapMonth(int year, int era)
471         {
472             return (helper.GetLeapMonth(year, era));
473         }
474
475
476         public override bool IsLeapMonth(int year, int month, int era) {
477             return (helper.IsLeapMonth(year, month, era));
478         }
479
480
481         public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) {
482             return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era));
483         }
484
485         // For Japanese calendar, four digit year is not used.  Few emperors will live for more than one hundred years.
486         // Therefore, for any two digit number, we just return the original number.
487
488         public override int ToFourDigitYear(int year) {
489             if (year <= 0) {
490                 throw new ArgumentOutOfRangeException("year",
491                     Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
492             }
493             Contract.EndContractBlock();
494
495             if (year > helper.MaxYear) {
496                 throw new ArgumentOutOfRangeException(
497                             "year",
498                             String.Format(
499                                 CultureInfo.CurrentCulture,
500                                 Environment.GetResourceString("ArgumentOutOfRange_Range"),
501                                 1,
502                                 helper.MaxYear));
503             }
504             return (year);
505         }
506
507
508         public override int[] Eras {
509             get {
510                 return (helper.Eras);
511             }
512         }
513
514         //
515         // Return the various era strings
516         // Note: The arrays are backwards of the eras
517         //
518         internal static String[] EraNames()
519         {
520             EraInfo[] eras = GetEraInfo();
521             String[] eraNames = new String[eras.Length];
522
523             for (int i = 0; i < eras.Length; i++)
524             {
525                 // Strings are in chronological order, eras are backwards order.
526                 eraNames[i] = eras[eras.Length - i - 1].eraName;
527             }
528
529             return eraNames;
530         }
531
532         internal static String[] AbbrevEraNames()
533         {
534             EraInfo[] eras = GetEraInfo();
535             String[] erasAbbrev = new String[eras.Length];
536
537             for (int i = 0; i < eras.Length; i++)
538             {
539                 // Strings are in chronological order, eras are backwards order.
540                 erasAbbrev[i] = eras[eras.Length - i - 1].abbrevEraName;
541             }
542
543             return erasAbbrev;
544         }
545
546         internal static String[] EnglishEraNames()
547         {
548             EraInfo[] eras = GetEraInfo();
549             String[] erasEnglish = new String[eras.Length];
550
551             for (int i = 0; i < eras.Length; i++)
552             {
553                 // Strings are in chronological order, eras are backwards order.
554                 erasEnglish[i] = eras[eras.Length - i - 1].englishEraName;
555             }
556
557             return erasEnglish;
558         }
559
560         private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 99;
561         
562         internal override bool IsValidYear(int year, int era) {
563             return helper.IsValidYear(year, era);
564         }       
565
566         public override int TwoDigitYearMax {
567             get {
568                 if (twoDigitYearMax == -1) {
569                     twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
570                 }
571                 return (twoDigitYearMax);
572             }
573
574             set {
575                 VerifyWritable();
576                 if (value < 99 || value > helper.MaxYear)
577                 {
578                     throw new ArgumentOutOfRangeException(
579                                 "year",
580                                 String.Format(
581                                     CultureInfo.CurrentCulture,
582                                     Environment.GetResourceString("ArgumentOutOfRange_Range"),
583                                     99,
584                                     helper.MaxYear));
585                 }
586                 twoDigitYearMax = value;
587             }
588         }
589     }
590 }