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