Merge pull request #726 from pruiz/xamarin-bug-13708
[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 //              private static readonly string[] INVARIANT_ERA_NAMES = {"A.D."};
63                 static readonly string[] INVARIANT_SHORT_DAY_NAMES =
64                         new string[7] { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" };
65                 private static DateTimeFormatInfo theInvariantDateTimeFormatInfo;
66
67 #pragma warning disable 169
68                 #region Sync with object-internals.h
69                 private bool m_isReadOnly;
70                 private string amDesignator;
71                 private string pmDesignator;
72                 private string dateSeparator;
73                 private string timeSeparator;
74                 private string shortDatePattern;
75                 private string longDatePattern;
76                 private string shortTimePattern;
77                 private string longTimePattern;
78                 private string monthDayPattern;
79                 private string yearMonthPattern;
80                 private int firstDayOfWeek;
81                 private int calendarWeekRule;
82                 private string[] abbreviatedDayNames;
83                 private string[] dayNames;
84                 private string[] monthNames;
85                 private string[] genitiveMonthNames;
86                 private string[] abbreviatedMonthNames;
87                 private string[] m_genitiveAbbreviatedMonthNames;
88
89                 private string[] allShortDatePatterns;
90                 private string[] allLongDatePatterns;
91                 private string[] allShortTimePatterns;
92                 private string[] allLongTimePatterns;
93                 private string[] monthDayPatterns;
94                 private string[] yearMonthPatterns;
95                 private string[] shortestDayNames;
96                 #endregion
97
98                 internal readonly CultureInfo culture;
99                 Calendar calendar;
100
101                 // MS Serialization needs this
102                 private string fullDateTimePattern;
103                 private int nDataItem;
104                 private bool m_useUserOverride;
105                 private bool m_isDefaultCalendar;
106                 private int CultureID;
107                 private bool bUseCalendarInfo;
108                 private string generalShortTimePattern;
109                 private string generalLongTimePattern;
110                 private string[] m_eraNames;
111                 private string[] m_abbrevEraNames;
112                 private string[] m_abbrevEnglishEraNames;
113                 private string[] m_dateWords;
114                 private int[] optionalCalendars;
115                 private string[] leapYearMonthNames;
116                 private DateTimeFormatFlags formatFlags;
117                 private string m_name; // Unused, but MS.NET serializes this
118 #pragma warning restore 169
119
120                 internal DateTimeFormatInfo (CultureInfo culture, bool read_only)
121                 {
122                         if (culture == null)
123                                 throw new ArgumentNullException ("culture");
124
125                         this.culture = culture;
126                         m_isReadOnly = read_only;
127
128                         amDesignator = "AM";
129                         pmDesignator = "PM";
130                         dateSeparator = "/";
131                         timeSeparator = ":";
132                         shortDatePattern = "MM/dd/yyyy";
133                         longDatePattern = "dddd, dd MMMM yyyy";
134                         shortTimePattern = "HH:mm";
135                         longTimePattern = "HH:mm:ss";
136                         monthDayPattern = "MMMM dd";
137                         yearMonthPattern = "yyyy MMMM";
138
139                         firstDayOfWeek = (int) DayOfWeek.Sunday;
140                         calendarWeekRule = (int) CalendarWeekRule.FirstDay;
141
142                         abbreviatedDayNames = INVARIANT_ABBREVIATED_DAY_NAMES;
143                         dayNames = INVARIANT_DAY_NAMES;
144                         abbreviatedMonthNames = INVARIANT_ABBREVIATED_MONTH_NAMES;
145                         monthNames = INVARIANT_MONTH_NAMES;
146                         m_genitiveAbbreviatedMonthNames = INVARIANT_ABBREVIATED_MONTH_NAMES;
147                         genitiveMonthNames = INVARIANT_MONTH_NAMES;
148                         shortestDayNames = INVARIANT_SHORT_DAY_NAMES;
149                 }
150
151                 public DateTimeFormatInfo ()
152                         : this (CultureInfo.InvariantCulture, false)
153                 {
154                 }
155                                 
156                 public static DateTimeFormatInfo GetInstance(IFormatProvider provider)
157                 {
158                         if (provider != null) {
159                                 DateTimeFormatInfo dtfi;
160                                 dtfi = (DateTimeFormatInfo)provider.GetFormat(typeof(DateTimeFormatInfo));
161                                 if (dtfi != null)
162                                         return dtfi;
163                         }
164                         
165                         return CurrentInfo;
166                 }
167
168                 public bool IsReadOnly {
169                         get {
170                                 return m_isReadOnly;
171                         }
172                 }
173
174                 public static DateTimeFormatInfo ReadOnly(DateTimeFormatInfo dtfi)
175                 {
176                         DateTimeFormatInfo copy = (DateTimeFormatInfo)dtfi.Clone();
177                         copy.m_isReadOnly = true;
178                         return copy;
179                 }                       
180
181                 public object Clone () 
182                 {
183                         DateTimeFormatInfo clone = (DateTimeFormatInfo) MemberwiseClone();
184                         // clone is not read only
185                         clone.m_isReadOnly = false;
186                         return clone;
187                 }
188
189                 public object GetFormat(Type formatType)
190                 {
191                         return (formatType == GetType()) ? this : null;
192                 }
193
194                 public string GetAbbreviatedEraName (int era)
195                 {
196                         if (era < 0 || era >= Calendar.AbbreviatedEraNames.Length)
197                                 throw new ArgumentOutOfRangeException ("era", era.ToString ());
198                         return Calendar.AbbreviatedEraNames [era];
199                 }
200
201                 public string GetAbbreviatedMonthName(int month)
202                 {
203                         if (month < 1 || month > 13) throw new ArgumentOutOfRangeException();
204                         return abbreviatedMonthNames[month-1];
205                 }
206
207                 public int GetEra (string eraName)
208                 {
209                         if (eraName == null)
210                                 throw new ArgumentNullException ();
211                         string [] eras = Calendar.EraNames;
212                         for (int i = 0; i < eras.Length; i++)
213                                 if (CultureInfo.InvariantCulture.CompareInfo
214                                         .Compare (eraName, eras [i],
215                                         CompareOptions.IgnoreCase) == 0)
216                                         return Calendar.Eras [i];
217                         
218                         eras = Calendar.AbbreviatedEraNames;
219                         for (int i = 0; i < eras.Length; i++)
220                                 if (CultureInfo.InvariantCulture.CompareInfo
221                                         .Compare (eraName, eras [i],
222                                         CompareOptions.IgnoreCase) == 0)
223                                         return Calendar.Eras [i];
224                         
225                         return -1;
226                 }
227
228                 public string GetEraName (int era)
229                 {
230                         if (era < 0 || era > Calendar.EraNames.Length)
231                                 throw new ArgumentOutOfRangeException ("era", era.ToString ());
232                         return Calendar.EraNames [era - 1];
233                 }
234
235                 public string GetMonthName(int month)
236                 {
237                         if (month < 1 || month > 13) throw new ArgumentOutOfRangeException();
238                         return monthNames[month-1];
239                 }
240                 
241                 internal string GetMonthGenitiveName (int month)
242                 {
243                         return genitiveMonthNames [month - 1];
244                 }
245
246                 public string[] AbbreviatedDayNames
247                 {
248                         get { return (string[]) RawAbbreviatedDayNames.Clone (); }
249                         set { RawAbbreviatedDayNames = value; }
250                 }
251
252                 internal string[] RawAbbreviatedDayNames
253                 {
254                         get
255                         {
256                                 return abbreviatedDayNames;
257                         }
258                         set {
259                                 CheckDaysValue (value);
260                                 abbreviatedDayNames = (string[]) value.Clone();
261                         }
262                 }
263
264                 public string[] AbbreviatedMonthNames
265                 {
266                         get { return (string[]) RawAbbreviatedMonthNames.Clone (); }
267                         set { RawAbbreviatedMonthNames = value; }
268                 }
269
270                 internal string[] RawAbbreviatedMonthNames
271                 {
272                         get
273                         {
274                                 return abbreviatedMonthNames;
275                         }
276                         set {
277                                 CheckMonthsValue (value);
278                                 abbreviatedMonthNames = (string[]) value.Clone();
279                         }
280                 }
281
282                 public string[] DayNames {
283                         get {
284                                 return (string[]) dayNames.Clone ();
285                         }
286                         set {
287                                 CheckDaysValue (value);
288                                 dayNames = (string[]) value.Clone();
289                         }
290                 }
291
292                 internal string[] RawDayNames  {
293                         get {
294                                 return dayNames;
295                         }
296                 }
297
298                 public string[] MonthNames {
299                         get {
300                                 return (string[]) monthNames.Clone ();
301                         }
302                         set {
303                                 CheckMonthsValue (value);
304                                 monthNames = (string[]) value.Clone();
305                         }
306                 }
307
308                 internal string[] RawMonthNames {
309                         get {
310                                 return monthNames;
311                         }
312                 }
313                 
314                 [ComVisible (false)]
315                 public string[] AbbreviatedMonthGenitiveNames {
316                         get {
317                                 return (string[]) m_genitiveAbbreviatedMonthNames.Clone ();
318                         }
319                         set {
320                                 CheckMonthsValue (value);
321                                 m_genitiveAbbreviatedMonthNames = value;
322                         }
323                 }
324
325                 [ComVisible (false)]
326                 public string[] MonthGenitiveNames {
327                         get {
328                                 return (string[]) genitiveMonthNames.Clone ();
329                         }
330                         set {
331                                 CheckMonthsValue (value);
332                                 genitiveMonthNames = value;
333                         }
334                 }
335                 
336                 [MonoLimitation ("Only default calendar is supported")]
337                 [ComVisible (false)]
338                 public string NativeCalendarName {
339                         get {
340                                 if (Calendar != culture.Calendar)
341                                         return "";
342
343                                 return culture.NativeCalendarName;
344                         }
345                 }
346
347                 [ComVisible (false)]
348                 public string[] ShortestDayNames {
349                         get {
350                                 return (string[]) shortestDayNames.Clone ();
351                         }
352
353                         set {
354                                 CheckDaysValue (value);
355                                 shortestDayNames = value;
356                         }
357                 }
358
359                 public string AMDesignator {
360                         get {
361                                 return amDesignator;
362                         }
363                         set {
364                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
365                                 if (value == null) throw new ArgumentNullException();
366                                 amDesignator = value;
367                         }
368                 }
369
370                 public string PMDesignator {
371                         get {
372                                 return pmDesignator;
373                         }
374                         set {
375                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
376                                 if (value == null) throw new ArgumentNullException();
377                                 pmDesignator = value;
378                         }
379                 }
380
381                 public string DateSeparator
382                 {
383                         get
384                         {
385                                 return dateSeparator;
386                         }
387                         set
388                         {
389                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
390                                 if (value == null) throw new ArgumentNullException();
391                                 dateSeparator = value;
392                         }
393                 }
394
395                 public string TimeSeparator
396                 {
397                         get
398                         {
399                                 return timeSeparator;
400                         }
401                         set
402                         {
403                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
404                                 if (value == null) throw new ArgumentNullException();
405                                 timeSeparator = value;
406                         }
407                 }
408
409                 public string LongDatePattern
410                 {
411                         get
412                         {
413                                 return longDatePattern;
414                         }
415                         set
416                         {
417                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
418                                 if (value == null) throw new ArgumentNullException();
419                                 longDatePattern = value;
420                         }
421                 }
422
423                 public string ShortDatePattern
424                 {
425                         get
426                         {
427                                 return shortDatePattern;
428                         }
429                         set
430                         {
431                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
432                                 if (value == null) throw new ArgumentNullException();
433                                 shortDatePattern = value;
434                         }
435                 }
436
437                 public string ShortTimePattern
438                 {
439                         get
440                         {
441                                 return shortTimePattern;
442                         }
443                         set
444                         {
445                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
446                                 if (value == null) throw new ArgumentNullException();
447                                 shortTimePattern = value;
448                         }
449                 }
450
451                 public string LongTimePattern
452                 {
453                         get
454                         {
455                                 return longTimePattern;
456                         }
457                         set
458                         {
459                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
460                                 if (value == null) throw new ArgumentNullException();
461                                 longTimePattern = value;
462                         }
463                 }
464
465                 public string MonthDayPattern
466                 {
467                         get
468                         {
469                                 return monthDayPattern;
470                         }
471                         set
472                         {
473                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
474                                 if (value == null) throw new ArgumentNullException();
475                                 monthDayPattern = value;
476                         }
477                 }
478
479                 public string YearMonthPattern
480                 {
481                         get
482                         {
483                                 return yearMonthPattern;
484                         }
485                         set
486                         {
487                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
488                                 if (value == null) throw new ArgumentNullException();
489                                 yearMonthPattern = value;
490                         }
491                 }
492
493                 public string FullDateTimePattern
494                 {
495                         get {
496                                 return fullDateTimePattern ?? (longDatePattern + " " + longTimePattern);
497                         }
498                         set
499                         {
500                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
501                                 if (value == null) throw new ArgumentNullException();
502                                 fullDateTimePattern = value;
503                         }
504                 }
505
506                 public static DateTimeFormatInfo CurrentInfo
507                 {
508                         get
509                         {
510                                 return Thread.CurrentThread.CurrentCulture.DateTimeFormat;
511                         }
512                 }
513
514                 public static DateTimeFormatInfo InvariantInfo
515                 {
516                         get {
517                                 if (theInvariantDateTimeFormatInfo == null) {
518                                         var tmp = new DateTimeFormatInfo (CultureInfo.InvariantCulture, true);
519                                         tmp.FillInvariantPatterns ();
520                                         theInvariantDateTimeFormatInfo = tmp;
521                                 }
522
523                                 return theInvariantDateTimeFormatInfo;
524                         }
525                 }
526
527                 public DayOfWeek FirstDayOfWeek
528                 {
529                         get
530                         {
531                                 return (DayOfWeek)firstDayOfWeek;
532                         }
533                         set
534                         {
535                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
536                                 if ((int) value < 0 || (int) value > 6) throw new ArgumentOutOfRangeException();
537                                 firstDayOfWeek = (int)value;
538                         }
539                 }
540
541                 public Calendar Calendar {
542                         get {
543                                 return calendar ?? culture.Calendar;
544                         }
545
546                         [MonoLimitation ("Only default calendar specific data are available")]
547                         set {
548                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
549                                 if (value == null) throw new ArgumentNullException();
550                                 calendar = value;
551                         }
552                 }
553
554                 public CalendarWeekRule CalendarWeekRule
555                 {
556                         get
557                         {
558                                 return (CalendarWeekRule)calendarWeekRule;
559                         }
560                         set
561                         {
562                                 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
563                                 calendarWeekRule = (int)value;
564                         }
565                 }
566
567                 public string RFC1123Pattern {
568                         get {
569                                 return "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'";
570                         }
571                 }
572
573                 internal string RoundtripPattern {
574                         get {
575                                 return "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK";
576                         }
577                 }
578
579                 public string SortableDateTimePattern {
580                         get {
581                                 return "yyyy'-'MM'-'dd'T'HH':'mm':'ss";
582                         }
583                 }
584
585                 public string UniversalSortableDateTimePattern {
586                         get {
587                                 return "yyyy'-'MM'-'dd HH':'mm':'ss'Z'";
588                         }
589                 }
590                 
591                 // FIXME: Not complete depending on GetAllDateTimePatterns(char)")]
592                 public string[] GetAllDateTimePatterns () 
593                 {
594                         return (string[]) GetAllDateTimePatternsInternal ().Clone ();
595                 }
596
597                 // Same as above, but with no cloning, because we know that
598                 // clients are friendly
599                 internal string [] GetAllDateTimePatternsInternal ()
600                 {
601                         FillAllDateTimePatterns ();
602                         return all_date_time_patterns;
603                 }
604                 
605                 // Prevent write reordering
606                 volatile string [] all_date_time_patterns;
607                 
608                 void FillAllDateTimePatterns (){
609
610                         if (all_date_time_patterns != null)
611                                 return;
612                         
613                         var al = new List<string> (16);
614                         al.AddRange (GetAllRawDateTimePatterns ('d'));
615                         al.AddRange (GetAllRawDateTimePatterns ('D'));
616                         al.AddRange (GetAllRawDateTimePatterns ('f'));
617                         al.AddRange (GetAllRawDateTimePatterns ('F'));
618                         al.AddRange (GetAllRawDateTimePatterns ('g'));
619                         al.AddRange (GetAllRawDateTimePatterns ('G'));
620                         al.AddRange (GetAllRawDateTimePatterns ('m'));
621                         al.AddRange (GetAllRawDateTimePatterns ('M'));
622                         al.AddRange (GetAllRawDateTimePatterns ('o'));
623                         al.AddRange (GetAllRawDateTimePatterns ('O'));
624                         al.AddRange (GetAllRawDateTimePatterns ('r'));
625                         al.AddRange (GetAllRawDateTimePatterns ('R'));
626                         al.AddRange (GetAllRawDateTimePatterns ('s'));
627                         al.AddRange (GetAllRawDateTimePatterns ('t'));
628                         al.AddRange (GetAllRawDateTimePatterns ('T'));
629                         al.AddRange (GetAllRawDateTimePatterns ('u'));
630                         al.AddRange (GetAllRawDateTimePatterns ('U'));
631                         al.AddRange (GetAllRawDateTimePatterns ('y'));
632                         al.AddRange (GetAllRawDateTimePatterns ('Y'));
633
634                         // all_date_time_patterns needs to be volatile to prevent
635                         // reordering of writes here and still avoid any locking.
636                         all_date_time_patterns = al.ToArray ();
637                 }
638
639                 //
640                 // FIXME: We need more culture data in locale-builder
641                 //   Whoever put that comment, please expand.
642                 //
643                 public string[] GetAllDateTimePatterns (char format)
644                 {
645                         return (string[]) GetAllRawDateTimePatterns (format).Clone ();
646                 }
647
648                 internal string[] GetAllRawDateTimePatterns (char format)
649                 {
650                         switch (format) {
651                         // Date
652                         case 'D':
653                                 if (allLongDatePatterns != null && allLongDatePatterns.Length > 0)
654                                         return allLongDatePatterns;
655                                 return new string [] {LongDatePattern};
656                         case 'd':
657                                 if (allShortDatePatterns != null && allShortDatePatterns.Length > 0)
658                                         return allShortDatePatterns;
659                                 return new string [] {ShortDatePattern};
660                         // Time
661                         case 'T':
662                                 if (allLongTimePatterns != null && allLongTimePatterns.Length > 0)
663                                         return allLongTimePatterns;
664                                 return new string [] {LongTimePattern};
665                         case 't':
666                                 if (allShortTimePatterns != null && allShortTimePatterns.Length > 0)
667                                         return allShortTimePatterns;
668                                 return new string [] {ShortTimePattern};
669                         // MonthDay
670                         case 'm':
671                         case 'M':
672                                 if (monthDayPatterns != null && monthDayPatterns.Length > 0)
673                                         return monthDayPatterns;
674                                 return new string[] { MonthDayPattern };
675                         // YearMonth
676                         case 'Y':
677                         case 'y':
678                                 if (yearMonthPatterns != null && yearMonthPatterns.Length > 0)
679                                         return yearMonthPatterns;
680                                 return new string[] { YearMonthPattern };
681                         case 'r':
682                         case 'R':
683                                 return new string[] { RFC1123Pattern };
684                         case 'O':
685                         case 'o':
686                                 return new string[] { RoundtripPattern };
687                         case 's':
688                                 return new string[] { SortableDateTimePattern };
689                         case 'u':
690                                 return new string[] { UniversalSortableDateTimePattern };
691
692                         //
693                         // Following patterns are combinations of {Short|Long}Date + {Short|Long}Time. Patters can
694                         // be null for non-readonly invariant culture
695                         //
696                         case 'G':
697                                 return allShortDatePatterns == null ?
698                                         new string [] { ShortDatePattern + " " + LongTimePattern } :
699                                         PopulateCombinedList (allShortDatePatterns, allLongTimePatterns);
700                         case 'g':
701                                 return allShortDatePatterns == null ?
702                                         new string [] { ShortDatePattern + " " + ShortTimePattern } :
703                                         PopulateCombinedList (allShortDatePatterns, allShortTimePatterns);
704                         case 'U': // The 'U' pattern strings are always the same as 'F' (only differs in assuming UTC or not.)
705                         case 'F':
706                                 return allLongDatePatterns == null ?
707                                         new string [] { LongDatePattern + " " + ShortTimePattern } :
708                                         PopulateCombinedList (allLongDatePatterns, allLongTimePatterns);
709                         case 'f':
710                                 return allLongDatePatterns == null ?
711                                         new string [] { LongDatePattern + " " + ShortTimePattern } :
712                                         PopulateCombinedList (allLongDatePatterns, allShortTimePatterns);
713                         }
714                         throw new ArgumentException ("Format specifier was invalid.");
715                 }
716
717                 public string GetDayName(DayOfWeek dayofweek)
718                 {
719                         int index = (int) dayofweek;
720                         if (index < 0 || index > 6) throw new ArgumentOutOfRangeException();
721                         return dayNames[index];
722                 }
723
724                 public string GetAbbreviatedDayName(DayOfWeek dayofweek)
725                 {
726                         int index = (int) dayofweek;
727                         if (index < 0 || index > 6) throw new ArgumentOutOfRangeException();
728                         return abbreviatedDayNames[index];
729                 }
730
731                 void FillInvariantPatterns ()
732                 {
733                         allShortDatePatterns = new string [] {"MM/dd/yyyy"};
734                         allLongDatePatterns = new string [] {"dddd, dd MMMM yyyy"};
735                         allLongTimePatterns = new string [] {"HH:mm:ss"};
736                         allShortTimePatterns = new string [] {
737                                 "HH:mm",
738                                 "hh:mm tt",
739                                 "H:mm",
740                                 "h:mm tt"
741                         };
742                         monthDayPatterns = new string [] {"MMMM dd"};
743                         yearMonthPatterns = new string [] {"yyyy MMMM"};
744
745                         fullDateTimePattern = "dddd, dd MMMM yyyy HH:mm:ss";
746                 }
747
748                 static string [] PopulateCombinedList (string [] dates, string [] times)
749                 {
750                         string[] list = new string[dates.Length * times.Length];
751                         int i = 0;
752                         foreach (string d in dates)
753                                 foreach (string t in times)
754                                         list[i++] = d + " " + t;
755                         return list;
756                 }
757
758                 [ComVisible (false)]
759                 public string GetShortestDayName (DayOfWeek dayOfWeek)
760                 {
761                         int index = (int) dayOfWeek;
762                         if (index < 0 || index > 6)
763                                 throw new ArgumentOutOfRangeException ("dayOfWeek");
764
765                         return shortestDayNames [index];
766                 }
767
768                 [ComVisible (false)]
769                 public void SetAllDateTimePatterns (string [] patterns, char format)
770                 {
771                         if (patterns == null)
772                                 throw new ArgumentNullException ("patterns");
773                         if (patterns.Length == 0)
774                                 throw new ArgumentException ("patterns", "The argument patterns must not be of zero-length");
775
776                         switch (format) {
777                         // YearMonth
778                         case 'Y':
779                         case 'y':
780                                 yearMonthPatterns = patterns;
781                                 break;
782                         // MonthDay
783                         case 'm':
784                         case 'M':
785                                 monthDayPatterns = patterns;
786                                 break;
787                         // Date
788                         case 'D':
789                                 allLongDatePatterns = patterns;
790                                 break;
791                         case 'd':
792                                 allShortDatePatterns = patterns;
793                                 break;
794                         // Time
795                         case 'T':
796                                 allLongTimePatterns = patterns;
797                                 break;
798                         case 't':
799                                 allShortTimePatterns = patterns;
800                                 break;
801                         default:
802                                 // note that any other formats are invalid (such as 'r', 'g', 'U')
803                                 throw new ArgumentException ("format", "Format specifier is invalid");
804                         }
805                 }
806                 
807                 void CheckDaysValue (string[] value)
808                 {
809                         if (IsReadOnly)
810                                 throw new InvalidOperationException (MSG_READONLY);
811                                 
812                         if (value == null)
813                                 throw new ArgumentNullException ();
814
815                         if (value.Length != 7)
816                                 throw new ArgumentException ("An array with exactly 7 elements is required");
817
818                         int ni = Array.IndexOf (value, null);
819                         if (ni >= 0)
820                                 throw new ArgumentNullException (string.Format ("Element at index {0} is null", ni));
821                 }
822                 
823                 void CheckMonthsValue (string[] value)
824                 {
825                         if (IsReadOnly)
826                                 throw new InvalidOperationException (MSG_READONLY);
827                                 
828                         if (value == null)
829                                 throw new ArgumentNullException ();
830
831                         if (value.Length != 13)
832                                 throw new ArgumentException ("An array with exactly 13 elements is required");
833
834                         int ni = Array.IndexOf (value, null);
835                         if (ni >= 0)
836                                 throw new ArgumentNullException (string.Format ("Element at index {0} is null", ni));
837                 }               
838         }
839 }