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