Using running process to determine mono exe path on windows
[mono.git] / mcs / class / referencesource / mscorlib / system / globalization / datetimeformatinfo.cs
1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6
7 namespace System.Globalization {
8     using System;
9     using System.Security;
10     using System.Threading;
11     using System.Collections;
12     using System.Collections.Generic;
13     using System.Runtime.Serialization;
14     using System.Security.Permissions;
15     using System.Runtime.InteropServices;
16     using System.Runtime.Versioning;
17     using System.Text;
18     using System.Diagnostics.Contracts;
19
20     //
21     // Flags used to indicate different styles of month names.
22     // This is an internal flag used by internalGetMonthName().
23     // Use flag here in case that we need to provide a combination of these styles
24     // (such as month name of a leap year in genitive form.  Not likely for now,
25     // but would like to keep the option open).
26     //
27
28     [Flags]
29     internal enum MonthNameStyles {
30         Regular     = 0x00000000,
31         Genitive    = 0x00000001,
32         LeapYear    = 0x00000002,
33     }
34
35     //
36     // Flags used to indicate special rule used in parsing/formatting
37     // for a specific DateTimeFormatInfo instance.
38     // This is an internal flag.
39     //
40     // This flag is different from MonthNameStyles because this flag
41     // can be expanded to accomodate parsing behaviors like CJK month names
42     // or alternative month names, etc.
43
44     [Flags]
45     internal enum DateTimeFormatFlags {
46         None                    = 0x00000000,
47         UseGenitiveMonth        = 0x00000001,
48         UseLeapYearMonth        = 0x00000002,
49         UseSpacesInMonthNames   = 0x00000004, // Has spaces or non-breaking space in the month names.
50         UseHebrewRule           = 0x00000008,   // Format/Parse using the Hebrew calendar rule.
51         UseSpacesInDayNames     = 0x00000010,   // Has spaces or non-breaking space in the day names.
52         UseDigitPrefixInTokens  = 0x00000020,   // Has token starting with numbers.
53
54         NotInitialized          = -1,
55     }
56
57
58     [Serializable]
59 [System.Runtime.InteropServices.ComVisible(true)]
60     public sealed class DateTimeFormatInfo : ICloneable, IFormatProvider
61     {
62         //
63         // Note, some fields are derived so don't really need to be serialized, but we can't mark as
64         // optional because Whidbey was expecting them.  Post-Arrowhead we could fix that
65         // once Whidbey serialization is no longer necessary.
66         //
67     
68         // cache for the invariant culture.
69         // invariantInfo is constant irrespective of your current culture.
70         private static volatile DateTimeFormatInfo invariantInfo;
71
72         // an index which points to a record in Culture Data Table.
73         [NonSerialized]private CultureData m_cultureData;
74
75         // The culture name used to create this DTFI.
76         [OptionalField(VersionAdded = 2)]
77         internal String m_name = null;
78      
79         // The language name of the culture used to create this DTFI.
80         [NonSerialized]private String m_langName = null;
81
82         // CompareInfo usually used by the parser.
83         [NonSerialized]private CompareInfo m_compareInfo = null;
84
85         // Culture matches current DTFI. mainly used for string comparisons during parsing.
86         [NonSerialized]private CultureInfo m_cultureInfo = null;
87
88         //
89         // Caches for various properties.
90         //
91
92         // 
93
94         internal String amDesignator     = null;
95         internal String pmDesignator     = null;
96         [OptionalField(VersionAdded = 1)]
97         internal String dateSeparator    = null;            // derived from short date (whidbey expects, arrowhead doesn't)
98         [OptionalField(VersionAdded = 1)]
99         internal String generalShortTimePattern = null;     // short date + short time (whidbey expects, arrowhead doesn't)
100         [OptionalField(VersionAdded = 1)]
101         internal String generalLongTimePattern  = null;     // short date + long time (whidbey expects, arrowhead doesn't)
102         [OptionalField(VersionAdded = 1)]
103         internal String timeSeparator    = null;            // derived from long time (whidbey expects, arrowhead doesn't)
104         internal String monthDayPattern  = null;
105         [OptionalField(VersionAdded = 2)]                   // added in .NET Framework Release {2.0SP1/3.0SP1/3.5RTM}
106         internal String dateTimeOffsetPattern = null;
107
108         //
109         // The following are constant values.
110         //
111         internal const String rfc1123Pattern   = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'";
112
113         // The sortable pattern is based on ISO 8601.
114         internal const String sortableDateTimePattern = "yyyy'-'MM'-'dd'T'HH':'mm':'ss";
115         internal const String universalSortableDateTimePattern = "yyyy'-'MM'-'dd HH':'mm':'ss'Z'";
116
117         //
118         // The following are affected by calendar settings.
119         //
120         internal Calendar calendar  = null;
121
122         internal int firstDayOfWeek = -1;
123         internal int calendarWeekRule = -1;
124
125 [NonSerialized]
126         [OptionalField(VersionAdded = 1)]
127         internal String fullDateTimePattern  = null;        // long date + long time (whidbey expects, arrowhead doesn't)
128
129         internal String[] abbreviatedDayNames    = null;
130
131         [OptionalField(VersionAdded = 2)]
132         internal String[] m_superShortDayNames    = null;
133
134         internal String[] dayNames                 = null;
135         internal String[] abbreviatedMonthNames    = null;
136         internal String[] monthNames               = null;
137         // Cache the genitive month names that we retrieve from the data table.
138         [OptionalField(VersionAdded = 2)]
139         internal String[] genitiveMonthNames       = null;
140
141         // Cache the abbreviated genitive month names that we retrieve from the data table.
142         [OptionalField(VersionAdded = 2)]
143         internal String[] m_genitiveAbbreviatedMonthNames = null;
144
145         // Cache the month names of a leap year that we retrieve from the data table.
146         [OptionalField(VersionAdded = 2)]
147         internal String[] leapYearMonthNames     = null;
148
149         // For our "patterns" arrays we have 2 variables, a string and a string[]
150         //
151         // The string[] contains the list of patterns, EXCEPT the default may not be included.
152         // The string contains the default pattern.
153         // When we initially construct our string[], we set the string to string[0]
154
155         // The "default" Date/time patterns 
156         internal String longDatePattern  = null;
157         internal String shortDatePattern = null;
158         internal String yearMonthPattern = null;
159         internal String longTimePattern  = null;
160         internal String shortTimePattern = null;
161
162         // These are Whidbey-serialization compatable arrays (eg: default not included)
163         // "all" is a bit of a misnomer since the "default" pattern stored above isn't
164         // necessarily a member of the list
165         [OptionalField(VersionAdded = 3)]
166         private String[] allYearMonthPatterns   = null;   // This was wasn't serialized in Whidbey
167         internal String[] allShortDatePatterns   = null;
168         internal String[] allLongDatePatterns    = null;
169         internal String[] allShortTimePatterns   = null;
170         internal String[] allLongTimePatterns    = null;
171
172         // Cache the era names for this DateTimeFormatInfo instance.
173         internal String[] m_eraNames = null;
174         internal String[] m_abbrevEraNames = null;
175         internal String[] m_abbrevEnglishEraNames = null;
176
177         internal int[] optionalCalendars = null;
178
179         private const int DEFAULT_ALL_DATETIMES_SIZE = 132;
180
181         // CultureInfo updates this
182         internal bool m_isReadOnly=false;
183         
184         // This flag gives hints about if formatting/parsing should perform special code path for things like
185         // genitive form or leap year month names.
186         [OptionalField(VersionAdded = 2)]
187         internal DateTimeFormatFlags formatFlags = DateTimeFormatFlags.NotInitialized;
188         internal static bool preferExistingTokens = InitPreferExistingTokens();
189
190
191         [System.Security.SecuritySafeCritical]
192         static bool InitPreferExistingTokens()
193         {
194             bool ret = false;
195 #if !FEATURE_CORECLR && !MONO
196             ret = DateTime.LegacyParseMode();
197 #endif
198             return ret;
199         }
200
201
202         // 
203         private String CultureName
204         {
205             get
206             {
207                 if (m_name == null)
208                 {
209                     m_name = this.m_cultureData.CultureName;
210                 }
211                 return (m_name);
212             }
213         }
214
215         private CultureInfo Culture
216         {
217             get 
218             {
219                 if (m_cultureInfo == null)
220                 {
221                     m_cultureInfo = CultureInfo.GetCultureInfo(this.CultureName);
222                 }
223                 return m_cultureInfo;
224             }
225         }
226
227         // 
228         private String LanguageName
229         {
230             [System.Security.SecurityCritical]  // auto-generated
231             get
232             {
233                 if (m_langName == null)
234                 {
235                     m_langName = this.m_cultureData.SISO639LANGNAME;
236                 }
237                 return (m_langName);
238             }
239         }
240
241         ////////////////////////////////////////////////////////////////////////////
242         //
243         // Create an array of string which contains the abbreviated day names.
244         //
245         ////////////////////////////////////////////////////////////////////////////
246
247         private String[] internalGetAbbreviatedDayOfWeekNames()
248         {
249             if (this.abbreviatedDayNames == null)
250             {
251                 // Get the abbreviated day names for our current calendar
252                 this.abbreviatedDayNames = this.m_cultureData.AbbreviatedDayNames(Calendar.ID);
253                 Contract.Assert(this.abbreviatedDayNames.Length == 7, "[DateTimeFormatInfo.GetAbbreviatedDayOfWeekNames] Expected 7 day names in a week");
254             }
255             return (this.abbreviatedDayNames);
256         }
257
258         ////////////////////////////////////////////////////////////////////////
259         //
260         // Action: Returns the string array of the one-letter day of week names.
261         // Returns:
262         //  an array of one-letter day of week names
263         // Arguments:
264         //  None
265         // Exceptions:
266         //  None
267         //
268         ////////////////////////////////////////////////////////////////////////
269
270         private String[] internalGetSuperShortDayNames()
271         {
272             if (this.m_superShortDayNames == null)
273             {
274                 // Get the super short day names for our current calendar
275                 this.m_superShortDayNames = this.m_cultureData.SuperShortDayNames(Calendar.ID);
276                 Contract.Assert(this.m_superShortDayNames.Length == 7, "[DateTimeFormatInfo.internalGetSuperShortDayNames] Expected 7 day names in a week");
277             }
278             return (this.m_superShortDayNames);
279         }
280
281         ////////////////////////////////////////////////////////////////////////////
282         //
283         // Create an array of string which contains the day names.
284         //
285         ////////////////////////////////////////////////////////////////////////////
286
287         private String[] internalGetDayOfWeekNames()
288         {
289             if (this.dayNames == null)
290             {
291                 // Get the day names for our current calendar
292                 this.dayNames = this.m_cultureData.DayNames(Calendar.ID);
293                 Contract.Assert(this.dayNames.Length == 7, "[DateTimeFormatInfo.GetDayOfWeekNames] Expected 7 day names in a week");
294             }
295             return (this.dayNames);
296         }
297
298         ////////////////////////////////////////////////////////////////////////////
299         //
300         // Create an array of string which contains the abbreviated month names.
301         //
302         ////////////////////////////////////////////////////////////////////////////
303
304         private String[] internalGetAbbreviatedMonthNames()
305         {
306             if (this.abbreviatedMonthNames == null)
307             {
308                 // Get the month names for our current calendar
309                 this.abbreviatedMonthNames = this.m_cultureData.AbbreviatedMonthNames(Calendar.ID);
310                 Contract.Assert(this.abbreviatedMonthNames.Length == 12 || this.abbreviatedMonthNames.Length == 13,
311                     "[DateTimeFormatInfo.GetAbbreviatedMonthNames] Expected 12 or 13 month names in a year");
312             }
313             return (this.abbreviatedMonthNames);
314         }
315
316
317         ////////////////////////////////////////////////////////////////////////////
318         //
319         // Create an array of string which contains the month names.
320         //
321         ////////////////////////////////////////////////////////////////////////////
322
323         private String[] internalGetMonthNames()
324         {
325             if (this.monthNames == null)
326             {
327                 // Get the month names for our current calendar
328                 this.monthNames = this.m_cultureData.MonthNames(Calendar.ID);
329                 Contract.Assert(this.monthNames.Length == 12 || this.monthNames.Length == 13,
330                     "[DateTimeFormatInfo.GetMonthNames] Expected 12 or 13 month names in a year");
331             }
332
333             return (this.monthNames);
334         }
335
336
337         //
338         // Invariant DateTimeFormatInfo doesn't have user-overriden values
339         // Default calendar is gregorian
340         public DateTimeFormatInfo()
341             : this(CultureInfo.InvariantCulture.m_cultureData, 
342                     GregorianCalendar.GetDefaultInstance())
343         {
344         }
345
346         internal DateTimeFormatInfo(CultureData cultureData, Calendar cal)
347         {
348             Contract.Requires(cultureData != null);
349             Contract.Requires(cal != null);
350
351             // Remember our culture
352             this.m_cultureData = cultureData;
353
354             // m_isDefaultCalendar is set in the setter of Calendar below.
355             this.Calendar = cal;
356         }
357
358 #if !FEATURE_CORECLR
359         [System.Security.SecuritySafeCritical]
360 #endif
361         private void InitializeOverridableProperties(CultureData cultureData, int calendarID)
362         {
363 #if !FEATURE_CORECLR
364             // Silverlight 2.0 never took a snapshot of the user's overridable properties
365             // This has a substantial performance impact so skip when CoreCLR
366             Contract.Requires(cultureData != null);
367             Contract.Assert(calendarID > 0, "[DateTimeFormatInfo.Populate] Expected Calendar.ID > 0");
368
369             if (this.firstDayOfWeek == -1) { this.firstDayOfWeek = cultureData.IFIRSTDAYOFWEEK; }
370             if (this.calendarWeekRule == -1) { this.calendarWeekRule = cultureData.IFIRSTWEEKOFYEAR; }
371
372             if (this.amDesignator == null) { this.amDesignator = cultureData.SAM1159; }
373             if (this.pmDesignator == null) { this.pmDesignator = cultureData.SPM2359; }
374             if (this.timeSeparator == null) { this.timeSeparator = cultureData.TimeSeparator; }
375             if (this.dateSeparator == null) { this.dateSeparator = cultureData.DateSeparator(calendarID); }
376
377             this.allLongTimePatterns = this.m_cultureData.LongTimes;
378             Contract.Assert(this.allLongTimePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some long time patterns");
379
380             this.allShortTimePatterns = this.m_cultureData.ShortTimes;
381             Contract.Assert(this.allShortTimePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some short time patterns");
382
383             this.allLongDatePatterns = cultureData.LongDates(calendarID);
384             Contract.Assert(this.allLongDatePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some long date patterns");
385
386             this.allShortDatePatterns = cultureData.ShortDates(calendarID);
387             Contract.Assert(this.allShortDatePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some short date patterns");
388
389             this.allYearMonthPatterns = cultureData.YearMonths(calendarID);
390             Contract.Assert(this.allYearMonthPatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some year month patterns");
391 #endif
392         }
393
394         #region Serialization
395         // The following fields are defined to keep the serialization compatibility with .NET V1.0/V1.1.
396         [OptionalField(VersionAdded = 1)]
397         private int    CultureID;
398         [OptionalField(VersionAdded = 1)]
399         private bool   m_useUserOverride;
400 #if !FEATURE_CORECLR
401         [OptionalField(VersionAdded = 1)]
402         private bool bUseCalendarInfo;
403         [OptionalField(VersionAdded = 1)]
404         private int    nDataItem;
405         [OptionalField(VersionAdded = 2)]
406         internal bool m_isDefaultCalendar;                // NEVER USED, DO NOT USE THIS! (Serialized in Whidbey)
407         [OptionalField(VersionAdded = 2)]
408         private static volatile Hashtable s_calendarNativeNames;   // NEVER USED, DO NOT USE THIS! (Serialized in Whidbey)
409 #endif // !FEATURE_CORECLR
410
411         // This was synthesized by Whidbey so we knew what words might appear in the middle of a date string
412         // Now we always synthesize so its not helpful
413         [OptionalField(VersionAdded = 1)]
414         internal String[] m_dateWords = null;               // calculated, no need to serialze  (whidbey expects, arrowhead doesn't)
415
416         [OnDeserialized]
417         private void OnDeserialized(StreamingContext ctx)
418         {
419             if (this.m_name != null)
420             {
421                 m_cultureData = CultureData.GetCultureData(m_name, m_useUserOverride);
422                 // 
423                 if (this.m_cultureData == null)
424                     throw new CultureNotFoundException(
425                         "m_name", m_name, Environment.GetResourceString("Argument_CultureNotSupported"));
426             }
427             // Note: This is for Everett compatibility
428             // 
429 #if FEATURE_USE_LCID
430             else
431                 m_cultureData = CultureData.GetCultureData(CultureID, m_useUserOverride);
432 #endif
433             if (calendar == null)
434             {
435                 calendar = (Calendar) GregorianCalendar.GetDefaultInstance().Clone();
436                 calendar.SetReadOnlyState(m_isReadOnly);
437             }
438             else
439             {
440                 CultureInfo.CheckDomainSafetyObject(calendar, this);
441             }
442             InitializeOverridableProperties(m_cultureData, calendar.ID);
443
444             //
445             //  turn off read only state till we finish initializing all fields and then store read only state after we are done.
446             //
447             bool isReadOnly = m_isReadOnly;
448             m_isReadOnly = false;
449
450             // If we deserialized defaults ala Whidbey, make sure they're still defaults
451             // Whidbey's arrays could get a bit mixed up.
452             if (longDatePattern  != null) this.LongDatePattern  = longDatePattern;
453             if (shortDatePattern != null) this.ShortDatePattern = shortDatePattern;
454             if (yearMonthPattern != null) this.YearMonthPattern = yearMonthPattern;
455             if (longTimePattern  != null) this.LongTimePattern  = longTimePattern;
456             if (shortTimePattern != null) this.ShortTimePattern = shortTimePattern;
457             
458             m_isReadOnly = isReadOnly;
459         }
460
461         [OnSerializing]
462         private void OnSerializing(StreamingContext ctx)
463         {
464 #if FEATURE_USE_LCID
465             CultureID           = this.m_cultureData.ILANGUAGE;       // Used for serialization compatibility with Whidbey which didn't always serialize the name
466 #endif
467             m_useUserOverride   = this.m_cultureData.UseUserOverride;
468
469             // make sure the m_name is initialized.
470             m_name = this.CultureName;
471
472 #if !FEATURE_CORECLR
473             if (s_calendarNativeNames == null)
474                 s_calendarNativeNames = new Hashtable();
475 #endif // FEATURE_CORECLR
476
477             // Important to initialize these fields otherwise we may run into exception when deserializing on Whidbey
478             // because Whidbey try to initialize some of these fields using calendar data which could be null values 
479             // and then we get exceptions.  So we call the accessors to force the caches to get loaded.
480             Object o;
481             o = this.LongTimePattern;
482             o = this.LongDatePattern;
483             o = this.ShortTimePattern;
484             o = this.ShortDatePattern;
485             o = this.YearMonthPattern;
486             o = this.AllLongTimePatterns;
487             o = this.AllLongDatePatterns;
488             o = this.AllShortTimePatterns;
489             o = this.AllShortDatePatterns;
490             o = this.AllYearMonthPatterns;
491         }
492 #endregion Serialization
493
494         // Returns a default DateTimeFormatInfo that will be universally
495         // supported and constant irrespective of the current culture.
496         // Used by FromString methods.
497         //
498
499         public static DateTimeFormatInfo InvariantInfo {
500             get {
501                 Contract.Ensures(Contract.Result<DateTimeFormatInfo>() != null);
502                 if (invariantInfo == null)
503                 {
504                     DateTimeFormatInfo info = new DateTimeFormatInfo();
505                     info.Calendar.SetReadOnlyState(true);
506                     info.m_isReadOnly = true;
507                     invariantInfo = info;
508                 }
509                 return (invariantInfo);
510             }
511         }
512
513         // Returns the current culture's DateTimeFormatInfo.  Used by Parse methods.
514         //
515
516         public static DateTimeFormatInfo CurrentInfo {
517             get {
518                 Contract.Ensures(Contract.Result<DateTimeFormatInfo>() != null);
519                 System.Globalization.CultureInfo culture = System.Threading.Thread.CurrentThread.CurrentCulture;
520                 if (!culture.m_isInherited) {
521                     DateTimeFormatInfo info = culture.dateTimeInfo;
522                     if (info != null) {
523                         return info;
524                     }
525                 }
526                 return (DateTimeFormatInfo)culture.GetFormat(typeof(DateTimeFormatInfo));
527             }
528         }
529
530
531         public static DateTimeFormatInfo GetInstance(IFormatProvider provider) {
532             // Fast case for a regular CultureInfo
533             DateTimeFormatInfo info;
534             CultureInfo cultureProvider = provider as CultureInfo;
535             if (cultureProvider != null && !cultureProvider.m_isInherited)
536             {
537                 return cultureProvider.DateTimeFormat;
538             }
539             // Fast case for a DTFI;
540             info = provider as DateTimeFormatInfo;
541             if (info != null) {
542                 return info;
543             }
544             // Wasn't cultureInfo or DTFI, do it the slower way
545             if (provider != null) {
546                 info = provider.GetFormat(typeof(DateTimeFormatInfo)) as DateTimeFormatInfo;
547                 if (info != null) {
548                     return info;
549                 }
550             }
551             // Couldn't get anything, just use currentInfo as fallback
552             return CurrentInfo;
553         }
554
555
556         public  Object GetFormat(Type formatType)
557         {
558             return (formatType == typeof(DateTimeFormatInfo)? this: null);
559         }
560
561
562         public  Object Clone()
563         {
564             DateTimeFormatInfo n = (DateTimeFormatInfo)MemberwiseClone();
565             // We can use the data member calendar in the setter, instead of the property Calendar,
566             // since the cloned copy should have the same state as the original copy.
567             n.calendar = (Calendar) this.Calendar.Clone();
568             n.m_isReadOnly = false;
569             return n;
570         }
571
572
573         public  String AMDesignator
574          {
575 #if FEATURE_CORECLR
576             [System.Security.SecuritySafeCritical]  // auto-generated
577 #endif
578             get
579             {
580 #if FEATURE_CORECLR
581                 if (this.amDesignator == null)
582                 {
583                     this.amDesignator = this.m_cultureData.SAM1159;
584                 }
585 #endif
586                 Contract.Assert(this.amDesignator != null, "DateTimeFormatInfo.AMDesignator, amDesignator != null");
587                 return (this.amDesignator);
588             }
589
590             set
591             {
592                 if (IsReadOnly)
593                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
594                 if (value == null)
595                 {
596                     throw new ArgumentNullException("value",
597                         Environment.GetResourceString("ArgumentNull_String"));
598                 }
599                 Contract.EndContractBlock();
600                 ClearTokenHashTable();
601                 amDesignator = value;
602             }
603         }
604
605
606         public  Calendar Calendar {
607             get {
608                 Contract.Ensures(Contract.Result<Calendar>() != null);
609                 
610                 Contract.Assert(this.calendar != null, "DateTimeFormatInfo.Calendar: calendar != null");
611                 return (this.calendar);
612             }
613
614             set {
615                 if (IsReadOnly)
616                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
617                 if (value == null) {
618                     throw new ArgumentNullException("value",
619                         Environment.GetResourceString("ArgumentNull_Obj"));
620                 }
621                 Contract.EndContractBlock();
622                 if (value == calendar) {
623                     return;
624                 }
625
626                 //
627                 // Because the culture is agile object which can be attached to a thread and then thread can travel
628                 // to another app domain then we prevent attaching any customized object to culture that we cannot contol.
629                 //
630                 CultureInfo.CheckDomainSafetyObject(value, this);
631
632                 for (int i = 0; i < this.OptionalCalendars.Length; i++)
633                 {
634                     if (this.OptionalCalendars[i] == value.ID)
635                     {
636                         // We can use this one, so do so.
637
638                         // Clean related properties if we already had a calendar set
639                         if (calendar != null)
640                         {
641                             // clean related properties which are affected by the calendar setting,
642                             // so that they will be refreshed when they are accessed next time.
643                             //
644
645                             // These properites are in the order as appearing in calendar.xml.
646                             m_eraNames              = null;
647                             m_abbrevEraNames        = null;
648                             m_abbrevEnglishEraNames = null;
649
650                             monthDayPattern         = null;
651
652                             dayNames                = null;
653                             abbreviatedDayNames     = null;
654                             m_superShortDayNames    = null;
655                             monthNames              = null;
656                             abbreviatedMonthNames   = null;
657                             genitiveMonthNames      = null;
658                             m_genitiveAbbreviatedMonthNames = null;
659                             leapYearMonthNames      = null;
660                             formatFlags = DateTimeFormatFlags.NotInitialized;
661
662                             allShortDatePatterns    = null;
663                             allLongDatePatterns     = null;
664                             allYearMonthPatterns    = null;
665                             dateTimeOffsetPattern   = null;                           
666
667                             // The defaults need reset as well:
668                             longDatePattern         = null;
669                             shortDatePattern        = null;
670                             yearMonthPattern        = null;
671
672                             // These properies are not in the OS data, but they are dependent on the values like shortDatePattern.
673                             fullDateTimePattern     = null; // Long date + long time
674                             generalShortTimePattern = null; // short date + short time
675                             generalLongTimePattern  = null; // short date + long time
676                             
677                             // Derived item that changes
678                             dateSeparator           = null;
679
680                             // We don't need to do these because they are not changed by changing calendar
681                             //      amDesignator
682                             //      pmDesignator
683                             //      timeSeparator
684                             //      longTimePattern
685                             //      firstDayOfWeek
686                             //      calendarWeekRule
687
688                             // We don't need to clear these because they're only used for whidbey compat serialization
689                             // the only values we use are the all...Patterns[0]
690                             //      longDatePattern
691                             //      shortDatePattern
692                             //      yearMonthPattern
693
694                             // remember to reload tokens
695                             ClearTokenHashTable();
696                         }
697
698                         // Remember the new calendar
699                         calendar = value;
700                         InitializeOverridableProperties(m_cultureData, calendar.ID);
701
702                         // We succeeded, return
703                         return;
704                     }
705                 }
706                 
707                 // The assigned calendar is not a valid calendar for this culture, throw
708                 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("Argument_InvalidCalendar"));
709             }
710         }
711
712         private int[] OptionalCalendars {
713             get {
714                 if (this.optionalCalendars == null) {
715                     this.optionalCalendars = this.m_cultureData.CalendarIds;
716                 }
717                 return (this.optionalCalendars);
718             }
719         }
720
721         /*=================================GetEra==========================
722         **Action: Get the era value by parsing the name of the era.
723         **Returns: The era value for the specified era name.
724         **      -1 if the name of the era is not valid or not supported.
725         **Arguments: eraName    the name of the era.
726         **Exceptions: None.
727         ============================================================================*/
728
729
730         public  int GetEra(String eraName) {
731             if (eraName == null) {
732                 throw new ArgumentNullException("eraName",
733                     Environment.GetResourceString("ArgumentNull_String"));
734             }
735             Contract.EndContractBlock();
736
737             // For Geo-----al reasons, the Era Name and Abbreviated Era Name 
738             // for ---- Calendar on non----- SKU returns empty string (which 
739             // would be matched below) but we don't want the empty string to give
740             // us an Era number
741             // confer 85900 DTFI.GetEra("") should fail on all cultures
742             if (eraName.Length == 0) {
743                 return (-1);
744             }
745
746             // The following is based on the assumption that the era value is starting from 1, and has a
747             // serial values.
748             // If that ever changes, the code has to be changed.
749
750             // The calls to String.Compare should use the current culture for the string comparisons, but the
751             // invariant culture when comparing against the english names.
752             for (int i = 0; i < EraNames.Length; i++) {
753                 // Compare the era name in a case-insensitive way for the appropriate culture.
754                 if (m_eraNames[i].Length > 0) {
755                     if (String.Compare(eraName, m_eraNames[i], this.Culture, CompareOptions.IgnoreCase)==0) {
756                         return (i+1);
757                     }
758                 }
759             }
760             for (int i = 0; i < AbbreviatedEraNames.Length; i++) {
761                 // Compare the abbreviated era name in a case-insensitive way for the appropriate culture.              
762                 if (String.Compare(eraName, m_abbrevEraNames[i], this.Culture, CompareOptions.IgnoreCase)==0) {
763                     return (i+1);
764                 }
765             }
766             for (int i = 0; i < AbbreviatedEnglishEraNames.Length; i++) {
767                 // this comparison should use the InvariantCulture.  The English name could have linguistically
768                 // interesting characters.
769                 if (String.Compare(eraName, m_abbrevEnglishEraNames[i], StringComparison.InvariantCultureIgnoreCase)==0) {
770                     return (i+1);
771                 }
772             }
773             return (-1);
774         }
775
776         internal String[] EraNames
777         {
778             get
779             {
780                 if (this.m_eraNames == null)
781                 {
782                     this.m_eraNames = this.m_cultureData.EraNames(Calendar.ID);;
783                 }
784                 return (this.m_eraNames);
785             }
786         }
787
788         /*=================================GetEraName==========================
789         **Action: Get the name of the era for the specified era value.
790         **Returns: The name of the specified era.
791         **Arguments:
792         **      era the era value.
793         **Exceptions:
794         **      ArguementException if the era valie is invalid.
795         ============================================================================*/
796
797         // Era names are 1 indexed
798         public  String GetEraName(int era) {
799             if (era == Calendar.CurrentEra) {
800                 era = Calendar.CurrentEraValue;
801             }
802
803             // The following is based on the assumption that the era value is starting from 1, and has a
804             // serial values.
805             // If that ever changes, the code has to be changed.
806             if ((--era) < EraNames.Length && (era >= 0)) {
807                 return (m_eraNames[era]);
808             }
809             throw new ArgumentOutOfRangeException("era", Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue"));
810         }
811
812         internal String[] AbbreviatedEraNames
813         {
814             get
815             {
816                 if (this.m_abbrevEraNames == null)
817                 {
818                     this.m_abbrevEraNames = this.m_cultureData.AbbrevEraNames(Calendar.ID);
819                 }
820                 return (this.m_abbrevEraNames);
821             }
822         }
823
824         // Era names are 1 indexed
825         public String GetAbbreviatedEraName(int era) {
826             if (AbbreviatedEraNames.Length == 0) {
827                 // If abbreviation era name is not used in this culture,
828                 // return the full era name.
829                 return (GetEraName(era));
830             }
831             if (era == Calendar.CurrentEra) {
832                 era = Calendar.CurrentEraValue;
833             }
834             if ((--era) < m_abbrevEraNames.Length && (era >= 0)) {
835                 return (m_abbrevEraNames[era]);
836             }
837             throw new ArgumentOutOfRangeException("era", Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue"));
838         }
839
840         internal String[] AbbreviatedEnglishEraNames
841         {
842             get
843             {
844                 if (this.m_abbrevEnglishEraNames == null)
845                 {
846                     Contract.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.AbbreviatedEnglishEraNames] Expected Calendar.ID > 0");
847                     this.m_abbrevEnglishEraNames = this.m_cultureData.AbbreviatedEnglishEraNames(Calendar.ID);
848                 }
849                 return (this.m_abbrevEnglishEraNames);
850             }
851         }
852
853
854         // Note that cultureData derives this from the short date format (unless someone's set this previously)
855         // Note that this property is quite undesirable.
856         public  String DateSeparator
857         {
858             get
859             {
860 #if FEATURE_CORECLR
861                 if (this.dateSeparator == null)
862                 {
863                     this.dateSeparator = this.m_cultureData.DateSeparator(Calendar.ID);
864                 }
865 #endif
866                 Contract.Assert(this.dateSeparator != null, "DateTimeFormatInfo.DateSeparator, dateSeparator != null");
867                 return (this.dateSeparator);
868             }
869
870 #if !FEATURE_CORECLR
871             set {
872                 if (IsReadOnly)
873                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
874                 if (value == null) {
875                     throw new ArgumentNullException("value",
876                         Environment.GetResourceString("ArgumentNull_String"));
877                 }
878                 Contract.EndContractBlock();
879                 ClearTokenHashTable();
880                 this.dateSeparator = value;
881             }
882 #endif
883         }
884
885
886         public  DayOfWeek FirstDayOfWeek
887         {
888             get
889             {
890 #if FEATURE_CORECLR
891                 if (this.firstDayOfWeek == -1)
892                 {
893                     this.firstDayOfWeek = this.m_cultureData.IFIRSTDAYOFWEEK;
894                 }
895 #endif
896                 Contract.Assert(this.firstDayOfWeek != -1, "DateTimeFormatInfo.FirstDayOfWeek, firstDayOfWeek != -1");
897                 
898                 return ((DayOfWeek)this.firstDayOfWeek);
899             }
900
901             set {
902                 if (IsReadOnly)
903                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
904                 if (value >= DayOfWeek.Sunday && value <= DayOfWeek.Saturday) {
905                 firstDayOfWeek = (int)value;
906                 } else {
907                     throw new ArgumentOutOfRangeException(
908                         "value", Environment.GetResourceString("ArgumentOutOfRange_Range",
909                         DayOfWeek.Sunday, DayOfWeek.Saturday));
910                 }
911             }
912         }
913
914
915         public  CalendarWeekRule CalendarWeekRule
916         {
917             get
918             {
919 #if FEATURE_CORECLR
920                 if (this.calendarWeekRule == -1)
921                 {
922                     this.calendarWeekRule = this.m_cultureData.IFIRSTWEEKOFYEAR;
923                 }
924 #endif
925                 Contract.Assert(this.calendarWeekRule != -1, "DateTimeFormatInfo.CalendarWeekRule, calendarWeekRule != -1");
926                 return ((CalendarWeekRule)this.calendarWeekRule);
927             }
928
929             set {
930                 if (IsReadOnly)
931                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
932                 if (value >= CalendarWeekRule.FirstDay && value <= CalendarWeekRule.FirstFourDayWeek) {
933                     calendarWeekRule = (int)value;
934                 } else {
935                     throw new ArgumentOutOfRangeException(
936                         "value", Environment.GetResourceString("ArgumentOutOfRange_Range",
937                         CalendarWeekRule.FirstDay, CalendarWeekRule.FirstFourDayWeek));
938                 }
939             }
940         }
941
942
943
944         public  String FullDateTimePattern
945         {
946             get
947             {
948                 if (fullDateTimePattern == null)
949                 {
950                     fullDateTimePattern = LongDatePattern + " " + LongTimePattern;
951                 }
952                 return (fullDateTimePattern);
953             }
954
955             set {
956                 if (IsReadOnly)
957                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
958                 if (value == null) {
959                     throw new ArgumentNullException("value",
960                         Environment.GetResourceString("ArgumentNull_String"));
961                 }
962                 Contract.EndContractBlock();
963                 fullDateTimePattern = value;
964             }
965         }
966
967
968         // For our "patterns" arrays we have 2 variables, a string and a string[]
969         //
970         // The string[] contains the list of patterns, EXCEPT the default may not be included.
971         // The string contains the default pattern.
972         // When we initially construct our string[], we set the string to string[0]
973         public  String LongDatePattern
974         {          
975             get
976             {
977                 // Initialize our long date pattern from the 1st array value if not set
978                 if (this.longDatePattern == null)
979                 {
980                     // Initialize our data
981                     this.longDatePattern = this.UnclonedLongDatePatterns[0];
982                 }
983                 
984                 return this.longDatePattern;
985             }
986
987             set {
988                 if (IsReadOnly)
989                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
990                 if (value == null) {
991                     throw new ArgumentNullException("value",
992                         Environment.GetResourceString("ArgumentNull_String"));
993                 }
994                 Contract.EndContractBlock();
995
996                 // Remember the new string
997                 this.longDatePattern = value;
998
999                 // Clear the token hash table
1000                 ClearTokenHashTable();
1001
1002                 // Clean up cached values that will be affected by this property.
1003                 this.fullDateTimePattern = null;
1004             }
1005         }
1006
1007         // For our "patterns" arrays we have 2 variables, a string and a string[]
1008         //
1009         // The string[] contains the list of patterns, EXCEPT the default may not be included.
1010         // The string contains the default pattern.
1011         // When we initially construct our string[], we set the string to string[0]
1012         public  String LongTimePattern 
1013         {
1014             get 
1015             {
1016                 // Initialize our long time pattern from the 1st array value if not set
1017                 if (this.longTimePattern == null)
1018                 {
1019                     // Initialize our data
1020                     this.longTimePattern = this.UnclonedLongTimePatterns[0];
1021                 }
1022                 
1023                 return this.longTimePattern;                
1024             }
1025
1026             set {
1027                 if (IsReadOnly)
1028                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1029                 if (value == null) {
1030                     throw new ArgumentNullException("value",
1031                         Environment.GetResourceString("ArgumentNull_String"));
1032                 }
1033                 Contract.EndContractBlock();
1034
1035                 // Remember the new string
1036                 this.longTimePattern = value;
1037
1038                 // Clear the token hash table
1039                 ClearTokenHashTable();
1040                 
1041                 // Clean up cached values that will be affected by this property.
1042                 this.fullDateTimePattern = null;     // Full date = long date + long Time
1043                 this.generalLongTimePattern = null;  // General long date = short date + long Time
1044                 this.dateTimeOffsetPattern = null;
1045             }
1046         }
1047
1048
1049         // Note: just to be confusing there's only 1 month day pattern, not a whole list
1050         public  String MonthDayPattern
1051         {
1052             get 
1053             {
1054                 if (this.monthDayPattern == null) 
1055                 {
1056                     Contract.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.MonthDayPattern] Expected calID > 0");
1057                     this.monthDayPattern = this.m_cultureData.MonthDay(Calendar.ID);
1058                 }
1059                 Contract.Assert(this.monthDayPattern != null, "DateTimeFormatInfo.MonthDayPattern, monthDayPattern != null");
1060                 return (this.monthDayPattern);
1061             }
1062
1063             set {
1064                 if (IsReadOnly)
1065                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1066                 if (value == null) {
1067                     throw new ArgumentNullException("value",
1068                         Environment.GetResourceString("ArgumentNull_String"));
1069                 }
1070                 Contract.EndContractBlock();
1071
1072                 this.monthDayPattern = value;
1073             }
1074         }
1075
1076
1077         public  String PMDesignator
1078         {
1079 #if FEATURE_CORECLR
1080             [System.Security.SecuritySafeCritical]  // auto-generated
1081 #endif
1082             get
1083             {
1084 #if FEATURE_CORECLR
1085                 if (this.pmDesignator == null)
1086                 {
1087                     this.pmDesignator = this.m_cultureData.SPM2359;
1088                 }
1089 #endif
1090                 Contract.Assert(this.pmDesignator != null, "DateTimeFormatInfo.PMDesignator, pmDesignator != null");
1091                 return (this.pmDesignator);
1092             }
1093
1094             set {
1095                 if (IsReadOnly)
1096                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1097                 if (value == null) {
1098                     throw new ArgumentNullException("value",
1099                         Environment.GetResourceString("ArgumentNull_String"));
1100                 }
1101                 Contract.EndContractBlock();
1102                 ClearTokenHashTable();
1103
1104                 pmDesignator = value;
1105             }
1106
1107         }
1108
1109
1110         public  String RFC1123Pattern
1111         {
1112             get
1113             {
1114                 return (rfc1123Pattern);
1115             }
1116         }
1117
1118         // For our "patterns" arrays we have 2 variables, a string and a string[]
1119         //
1120         // The string[] contains the list of patterns, EXCEPT the default may not be included.
1121         // The string contains the default pattern.
1122         // When we initially construct our string[], we set the string to string[0]
1123         public  String ShortDatePattern
1124         {
1125             get
1126             {
1127                 // Initialize our short date pattern from the 1st array value if not set
1128                 if (this.shortDatePattern == null)
1129                 {
1130                     // Initialize our data
1131                     this.shortDatePattern = this.UnclonedShortDatePatterns[0];
1132                 }
1133                 
1134                 return this.shortDatePattern;
1135             }
1136
1137             set
1138             {
1139                 if (IsReadOnly)
1140                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1141                 if (value == null)
1142                     throw new ArgumentNullException("value",
1143                         Environment.GetResourceString("ArgumentNull_String"));
1144                 Contract.EndContractBlock();
1145
1146                 // Remember the new string
1147                 this.shortDatePattern = value;
1148
1149                 // Clear the token hash table, note that even short dates could require this
1150                 ClearTokenHashTable();
1151
1152                 // Clean up cached values that will be affected by this property.
1153                 generalLongTimePattern = null;   // General long time = short date + long time
1154                 generalShortTimePattern = null;  // General short time = short date + short Time
1155                 dateTimeOffsetPattern = null;
1156             }
1157         }
1158
1159
1160         // For our "patterns" arrays we have 2 variables, a string and a string[]
1161         //
1162         // The string[] contains the list of patterns, EXCEPT the default may not be included.
1163         // The string contains the default pattern.
1164         // When we initially construct our string[], we set the string to string[0]
1165         public  String ShortTimePattern
1166         {
1167             get
1168             {
1169                 // Initialize our short time pattern from the 1st array value if not set
1170                 if (this.shortTimePattern == null)
1171                 {
1172                     // Initialize our data
1173                     this.shortTimePattern = this.UnclonedShortTimePatterns[0];
1174                 }
1175                 return this.shortTimePattern;
1176             }
1177
1178             set {
1179                 if (IsReadOnly)
1180                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1181                 if (value == null) {
1182                     throw new ArgumentNullException("value",
1183                         Environment.GetResourceString("ArgumentNull_String"));
1184                 }
1185                 Contract.EndContractBlock();
1186
1187                 // Remember the new string
1188                 this.shortTimePattern= value;
1189
1190                 // Clear the token hash table, note that even short times could require this
1191                 ClearTokenHashTable();
1192                 
1193                 // Clean up cached values that will be affected by this property.
1194                 generalShortTimePattern = null; // General short date = short date + short time.
1195             }
1196         }
1197
1198
1199         public  String SortableDateTimePattern {
1200             get {
1201                 return (sortableDateTimePattern);
1202             }
1203         }
1204
1205         /*=================================GeneralShortTimePattern=====================
1206         **Property: Return the pattern for 'g' general format: shortDate + short time
1207         **Note: This is used by DateTimeFormat.cs to get the pattern for 'g'
1208         **      We put this internal property here so that we can avoid doing the
1209         **      concatation every time somebody asks for the general format.
1210         ==============================================================================*/
1211
1212         internal String GeneralShortTimePattern {
1213             get {
1214                 if (generalShortTimePattern == null) {
1215                     generalShortTimePattern = ShortDatePattern + " " + ShortTimePattern;
1216                 }
1217                 return (generalShortTimePattern);
1218             }
1219         }
1220
1221         /*=================================GeneralLongTimePattern=====================
1222         **Property: Return the pattern for 'g' general format: shortDate + Long time
1223         **Note: This is used by DateTimeFormat.cs to get the pattern for 'g'
1224         **      We put this internal property here so that we can avoid doing the
1225         **      concatation every time somebody asks for the general format.
1226         ==============================================================================*/
1227
1228         internal String GeneralLongTimePattern {
1229             get {
1230                 if (generalLongTimePattern == null) {
1231                     generalLongTimePattern = ShortDatePattern + " " + LongTimePattern;
1232                 }
1233                 return (generalLongTimePattern);
1234             }
1235         }
1236
1237         /*=================================DateTimeOffsetPattern==========================
1238         **Property: Return the default pattern DateTimeOffset : shortDate + long time + time zone offset
1239         **Note: This is used by DateTimeFormat.cs to get the pattern for short Date + long time +  time zone offset
1240         **      We put this internal property here so that we can avoid doing the
1241         **      concatation every time somebody uses this form
1242         ==============================================================================*/
1243
1244         /*=================================DateTimeOffsetPattern==========================
1245         **Property: Return the default pattern DateTimeOffset : shortDate + long time + time zone offset
1246         **Note: This is used by DateTimeFormat.cs to get the pattern for short Date + long time +  time zone offset
1247         **      We put this internal property here so that we can avoid doing the
1248         **      concatation every time somebody uses this form
1249         ==============================================================================*/
1250
1251         internal String DateTimeOffsetPattern {
1252             get {
1253                 if (dateTimeOffsetPattern == null) {
1254
1255                     dateTimeOffsetPattern = ShortDatePattern + " " + LongTimePattern;
1256
1257                     /* LongTimePattern might contain a "z" as part of the format string in which case we don't want to append a time zone offset */
1258
1259                     bool foundZ = false;
1260                     bool inQuote = false;
1261                     char quote = '\''; 
1262                     for (int i = 0; !foundZ && i < LongTimePattern.Length; i++) {
1263                         switch (LongTimePattern[i]) {
1264                             case 'z':
1265                                 /* if we aren't in a quote, we've found a z */
1266                                 foundZ = !inQuote;
1267                                 /* we'll fall out of the loop now because the test includes !foundZ */
1268                                 break;
1269                             case '\'':
1270                             case '\"':
1271                                 if (inQuote && (quote == LongTimePattern[i])) {
1272                                     /* we were in a quote and found a matching exit quote, so we are outside a quote now */
1273                                     inQuote = false;
1274                                 } else if (!inQuote) {
1275                                     quote = LongTimePattern[i];
1276                                     inQuote = true;
1277                                 } else {
1278                                     /* we were in a quote and saw the other type of quote character, so we are still in a quote */
1279                                 }
1280                                 break;
1281                             case '%':
1282                             case '\\':
1283                                 i++; /* skip next character that is escaped by this backslash */
1284                                 break;
1285                             default:
1286                                 break;
1287                         }
1288                     }
1289
1290                     if (!foundZ) {
1291                         dateTimeOffsetPattern = dateTimeOffsetPattern + " zzz";
1292                     } 
1293                 }
1294                 return (dateTimeOffsetPattern);
1295             }
1296         }
1297
1298         // Note that cultureData derives this from the long time format (unless someone's set this previously)
1299         // Note that this property is quite undesirable.
1300         // 
1301         public  String TimeSeparator
1302         {
1303             get
1304             {
1305 #if FEATURE_CORECLR
1306                 if (timeSeparator == null)
1307                 {
1308                     timeSeparator = this.m_cultureData.TimeSeparator;
1309                 }
1310 #endif
1311                 Contract.Assert(this.timeSeparator != null, "DateTimeFormatInfo.TimeSeparator, timeSeparator != null");
1312                 return (timeSeparator);
1313             }
1314
1315 #if !FEATURE_CORECLR
1316             set {
1317                 if (IsReadOnly)
1318                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1319                 if (value == null) {
1320                     throw new ArgumentNullException("value",
1321                         Environment.GetResourceString("ArgumentNull_String"));
1322                 }
1323                 Contract.EndContractBlock();
1324                 ClearTokenHashTable();
1325
1326                 timeSeparator = value;
1327             }
1328 #endif
1329         }
1330
1331
1332         public  String UniversalSortableDateTimePattern
1333         {
1334             get
1335             {
1336                 return (universalSortableDateTimePattern);
1337             }
1338         }
1339         
1340         // For our "patterns" arrays we have 2 variables, a string and a string[]
1341         //
1342         // The string[] contains the list of patterns, EXCEPT the default may not be included.
1343         // The string contains the default pattern.
1344         // When we initially construct our string[], we set the string to string[0]
1345         public String YearMonthPattern
1346         {
1347             get
1348             {
1349                 // Initialize our year/month pattern from the 1st array value if not set
1350                 if (this.yearMonthPattern == null)
1351                 {
1352                     // Initialize our data
1353                     this.yearMonthPattern = this.UnclonedYearMonthPatterns[0];
1354                 }
1355                 return this.yearMonthPattern;
1356             }
1357
1358             set {
1359                 if (IsReadOnly)
1360                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1361                 if (value == null) {
1362                     throw new ArgumentNullException("value",
1363                         Environment.GetResourceString("ArgumentNull_String"));
1364                 }
1365                 Contract.EndContractBlock();
1366
1367                 // Remember the new string
1368                 this.yearMonthPattern = value;
1369
1370                 // Clear the token hash table, note that even short times could require this
1371                 ClearTokenHashTable();
1372             }
1373         }
1374
1375         //
1376         // Check if a string array contains a null value, and throw ArgumentNullException with parameter name "value"
1377         //
1378         static private void CheckNullValue(String[] values, int length) {
1379             Contract.Requires(values != null, "value != null");
1380             Contract.Requires(values.Length >= length);
1381             for (int i = 0; i < length; i++) {
1382                 if (values[i] == null) {
1383                     throw new ArgumentNullException("value",
1384                         Environment.GetResourceString("ArgumentNull_ArrayValue"));
1385                 }
1386             }
1387         }
1388
1389
1390         public  String[] AbbreviatedDayNames
1391          {
1392             get
1393             {
1394                 return ((String[])internalGetAbbreviatedDayOfWeekNames().Clone());
1395             }
1396
1397             set {
1398                 if (IsReadOnly)
1399                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1400                 if (value == null) {
1401                     throw new ArgumentNullException("value",
1402                         Environment.GetResourceString("ArgumentNull_Array"));
1403                 }
1404                 if (value.Length != 7) {
1405                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidArrayLength", 7), "value");
1406                 }
1407                 Contract.EndContractBlock();
1408                 CheckNullValue(value, value.Length);
1409                 ClearTokenHashTable();
1410
1411                 abbreviatedDayNames = value;
1412             }
1413         }
1414
1415
1416         // Returns the string array of the one-letter day of week names.
1417         [System.Runtime.InteropServices.ComVisible(false)]
1418         public String[] ShortestDayNames
1419         {
1420             get
1421             {
1422                 return ((String[])internalGetSuperShortDayNames().Clone());
1423             }
1424
1425             set {
1426                 if (IsReadOnly)
1427                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1428                 if (value == null) {
1429                     throw new ArgumentNullException("value",
1430                         Environment.GetResourceString("ArgumentNull_Array"));
1431                 }
1432                 if (value.Length != 7)
1433                 {
1434                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidArrayLength", 7), "value");
1435                 }
1436                 Contract.EndContractBlock();
1437                 CheckNullValue(value, value.Length);
1438                 this.m_superShortDayNames = value;
1439             }
1440         }
1441
1442
1443         public  String[] DayNames
1444          {
1445             get
1446             {
1447                 return ((String[])internalGetDayOfWeekNames().Clone());
1448             }
1449
1450             set {
1451                 if (IsReadOnly)
1452                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1453                 if (value == null) {
1454                     throw new ArgumentNullException("value",
1455                         Environment.GetResourceString("ArgumentNull_Array"));
1456                 }
1457                 if (value.Length != 7)
1458                 {
1459                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidArrayLength", 7), "value");
1460                 }
1461                 Contract.EndContractBlock();
1462                 CheckNullValue(value, value.Length);
1463                 ClearTokenHashTable();
1464
1465                 dayNames = value;
1466             }
1467         }
1468
1469
1470         public  String[] AbbreviatedMonthNames {
1471             get {
1472                 return ((String[])internalGetAbbreviatedMonthNames().Clone());
1473             }
1474
1475             set {
1476                 if (IsReadOnly)
1477                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1478                 if (value == null) {
1479                     throw new ArgumentNullException("value",
1480                         Environment.GetResourceString("ArgumentNull_Array"));
1481                 }
1482                 if (value.Length != 13)
1483                 {
1484                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidArrayLength", 13), "value");
1485                 }
1486                 Contract.EndContractBlock();
1487                 CheckNullValue(value, value.Length - 1);
1488                 ClearTokenHashTable();
1489                 abbreviatedMonthNames = value;
1490             }
1491         }
1492
1493
1494         public  String[] MonthNames
1495          {
1496             get
1497             {
1498                 return ((String[])internalGetMonthNames().Clone());
1499             }
1500
1501             set {
1502                 if (IsReadOnly)
1503                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
1504                 if (value == null) {
1505                     throw new ArgumentNullException("value",
1506                         Environment.GetResourceString("ArgumentNull_Array"));
1507                 }
1508                 if (value.Length != 13)
1509                 {
1510                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidArrayLength", 13), "value");
1511                 }
1512                 Contract.EndContractBlock();
1513                 CheckNullValue(value, value.Length - 1);
1514                 monthNames = value;
1515                 ClearTokenHashTable();
1516             }
1517         }
1518
1519         // Whitespaces that we allow in the month names.
1520         // U+00a0 is non-breaking space.
1521         static char[] MonthSpaces = {' ', '\u00a0'};
1522
1523         internal bool HasSpacesInMonthNames {
1524             get {
1525                 return (FormatFlags & DateTimeFormatFlags.UseSpacesInMonthNames) != 0;
1526             }
1527         }
1528
1529         internal bool HasSpacesInDayNames {
1530             get {
1531                 return (FormatFlags & DateTimeFormatFlags.UseSpacesInDayNames) != 0;
1532             }
1533         }
1534
1535
1536         //
1537         //  internalGetMonthName
1538         //
1539         // Actions: Return the month name using the specified MonthNameStyles in either abbreviated form
1540         //      or full form.
1541         // Arguments:
1542         //      month
1543         //      style           To indicate a form like regular/genitive/month name in a leap year.
1544         //      abbreviated     When true, return abbreviated form.  Otherwise, return a full form.
1545         //  Exceptions:
1546         //      ArgumentOutOfRangeException When month name is invalid.
1547         //
1548         internal String internalGetMonthName(int month, MonthNameStyles style, bool abbreviated) {
1549             //
1550             // Right now, style is mutual exclusive, but I make the style to be flag so that
1551             // maybe we can combine flag if there is such a need.
1552             //
1553             String[] monthNamesArray = null;
1554             switch (style) {
1555                 case MonthNameStyles.Genitive:
1556                     monthNamesArray = internalGetGenitiveMonthNames(abbreviated);
1557                     break;
1558                 case MonthNameStyles.LeapYear:
1559                     monthNamesArray = internalGetLeapYearMonthNames(/*abbreviated*/);
1560                     break;
1561                 default:
1562                     monthNamesArray = (abbreviated ? internalGetAbbreviatedMonthNames(): internalGetMonthNames());
1563                     break;
1564             }
1565             // The month range is from 1 ~ this.m_monthNames.Length
1566             // (actually is 13 right now for all cases)
1567             if ((month < 1) || (month > monthNamesArray.Length)) {
1568                 throw new ArgumentOutOfRangeException(
1569                     "month", Environment.GetResourceString("ArgumentOutOfRange_Range",
1570                     1, monthNamesArray.Length));
1571             }
1572             return (monthNamesArray[month-1]);
1573         }
1574
1575         //
1576         //  internalGetGenitiveMonthNames
1577         //
1578         //  Action: Retrieve the array which contains the month names in genitive form.
1579         //      If this culture does not use the gentive form, the normal month name is returned.
1580         //  Arguments:
1581         //      abbreviated     When true, return abbreviated form.  Otherwise, return a full form.
1582         //
1583         private String[] internalGetGenitiveMonthNames(bool abbreviated) {
1584             if (abbreviated) {
1585                 if (this.m_genitiveAbbreviatedMonthNames == null)
1586                 {
1587                     this.m_genitiveAbbreviatedMonthNames = this.m_cultureData.AbbreviatedGenitiveMonthNames(this.Calendar.ID);
1588                     Contract.Assert(this.m_genitiveAbbreviatedMonthNames.Length == 13,
1589                         "[DateTimeFormatInfo.GetGenitiveMonthNames] Expected 13 abbreviated genitive month names in a year");
1590                 }
1591                 return (this.m_genitiveAbbreviatedMonthNames);
1592             }
1593
1594             if (this.genitiveMonthNames == null)
1595             {
1596                 this.genitiveMonthNames = this.m_cultureData.GenitiveMonthNames(this.Calendar.ID);
1597                 Contract.Assert(this.genitiveMonthNames.Length == 13,
1598                     "[DateTimeFormatInfo.GetGenitiveMonthNames] Expected 13 genitive month names in a year");
1599             }
1600             return (this.genitiveMonthNames);
1601         }
1602
1603         //
1604         //  internalGetLeapYearMonthNames
1605         //
1606         //  Actions: Retrieve the month names used in a leap year.
1607         //      If this culture does not have different month names in a leap year, the normal month name is returned.
1608         //  Agruments: None. (can use abbreviated later if needed)
1609         //
1610         internal String[] internalGetLeapYearMonthNames(/*bool abbreviated*/) {
1611             if (this.leapYearMonthNames == null)
1612             {
1613                 Contract.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.internalGetLeapYearMonthNames] Expected Calendar.ID > 0");
1614                 this.leapYearMonthNames = this.m_cultureData.LeapYearMonthNames(Calendar.ID);
1615                 Contract.Assert(this.leapYearMonthNames.Length == 13,
1616                     "[DateTimeFormatInfo.internalGetLeapYearMonthNames] Expepcted 13 leap year month names");
1617             }
1618             return (leapYearMonthNames);
1619         }
1620
1621
1622         public  String GetAbbreviatedDayName(DayOfWeek dayofweek)
1623         {
1624
1625             if ((int)dayofweek < 0 || (int)dayofweek > 6) {
1626                 throw new ArgumentOutOfRangeException(
1627                     "dayofweek", Environment.GetResourceString("ArgumentOutOfRange_Range",
1628                     DayOfWeek.Sunday, DayOfWeek.Saturday));
1629             }
1630             Contract.EndContractBlock();
1631             //
1632             // Don't call the public property AbbreviatedDayNames here since a clone is needed in that
1633             // property, so it will be slower.  Instead, use GetAbbreviatedDayOfWeekNames() directly.
1634             //
1635             return (internalGetAbbreviatedDayOfWeekNames()[(int)dayofweek]);
1636         }
1637
1638
1639         // Returns the super short day of week names for the specified day of week.
1640         [System.Runtime.InteropServices.ComVisible(false)]
1641         public  String GetShortestDayName(DayOfWeek dayOfWeek)
1642         {
1643
1644             if ((int)dayOfWeek < 0 || (int)dayOfWeek > 6) {
1645                 throw new ArgumentOutOfRangeException(
1646                     "dayOfWeek", Environment.GetResourceString("ArgumentOutOfRange_Range",
1647                     DayOfWeek.Sunday, DayOfWeek.Saturday));
1648             }
1649             Contract.EndContractBlock();
1650             //
1651             // Don't call the public property SuperShortDayNames here since a clone is needed in that
1652             // property, so it will be slower.  Instead, use internalGetSuperShortDayNames() directly.
1653             //
1654             return (internalGetSuperShortDayNames()[(int)dayOfWeek]);
1655         }
1656
1657         // Get all possible combination of inputs
1658         static private  String[] GetCombinedPatterns(String[] patterns1, String[] patterns2, String connectString)
1659         {
1660             Contract.Requires(patterns1 != null);
1661             Contract.Requires(patterns2 != null);
1662
1663             // Get array size
1664             String[] result = new String[patterns1.Length * patterns2.Length];
1665
1666             // Counter of actual results
1667             int k = 0;
1668             for (int i = 0; i < patterns1.Length; i++)
1669             {
1670                 for (int j = 0; j < patterns2.Length; j++)
1671                 {
1672                     // Can't combine if null or empty
1673                     result[k++] = patterns1[i] + connectString + patterns2[j];
1674                 }
1675             }
1676
1677             // Return the combinations
1678             return (result);
1679         }
1680
1681
1682         public  String[] GetAllDateTimePatterns()
1683         {
1684             List<String> results = new List<String>(DEFAULT_ALL_DATETIMES_SIZE);
1685
1686             for (int i = 0; i < DateTimeFormat.allStandardFormats.Length; i++)
1687             {
1688                 String[] strings = GetAllDateTimePatterns(DateTimeFormat.allStandardFormats[i]);
1689                 for (int j = 0; j < strings.Length; j++)
1690                 {
1691                     results.Add(strings[j]);
1692                 }
1693             }
1694             return results.ToArray();
1695         }
1696
1697
1698         public  String[] GetAllDateTimePatterns(char format)
1699         {
1700             Contract.Ensures(Contract.Result<String[]>() != null);
1701             String [] result = null;
1702
1703             switch (format)
1704             {
1705                 case 'd':
1706                     result = this.AllShortDatePatterns;
1707                     break;
1708                 case 'D':
1709                     result = this.AllLongDatePatterns;
1710                     break;
1711                 case 'f':
1712                     result = GetCombinedPatterns(AllLongDatePatterns, AllShortTimePatterns, " ");
1713                     break;
1714                 case 'F':
1715                 case 'U':
1716                     result = GetCombinedPatterns(AllLongDatePatterns, AllLongTimePatterns, " ");
1717                     break;
1718                 case 'g':
1719                     result = GetCombinedPatterns(AllShortDatePatterns, AllShortTimePatterns, " ");
1720                     break;
1721                 case 'G':
1722                     result = GetCombinedPatterns(AllShortDatePatterns, AllLongTimePatterns, " ");
1723                     break;
1724                 case 'm':
1725                 case 'M':
1726                     result = new String[] {MonthDayPattern};
1727                     break;
1728                 case 'o':
1729                 case 'O':
1730                     result = new String[] {DateTimeFormat.RoundtripFormat};
1731                     break;
1732                 case 'r':
1733                 case 'R':
1734                     result = new String[] {rfc1123Pattern};
1735                     break;
1736                 case 's':
1737                     result = new String[] {sortableDateTimePattern};
1738                     break;
1739                 case 't':
1740                     result = this.AllShortTimePatterns;
1741                     break;
1742                 case 'T':
1743                     result = this.AllLongTimePatterns;
1744                     break;
1745                 case 'u':
1746                     result = new String[] {UniversalSortableDateTimePattern};
1747                     break;
1748                 case 'y':
1749                 case 'Y':
1750                     result = this.AllYearMonthPatterns;
1751                     break;
1752                 default:
1753                     throw new ArgumentException(Environment.GetResourceString("Format_BadFormatSpecifier"), "format");
1754             }
1755             return (result);
1756         }
1757
1758
1759         public  String GetDayName(DayOfWeek dayofweek)
1760         {
1761             if ((int)dayofweek < 0 || (int)dayofweek > 6) {
1762                 throw new ArgumentOutOfRangeException(
1763                     "dayofweek", Environment.GetResourceString("ArgumentOutOfRange_Range",
1764                     DayOfWeek.Sunday, DayOfWeek.Saturday));
1765             }
1766             Contract.EndContractBlock();
1767
1768             // Use the internal one so that we don't clone the array unnecessarily
1769             return (internalGetDayOfWeekNames()[(int)dayofweek]);
1770         }
1771
1772
1773
1774         public  String GetAbbreviatedMonthName(int month)
1775         {
1776             if (month < 1 || month > 13) {
1777                 throw new ArgumentOutOfRangeException(
1778                     "month", Environment.GetResourceString("ArgumentOutOfRange_Range",
1779                     1, 13));
1780             }
1781             Contract.EndContractBlock();
1782             // Use the internal one so we don't clone the array unnecessarily
1783             return (internalGetAbbreviatedMonthNames()[month-1]);
1784         }
1785
1786
1787         public  String GetMonthName(int month)
1788         {
1789             if (month < 1 || month > 13) {
1790                 throw new ArgumentOutOfRangeException(
1791                     "month", Environment.GetResourceString("ArgumentOutOfRange_Range",
1792                     1, 13));
1793             }
1794             Contract.EndContractBlock();
1795             // Use the internal one so we don't clone the array unnecessarily
1796             return (internalGetMonthNames()[month-1]);
1797         }
1798
1799         // For our "patterns" arrays we have 2 variables, a string and a string[]
1800         //
1801         // The string[] contains the list of patterns, EXCEPT the default may not be included.
1802         // The string contains the default pattern.
1803         // When we initially construct our string[], we set the string to string[0]
1804         //
1805         // The resulting [] can get returned to the calling app, so clone it.
1806         private static string[] GetMergedPatterns(string [] patterns, string defaultPattern)
1807         {
1808             Contract.Assert(patterns != null && patterns.Length > 0,
1809                             "[DateTimeFormatInfo.GetMergedPatterns]Expected array of at least one pattern");
1810             Contract.Assert(defaultPattern != null, 
1811                             "[DateTimeFormatInfo.GetMergedPatterns]Expected non null default string");
1812
1813             // If the default happens to be the first in the list just return (a cloned) copy
1814             if (defaultPattern == patterns[0])
1815             {
1816                 return (string[])patterns.Clone();
1817             }
1818
1819             // We either need a bigger list, or the pattern from the list.
1820             int i;
1821             for (i = 0; i < patterns.Length; i++)
1822             {
1823                 // Stop if we found it
1824                 if (defaultPattern == patterns[i])
1825                     break;
1826             }
1827
1828             // Either way we're going to need a new array
1829             string[] newPatterns;
1830
1831             // Did we find it
1832             if (i < patterns.Length)
1833             {
1834                 // Found it, output will be same size
1835                 newPatterns = (string[])patterns.Clone();
1836
1837                 // Have to move [0] item to [i] so we can re-write default at [0]
1838                 // (remember defaultPattern == [i] so this is OK)
1839                 newPatterns[i] = newPatterns[0];
1840             }
1841             else
1842             {
1843                 // Not found, make room for it
1844                 newPatterns = new String[patterns.Length + 1];
1845
1846                 // Copy existing array
1847                 Array.Copy(patterns, 0, newPatterns, 1, patterns.Length);
1848             }
1849
1850             // Remember the default
1851             newPatterns[0] = defaultPattern;
1852
1853             // Return the reconstructed list
1854             return newPatterns;
1855         }
1856
1857         // Default string isn't necessarily in our string array, so get the 
1858         // merged patterns of both
1859         private String[] AllYearMonthPatterns
1860         {
1861             get
1862             {
1863                 return GetMergedPatterns(this.UnclonedYearMonthPatterns, this.YearMonthPattern);
1864             }
1865         }
1866         
1867         private String[] AllShortDatePatterns
1868         {
1869             get
1870             {
1871                 return GetMergedPatterns(this.UnclonedShortDatePatterns, this.ShortDatePattern);
1872             }
1873         }
1874
1875         private String[] AllShortTimePatterns
1876         {
1877             get
1878             {
1879                 return GetMergedPatterns(this.UnclonedShortTimePatterns, this.ShortTimePattern);
1880             }
1881         }
1882         
1883         private String[] AllLongDatePatterns
1884         {
1885             get
1886             {
1887                 return GetMergedPatterns(this.UnclonedLongDatePatterns, this.LongDatePattern);
1888             }
1889         }
1890
1891         private String[] AllLongTimePatterns
1892         {
1893             get
1894             {
1895                 return GetMergedPatterns(this.UnclonedLongTimePatterns, this.LongTimePattern);
1896             }
1897         }
1898
1899         // NOTE: Clone this string array if you want to return it to user.  Otherwise, you are returning a writable cache copy.
1900         // This won't include default, call AllYearMonthPatterns
1901         private String[] UnclonedYearMonthPatterns
1902         {
1903             get
1904             {
1905                 if (this.allYearMonthPatterns == null)
1906                 {
1907                     Contract.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.UnclonedYearMonthPatterns] Expected Calendar.ID > 0");                    
1908                     this.allYearMonthPatterns = this.m_cultureData.YearMonths(this.Calendar.ID);
1909                     Contract.Assert(this.allYearMonthPatterns.Length > 0,
1910                         "[DateTimeFormatInfo.UnclonedYearMonthPatterns] Expected some year month patterns");
1911                 }
1912
1913                 return this.allYearMonthPatterns;
1914             }
1915         }
1916
1917
1918         // NOTE: Clone this string array if you want to return it to user.  Otherwise, you are returning a writable cache copy.
1919         // This won't include default, call AllShortDatePatterns
1920         private String [] UnclonedShortDatePatterns
1921         {
1922             get
1923             {
1924                 if (allShortDatePatterns == null)
1925                 {
1926                     Contract.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.UnclonedShortDatePatterns] Expected Calendar.ID > 0");
1927                     this.allShortDatePatterns = this.m_cultureData.ShortDates(this.Calendar.ID);
1928                     Contract.Assert(this.allShortDatePatterns.Length > 0,
1929                         "[DateTimeFormatInfo.UnclonedShortDatePatterns] Expected some short date patterns");
1930                 }
1931
1932                 return this.allShortDatePatterns;
1933             }
1934         }
1935
1936         // NOTE: Clone this string array if you want to return it to user.  Otherwise, you are returning a writable cache copy.
1937         // This won't include default, call AllLongDatePatterns
1938         private String[] UnclonedLongDatePatterns
1939         {
1940             get
1941             {
1942                 if (allLongDatePatterns == null)
1943                 {
1944                     Contract.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.UnclonedLongDatePatterns] Expected Calendar.ID > 0");
1945                     this.allLongDatePatterns = this.m_cultureData.LongDates(this.Calendar.ID);
1946                     Contract.Assert(this.allLongDatePatterns.Length > 0,
1947                         "[DateTimeFormatInfo.UnclonedLongDatePatterns] Expected some long date patterns");
1948                 }
1949
1950                 return this.allLongDatePatterns;
1951             }
1952         }
1953
1954         // NOTE: Clone this string array if you want to return it to user.  Otherwise, you are returning a writable cache copy.
1955         // This won't include default, call AllShortTimePatterns
1956         private String[] UnclonedShortTimePatterns
1957         {
1958             get
1959             {
1960                 if (this.allShortTimePatterns == null)
1961                 {
1962                     this.allShortTimePatterns = this.m_cultureData.ShortTimes;
1963                     Contract.Assert(this.allShortTimePatterns.Length > 0,
1964                         "[DateTimeFormatInfo.UnclonedShortTimePatterns] Expected some short time patterns");
1965                 }
1966                 
1967                 return this.allShortTimePatterns;
1968             }
1969         }
1970
1971         // NOTE: Clone this string array if you want to return it to user.  Otherwise, you are returning a writable cache copy.
1972         // This won't include default, call AllLongTimePatterns
1973         private String[] UnclonedLongTimePatterns
1974         {
1975             get
1976             {
1977                 if (this.allLongTimePatterns == null)
1978                 {
1979                     this.allLongTimePatterns = this.m_cultureData.LongTimes;
1980                     Contract.Assert(this.allLongTimePatterns.Length > 0,
1981                         "[DateTimeFormatInfo.UnclonedLongTimePatterns] Expected some long time patterns");
1982                 }
1983                 
1984                 return this.allLongTimePatterns;
1985             }
1986         }
1987
1988         public static DateTimeFormatInfo ReadOnly(DateTimeFormatInfo dtfi) {
1989             if (dtfi == null) {
1990                 throw new ArgumentNullException("dtfi",
1991                     Environment.GetResourceString("ArgumentNull_Obj"));
1992             }
1993             Contract.EndContractBlock();
1994             if (dtfi.IsReadOnly) {
1995                 return (dtfi);
1996             }
1997             DateTimeFormatInfo newInfo = (DateTimeFormatInfo)(dtfi.MemberwiseClone());
1998             // We can use the data member calendar in the setter, instead of the property Calendar,
1999             // since the cloned copy should have the same state as the original copy.
2000             newInfo.calendar = Calendar.ReadOnly(dtfi.Calendar);
2001             newInfo.m_isReadOnly = true;
2002             return (newInfo);
2003         }
2004
2005
2006         public  bool IsReadOnly {
2007             get {
2008                 return (m_isReadOnly);
2009             }
2010         }
2011
2012         // Return the native name for the calendar in DTFI.Calendar.  The native name is referred to
2013         // the culture used to create the DTFI.  E.g. in the following example, the native language is Japanese.
2014         // DateTimeFormatInfo dtfi = new CultureInfo("ja-JP", false).DateTimeFormat.Calendar = new JapaneseCalendar();
2015         // String nativeName = dtfi.NativeCalendarName; // Get the Japanese name for the Japanese calendar.
2016         // DateTimeFormatInfo dtfi = new CultureInfo("ja-JP", false).DateTimeFormat.Calendar = new GregorianCalendar(GregorianCalendarTypes.Localized);
2017         // String nativeName = dtfi.NativeCalendarName; // Get the Japanese name for the Gregorian calendar.
2018         [System.Runtime.InteropServices.ComVisible(false)]
2019         public String NativeCalendarName
2020         {
2021             get
2022             {
2023                 return m_cultureData.CalendarName(Calendar.ID);
2024             }
2025         }
2026
2027         //
2028         // Used by custom cultures and others to set the list of available formats. Note that none of them are
2029         // explicitly used unless someone calls GetAllDateTimePatterns and subsequently uses one of the items
2030         // from the list.
2031         //
2032         // Most of the format characters that can be used in GetAllDateTimePatterns are
2033         // not really needed since they are one of the following:
2034         //
2035         //  r/R/s/u     locale-independent constants -- cannot be changed!
2036         //  m/M/y/Y     fields with a single string in them -- that can be set through props directly
2037         //  f/F/g/G/U   derived fields based on combinations of various of the below formats
2038         //
2039         // NOTE: No special validation is done here beyond what is done when the actual respective fields
2040         // are used (what would be the point of disallowing here what we allow in the appropriate property?)
2041         //
2042         // WARNING: If more validation is ever done in one place, it should be done in the other.
2043         //
2044
2045         [System.Runtime.InteropServices.ComVisible(false)]
2046         public void SetAllDateTimePatterns(String[] patterns, char format)
2047         {
2048             if (IsReadOnly)
2049                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
2050             if (patterns == null) {
2051                 throw new ArgumentNullException("patterns",
2052                     Environment.GetResourceString("ArgumentNull_Array"));
2053             }
2054
2055             if (patterns.Length == 0)
2056             {
2057                 throw new ArgumentException(Environment.GetResourceString("Arg_ArrayZeroError"), "patterns");
2058             }
2059             Contract.EndContractBlock();
2060
2061             for (int i=0; i<patterns.Length; i++)
2062             {
2063                 if (patterns[i] == null)
2064                 {
2065                     throw new ArgumentNullException(Environment.GetResourceString("ArgumentNull_ArrayValue"));
2066                 }
2067             }
2068
2069             // Remember the patterns, and use the 1st as default
2070             switch (format)
2071             {
2072                 case 'd':
2073                     this.allShortDatePatterns = patterns;
2074                     this.shortDatePattern = this.allShortDatePatterns[0];
2075                     break;
2076                     
2077                 case 'D':
2078                     this.allLongDatePatterns = patterns;
2079                     this.longDatePattern = this.allLongDatePatterns[0];
2080                     break;
2081                     
2082                 case 't':
2083                     this.allShortTimePatterns = patterns;
2084                     this.shortTimePattern = this.allShortTimePatterns[0];
2085                     break;
2086                     
2087                 case 'T':
2088                     this.allLongTimePatterns = patterns;
2089                     this.longTimePattern = this.allLongTimePatterns[0];
2090                     break;
2091
2092                 case 'y':
2093                 case 'Y':
2094                     this.allYearMonthPatterns = patterns;
2095                     this.yearMonthPattern = this.allYearMonthPatterns[0];
2096                     break;
2097                     
2098                 default:
2099                     throw new ArgumentException(Environment.GetResourceString("Format_BadFormatSpecifier"), "format");
2100             }
2101
2102             // Clear the token hash table, note that even short dates could require this
2103             ClearTokenHashTable();
2104
2105             return;
2106         }
2107
2108         [System.Runtime.InteropServices.ComVisible(false)]
2109         public String[] AbbreviatedMonthGenitiveNames
2110         {
2111             get
2112             {
2113                 return ((String[])internalGetGenitiveMonthNames(true).Clone());
2114             }
2115
2116             set
2117             {
2118                 if (IsReadOnly)
2119                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
2120                 if (value == null)
2121                 {
2122                     throw new ArgumentNullException("value",
2123                         Environment.GetResourceString("ArgumentNull_Array"));
2124                 }
2125                 if (value.Length != 13)
2126                 {
2127                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidArrayLength", 13), "value");
2128                 }
2129                 Contract.EndContractBlock();
2130                 CheckNullValue(value, value.Length - 1);
2131                 ClearTokenHashTable();
2132                 this.m_genitiveAbbreviatedMonthNames= value;
2133             }
2134         }
2135
2136         [System.Runtime.InteropServices.ComVisible(false)]
2137         public String[] MonthGenitiveNames
2138          {
2139             get
2140             {
2141                 return ((String[])internalGetGenitiveMonthNames(false).Clone());
2142             }
2143
2144             set
2145             {
2146                 if (IsReadOnly)
2147                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
2148                 if (value == null)
2149                 {
2150                     throw new ArgumentNullException("value",
2151                         Environment.GetResourceString("ArgumentNull_Array"));
2152                 }
2153                 if (value.Length != 13)
2154                 {
2155                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidArrayLength", 13), "value");
2156                 }
2157                 Contract.EndContractBlock();
2158                 CheckNullValue(value, value.Length - 1);
2159                 genitiveMonthNames= value;
2160                 ClearTokenHashTable();
2161             }
2162         }
2163
2164         //
2165         // Positive TimeSpan Pattern
2166         //
2167         [NonSerialized]
2168         private string m_fullTimeSpanPositivePattern;
2169         internal String FullTimeSpanPositivePattern 
2170         {
2171             get
2172             {
2173                 if (m_fullTimeSpanPositivePattern == null)
2174                 {
2175                     CultureData cultureDataWithoutUserOverrides;
2176                     if (m_cultureData.UseUserOverride)
2177                         cultureDataWithoutUserOverrides = CultureData.GetCultureData(m_cultureData.CultureName, false);
2178                     else
2179                         cultureDataWithoutUserOverrides = m_cultureData;
2180                     String decimalSeparator = new NumberFormatInfo(cultureDataWithoutUserOverrides).NumberDecimalSeparator;
2181                     
2182                     m_fullTimeSpanPositivePattern = "d':'h':'mm':'ss'" + decimalSeparator + "'FFFFFFF";
2183                 }
2184                 return m_fullTimeSpanPositivePattern;
2185             }
2186         }
2187
2188         //
2189         // Negative TimeSpan Pattern
2190         //
2191         [NonSerialized]
2192         private string m_fullTimeSpanNegativePattern;
2193         internal String FullTimeSpanNegativePattern 
2194         {
2195             get
2196             {
2197                 if (m_fullTimeSpanNegativePattern == null)
2198                     m_fullTimeSpanNegativePattern = "'-'" + FullTimeSpanPositivePattern;
2199                 return m_fullTimeSpanNegativePattern;
2200             }
2201         }
2202
2203         //
2204         // Get suitable CompareInfo from current DTFI object.
2205         //
2206         internal CompareInfo CompareInfo
2207         {
2208             get 
2209             {
2210                 if (m_compareInfo == null)
2211                 {
2212                     // We use the regular GetCompareInfo here to make sure the created CompareInfo object is stored in the
2213                     // CompareInfo cache. otherwise we would just create CompareInfo using m_cultureData.
2214                     m_compareInfo = CompareInfo.GetCompareInfo(m_cultureData.SCOMPAREINFO);
2215                 }
2216                 
2217                 return m_compareInfo;
2218             }
2219         }
2220
2221         internal const DateTimeStyles InvalidDateTimeStyles = ~(DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite
2222                                                                | DateTimeStyles.AllowInnerWhite | DateTimeStyles.NoCurrentDateDefault
2223                                                                | DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeLocal
2224                                                                | DateTimeStyles.AssumeUniversal | DateTimeStyles.RoundtripKind);
2225
2226         internal static void ValidateStyles(DateTimeStyles style, String parameterName) {
2227             if ((style & InvalidDateTimeStyles) != 0) {
2228                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDateTimeStyles"), parameterName);
2229             }
2230             if (((style & (DateTimeStyles.AssumeLocal)) != 0) && ((style & (DateTimeStyles.AssumeUniversal)) != 0)) {
2231                 throw new ArgumentException(Environment.GetResourceString("Argument_ConflictingDateTimeStyles"), parameterName);
2232             }
2233             Contract.EndContractBlock();
2234             if (((style & DateTimeStyles.RoundtripKind) != 0)
2235                 && ((style & (DateTimeStyles.AssumeLocal | DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)) != 0)) {
2236                 throw new ArgumentException(Environment.GetResourceString("Argument_ConflictingDateTimeRoundtripStyles"), parameterName);
2237             }
2238         }
2239
2240         //
2241         // Actions: Return the internal flag used in formatting and parsing.
2242         //  The flag can be used to indicate things like if genitive forms is used in this DTFi, or if leap year gets different month names.
2243         //
2244         internal DateTimeFormatFlags FormatFlags
2245         {
2246             get
2247             {
2248                 if (formatFlags == DateTimeFormatFlags.NotInitialized)
2249                 {
2250                     // Build the format flags from the data in this DTFI
2251                     formatFlags = DateTimeFormatFlags.None;
2252                     formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagGenitiveMonth(
2253                         MonthNames, internalGetGenitiveMonthNames(false), AbbreviatedMonthNames, internalGetGenitiveMonthNames(true));
2254                     formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseSpaceInMonthNames(
2255                         MonthNames, internalGetGenitiveMonthNames(false), AbbreviatedMonthNames, internalGetGenitiveMonthNames(true));
2256                     formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseSpaceInDayNames(DayNames, AbbreviatedDayNames);
2257                     formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseHebrewCalendar((int)Calendar.ID);                    
2258                 }
2259                 return (formatFlags);
2260             }
2261         }
2262
2263         internal Boolean HasForceTwoDigitYears {
2264             get {
2265                 switch (calendar.ID)
2266                 {
2267                     /*  */ 
2268                     // If is y/yy, do not get (year % 100). "y" will print
2269                     // year without leading zero.  "yy" will print year with two-digit in leading zero.
2270                     // If pattern is yyy/yyyy/..., print year value with two-digit in leading zero.
2271                     // So year 5 is "05", and year 125 is "125".
2272                     // The reason for not doing (year % 100) is for Taiwan calendar.
2273                     // If year 125, then output 125 and not 25.
2274                     // Note: OS uses "yyyy" for Taiwan calendar by default.
2275                     case (Calendar.CAL_JAPAN):
2276                     case (Calendar.CAL_TAIWAN):
2277                         return true;
2278                 }
2279                 return false;
2280             }
2281         }
2282
2283         // Returns whether the YearMonthAdjustment function has any fix-up work to do for this culture/calendar.
2284         internal Boolean HasYearMonthAdjustment {
2285             get {
2286                 return ((FormatFlags & DateTimeFormatFlags.UseHebrewRule) != 0);
2287             }
2288         }
2289
2290         // This is a callback that the parser can make back into the DTFI to let it fiddle with special
2291         // cases associated with that culture or calendar. Currently this only has special cases for
2292         // the Hebrew calendar, but this could be extended to other cultures.
2293         //
2294         // The return value is whether the year and month are actually valid for this calendar.
2295         internal Boolean YearMonthAdjustment(ref int year, ref int month, Boolean parsedMonthName) {
2296             if ((FormatFlags & DateTimeFormatFlags.UseHebrewRule) != 0) {
2297
2298                 // Special rules to fix up the Hebrew year/month
2299
2300                 // When formatting, we only format up to the hundred digit of the Hebrew year, although Hebrew year is now over 5000.
2301                 // E.g. if the year is 5763, we only format as 763.
2302                 if (year < 1000) {
2303                     year += 5000;
2304                 }
2305
2306                 // Because we need to calculate leap year, we should fall out now for an invalid year.
2307                 if (year < Calendar.GetYear(Calendar.MinSupportedDateTime) || year > Calendar.GetYear(Calendar.MaxSupportedDateTime)) {
2308                     return false;
2309                 }
2310
2311                 // To handle leap months, the set of month names in the symbol table does not always correspond to the numbers.
2312                 // For non-leap years, month 7 (Adar Bet) is not present, so we need to make using this month invalid and
2313                 // shuffle the other months down.
2314                 if (parsedMonthName) {
2315                     if (!Calendar.IsLeapYear(year)) {
2316                         if (month >= 8) {
2317                             month--;
2318                         }
2319                         else if (month == 7) {
2320                             return false;
2321                         }
2322                     }
2323                 }
2324             }
2325             return true;
2326         }
2327
2328         //
2329         // DateTimeFormatInfo tokenizer.  This is used by DateTime.Parse() to break input string into tokens.
2330         //
2331         [NonSerialized]
2332         TokenHashValue[] m_dtfiTokenHash;
2333
2334         private const int TOKEN_HASH_SIZE = 199;
2335         private const int SECOND_PRIME = 197;
2336         private const String dateSeparatorOrTimeZoneOffset = "-";
2337         private const String invariantDateSeparator = "/";
2338         private const String invariantTimeSeparator = ":";
2339
2340         //
2341         // Common Ignorable Symbols
2342         //
2343         internal const String IgnorablePeriod         = ".";
2344         internal const String IgnorableComma          = ",";
2345
2346         //
2347         // Year/Month/Day suffixes
2348         //
2349         internal const String CJKYearSuff             = "\u5e74";
2350         internal const String CJKMonthSuff            = "\u6708";
2351         internal const String CJKDaySuff              = "\u65e5";
2352
2353         internal const String KoreanYearSuff          = "\ub144";
2354         internal const String KoreanMonthSuff         = "\uc6d4";
2355         internal const String KoreanDaySuff           = "\uc77c";
2356
2357         internal const String KoreanHourSuff          = "\uc2dc";
2358         internal const String KoreanMinuteSuff        = "\ubd84";
2359         internal const String KoreanSecondSuff        = "\ucd08";
2360
2361         internal const String CJKHourSuff             = "\u6642";
2362         internal const String ChineseHourSuff         = "\u65f6";
2363
2364         internal const String CJKMinuteSuff           = "\u5206";
2365         internal const String CJKSecondSuff           = "\u79d2";
2366
2367         internal const String LocalTimeMark           = "T";
2368
2369         internal const String KoreanLangName = "ko";
2370         internal const String JapaneseLangName = "ja";
2371         internal const String EnglishLangName = "en";
2372
2373         private static volatile DateTimeFormatInfo s_jajpDTFI;
2374         private static volatile DateTimeFormatInfo s_zhtwDTFI;
2375
2376         //
2377         // Create a Japanese DTFI which uses JapaneseCalendar.  This is used to parse
2378         // date string with Japanese era name correctly even when the supplied DTFI
2379         // does not use Japanese calendar.
2380         // The created instance is stored in global s_jajpDTFI.
2381         //
2382         internal static DateTimeFormatInfo GetJapaneseCalendarDTFI() {
2383             DateTimeFormatInfo temp = s_jajpDTFI;
2384             if (temp == null) {
2385                 temp = new CultureInfo("ja-JP", false).DateTimeFormat;
2386                 temp.Calendar = JapaneseCalendar.GetDefaultInstance();
2387                 s_jajpDTFI = temp;
2388             }
2389             return (temp);
2390         }
2391
2392 /* 
2393
2394
2395
2396
2397
2398  */ 
2399         internal static DateTimeFormatInfo GetTaiwanCalendarDTFI() {
2400             DateTimeFormatInfo temp = s_zhtwDTFI;
2401             if (temp == null) {
2402                 temp = new CultureInfo("zh-TW", false).DateTimeFormat;
2403                 temp.Calendar = TaiwanCalendar.GetDefaultInstance();
2404                 s_zhtwDTFI = temp;
2405             }
2406             return (temp);
2407         }
2408
2409
2410         // DTFI properties should call this when the setter are called.
2411         private void ClearTokenHashTable()
2412         {
2413             m_dtfiTokenHash = null;
2414             formatFlags = DateTimeFormatFlags.NotInitialized;
2415         }
2416
2417         [System.Security.SecurityCritical]  // auto-generated
2418         internal TokenHashValue[] CreateTokenHashTable() {
2419             TokenHashValue[] temp = m_dtfiTokenHash;
2420             if (temp == null) {
2421                 temp = new TokenHashValue[TOKEN_HASH_SIZE];
2422
2423                 bool koreanLanguage = LanguageName.Equals(KoreanLangName);
2424
2425                 string sep = this.TimeSeparator.Trim();
2426                 if (IgnorableComma  != sep) InsertHash(temp, IgnorableComma, TokenType.IgnorableSymbol, 0);
2427                 if (IgnorablePeriod != sep) InsertHash(temp, IgnorablePeriod, TokenType.IgnorableSymbol, 0);
2428                 
2429                 if (KoreanHourSuff != sep && CJKHourSuff != sep && ChineseHourSuff != sep) {
2430                     //
2431                     // On the Macintosh, the default TimeSeparator is identical to the KoreanHourSuff, CJKHourSuff, or ChineseHourSuff for some cultures like
2432                     // ja-JP and ko-KR.  In these cases having the same symbol inserted into the hash table with multiple TokenTypes causes undesirable
2433                     // DateTime.Parse behavior.  For instance, the DateTimeFormatInfo.Tokenize() method might return SEP_DateOrOffset for KoreanHourSuff
2434                     // instead of SEP_HourSuff.
2435                     //
2436                     InsertHash(temp, this.TimeSeparator, TokenType.SEP_Time, 0);
2437                 }
2438
2439                 InsertHash(temp, this.AMDesignator, TokenType.SEP_Am | TokenType.Am, 0);
2440                 InsertHash(temp, this.PMDesignator, TokenType.SEP_Pm | TokenType.Pm, 1);
2441
2442                 // 
2443                 if (LanguageName.Equals("sq")) {
2444                     // Albanian allows time formats like "12:00.PD"
2445                     InsertHash(temp, IgnorablePeriod + this.AMDesignator, TokenType.SEP_Am | TokenType.Am, 0);
2446                     InsertHash(temp, IgnorablePeriod + this.PMDesignator, TokenType.SEP_Pm | TokenType.Pm, 1);
2447                 }
2448
2449                 // CJK suffix
2450                 InsertHash(temp, CJKYearSuff, TokenType.SEP_YearSuff, 0);
2451                 InsertHash(temp, KoreanYearSuff, TokenType.SEP_YearSuff, 0);
2452                 InsertHash(temp, CJKMonthSuff, TokenType.SEP_MonthSuff, 0);
2453                 InsertHash(temp, KoreanMonthSuff, TokenType.SEP_MonthSuff, 0);
2454                 InsertHash(temp, CJKDaySuff, TokenType.SEP_DaySuff, 0);
2455                 InsertHash(temp, KoreanDaySuff, TokenType.SEP_DaySuff, 0);
2456
2457                 InsertHash(temp, CJKHourSuff, TokenType.SEP_HourSuff, 0);
2458                 InsertHash(temp, ChineseHourSuff, TokenType.SEP_HourSuff, 0);
2459                 InsertHash(temp, CJKMinuteSuff, TokenType.SEP_MinuteSuff, 0);
2460                 InsertHash(temp, CJKSecondSuff, TokenType.SEP_SecondSuff, 0);
2461
2462                 // 
2463                 if (koreanLanguage) {
2464                     // Korean suffix
2465                     InsertHash(temp, KoreanHourSuff, TokenType.SEP_HourSuff, 0);
2466                     InsertHash(temp, KoreanMinuteSuff, TokenType.SEP_MinuteSuff, 0);
2467                     InsertHash(temp, KoreanSecondSuff, TokenType.SEP_SecondSuff, 0);
2468                 }
2469                 
2470                 if ( LanguageName.Equals("ky")) {
2471                     // For some cultures, the date separator works more like a comma, being allowed before or after any date part
2472                     InsertHash(temp, dateSeparatorOrTimeZoneOffset, TokenType.IgnorableSymbol, 0);
2473                 }
2474                 else {
2475                     InsertHash(temp, dateSeparatorOrTimeZoneOffset, TokenType.SEP_DateOrOffset, 0);
2476                 }
2477
2478                 String[] dateWords = null;
2479                 DateTimeFormatInfoScanner scanner = null;
2480
2481                 // We need to rescan the date words since we're always synthetic
2482                 scanner = new DateTimeFormatInfoScanner();
2483                 // Enumarate all LongDatePatterns, and get the DateWords and scan for month postfix.
2484                 // The only reason they're being assigned to m_dateWords is for Whidbey Deserialization
2485                 m_dateWords = dateWords = scanner.GetDateWordsOfDTFI(this);
2486                 // Ensure the formatflags is initialized.
2487                 DateTimeFormatFlags flag = FormatFlags;
2488
2489                 // For some cultures, the date separator works more like a comma, being allowed before or after any date part.
2490                 // In these cultures, we do not use normal date separator since we disallow date separator after a date terminal state.
2491                 // This is determined in DateTimeFormatInfoScanner.  Use this flag to determine if we should treat date separator as ignorable symbol.
2492                 bool useDateSepAsIgnorableSymbol = false;
2493                 
2494                 String monthPostfix = null;
2495                 if (dateWords != null)
2496                 {
2497                     // There are DateWords.  It could be a real date word (such as "de"), or a monthPostfix.
2498                     // The monthPostfix starts with '\xfffe' (MonthPostfixChar), followed by the real monthPostfix.
2499                     for (int i = 0; i < dateWords.Length; i++)
2500                     {
2501                         switch (dateWords[i][0])
2502                         {
2503                             // This is a month postfix
2504                             case DateTimeFormatInfoScanner.MonthPostfixChar:
2505                                 // Get the real month postfix.
2506                                 monthPostfix = dateWords[i].Substring(1);
2507                                 // Add the month name + postfix into the token.
2508                                 AddMonthNames(temp, monthPostfix);
2509                                 break;
2510                             case DateTimeFormatInfoScanner.IgnorableSymbolChar:
2511                                 String symbol = dateWords[i].Substring(1);
2512                                 InsertHash(temp, symbol, TokenType.IgnorableSymbol, 0);
2513                                 if (this.DateSeparator.Trim(null).Equals(symbol))
2514                                 {
2515                                     // The date separator is the same as the ingorable symbol.
2516                                     useDateSepAsIgnorableSymbol = true;
2517                                 }
2518                                 break;
2519                             default:
2520                                 InsertHash(temp, dateWords[i], TokenType.DateWordToken, 0);
2521                                 // 
2522                                 if (LanguageName.Equals("eu")) {
2523                                     // Basque has date words with leading dots
2524                                     InsertHash(temp, IgnorablePeriod + dateWords[i], TokenType.DateWordToken, 0);
2525                                 }
2526                                 break;
2527                         }
2528                     }
2529                 }
2530
2531                 if (!useDateSepAsIgnorableSymbol)
2532                 {
2533                     // Use the normal date separator.
2534                     InsertHash(temp, this.DateSeparator, TokenType.SEP_Date, 0);
2535                 }
2536                 // Add the regular month names.
2537                 AddMonthNames(temp, null);
2538
2539                 // Add the abbreviated month names.
2540                 for (int i = 1; i <= 13; i++) {
2541                     InsertHash(temp, GetAbbreviatedMonthName(i), TokenType.MonthToken, i);
2542                 }
2543                 
2544
2545                 if ((FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0) {
2546                     for (int i = 1; i <= 13; i++) {
2547                         String str;
2548                         str = internalGetMonthName(i, MonthNameStyles.Genitive, false);
2549                         InsertHash(temp, str, TokenType.MonthToken, i);
2550                     }
2551                 }
2552
2553                 if ((FormatFlags & DateTimeFormatFlags.UseLeapYearMonth) != 0) {
2554                     for (int i = 1; i <= 13; i++) {
2555                         String str;
2556                         str = internalGetMonthName(i, MonthNameStyles.LeapYear, false);
2557                         InsertHash(temp, str, TokenType.MonthToken, i);
2558                     }
2559                 }
2560
2561                 for (int i = 0; i < 7; i++) {
2562                     //String str = GetDayOfWeekNames()[i];
2563                     // We have to call public methods here to work with inherited DTFI.
2564                     String str = GetDayName((DayOfWeek)i);
2565                     InsertHash(temp, str, TokenType.DayOfWeekToken, i);
2566
2567                     str = GetAbbreviatedDayName((DayOfWeek)i);
2568                     InsertHash(temp, str, TokenType.DayOfWeekToken, i);
2569
2570                 }
2571
2572                 int[] eras = calendar.Eras;
2573                 for (int i = 1; i <= eras.Length; i++) {
2574                     InsertHash(temp, GetEraName(i), TokenType.EraToken, i);
2575                     InsertHash(temp, GetAbbreviatedEraName(i), TokenType.EraToken, i);
2576                 }
2577
2578                 // 
2579                 if (LanguageName.Equals(JapaneseLangName)) {
2580                     // Japanese allows day of week forms like: "(Tue)"
2581                     for (int i = 0; i < 7; i++) {
2582                         String specialDayOfWeek = "(" + GetAbbreviatedDayName((DayOfWeek)i) + ")";
2583                         InsertHash(temp, specialDayOfWeek, TokenType.DayOfWeekToken, i);
2584                     }
2585                     if (this.Calendar.GetType() != typeof(JapaneseCalendar)) {
2586                         // Special case for Japanese.  If this is a Japanese DTFI, and the calendar is not Japanese calendar,
2587                         // we will check Japanese Era name as well when the calendar is Gregorian.
2588                         DateTimeFormatInfo jaDtfi = GetJapaneseCalendarDTFI();
2589                         for (int i = 1; i <= jaDtfi.Calendar.Eras.Length; i++) {
2590                             InsertHash(temp, jaDtfi.GetEraName(i), TokenType.JapaneseEraToken, i);
2591                             InsertHash(temp, jaDtfi.GetAbbreviatedEraName(i), TokenType.JapaneseEraToken, i);
2592                             // m_abbrevEnglishEraNames[0] contains the name for era 1, so the token value is i+1.
2593                             InsertHash(temp, jaDtfi.AbbreviatedEnglishEraNames[i-1], TokenType.JapaneseEraToken, i);
2594                         }
2595                     }
2596                 }
2597                 // 
2598                 else if (CultureName.Equals("zh-TW")) {
2599                     DateTimeFormatInfo twDtfi = GetTaiwanCalendarDTFI();
2600                     for (int i = 1; i <= twDtfi.Calendar.Eras.Length; i++) {
2601                         if (twDtfi.GetEraName(i).Length > 0) {
2602                             InsertHash(temp, twDtfi.GetEraName(i), TokenType.TEraToken, i);
2603                         }
2604                     }
2605                 }
2606
2607                 InsertHash(temp, InvariantInfo.AMDesignator, TokenType.SEP_Am | TokenType.Am, 0);
2608                 InsertHash(temp, InvariantInfo.PMDesignator, TokenType.SEP_Pm | TokenType.Pm, 1);
2609
2610                 // Add invariant month names and day names.
2611                 for (int i = 1; i <= 12; i++) {
2612                     String str;
2613                     // We have to call public methods here to work with inherited DTFI.
2614                     // Insert the month name first, so that they are at the front of abbrevaited
2615                     // month names.
2616                     str = InvariantInfo.GetMonthName(i);
2617                     InsertHash(temp, str, TokenType.MonthToken, i);
2618                     str = InvariantInfo.GetAbbreviatedMonthName(i);
2619                         InsertHash(temp, str, TokenType.MonthToken, i);
2620                 }
2621
2622                 for (int i = 0; i < 7; i++) {
2623                     // We have to call public methods here to work with inherited DTFI.
2624                     String str = InvariantInfo.GetDayName((DayOfWeek)i);
2625                     InsertHash(temp, str, TokenType.DayOfWeekToken, i);
2626
2627                     str = InvariantInfo.GetAbbreviatedDayName((DayOfWeek)i);
2628                     InsertHash(temp, str, TokenType.DayOfWeekToken, i);
2629
2630                 }
2631
2632                 for (int i = 0; i < AbbreviatedEnglishEraNames.Length; i++) {
2633                     // m_abbrevEnglishEraNames[0] contains the name for era 1, so the token value is i+1.
2634                     InsertHash(temp, AbbreviatedEnglishEraNames[i], TokenType.EraToken, i + 1);
2635                 }
2636
2637                 InsertHash(temp, LocalTimeMark, TokenType.SEP_LocalTimeMark, 0);
2638                 InsertHash(temp, DateTimeParse.GMTName, TokenType.TimeZoneToken, 0);
2639                 InsertHash(temp, DateTimeParse.ZuluName, TokenType.TimeZoneToken, 0);
2640
2641                 InsertHash(temp, invariantDateSeparator, TokenType.SEP_Date, 0);
2642                 InsertHash(temp, invariantTimeSeparator, TokenType.SEP_Time, 0);
2643
2644                 m_dtfiTokenHash = temp;
2645             }
2646             return (temp);
2647         }
2648
2649         private void AddMonthNames(TokenHashValue[] temp, String monthPostfix)
2650         {
2651             for (int i = 1; i <= 13; i++) {
2652                 String str;
2653                 //str = internalGetMonthName(i, MonthNameStyles.Regular, false);
2654                 // We have to call public methods here to work with inherited DTFI.
2655                 // Insert the month name first, so that they are at the front of abbrevaited
2656                 // month names.
2657                 str = GetMonthName(i);
2658                 if (str.Length > 0) {
2659                     if (monthPostfix != null) {
2660                         // Insert the month name with the postfix first, so it can be matched first.
2661                         InsertHash(temp, str + monthPostfix, TokenType.MonthToken, i);
2662                     } else
2663                     {
2664                         InsertHash(temp, str, TokenType.MonthToken, i);
2665                     }
2666                 }
2667                 str = GetAbbreviatedMonthName(i);
2668                 InsertHash(temp, str, TokenType.MonthToken, i);
2669             }
2670         
2671         }
2672
2673         ////////////////////////////////////////////////////////////////////////
2674         //
2675         // Actions:
2676         // Try to parse the current word to see if it is a Hebrew number.
2677         // Tokens will be updated accordingly.
2678         // This is called by the Lexer of DateTime.Parse().
2679         //
2680         // Unlike most of the functions in this class, the return value indicates
2681         // whether or not it started to parse. The badFormat parameter indicates
2682         // if parsing began, but the format was bad.
2683         //
2684         ////////////////////////////////////////////////////////////////////////
2685
2686         private static bool TryParseHebrewNumber(
2687             ref __DTString str,
2688             out Boolean badFormat,
2689             out int number)  {
2690
2691             number = -1;
2692             badFormat = false;
2693
2694             int i = str.Index;
2695             if (!HebrewNumber.IsDigit(str.Value[i])) {
2696                 // If the current character is not a Hebrew digit, just return false.
2697                 // There is no chance that we can parse a valid Hebrew number from here.
2698                 return (false);
2699             }
2700             // The current character is a Hebrew digit.  Try to parse this word as a Hebrew number.
2701             HebrewNumberParsingContext context = new HebrewNumberParsingContext(0);
2702             HebrewNumberParsingState state;
2703
2704             do {
2705                 state = HebrewNumber.ParseByChar(str.Value[i++], ref context);
2706                 switch (state) {
2707                     case HebrewNumberParsingState.InvalidHebrewNumber:    // Not a valid Hebrew number.
2708                     case HebrewNumberParsingState.NotHebrewDigit:         // The current character is not a Hebrew digit character.
2709                         // Break out so that we don't continue to try parse this as a Hebrew number.
2710                         return (false);
2711                 }
2712             } while (i < str.Value.Length && (state != HebrewNumberParsingState.FoundEndOfHebrewNumber));
2713
2714             // When we are here, we are either at the end of the string, or we find a valid Hebrew number.
2715             Contract.Assert(state == HebrewNumberParsingState.ContinueParsing || state == HebrewNumberParsingState.FoundEndOfHebrewNumber,
2716                 "Invalid returned state from HebrewNumber.ParseByChar()");
2717
2718             if (state != HebrewNumberParsingState.FoundEndOfHebrewNumber) {
2719                 // We reach end of the string but we can't find a terminal state in parsing Hebrew number.
2720                 return (false);
2721             }
2722
2723             // We have found a valid Hebrew number.  Update the index.
2724             str.Advance(i - str.Index);
2725
2726             // Get the final Hebrew number value from the HebrewNumberParsingContext.
2727             number = context.result;
2728
2729             return (true);
2730         }
2731
2732         private static bool IsHebrewChar(char ch) {
2733             return (ch >= '\x0590' && ch <= '\x05ff');
2734         }
2735
2736         [System.Security.SecurityCritical]  // auto-generated
2737         internal bool Tokenize(TokenType TokenMask, out TokenType tokenType, out int tokenValue, ref __DTString str) {
2738             tokenType = TokenType.UnknownToken;
2739             tokenValue = 0;
2740
2741             TokenHashValue value;
2742             Contract.Assert(str.Index < str.Value.Length, "DateTimeFormatInfo.Tokenize(): start < value.Length");
2743
2744             char ch = str.m_current;
2745             bool isLetter = Char.IsLetter(ch);
2746             if (isLetter) {
2747                 ch = Char.ToLower(ch, this.Culture);
2748                 if (IsHebrewChar(ch) && TokenMask == TokenType.RegularTokenMask) {
2749                     bool badFormat;
2750                     if (TryParseHebrewNumber(ref str, out badFormat, out tokenValue)) {
2751                         if (badFormat) {
2752                             tokenType = TokenType.UnknownToken;
2753                             return (false);
2754                         }
2755                         // This is a Hebrew number.
2756                         // Do nothing here.  TryParseHebrewNumber() will update token accordingly.
2757                         tokenType = TokenType.HebrewNumber;
2758                         return (true);
2759                     }
2760                 }
2761             }
2762
2763
2764             int hashcode = ch % TOKEN_HASH_SIZE;
2765             int hashProbe = 1 + ch % SECOND_PRIME;
2766             int remaining = str.len - str.Index;
2767             int i = 0;
2768
2769             TokenHashValue[] hashTable = m_dtfiTokenHash;
2770             if (hashTable == null) {
2771                 hashTable = CreateTokenHashTable();
2772             }
2773             do {
2774                 value = hashTable[hashcode];
2775                 if (value == null) {
2776                     // Not found.
2777                     break;
2778                 }
2779                 // Check this value has the right category (regular token or separator token) that we are looking for.
2780                 if (((int)value.tokenType & (int)TokenMask) > 0 && value.tokenString.Length <= remaining) {
2781                     if (String.Compare(str.Value, str.Index, value.tokenString, 0, value.tokenString.Length, this.Culture, CompareOptions.IgnoreCase)==0) {
2782                         if (isLetter) {
2783                             // If this token starts with a letter, make sure that we won't allow partial match.  So you can't tokenize "MarchWed" separately.
2784                             int nextCharIndex;
2785                             if ((nextCharIndex = str.Index + value.tokenString.Length) < str.len) {
2786                                 // Check word boundary.  The next character should NOT be a letter.
2787                                 char nextCh = str.Value[nextCharIndex];
2788                                 if (Char.IsLetter(nextCh)) {
2789                                     return (false);
2790                                 }
2791                             }
2792                         }
2793                         tokenType = value.tokenType & TokenMask;
2794                         tokenValue = value.tokenValue;
2795                         str.Advance(value.tokenString.Length);
2796                         return (true);
2797                     }  else if (value.tokenType == TokenType.MonthToken && HasSpacesInMonthNames) {
2798                         // For month token, we will match the month names which have spaces.
2799                         int matchStrLen = 0;
2800                         if (str.MatchSpecifiedWords(value.tokenString, true, ref matchStrLen)) {
2801                             tokenType = value.tokenType & TokenMask;
2802                             tokenValue = value.tokenValue;
2803                             str.Advance(matchStrLen);
2804                             return (true);
2805                         }
2806                     }  else if (value.tokenType == TokenType.DayOfWeekToken && HasSpacesInDayNames) {
2807                         // For month token, we will match the month names which have spaces.
2808                         int matchStrLen = 0;
2809                         if (str.MatchSpecifiedWords(value.tokenString, true, ref matchStrLen)) {
2810                             tokenType = value.tokenType & TokenMask;
2811                             tokenValue = value.tokenValue;
2812                             str.Advance(matchStrLen);
2813                             return (true);
2814                         }
2815                     }
2816                 }
2817                 i++;
2818                 hashcode += hashProbe;
2819                 if (hashcode >= TOKEN_HASH_SIZE) hashcode -= TOKEN_HASH_SIZE;
2820             }while (i < TOKEN_HASH_SIZE);
2821
2822             return (false);
2823         }
2824
2825         void InsertAtCurrentHashNode(TokenHashValue[] hashTable, String str, char ch, TokenType tokenType, int tokenValue, int pos, int hashcode, int hashProbe) {
2826             // Remember the current slot.
2827             TokenHashValue previousNode = hashTable[hashcode];
2828
2829             //// Console.WriteLine("   Insert Key: {0} in {1}", str, slotToInsert);
2830             // Insert the new node into the current slot.
2831             hashTable[hashcode] = new TokenHashValue(str, tokenType, tokenValue);;
2832
2833             while (++pos < TOKEN_HASH_SIZE) {
2834                 hashcode += hashProbe;
2835                 if (hashcode >= TOKEN_HASH_SIZE) hashcode -= TOKEN_HASH_SIZE;
2836                 // Remember this slot
2837                 TokenHashValue temp = hashTable[hashcode];
2838
2839                 if (temp != null && Char.ToLower(temp.tokenString[0], this.Culture) != ch) {
2840                     continue;
2841                 }
2842                 // Put the previous slot into this slot.
2843                 hashTable[hashcode] = previousNode;
2844                 //// Console.WriteLine("  Move {0} to slot {1}", previousNode.tokenString, hashcode);
2845                 if (temp == null) {
2846                     // Done
2847                     return;
2848                 }
2849                 previousNode = temp;
2850             } ;
2851             Contract.Assert(true, "The hashtable is full.  This should not happen.");
2852         }
2853
2854         void InsertHash(TokenHashValue[] hashTable, String str, TokenType tokenType, int tokenValue) {
2855             // The month of the 13th month is allowed to be null, so make sure that we ignore null value here.
2856             if (str == null || str.Length == 0) {
2857                 return;
2858             }
2859             TokenHashValue value;
2860             int i = 0;
2861             // If there is whitespace characters in the beginning and end of the string, trim them since whitespaces are skipped by
2862             // DateTime.Parse().
2863             if (Char.IsWhiteSpace(str[0]) || Char.IsWhiteSpace(str[str.Length - 1])) {
2864                 str = str.Trim(null);   // Trim white space characters.
2865                 // Could have space for separators
2866                 if (str.Length == 0)
2867                     return;
2868                 }
2869             char ch = Char.ToLower(str[0], this.Culture);
2870             int hashcode = ch % TOKEN_HASH_SIZE;
2871             int hashProbe = 1 + ch % SECOND_PRIME;
2872             do {
2873                 value = hashTable[hashcode];
2874                 if (value == null) {
2875                     //// Console.WriteLine("   Put Key: {0} in {1}", str, hashcode);
2876                     hashTable[hashcode] = new TokenHashValue(str, tokenType, tokenValue);
2877                     return;
2878                 } else {
2879                     // Collision happens. Find another slot.
2880                     if (str.Length >= value.tokenString.Length) {
2881                         // If there are two tokens with the same prefix, we have to make sure that the longer token should be at the front of
2882                         // the shorter ones.
2883                         if (String.Compare(str, 0, value.tokenString, 0, value.tokenString.Length, this.Culture, CompareOptions.IgnoreCase) == 0) {
2884                             if (str.Length > value.tokenString.Length) {
2885                                 // The str to be inserted has the same prefix as the current token, and str is longer.
2886                                 // Insert str into this node, and shift every node behind it.
2887                                 InsertAtCurrentHashNode(hashTable, str, ch, tokenType, tokenValue, i, hashcode, hashProbe);
2888                                 return;
2889                             } else {
2890                                 // Same token.  If they have different types (regular token vs separator token).  Add them.
2891                                 // If we have the same regular token or separator token in the hash already, do NOT update the hash.
2892                                 // Therefore, the order of inserting token is significant here regarding what tokenType will be kept in the hash.
2893
2894
2895                                 //
2896                                 // Check the current value of RegularToken (stored in the lower 8-bit of tokenType) , and insert the tokenType into the hash ONLY when we don't have a RegularToken yet.
2897                                 // Also check the current value of SeparatorToken (stored in the upper 8-bit of token), and insert the tokenType into the hash ONLY when we don't have the SeparatorToken yet.
2898                                 //
2899
2900                                 int nTokenType = (int)tokenType;
2901                                 int nCurrentTokenTypeInHash = (int)value.tokenType;
2902
2903                                 // The idea behind this check is:
2904                                 // - if the app is targetting 4.5.1 or above OR the compat flag is set, use the correct behavior by default.
2905                                 // - if the app is targetting 4.5 or below AND the compat switch is set, use the correct behavior
2906                                 // - if the app is targetting 4.5 or below AND the compat switch is NOT set, use the incorrect behavior
2907                                 if (preferExistingTokens || BinaryCompatibility.TargetsAtLeast_Desktop_V4_5_1)
2908                                 {
2909                                     if (((nCurrentTokenTypeInHash & (int)TokenType.RegularTokenMask) == 0) && ((nTokenType & (int)TokenType.RegularTokenMask) != 0) ||
2910                                        ((nCurrentTokenTypeInHash & (int)TokenType.SeparatorTokenMask) == 0) && ((nTokenType & (int)TokenType.SeparatorTokenMask) != 0))
2911                                     {
2912                                         value.tokenType |= tokenType;
2913                                         if (tokenValue != 0)
2914                                         {
2915                                             value.tokenValue = tokenValue;
2916                                         }
2917                                     }
2918                                 }
2919                                 else
2920                                 {
2921                                     // The following logic is incorrect and causes updates to happen depending on the bitwise relationship between the existing token type and the
2922                                     // the stored token type.  It was this way in .NET 4 RTM.  The behavior above is correct and will be adopted going forward.
2923
2924                                     if ((((nTokenType | nCurrentTokenTypeInHash) & (int)TokenType.RegularTokenMask) == nTokenType) ||
2925                                        (((nTokenType | nCurrentTokenTypeInHash) & (int)TokenType.SeparatorTokenMask) == nTokenType))
2926                                     {
2927                                         value.tokenType |= tokenType;
2928                                         if (tokenValue != 0)
2929                                         {
2930                                             value.tokenValue = tokenValue;
2931                                         }
2932                                     }
2933                                 }
2934                                 // The token to be inserted is already in the table.  Skip it.
2935                             }
2936                         }
2937                     }
2938                 }
2939                 //// Console.WriteLine("  COLLISION. Old Key: {0}, New Key: {1}", hashTable[hashcode].tokenString, str);
2940                 i++;
2941                 hashcode += hashProbe;
2942                 if (hashcode >= TOKEN_HASH_SIZE) hashcode -= TOKEN_HASH_SIZE;
2943             } while (i < TOKEN_HASH_SIZE);
2944             Contract.Assert(true, "The hashtable is full.  This should not happen.");
2945         }
2946     }   // class DateTimeFormatInfo
2947
2948     internal class TokenHashValue {
2949         internal String tokenString;
2950         internal TokenType tokenType;
2951         internal int tokenValue;
2952
2953         internal TokenHashValue(String tokenString, TokenType tokenType, int tokenValue) {
2954             this.tokenString = tokenString;
2955             this.tokenType = tokenType;
2956             this.tokenValue = tokenValue;
2957         }
2958     }
2959 }