1 // System.Globalization.DateTimeFormatInfo
3 // Some useful functions are missing in the ECMA specs.
4 // They have been added following MS SDK Beta2
6 // Martin Weindel (martin.weindel@t-online.de)
8 // (C) Martin Weindel (martin.weindel@t-online.de)
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
35 using System.Threading;
37 namespace System.Globalization
40 public sealed class DateTimeFormatInfo : ICloneable, IFormatProvider {
41 private static readonly string MSG_READONLY = "This instance is read only";
42 private static readonly string MSG_ARRAYSIZE_MONTH = "An array with exactly 13 elements is needed";
43 private static readonly string MSG_ARRAYSIZE_DAY = "An array with exactly 7 elements is needed";
44 private static readonly string[] INVARIANT_ABBREVIATED_DAY_NAMES
45 = new string[7] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
46 private static readonly string[] INVARIANT_DAY_NAMES
47 = new string[7] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
48 private static readonly string[] INVARIANT_ABBREVIATED_MONTH_NAMES
49 = new string[13] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""};
50 private static readonly string[] INVARIANT_MONTH_NAMES
51 = new string[13] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ""};
52 private static readonly string[] INVARIANT_ERA_NAMES = {"A.D."};
54 private static DateTimeFormatInfo theInvariantDateTimeFormatInfo;
59 // DO NOT ALTER THE NAME OR ORDER OF THESE FIELDS
60 // AND DO NOT INSERT ANY VARIABLES ANYWHERE BUT AT THE END
61 // SINCE MONO RELIES ON THE ORDER AND ON THE NAMES
62 // see mono/mono/metadata/verify.c
64 private bool m_isReadOnly;
65 private string amDesignator;
66 private string pmDesignator;
67 private string dateSeparator;
68 private string timeSeparator;
69 private string shortDatePattern;
70 private string longDatePattern;
71 private string shortTimePattern;
72 private string longTimePattern;
73 private string monthDayPattern;
74 private string yearMonthPattern;
75 private string fullDateTimePattern;
76 private string _RFC1123Pattern;
77 private string _SortableDateTimePattern;
78 private string _UniversalSortableDateTimePattern;
79 private DayOfWeek firstDayOfWeek;
80 private Calendar calendar;
81 private CalendarWeekRule calendarWeekRule;
82 private string[] abbreviatedDayNames;
83 private string[] dayNames;
84 private string[] monthNames;
85 private string[] abbreviatedMonthNames;
87 // FIXME: not supported other than invariant
88 private string [] allShortDatePatterns;
89 private string [] allLongDatePatterns;
90 private string [] allShortTimePatterns;
91 private string [] allLongTimePatterns;
92 private string [] monthDayPatterns;
93 private string [] yearMonthPatterns;
96 // END OF BIG FAT WARNING
99 // MS Serialization needs this
100 private int nDataItem;
101 private bool m_useUserOverride;
102 private bool m_isDefaultCalendar;
103 private int CultureID;
104 private bool bUseCalendarInfo;
105 private string generalShortTimePattern;
106 private string generalLongTimePattern;
107 private string[] m_eraNames;
108 private string[] m_abbrevEraNames;
109 private string[] m_abbrevEnglishEraNames;
110 private string[] m_dateWords;
111 private int[] optionalCalendars;
113 public DateTimeFormatInfo()
115 m_isReadOnly = false;
120 shortDatePattern = "MM/dd/yyyy";
121 longDatePattern = "dddd, dd MMMM yyyy";
122 shortTimePattern = "HH:mm";
123 longTimePattern = "HH:mm:ss";
124 monthDayPattern = "MMMM dd";
125 yearMonthPattern = "yyyy MMMM";
126 fullDateTimePattern = "dddd, dd MMMM yyyy HH:mm:ss";
128 // FIXME: for the following three pattern: "The
129 // default value of this property is derived
130 // from the calendar that is set for
131 // CultureInfo.CurrentCulture or the default
132 // calendar of CultureInfo.CurrentCulture."
134 // Actually, no predefined culture has different values
135 // than those default values.
137 _RFC1123Pattern = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'";
138 _SortableDateTimePattern = "yyyy'-'MM'-'dd'T'HH':'mm':'ss";
139 _UniversalSortableDateTimePattern = "yyyy'-'MM'-'dd HH':'mm':'ss'Z'";
141 firstDayOfWeek = DayOfWeek.Sunday;
142 calendar = new GregorianCalendar();
143 calendarWeekRule = CalendarWeekRule.FirstDay;
145 abbreviatedDayNames = INVARIANT_ABBREVIATED_DAY_NAMES;
146 dayNames = INVARIANT_DAY_NAMES;
147 abbreviatedMonthNames = INVARIANT_ABBREVIATED_MONTH_NAMES;
148 monthNames = INVARIANT_MONTH_NAMES;
151 // LAMESPEC: this is not in ECMA specs
152 public static DateTimeFormatInfo GetInstance(IFormatProvider provider)
154 if (provider != null) {
155 DateTimeFormatInfo dtfi;
156 dtfi = (DateTimeFormatInfo)provider.GetFormat(typeof(DateTimeFormatInfo));
164 public bool IsReadOnly {
170 public static DateTimeFormatInfo ReadOnly(DateTimeFormatInfo dtfi)
172 DateTimeFormatInfo copy = (DateTimeFormatInfo)dtfi.Clone();
173 copy.m_isReadOnly = true;
177 public object Clone ()
179 DateTimeFormatInfo clone = (DateTimeFormatInfo) MemberwiseClone();
180 // clone is not read only
181 clone.m_isReadOnly = false;
185 public object GetFormat(Type formatType)
187 return (formatType == GetType()) ? this : null;
190 public string GetAbbreviatedEraName (int era)
192 if (era < 0 || era >= calendar.AbbreviatedEraNames.Length)
193 throw new ArgumentOutOfRangeException ("era", era.ToString ());
194 return calendar.AbbreviatedEraNames [era];
197 public string GetAbbreviatedMonthName(int month)
199 if (month < 1 || month > 13) throw new ArgumentOutOfRangeException();
200 return abbreviatedMonthNames[month-1];
203 public int GetEra (string eraName)
206 throw new ArgumentNullException ();
207 string [] eras = calendar.EraNames;
208 for (int i = 0; i < eras.Length; i++)
209 if (CultureInfo.InvariantCulture.CompareInfo
210 .Compare (eraName, eras [i],
211 CompareOptions.IgnoreCase) == 0)
216 public string GetEraName (int era)
218 if (era < 0 || era > calendar.EraNames.Length)
219 throw new ArgumentOutOfRangeException ("era", era.ToString ());
220 return calendar.EraNames [era - 1];
223 public string GetMonthName(int month)
225 if (month < 1 || month > 13) throw new ArgumentOutOfRangeException();
226 return monthNames[month-1];
229 public string[] AbbreviatedDayNames
233 return (string[]) abbreviatedDayNames.Clone();
237 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
238 if (value == null) throw new ArgumentNullException();
239 if (value.GetLength(0) != 7) throw new ArgumentException(MSG_ARRAYSIZE_DAY);
240 abbreviatedDayNames = (string[]) value.Clone();
244 public string[] AbbreviatedMonthNames
248 return (string[]) abbreviatedMonthNames.Clone();
252 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
253 if (value == null) throw new ArgumentNullException();
254 if (value.GetLength(0) != 13) throw new ArgumentException(MSG_ARRAYSIZE_MONTH);
255 abbreviatedMonthNames = (string[]) value.Clone();
259 public string[] DayNames
263 return (string[]) dayNames.Clone();
267 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
268 if (value == null) throw new ArgumentNullException();
269 if (value.GetLength(0) != 7) throw new ArgumentException(MSG_ARRAYSIZE_DAY);
270 dayNames = (string[]) value.Clone();
274 public string[] MonthNames
278 return (string[]) monthNames.Clone();
282 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
283 if (value == null) throw new ArgumentNullException();
284 if (value.GetLength(0) != 13) throw new ArgumentException(MSG_ARRAYSIZE_MONTH);
285 monthNames = (string[]) value.Clone();
289 public string AMDesignator
297 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
298 if (value == null) throw new ArgumentNullException();
299 amDesignator = value;
303 public string PMDesignator
311 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
312 if (value == null) throw new ArgumentNullException();
313 pmDesignator = value;
317 public string DateSeparator
321 return dateSeparator;
325 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
326 if (value == null) throw new ArgumentNullException();
327 dateSeparator = value;
331 public string TimeSeparator
335 return timeSeparator;
339 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
340 if (value == null) throw new ArgumentNullException();
341 timeSeparator = value;
345 public string LongDatePattern
349 return longDatePattern;
353 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
354 if (value == null) throw new ArgumentNullException();
355 longDatePattern = value;
359 public string ShortDatePattern
363 return shortDatePattern;
367 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
368 if (value == null) throw new ArgumentNullException();
369 shortDatePattern = value;
373 public string ShortTimePattern
377 return shortTimePattern;
381 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
382 if (value == null) throw new ArgumentNullException();
383 shortTimePattern = value;
387 public string LongTimePattern
391 return longTimePattern;
395 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
396 if (value == null) throw new ArgumentNullException();
397 longTimePattern = value;
401 public string MonthDayPattern
405 return monthDayPattern;
409 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
410 if (value == null) throw new ArgumentNullException();
411 monthDayPattern = value;
415 public string YearMonthPattern
419 return yearMonthPattern;
423 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
424 if (value == null) throw new ArgumentNullException();
425 yearMonthPattern = value;
429 public string FullDateTimePattern
433 if(fullDateTimePattern!=null) {
434 return fullDateTimePattern;
436 return(longDatePattern + " " + longTimePattern);
441 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
442 if (value == null) throw new ArgumentNullException();
443 fullDateTimePattern = value;
447 public static DateTimeFormatInfo CurrentInfo
451 return Thread.CurrentThread.CurrentCulture.DateTimeFormat;
455 public static DateTimeFormatInfo InvariantInfo
459 if (theInvariantDateTimeFormatInfo == null) {
460 theInvariantDateTimeFormatInfo =
461 DateTimeFormatInfo.ReadOnly(new DateTimeFormatInfo());
462 theInvariantDateTimeFormatInfo.FillInvariantPatterns ();
464 return theInvariantDateTimeFormatInfo;
468 // LAMESPEC: this is not in ECMA specs
469 public DayOfWeek FirstDayOfWeek
473 return firstDayOfWeek;
477 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
478 if ((int) value < 0 || (int) value > 6) throw new ArgumentOutOfRangeException();
479 firstDayOfWeek = value;
483 // LAMESPEC: this is not in ECMA specs
484 public Calendar Calendar
492 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
493 if (value == null) throw new ArgumentNullException();
498 public CalendarWeekRule CalendarWeekRule
502 return calendarWeekRule;
506 if (IsReadOnly) throw new InvalidOperationException(MSG_READONLY);
507 calendarWeekRule = value;
511 // LAMESPEC: this is not in ECMA specs
512 public string RFC1123Pattern
516 return _RFC1123Pattern;
520 // LAMESPEC: this is not in ECMA specs
521 public string SortableDateTimePattern
525 return _SortableDateTimePattern;
529 // LAMESPEC: this is not in ECMA specs
530 public string UniversalSortableDateTimePattern
534 return _UniversalSortableDateTimePattern;
538 // LAMESPEC: this is not in ECMA specs
539 [MonoTODO ("Not complete depending on GetAllDateTimePatterns(char)")]
540 public string[] GetAllDateTimePatterns()
542 FillAllDateTimePatterns ();
543 return (string []) all_date_time_patterns.Clone ();
547 // Same as above, but with no cloning, because we know that
548 // clients are friendly
549 internal string [] GetAllDateTimePatternsInternal ()
551 FillAllDateTimePatterns ();
552 return all_date_time_patterns;
555 // Prevent write reordering
556 volatile string [] all_date_time_patterns;
558 void FillAllDateTimePatterns (){
560 if (all_date_time_patterns != null)
563 ArrayList al = new ArrayList ();
564 foreach (string s in GetAllDateTimePatterns ('d'))
566 foreach (string s in GetAllDateTimePatterns ('D'))
568 foreach (string s in GetAllDateTimePatterns ('g'))
570 foreach (string s in GetAllDateTimePatterns ('G'))
572 foreach (string s in GetAllDateTimePatterns ('f'))
574 foreach (string s in GetAllDateTimePatterns ('F'))
576 // Yes, that is very meaningless, but that is what MS
577 // is doing (LAMESPEC: Since it is documented that
578 // 'M' and 'm' are equal, they should not cosider
579 // that there is a possibility that 'M' and 'm' are
581 foreach (string s in GetAllDateTimePatterns ('m'))
583 foreach (string s in GetAllDateTimePatterns ('M'))
585 foreach (string s in GetAllDateTimePatterns ('r'))
587 foreach (string s in GetAllDateTimePatterns ('R'))
589 foreach (string s in GetAllDateTimePatterns ('s'))
591 foreach (string s in GetAllDateTimePatterns ('t'))
593 foreach (string s in GetAllDateTimePatterns ('T'))
595 foreach (string s in GetAllDateTimePatterns ('u'))
597 foreach (string s in GetAllDateTimePatterns ('U'))
599 foreach (string s in GetAllDateTimePatterns ('y'))
601 foreach (string s in GetAllDateTimePatterns ('Y'))
604 // all_date_time_patterns needs to be volatile to prevent
605 // reordering of writes here and still avoid any locking.
606 all_date_time_patterns = (string []) al.ToArray (typeof (string)) as string [];
609 // LAMESPEC: this is not in ECMA specs
610 [MonoTODO ("We need more culture data in locale-builder")]
611 public string[] GetAllDateTimePatterns (char format)
617 if (allLongDatePatterns != null && allLongDatePatterns.Length > 0)
618 return allLongDatePatterns.Clone () as string [];
619 return new string [] {LongDatePattern};
621 if (allShortDatePatterns != null && allShortDatePatterns.Length > 0)
622 return allShortDatePatterns.Clone () as string [];
623 return new string [] {ShortDatePattern};
626 if (allLongTimePatterns != null && allLongTimePatterns.Length > 0)
627 return allLongTimePatterns.Clone () as string [];
628 return new string [] {LongTimePattern};
630 if (allShortTimePatterns != null && allShortTimePatterns.Length > 0)
631 return allShortTimePatterns.Clone () as string [];
632 return new string [] {ShortTimePattern};
633 // {Short|Long}Date + {Short|Long}Time
634 // FIXME: they should be the agglegation of the
635 // combination of the Date patterns and Time patterns.
637 list = PopulateCombinedList (allShortDatePatterns, allLongTimePatterns);
638 if (list != null && list.Length > 0)
640 return new string [] {ShortDatePattern + " " + LongTimePattern};
642 list = PopulateCombinedList (allShortDatePatterns, allShortTimePatterns);
643 if (list != null && list.Length > 0)
645 return new string [] {ShortDatePattern + " " + ShortTimePattern};
646 // The 'U' pattern strings are always the same as 'F'.
647 // (only differs in assuming UTC or not.)
650 list = PopulateCombinedList (allLongDatePatterns, allLongTimePatterns);
651 if (list != null && list.Length > 0)
653 return new string [] {LongDatePattern + " " + LongTimePattern};
655 list = PopulateCombinedList (allLongDatePatterns, allShortTimePatterns);
656 if (list != null && list.Length > 0)
658 return new string [] {LongDatePattern + " " + ShortTimePattern};
662 if (monthDayPatterns != null && monthDayPatterns.Length > 0)
663 return monthDayPatterns.Clone () as string [];
664 return new string [] {MonthDayPattern};
668 if (yearMonthPatterns != null && yearMonthPatterns.Length > 0)
669 return yearMonthPatterns.Clone () as string [];
670 return new string [] {YearMonthPattern};
674 return new string [] {RFC1123Pattern};
676 return new string [] {SortableDateTimePattern};
678 return new string [] {UniversalSortableDateTimePattern};
680 throw new ArgumentException ("Format specifier was invalid.");
683 // LAMESPEC: this is not in ECMA specs
684 public string GetDayName(DayOfWeek dayofweek)
686 int index = (int) dayofweek;
687 if (index < 0 || index > 6) throw new ArgumentOutOfRangeException();
688 return dayNames[index];
691 // LAMESPEC: this is not in ECMA specs
692 public string GetAbbreviatedDayName(DayOfWeek dayofweek)
694 int index = (int) dayofweek;
695 if (index < 0 || index > 6) throw new ArgumentOutOfRangeException();
696 return abbreviatedDayNames[index];
699 private void FillInvariantPatterns ()
701 allShortDatePatterns = new string [] {"MM/dd/yyyy"};
702 allLongDatePatterns = new string [] {"dddd, dd MMMM yyyy"};
703 allLongTimePatterns = new string [] {"HH:mm:ss"};
704 allShortTimePatterns = new string [] {
710 monthDayPatterns = new string [] {"MMMM dd"};
711 yearMonthPatterns = new string [] {"yyyy MMMM"};
714 private string [] PopulateCombinedList (string [] dates, string [] times)
716 if (dates != null && times != null) {
717 string [] list = new string [dates.Length * times.Length];
719 foreach (string d in dates)
720 foreach (string t in times)
721 list [i++] = d + " " + t;