[corlib] Calendars from reference sources
[mono.git] / mcs / class / corlib / System.Globalization / DateTimeFormatInfo.cs
1 //
2 // System.Globalization.DateTimeFormatInfo.cs
3 //
4 // Authors:
5 //   Martin Weindel (martin.weindel@t-online.de)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // (C) Martin Weindel (martin.weindel@t-online.de)
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
10 // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Collections.Generic;
34 using System.Runtime.InteropServices;
35 using System.Threading;
36
37 namespace System.Globalization
38 {
39         [Flags]
40         enum DateTimeFormatFlags {
41                 Unused,
42                 But,
43                 Serialized,
44                 By,
45                 Microsoft
46         }
47
48         [Serializable]
49         [ComVisible (true)]
50         [StructLayout (LayoutKind.Sequential)]
51         public sealed class DateTimeFormatInfo : ICloneable, IFormatProvider
52         {
53                 const string MSG_READONLY = "This instance is read only";
54                 private static readonly string[] INVARIANT_ABBREVIATED_DAY_NAMES
55                         = new string[7] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
56                 private static readonly string[] INVARIANT_DAY_NAMES
57                         = new string[7] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
58                 private static readonly string[] INVARIANT_ABBREVIATED_MONTH_NAMES
59                         = new string[13] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" };
60                 private static readonly string[] INVARIANT_MONTH_NAMES
61                         = new string[13] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" };
62                 static readonly string[] INVARIANT_SHORT_DAY_NAMES =
63                         new string[7] { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" };
64                 static readonly string[] INVARIANT_ERA_NAMES = { "A.D." };
65                 static readonly string[] INVARIANT_ABBREVIATED_ERA_NAMES = { "AD" };
66
67                 private static DateTimeFormatInfo theInvariantDateTimeFormatInfo;
68
69 #pragma warning disable 169
70                 #region Sync with object-internals.h
71                 private bool m_isReadOnly;
72                 private string amDesignator;
73                 private string pmDesignator;
74                 private string dateSeparator;
75                 private string timeSeparator;
76                 private string shortDatePattern;
77                 private string longDatePattern;
78                 private string shortTimePattern;
79                 private string longTimePattern;
80                 private string monthDayPattern;
81                 private string yearMonthPattern;
82                 private int firstDayOfWeek;
83                 private int calendarWeekRule;
84                 private string[] abbreviatedDayNames;
85                 private string[] dayNames;
86                 private string[] monthNames;
87                 private string[] genitiveMonthNames;
88                 private string[] abbreviatedMonthNames;
89                 private string[] m_genitiveAbbreviatedMonthNames;
90
91                 private string[] allShortDatePatterns;
92                 private string[] allLongDatePatterns;
93                 private string[] allShortTimePatterns;
94                 private string[] allLongTimePatterns;
95                 private string[] monthDayPatterns;
96                 private string[] yearMonthPatterns;
97                 private string[] shortestDayNames;
98                 #endregion
99
100                 internal readonly CultureInfo culture;
101                 Calendar calendar;
102
103                 // MS Serialization needs this
104                 private string fullDateTimePattern;
105                 private int nDataItem;
106                 private bool m_useUserOverride;
107                 private bool m_isDefaultCalendar;
108                 private int CultureID;
109                 private bool bUseCalendarInfo;
110                 private string generalShortTimePattern;
111                 private string generalLongTimePattern;
112                 private string[] m_eraNames;
113                 private string[] m_abbrevEraNames;
114                 private string[] m_abbrevEnglishEraNames;
115                 private string[] m_dateWords;
116                 private int[] optionalCalendars;
117                 private string[] leapYearMonthNames;
118                 private DateTimeFormatFlags formatFlags;
119                 private string m_name; // Unused, but MS.NET serializes this
120 #pragma warning restore 169
121
122                 internal DateTimeFormatInfo (CultureInfo culture, bool read_only)
123                 {
124                         if (culture == null)
125                                 throw new ArgumentNullException ("culture");
126
127                         this.culture = culture;
128                         m_isReadOnly = read_only;
129
130                         amDesignator = "AM";
131                         pmDesignator = "PM";
132                         dateSeparator = "/";
133                         timeSeparator = ":";
134                         shortDatePattern = "MM/dd/yyyy";
135                         longDatePattern = "dddd, dd MMMM yyyy";
136                         shortTimePattern = "HH:mm";
137                         longTimePattern = "HH:mm:ss";
138                         monthDayPattern = "MMMM dd";
139                         yearMonthPattern = "yyyy MMMM";
140
141                         firstDayOfWeek = (int) DayOfWeek.Sunday;
142                         calendarWeekRule = (int) CalendarWeekRule.FirstDay;
143
144                         abbreviatedDayNames = INVARIANT_ABBREVIATED_DAY_NAMES;
145                         dayNames = INVARIANT_DAY_NAMES;
146                         abbreviatedMonthNames = INVARIANT_ABBREVIATED_MONTH_NAMES;
147                         monthNames = INVARIANT_MONTH_NAMES;
148                         m_genitiveAbbreviatedMonthNames = INVARIANT_ABBREVIATED_MONTH_NAMES;
149                         genitiveMonthNames = INVARIANT_MONTH_NAMES;
150                         shortestDayNames = INVARIANT_SHORT_DAY_NAMES;
151                 }
152
153                 public DateTimeFormatInfo ()
154                         : this (CultureInfo.InvariantCulture, false)
155                 {
156                 }
157                                 
158                 public static DateTimeFormatInfo GetInstance(IFormatProvider provider)
159                 {
160                         if (provider != null) {
161                                 DateTimeFormatInfo dtfi;
162                                 dtfi = (DateTimeFormatInfo)provider.GetFormat(typeof(DateTimeFormatInfo));
163                                 if (dtfi != null)
164                                         return dtfi;
165                         }
166                         
167                         return CurrentInfo;
168                 }
169
170                 public bool IsReadOnly {
171                         get {
172                                 return m_isReadOnly;
173                         }
174                 }
175
176                 public static DateTimeFormatInfo ReadOnly(DateTimeFormatInfo dtfi)
177                 {
178                         DateTimeFormatInfo copy = (DateTimeFormatInfo)dtfi.Clone();
179                         copy.m_isReadOnly = true;
180                         return copy;
181                 }                       
182
183                 public object Clone () 
184                 {
185                         DateTimeFormatInfo clone = (DateTimeFormatInfo) MemberwiseClone();
186                         // clone is not read only
187                         clone.m_isReadOnly = false;
188                         return clone;
189                 }
190
191                 public object GetFormat(Type formatType)
192                 {
193                         return (formatType == GetType()) ? this : null;
194                 }
195
196                 public string GetAbbreviatedEraName (int era)
197                 {
198                         if (era < 0 || era > AbbreviatedEraNames.Length)
199                                 throw new ArgumentOutOfRangeException ("era", era.ToString ());
200                         if (era == Calendar.CurrentEra)
201                                 era = EraNames.Length;
202                         return AbbreviatedEraNames [era - 1];
203                 }
204
205                 public string GetAbbreviatedMonthName(int month)
206                 {
207                         if (month < 1 || month > 13) throw new ArgumentOutOfRangeException();
208                         return abbreviatedMonthNames[month-1];
209                 }
210
211                 public int GetEra (string eraName)
212                 {
213                         if (eraName == null)
214                                 throw new ArgumentNullException ();
215                         string [] eras = EraNames;
216                         for (int i = 0; i < eras.Length; i++)
217                                 if (CultureInfo.InvariantCulture.CompareInfo
218                                         .Compare (eraName, eras [i],
219                                         CompareOptions.IgnoreCase) == 0)
220                                         return Calendar.Eras [i];
221                         
222                         eras = AbbreviatedEraNames;
223                         for (int i = 0; i < eras.Length; i++)
224                                 if (CultureInfo.InvariantCulture.CompareInfo
225                                         .Compare (eraName, eras [i],
226                                         CompareOptions.IgnoreCase) == 0)
227                                         return Calendar.Eras [i];
228                         
229                         return -1;
230                 }
231
232                 public string GetEraName (int era)
233                 {
234                         if (era < 0 || era > EraNames.Length)
235                                 throw new ArgumentOutOfRangeException ("era", era.ToString ());
236                         if (era == Calendar.CurrentEra)
237                             era = EraNames.Length;
238                         return EraNames [era - 1];
239                 }
240
241                 internal string[] EraNames {
242                         get {
243                                 if (m_eraNames != null)
244                                         return m_eraNames;
245
246                                 // TODO: Should use Calendar.ID to initialize calendar specific era names
247                                 // from current culture. We don't have such data yet
248                                 m_eraNames = INVARIANT_ERA_NAMES;
249                                 return m_eraNames;
250                         }
251                 }
252
253                 internal string[] AbbreviatedEraNames {
254                         get {
255                                 if (m_abbrevEraNames != null)
256                                         return m_abbrevEraNames;
257
258                                 // TODO: Should use Calendar.ID to initialize calendar specific era names
259                                 // from current culture. We don't have such data yet
260                                 m_abbrevEraNames = INVARIANT_ABBREVIATED_ERA_NAMES;
261                                 return m_abbrevEraNames;
262                         }
263                 }
264
265                 public string GetMonthName(int month)
266                 {
267                         if (month < 1 || month > 13) throw new ArgumentOutOfRangeException();
268                         return monthNames[month-1];
269                 }
270                 
271                 internal string GetMonthGenitiveName (int month)
272                 {
273                         return genitiveMonthNames [month - 1];
274                 }
275
276                 public string[] AbbreviatedDayNames
277                 {
278                         get { return (string[]) RawAbbreviatedDayNames.Clone (); }
279                         set { RawAbbreviatedDayNames = value; }
280                 }
281
282                 internal string[] RawAbbreviatedDayNames
283                 {
284                         get
285                         {
286                                 return abbreviatedDayNames;
287                         }
288                         set {
289                                 CheckDaysValue (value);
290                                 abbreviatedDayNames = (string[]) value.Clone();
291                         }
292                 }
293
294                 public string[] AbbreviatedMonthNames
295                 {
296                         get { return (string[]) RawAbbreviatedMonthNames.Clone (); }
297                         set { RawAbbreviatedMonthNames = value; }
298                 }
299
300                 internal string[] RawAbbreviatedMonthNames
301                 {
302                         get
303                         {
304                                 return abbreviatedMonthNames;
305                         }
306                         set {
307                                 CheckMonthsValue (value);
308                                 abbreviatedMonthNames = (string[]) value.Clone();
309                         }
310                 }
311
312                 public string[] DayNames {
313                         get {
314                                 return (string[]) dayNames.Clone ();
315                         }
316                         set {
317                                 CheckDaysValue (value);
318                                 dayNames = (string[]) value.Clone();
319                         }
320                 }
321
322                 internal string[] RawDayNames  {
323                         get {
324                                 return dayNames;
325                         }
326                 }
327
328                 public string[] MonthNames {
329                         get {
330                                 return (string[]) monthNames.Clone ();
331                         }
332                         set {
333                                 CheckMonthsValue (value);
334                                 monthNames = (string[]) value.Clone();
335                         }
336                 }
337
338                 internal string[] RawMonthNames {
339                         get {
340                                 return monthNames;
341                         }
342                 }
343                 
344                 [ComVisible (false)]
345                 public string[] AbbreviatedMonthGenitiveNames {
346                         get {
347                                 return (string[]) m_genitiveAbbreviatedMonthNames.Clone ();
348                         }
349                         set {
350                                 CheckMonthsValue (value);
351                                 m_genitiveAbbreviatedMonthNames = value;
352                         }
353                 }
354
355                 [ComVisible (false)]
356                 public string[] MonthGenitiveNames {
357                         get {
358                                 return (string[]) genitiveMonthNames.Clone ();
359                         }
360                         set {
361                                 CheckMonthsValue (value);
362                                 genitiveMonthNames = value;
363                         }
364                 }
365                 
366                 [MonoLimitation ("Only default calendar is supported")]
367                 [ComVisible (false)]
368                 public string NativeCalendarName {
369                         get {
370                                 if (Calendar != culture.Calendar)
371                                         return "";
372
373                                 return culture.NativeCalendarName;
374                         }
375                 }
376
377                 [ComVisible (false)]
378                 public string[] ShortestDayNames {
379                         get {
380                                 return (string[]) shortestDayNames.Clone ();
381                         }
382
383                         set {
384                                 CheckDaysValue (value);
385                                 shortestDayNames = value;
386                         }
387                 }
388
389                 public string AMDesignator {
390                         get {
391                                 return amDesignator;
392                         }
393                         set {
394                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
395                                 if (value == null) throw new ArgumentNullException();
396                                 amDesignator = value;
397                         }
398                 }
399
400                 public string PMDesignator {
401                         get {
402                                 return pmDesignator;
403                         }
404                         set {
405                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
406                                 if (value == null) throw new ArgumentNullException();
407                                 pmDesignator = value;
408                         }
409                 }
410
411                 public string DateSeparator
412                 {
413                         get
414                         {
415                                 return dateSeparator;
416                         }
417                         set
418                         {
419                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
420                                 if (value == null) throw new ArgumentNullException();
421                                 dateSeparator = value;
422                         }
423                 }
424
425                 public string TimeSeparator
426                 {
427                         get
428                         {
429                                 return timeSeparator;
430                         }
431                         set
432                         {
433                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
434                                 if (value == null) throw new ArgumentNullException();
435                                 timeSeparator = value;
436                         }
437                 }
438
439                 public string LongDatePattern
440                 {
441                         get
442                         {
443                                 return longDatePattern;
444                         }
445                         set
446                         {
447                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
448                                 if (value == null) throw new ArgumentNullException();
449                                 longDatePattern = value;
450                         }
451                 }
452
453                 public string ShortDatePattern
454                 {
455                         get
456                         {
457                                 return shortDatePattern;
458                         }
459                         set
460                         {
461                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
462                                 if (value == null) throw new ArgumentNullException();
463                                 shortDatePattern = value;
464                         }
465                 }
466
467                 public string ShortTimePattern
468                 {
469                         get
470                         {
471                                 return shortTimePattern;
472                         }
473                         set
474                         {
475                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
476                                 if (value == null) throw new ArgumentNullException();
477                                 shortTimePattern = value;
478                         }
479                 }
480
481                 public string LongTimePattern
482                 {
483                         get
484                         {
485                                 return longTimePattern;
486                         }
487                         set
488                         {
489                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
490                                 if (value == null) throw new ArgumentNullException();
491                                 longTimePattern = value;
492                         }
493                 }
494
495                 public string MonthDayPattern
496                 {
497                         get
498                         {
499                                 return monthDayPattern;
500                         }
501                         set
502                         {
503                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
504                                 if (value == null) throw new ArgumentNullException();
505                                 monthDayPattern = value;
506                         }
507                 }
508
509                 public string YearMonthPattern
510                 {
511                         get
512                         {
513                                 return yearMonthPattern;
514                         }
515                         set
516                         {
517                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
518                                 if (value == null) throw new ArgumentNullException();
519                                 yearMonthPattern = value;
520                         }
521                 }
522
523                 public string FullDateTimePattern
524                 {
525                         get {
526                                 return fullDateTimePattern ?? (longDatePattern + " " + longTimePattern);
527                         }
528                         set
529                         {
530                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
531                                 if (value == null) throw new ArgumentNullException();
532                                 fullDateTimePattern = value;
533                         }
534                 }
535
536                 public static DateTimeFormatInfo CurrentInfo
537                 {
538                         get
539                         {
540                                 return Thread.CurrentThread.CurrentCulture.DateTimeFormat;
541                         }
542                 }
543
544                 public static DateTimeFormatInfo InvariantInfo
545                 {
546                         get {
547                                 if (theInvariantDateTimeFormatInfo == null) {
548                                         var tmp = new DateTimeFormatInfo (CultureInfo.InvariantCulture, true);
549                                         tmp.FillInvariantPatterns ();
550                                         theInvariantDateTimeFormatInfo = tmp;
551                                 }
552
553                                 return theInvariantDateTimeFormatInfo;
554                         }
555                 }
556
557                 public DayOfWeek FirstDayOfWeek
558                 {
559                         get
560                         {
561                                 return (DayOfWeek)firstDayOfWeek;
562                         }
563                         set
564                         {
565                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
566                                 if ((int) value < 0 || (int) value > 6) throw new ArgumentOutOfRangeException();
567                                 firstDayOfWeek = (int)value;
568                         }
569                 }
570
571                 public Calendar Calendar {
572                         get {
573                                 return calendar ?? culture.Calendar;
574                         }
575
576                         [MonoLimitation ("Only default calendar specific data are available")]
577                         set {
578                                 if (IsReadOnly)
579                                         throw new InvalidOperationException(MSG_READONLY);
580                                 if (value == null)
581                                         throw new ArgumentNullException();
582
583                                 if (calendar == value)
584                                         return;
585
586                                 // TODO: Should use only ids
587                                 var calendars = culture.OptionalCalendars;
588                                 for (int i = 0; i < calendars.Length; ++i) {
589                                         if (calendars [i].ID != value.ID)
590                                                 continue;
591
592                                         calendar = value;
593                                         m_eraNames = null;
594                                         m_abbrevEraNames = null;
595                                 }
596
597                                 throw new ArgumentOutOfRangeException ("Invalid calendar");
598                         }
599                 }
600
601                 public CalendarWeekRule CalendarWeekRule
602                 {
603                         get
604                         {
605                                 return (CalendarWeekRule)calendarWeekRule;
606                         }
607                         set
608                         {
609                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
610                                 calendarWeekRule = (int)value;
611                         }
612                 }
613
614                 public string RFC1123Pattern {
615                         get {
616                                 return "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'";
617                         }
618                 }
619
620                 internal string RoundtripPattern {
621                         get {
622                                 return "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK";
623                         }
624                 }
625
626                 public string SortableDateTimePattern {
627                         get {
628                                 return "yyyy'-'MM'-'dd'T'HH':'mm':'ss";
629                         }
630                 }
631
632                 public string UniversalSortableDateTimePattern {
633                         get {
634                                 return "yyyy'-'MM'-'dd HH':'mm':'ss'Z'";
635                         }
636                 }
637                 
638                 // FIXME: Not complete depending on GetAllDateTimePatterns(char)")]
639                 public string[] GetAllDateTimePatterns () 
640                 {
641                         return (string[]) GetAllDateTimePatternsInternal ().Clone ();
642                 }
643
644                 // Same as above, but with no cloning, because we know that
645                 // clients are friendly
646                 internal string [] GetAllDateTimePatternsInternal ()
647                 {
648                         FillAllDateTimePatterns ();
649                         return all_date_time_patterns;
650                 }
651                 
652                 // Prevent write reordering
653                 volatile string [] all_date_time_patterns;
654                 
655                 void FillAllDateTimePatterns (){
656
657                         if (all_date_time_patterns != null)
658                                 return;
659                         
660                         var al = new List<string> (16);
661                         al.AddRange (GetAllRawDateTimePatterns ('d'));
662                         al.AddRange (GetAllRawDateTimePatterns ('D'));
663                         al.AddRange (GetAllRawDateTimePatterns ('f'));
664                         al.AddRange (GetAllRawDateTimePatterns ('F'));
665                         al.AddRange (GetAllRawDateTimePatterns ('g'));
666                         al.AddRange (GetAllRawDateTimePatterns ('G'));
667                         al.AddRange (GetAllRawDateTimePatterns ('m'));
668                         al.AddRange (GetAllRawDateTimePatterns ('M'));
669                         al.AddRange (GetAllRawDateTimePatterns ('o'));
670                         al.AddRange (GetAllRawDateTimePatterns ('O'));
671                         al.AddRange (GetAllRawDateTimePatterns ('r'));
672                         al.AddRange (GetAllRawDateTimePatterns ('R'));
673                         al.AddRange (GetAllRawDateTimePatterns ('s'));
674                         al.AddRange (GetAllRawDateTimePatterns ('t'));
675                         al.AddRange (GetAllRawDateTimePatterns ('T'));
676                         al.AddRange (GetAllRawDateTimePatterns ('u'));
677                         al.AddRange (GetAllRawDateTimePatterns ('U'));
678                         al.AddRange (GetAllRawDateTimePatterns ('y'));
679                         al.AddRange (GetAllRawDateTimePatterns ('Y'));
680
681                         // all_date_time_patterns needs to be volatile to prevent
682                         // reordering of writes here and still avoid any locking.
683                         all_date_time_patterns = al.ToArray ();
684                 }
685
686                 //
687                 // FIXME: We need more culture data in locale-builder
688                 //   Whoever put that comment, please expand.
689                 //
690                 public string[] GetAllDateTimePatterns (char format)
691                 {
692                         return (string[]) GetAllRawDateTimePatterns (format).Clone ();
693                 }
694
695                 internal string[] GetAllRawDateTimePatterns (char format)
696                 {
697                         switch (format) {
698                         // Date
699                         case 'D':
700                                 if (allLongDatePatterns != null && allLongDatePatterns.Length > 0)
701                                         return allLongDatePatterns;
702                                 return new string [] {LongDatePattern};
703                         case 'd':
704                                 if (allShortDatePatterns != null && allShortDatePatterns.Length > 0)
705                                         return allShortDatePatterns;
706                                 return new string [] {ShortDatePattern};
707                         // Time
708                         case 'T':
709                                 if (allLongTimePatterns != null && allLongTimePatterns.Length > 0)
710                                         return allLongTimePatterns;
711                                 return new string [] {LongTimePattern};
712                         case 't':
713                                 if (allShortTimePatterns != null && allShortTimePatterns.Length > 0)
714                                         return allShortTimePatterns;
715                                 return new string [] {ShortTimePattern};
716                         // MonthDay
717                         case 'm':
718                         case 'M':
719                                 if (monthDayPatterns != null && monthDayPatterns.Length > 0)
720                                         return monthDayPatterns;
721                                 return new string[] { MonthDayPattern };
722                         // YearMonth
723                         case 'Y':
724                         case 'y':
725                                 if (yearMonthPatterns != null && yearMonthPatterns.Length > 0)
726                                         return yearMonthPatterns;
727                                 return new string[] { YearMonthPattern };
728                         case 'r':
729                         case 'R':
730                                 return new string[] { RFC1123Pattern };
731                         case 'O':
732                         case 'o':
733                                 return new string[] { RoundtripPattern };
734                         case 's':
735                                 return new string[] { SortableDateTimePattern };
736                         case 'u':
737                                 return new string[] { UniversalSortableDateTimePattern };
738
739                         //
740                         // Following patterns are combinations of {Short|Long}Date + {Short|Long}Time. Patters can
741                         // be null for non-readonly invariant culture
742                         //
743                         case 'G':
744                                 return allShortDatePatterns == null ?
745                                         new string [] { ShortDatePattern + " " + LongTimePattern } :
746                                         PopulateCombinedList (allShortDatePatterns, allLongTimePatterns);
747                         case 'g':
748                                 return allShortDatePatterns == null ?
749                                         new string [] { ShortDatePattern + " " + ShortTimePattern } :
750                                         PopulateCombinedList (allShortDatePatterns, allShortTimePatterns);
751                         case 'U': // The 'U' pattern strings are always the same as 'F' (only differs in assuming UTC or not.)
752                         case 'F':
753                                 return allLongDatePatterns == null ?
754                                         new string [] { LongDatePattern + " " + ShortTimePattern } :
755                                         PopulateCombinedList (allLongDatePatterns, allLongTimePatterns);
756                         case 'f':
757                                 return allLongDatePatterns == null ?
758                                         new string [] { LongDatePattern + " " + ShortTimePattern } :
759                                         PopulateCombinedList (allLongDatePatterns, allShortTimePatterns);
760                         }
761                         throw new ArgumentException ("Format specifier was invalid.");
762                 }
763
764                 public string GetDayName(DayOfWeek dayofweek)
765                 {
766                         int index = (int) dayofweek;
767                         if (index < 0 || index > 6) throw new ArgumentOutOfRangeException();
768                         return dayNames[index];
769                 }
770
771                 public string GetAbbreviatedDayName(DayOfWeek dayofweek)
772                 {
773                         int index = (int) dayofweek;
774                         if (index < 0 || index > 6) throw new ArgumentOutOfRangeException();
775                         return abbreviatedDayNames[index];
776                 }
777
778                 void FillInvariantPatterns ()
779                 {
780                         allShortDatePatterns = new string [] {"MM/dd/yyyy"};
781                         allLongDatePatterns = new string [] {"dddd, dd MMMM yyyy"};
782                         allLongTimePatterns = new string [] {"HH:mm:ss"};
783                         allShortTimePatterns = new string [] {
784                                 "HH:mm",
785                                 "hh:mm tt",
786                                 "H:mm",
787                                 "h:mm tt"
788                         };
789                         monthDayPatterns = new string [] {"MMMM dd"};
790                         yearMonthPatterns = new string [] {"yyyy MMMM"};
791
792                         fullDateTimePattern = "dddd, dd MMMM yyyy HH:mm:ss";
793                 }
794
795                 static string [] PopulateCombinedList (string [] dates, string [] times)
796                 {
797                         string[] list = new string[dates.Length * times.Length];
798                         int i = 0;
799                         foreach (string d in dates)
800                                 foreach (string t in times)
801                                         list[i++] = d + " " + t;
802                         return list;
803                 }
804
805                 [ComVisible (false)]
806                 public string GetShortestDayName (DayOfWeek dayOfWeek)
807                 {
808                         int index = (int) dayOfWeek;
809                         if (index < 0 || index > 6)
810                                 throw new ArgumentOutOfRangeException ("dayOfWeek");
811
812                         return shortestDayNames [index];
813                 }
814
815                 [ComVisible (false)]
816                 public void SetAllDateTimePatterns (string [] patterns, char format)
817                 {
818                         if (patterns == null)
819                                 throw new ArgumentNullException ("patterns");
820                         if (patterns.Length == 0)
821                                 throw new ArgumentException ("patterns", "The argument patterns must not be of zero-length");
822
823                         switch (format) {
824                         // YearMonth
825                         case 'Y':
826                         case 'y':
827                                 yearMonthPatterns = patterns;
828                                 break;
829                         // MonthDay
830                         case 'm':
831                         case 'M':
832                                 monthDayPatterns = patterns;
833                                 break;
834                         // Date
835                         case 'D':
836                                 allLongDatePatterns = patterns;
837                                 break;
838                         case 'd':
839                                 allShortDatePatterns = patterns;
840                                 break;
841                         // Time
842                         case 'T':
843                                 allLongTimePatterns = patterns;
844                                 break;
845                         case 't':
846                                 allShortTimePatterns = patterns;
847                                 break;
848                         default:
849                                 // note that any other formats are invalid (such as 'r', 'g', 'U')
850                                 throw new ArgumentException ("format", "Format specifier is invalid");
851                         }
852                 }
853                 
854                 void CheckDaysValue (string[] value)
855                 {
856                         if (IsReadOnly)
857                                 throw new InvalidOperationException (MSG_READONLY);
858                                 
859                         if (value == null)
860                                 throw new ArgumentNullException ();
861
862                         if (value.Length != 7)
863                                 throw new ArgumentException ("An array with exactly 7 elements is required");
864
865                         int ni = Array.IndexOf (value, null);
866                         if (ni >= 0)
867                                 throw new ArgumentNullException (string.Format ("Element at index {0} is null", ni));
868                 }
869                 
870                 void CheckMonthsValue (string[] value)
871                 {
872                         if (IsReadOnly)
873                                 throw new InvalidOperationException (MSG_READONLY);
874                                 
875                         if (value == null)
876                                 throw new ArgumentNullException ();
877
878                         if (value.Length != 13)
879                                 throw new ArgumentException ("An array with exactly 13 elements is required");
880
881                         int ni = Array.IndexOf (value, null);
882                         if (ni >= 0)
883                                 throw new ArgumentNullException (string.Format ("Element at index {0} is null", ni));
884                 }               
885         }
886 }