Revert this
[mono.git] / mcs / class / corlib / System / DateTime.cs
1 //
2 // System.DateTime.cs
3 //
4 // author:
5 //   Marcel Narings (marcel@narings.nl)
6 //   Martin Baulig (martin@gnome.org)
7 //   Atsushi Enomoto (atsushi@ximian.com)
8 //
9 //   (C) 2001 Marcel Narings
10 // Copyright (C) 2004-2006 Novell, Inc (http://www.novell.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.Collections;
33 using System.Globalization;
34 using System.Runtime.CompilerServices;
35 using System.Runtime.InteropServices;
36 using System.Text;
37
38 namespace System
39 {
40         /// <summary>
41         /// The DateTime structure represents dates and time ranging from
42         /// 1-1-0001 12:00:00 AM to 31-12-9999 23:59:00 Common Era.
43         /// </summary>
44         /// 
45         [Serializable]
46         [StructLayout (LayoutKind.Auto)]
47         public struct DateTime : IFormattable, IConvertible, IComparable
48 #if NET_2_0
49                 , IComparable<DateTime>, IEquatable <DateTime>
50 #endif
51         {
52                 private TimeSpan ticks;
53
54 #if NET_2_0
55                 DateTimeKind kind;
56 #endif
57
58                 private const int dp400 = 146097;
59                 private const int dp100 = 36524;
60                 private const int dp4 = 1461;
61
62                 // w32 file time starts counting from 1/1/1601 00:00 GMT
63                 // which is the constant ticks from the .NET epoch
64                 private const long w32file_epoch = 504911232000000000L;
65
66                 //private const long MAX_VALUE_TICKS = 3155378975400000000L;
67                 // -- Microsoft .NET has this value.
68                 private const long MAX_VALUE_TICKS = 3155378975999999999L;
69
70                 //
71                 // The UnixEpoch, it begins on Jan 1, 1970 at 0:0:0, expressed
72                 // in Ticks
73                 //
74                 internal const long UnixEpoch = 621355968000000000L;
75
76                 // for OLE Automation dates
77                 private const long ticks18991230 = 599264352000000000L;
78                 private const double OAMinValue = -657435.0d;
79                 private const double OAMaxValue = 2958466.0d;
80
81                 public static readonly DateTime MaxValue = new DateTime (false, new TimeSpan (MAX_VALUE_TICKS));
82                 public static readonly DateTime MinValue = new DateTime (false, new TimeSpan (0));
83
84                 private static readonly string[] commonFormats = {
85                         // For compatibility with MS's CLR, this format (which
86                         // doesn't have a one-letter equivalent) is parsed
87                         // too. It's important because it's used in XML
88                         // serialization.
89
90                         // Note that those format should be tried only for
91                         // invalid patterns; 
92
93                         // FIXME: SOME OF those patterns looks tried against 
94                         // the current culture, since some patterns fail in 
95                         // some culture.
96
97                         "yyyy-MM-dd",
98                         "yyyy-MM-ddTHH:mm:sszzz",
99                         "yyyy-MM-ddTHH:mm:ss.fffffff",
100                         "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
101                         // bug #78618
102                         "yyyy-M-d H:m:s.fffffff",
103                         // UTC / allow any separator
104                         "yyyy/MM/ddTHH:mm:ssZ",
105                         "yyyy/M/dZ",
106                         // bug #58938
107                         "yyyy/M/d HH:mm:ss",
108                         // bug #47720
109                         "yyyy/MM/dd HH:mm:ss 'GMT'",
110                         // bug #53023
111                         "MM/dd/yyyy",
112                         // Close to RFC1123, but without 'GMT'
113                         "ddd, d MMM yyyy HH:mm:ss",
114                         // use UTC ('Z'; not literal "'Z'")
115                         // FIXME: 1078(af-ZA) and 1079(ka-GE) reject it
116                         "yyyy/MM/dd HH':'mm':'ssZ", 
117
118                         // bug #60912
119                         "M/d/yyyy HH':'mm':'ss tt",
120                         "H':'mm':'ss tt",
121                         // another funky COM dependent one
122                         "dd-MMM-yy",
123
124                         // DayOfTheWeek, dd full_month_name yyyy
125                         // FIXME: 1054(th-TH) rejects them
126                         "dddd, dd MMMM yyyy",
127                         "dddd, dd MMMM yyyy HH:mm",
128                         "dddd, dd MMMM yyyy HH:mm:ss",
129
130                         "yyyy MMMM",
131                         // DayOfTheWeek, dd yyyy. This works for every locales.
132                         "MMMM dd, yyyy",
133 #if NET_1_1
134                         // X509Certificate pattern is accepted by Parse() *in every culture*
135                         "yyyyMMddHHmmssZ",
136 #endif
137                         // In Parse() the 'r' equivalent pattern is first parsed as universal time
138                         "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'",
139
140                         // Additionally there seems language-specific format
141                         // patterns that however works in all language
142                         // environment.
143                         // For example, the pattern below is for Japanese.
144                         "yyyy'\u5E74'MM'\u6708'dd'\u65E5' HH'\u6642'mm'\u5206'ss'\u79D2'",
145
146 /*
147                         // Full date and time
148                         "F", "G", "r", "s", "u", "U",
149                         // Full date and time, but no seconds
150                         "f", "g",
151                         // Only date
152                         "d", "D",
153                         // Only time
154                         "T", "t",
155                         // Only date, but no year
156                         "m",
157                         // Only date, but no day
158                         "y" 
159 */
160                 };
161
162                 private enum Which 
163                 {
164                         Day,
165                         DayYear,
166                         Month,
167                         Year
168                 };
169         
170                 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };        
171                 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };    
172
173                 private static int AbsoluteDays (int year, int month, int day)
174                 {
175                         int[] days;
176                         int temp = 0, m=1 ;
177                 
178                         days = (IsLeapYear(year) ? daysmonthleap  : daysmonth);
179                         
180                         while (m < month)
181                                 temp += days[m++];
182                         return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
183                 }
184
185                 private int FromTicks(Which what)
186                 {
187                         int num400, num100, num4, numyears; 
188                         int M =1;
189
190                         int[] days = daysmonth;
191                         int totaldays = this.ticks.Days;
192
193                         num400 = (totaldays / dp400);
194                         totaldays -=  num400 * dp400;
195                 
196                         num100 = (totaldays / dp100);
197                         if (num100 == 4)   // leap
198                                 num100 = 3;
199                         totaldays -= (num100 * dp100);
200
201                         num4 = totaldays / dp4;
202                         totaldays -= (num4 * dp4);
203
204                         numyears = totaldays / 365 ;
205
206                         if (numyears == 4)  //leap
207                                 numyears =3 ;
208                         if (what == Which.Year )
209                                 return num400*400 + num100*100 + num4*4 + numyears + 1;
210
211                         totaldays -= (numyears * 365) ;
212                         if (what == Which.DayYear )
213                                 return totaldays + 1;
214                         
215                         if  ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
216                                 days = daysmonthleap;
217                                 
218                         while (totaldays >= days[M])
219                                 totaldays -= days[M++];
220
221                         if (what == Which.Month )
222                                 return M;
223
224                         return totaldays +1; 
225                 }
226
227
228                 // Constructors
229                 
230                 /// <summary>
231                 /// Constructs a DateTime for specified ticks
232                 /// </summary>
233                 /// 
234                 public DateTime (long ticks)
235                 {
236                         this.ticks = new TimeSpan (ticks);
237                         if (ticks < MinValue.Ticks || ticks > MaxValue.Ticks) {
238                                 string msg = Locale.GetText ("Value {0} is outside the valid range [{1},{2}].", 
239                                         ticks, MinValue.Ticks, MaxValue.Ticks);
240                                 throw new ArgumentOutOfRangeException ("ticks", msg);
241                         }
242 #if NET_2_0
243                         kind = DateTimeKind.Unspecified;
244 #endif
245                 }
246
247                 public DateTime (int year, int month, int day)
248                         : this (year, month, day,0,0,0,0) {}
249
250                 public DateTime (int year, int month, int day, int hour, int minute, int second)
251                         : this (year, month, day, hour, minute, second, 0)      {}
252
253                 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
254                         {
255                         if ( year < 1 || year > 9999 || 
256                                 month < 1 || month >12  ||
257                                 day < 1 || day > DaysInMonth(year, month) ||
258                                 hour < 0 || hour > 23 ||
259                                 minute < 0 || minute > 59 ||
260                                 second < 0 || second > 59 ||
261                                 millisecond < 0 || millisecond > 999)
262                                 throw new ArgumentOutOfRangeException ("Parameters describe an " +
263                                                                         "unrepresentable DateTime.");
264
265                         ticks = new TimeSpan (AbsoluteDays(year,month,day), hour, minute, second, millisecond);
266
267 #if NET_2_0
268                         kind = DateTimeKind.Unspecified;
269 #endif
270                 }
271
272                 [MonoTODO ("The Calendar is not taken into consideration")]
273                 public DateTime (int year, int month, int day, Calendar calendar)
274                         : this (year, month, day, 0, 0, 0, 0, calendar)
275                 {
276                 }
277                 
278                 [MonoTODO ("The Calendar is not taken into consideration")]
279                 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
280                         : this (year, month, day, hour, minute, second, 0, calendar)
281                 {
282                 }
283
284                 [MonoTODO ("The Calendar is not taken into consideration")]
285                 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
286                         : this (year, month, day, hour, minute, second, millisecond) 
287                 {
288                         if (calendar == null)
289                                 throw new ArgumentNullException ("calendar");
290                 }
291
292                 internal DateTime (bool check, TimeSpan value)
293                 {
294                         if (check && (value.Ticks < MinValue.Ticks || value.Ticks > MaxValue.Ticks))
295                             throw new ArgumentOutOfRangeException ();
296
297                         ticks = value;
298
299 #if NET_2_0
300                         kind = DateTimeKind.Unspecified;
301 #endif
302                 }
303
304 #if NET_2_0
305                 public DateTime (long ticks, DateTimeKind kind) : this (ticks)
306                 {
307                         CheckDateTimeKind (kind);
308                         this.kind = kind;
309                 }
310
311                 public DateTime (int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
312                         : this (year, month, day, hour, minute, second)
313                 {
314                         CheckDateTimeKind (kind);
315                         this.kind = kind;
316                 }
317
318                 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
319                         : this (year, month, day, hour, minute, second, millisecond)
320                 {
321                         CheckDateTimeKind (kind);
322                         this.kind = kind;
323                 }
324
325                 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind)
326                         : this (year, month, day, hour, minute, second, millisecond, calendar)
327                 {
328                         CheckDateTimeKind (kind);
329                         this.kind = kind;
330                 }                       
331 #endif
332
333                 /* Properties  */
334
335                 public DateTime Date 
336                 {
337                         get     
338                         { 
339                                 DateTime ret = new DateTime (Year, Month, Day);
340 #if NET_2_0
341                                 ret.kind = kind;
342 #endif
343                                 return ret;
344                         }
345                 }
346         
347                 public int Month 
348                 {
349                         get     
350                         { 
351                                 return FromTicks(Which.Month); 
352                         }
353                 }
354
355                
356                 public int Day
357                 {
358                         get 
359                         { 
360                                 return FromTicks(Which.Day); 
361                         }
362                 }
363
364                 public DayOfWeek DayOfWeek 
365                 {
366                         get 
367                         { 
368                                 return ( (DayOfWeek) ((ticks.Days+1) % 7) ); 
369                         }
370                 }
371
372                 public int DayOfYear 
373                 {
374                         get 
375                         { 
376                                 return FromTicks(Which.DayYear); 
377                         }
378                 }
379
380                 public TimeSpan TimeOfDay 
381                 {
382                         get     
383                         { 
384                                 return new TimeSpan(ticks.Ticks % TimeSpan.TicksPerDay );
385                         }
386                         
387                 }
388
389                 public int Hour 
390                 {
391                         get 
392                         { 
393                                 return ticks.Hours;
394                         }
395                 }
396
397                 public int Minute 
398                 {
399                         get 
400                         { 
401                                 return ticks.Minutes;
402                         }
403                 }
404
405                 public int Second 
406                 {
407                         get     
408                         { 
409                                 return ticks.Seconds;
410                         }
411                 }
412
413                 public int Millisecond 
414                 {
415                         get 
416                         { 
417                                 return ticks.Milliseconds;
418                         }
419                 }
420                 
421                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
422                 internal static extern long GetNow ();
423
424                 //
425                 // To reduce the time consumed by DateTime.Now, we keep
426                 // the difference to map the system time into a local
427                 // time into `to_local_time_span', we record the timestamp
428                 // for this in `last_now'
429                 //
430                 static object to_local_time_span_object;
431                 static long last_now;
432                 
433                 public static DateTime Now 
434                 {
435                         get     
436                         {
437                                 long now = GetNow ();
438                                 DateTime dt = new DateTime (now);
439
440                                 if ((now - last_now) > TimeSpan.TicksPerMinute){
441                                         to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
442                                         last_now = now;
443
444                                 }
445
446                                 // This is boxed, so we avoid locking.
447                                 DateTime ret = dt + (TimeSpan) to_local_time_span_object;
448 #if NET_2_0
449                                 ret.kind = DateTimeKind.Local;
450 #endif
451                                 return ret;
452                         }
453                 }
454
455                 public long Ticks
456                 { 
457                         get     
458                         { 
459                                 return ticks.Ticks;
460                         }
461                 }
462         
463                 public static DateTime Today 
464                 {
465                         get {
466                                 DateTime now = Now;
467                                 DateTime today = new DateTime (now.Year, now.Month, now.Day);
468 #if NET_2_0
469                                 today.kind = now.kind;
470 #endif
471                                 return today;
472                         }
473                 }
474
475                 public static DateTime UtcNow 
476                 {
477                         get {
478 #if NET_2_0
479                                 return new DateTime (GetNow (), DateTimeKind.Utc);
480 #else
481                                 return new DateTime (GetNow ());
482 #endif
483                         }
484                 }
485
486                 public int Year 
487                 {
488                         get 
489                         { 
490                                 return FromTicks(Which.Year); 
491                         }
492                 }
493
494 #if NET_2_0
495                 public DateTimeKind Kind {
496                         get {
497                                 return kind;
498                         }
499                 }
500 #endif
501
502                 /* methods */
503
504                 public DateTime Add (TimeSpan ts)
505                 {
506                         DateTime ret = AddTicks (ts.Ticks);
507 #if NET_2_0
508                         ret.kind = kind;
509 #endif
510                         return ret;
511                 }
512
513                 public DateTime AddDays (double days)
514                 {
515                         return AddMilliseconds (Math.Round (days * 86400000));
516                 }
517                 
518                 public DateTime AddTicks (long t)
519                 {
520                         if ((t + ticks.Ticks) > MAX_VALUE_TICKS || (t + ticks.Ticks) < 0) {
521                                 throw new ArgumentOutOfRangeException();
522                         }
523                         DateTime ret = new DateTime (t + ticks.Ticks);
524 #if NET_2_0
525                         ret.kind = kind;
526 #endif
527                         return ret;
528                 }
529
530                 public DateTime AddHours (double hours)
531                 {
532                         return AddMilliseconds (hours * 3600000);
533                 }
534
535                 public DateTime AddMilliseconds (double ms)
536                 {
537                         if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
538                                         (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
539                                 throw new ArgumentOutOfRangeException();
540                         }
541                         long msticks = (long) (ms * TimeSpan.TicksPerMillisecond);
542
543                         return AddTicks (msticks);
544                 }
545
546                 // required to match MS implementation for OADate (OLE Automation)
547                 private DateTime AddRoundedMilliseconds (double ms)
548                 {
549                         if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
550                                 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
551                                 throw new ArgumentOutOfRangeException ();
552                         }
553                         long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
554
555                         return AddTicks (msticks);
556                 }
557
558                 public DateTime AddMinutes (double minutes)
559                 {
560                         return AddMilliseconds (minutes * 60000);
561                 }
562                 
563                 public DateTime AddMonths (int months)
564                 {
565                         int day, month, year,  maxday ;
566                         DateTime temp ;
567
568                         day = this.Day;
569                         month = this.Month + (months % 12);
570                         year = this.Year + months/12 ;
571                         
572                         if (month < 1)
573                         {
574                                 month = 12 + month ;
575                                 year -- ;
576                         }
577                         else if (month>12) 
578                         {
579                                 month = month -12;
580                                 year ++;
581                         }
582                         maxday = DaysInMonth(year, month);
583                         if (day > maxday)
584                                 day = maxday;
585
586                         temp = new DateTime (year, month, day);
587 #if NET_2_0
588                         temp.kind = kind;
589 #endif
590                         return  temp.Add (this.TimeOfDay);
591                 }
592
593                 public DateTime AddSeconds (double seconds)
594                 {
595                         return AddMilliseconds (seconds*1000);
596                 }
597
598                 public DateTime AddYears (int years )
599                 {
600                         return AddMonths(years * 12);
601                 }
602
603                 public static int Compare (DateTime t1, DateTime t2)
604                 {
605                         if (t1.ticks < t2.ticks) 
606                                 return -1;
607                         else if (t1.ticks > t2.ticks) 
608                                 return 1;
609                         else
610                                 return 0;
611                 }
612
613                 public int CompareTo (object v)
614                 {
615                         if ( v == null)
616                                 return 1;
617
618                         if (!(v is System.DateTime))
619                                 throw new ArgumentException (Locale.GetText (
620                                         "Value is not a System.DateTime"));
621
622                         return Compare (this, (DateTime) v);
623                 }
624
625 #if NET_2_0
626                 public bool IsDaylightSavingTime ()
627                 {
628                         if (kind == DateTimeKind.Utc)
629                                 return false;
630                         return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
631                 }
632
633                 public int CompareTo (DateTime value)
634                 {
635                         return Compare (this, value);
636                 }
637
638                 public bool Equals (DateTime value)
639                 {
640                         return value.ticks == ticks;
641                 }
642
643                 public long ToBinary ()
644                 {
645                         switch (kind) {
646                         case DateTimeKind.Utc:
647                                 return Ticks | 0x4000000000000000;
648                         case DateTimeKind.Local:
649                                 return (long) ((ulong)Ticks | 0x8000000000000000);
650                         default:
651                                 return Ticks;
652                         }
653                 }
654
655                 public static DateTime FromBinary (long dateData)
656                 {
657                         switch ((ulong)dateData >> 62) {
658                         case 1:
659                                 return new DateTime (dateData ^ 0x4000000000000000, DateTimeKind.Utc);
660                         case 0:
661                                 return new DateTime (dateData, DateTimeKind.Unspecified);
662                         default:
663                                 return new DateTime (((dateData << 2) >> 2), DateTimeKind.Local);
664                         }
665                 }
666
667                 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
668                 {
669                         return new DateTime (value.Ticks, kind);
670                 }
671 #endif
672
673                 public static int DaysInMonth (int year, int month)
674                 {
675                         int[] days ;
676
677                         if (month < 1 || month >12)
678                                 throw new ArgumentOutOfRangeException ();
679
680                         days = (IsLeapYear(year) ? daysmonthleap  : daysmonth);
681                         return days[month];                     
682                 }
683                 
684                 public override bool Equals (object o)
685                 {
686                         if (!(o is System.DateTime))
687                                 return false;
688
689                         return ((DateTime) o).ticks == ticks;
690                 }
691
692                 public static bool Equals (DateTime t1, DateTime t2 )
693                 {
694                         return (t1.ticks == t2.ticks );
695                 }
696
697                 public static DateTime FromFileTime (long fileTime) 
698                 {
699                         if (fileTime < 0)
700                                 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
701
702                         return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
703                 }
704
705 #if NET_1_1
706                 public static DateTime FromFileTimeUtc (long fileTime) 
707                 {
708                         if (fileTime < 0)
709                                 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
710
711                         return new DateTime (w32file_epoch + fileTime);
712                 }
713 #endif
714
715                 public static DateTime FromOADate (double d)
716                 {
717                         // An OLE Automation date is implemented as a floating-point number
718                         // whose value is the number of days from midnight, 30 December 1899.
719
720                         // d must be negative 657435.0 through positive 2958466.0.
721                         if ((d <= OAMinValue) || (d >= OAMaxValue))
722                                 throw new ArgumentException ("d", "[-657435,2958466]");
723
724                         DateTime dt = new DateTime (ticks18991230);
725                         if (d < 0.0d) {
726                                 Double days = Math.Ceiling (d);
727                                 // integer part is the number of days (negative)
728                                 dt = dt.AddRoundedMilliseconds (days * 86400000);
729                                 // but decimals are the number of hours (in days fractions) and positive
730                                 Double hours = (days - d);
731                                 dt = dt.AddRoundedMilliseconds (hours * 86400000);
732                         }
733                         else {
734                                 dt = dt.AddRoundedMilliseconds (d * 86400000);
735                         }
736
737                         return dt;
738                 }
739
740                 public string[] GetDateTimeFormats() 
741                 {
742                         return GetDateTimeFormats (CultureInfo.CurrentCulture);
743                 }
744
745                 public string[] GetDateTimeFormats(char format)
746                 {
747                         if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
748                                 throw new FormatException ("Invalid format character.");
749                         string[] result = new string[1];
750                         result[0] = this.ToString(format.ToString());
751                         return result;
752                 }
753                 
754                 public string[] GetDateTimeFormats(IFormatProvider provider)
755                 {
756                         DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
757 //                      return GetDateTimeFormats (info.GetAllDateTimePatterns ());
758                         ArrayList al = new ArrayList ();
759                         foreach (char c in "dDgGfFmMrRstTuUyY")
760                                 al.AddRange (GetDateTimeFormats (c, info));
761                         return al.ToArray (typeof (string)) as string [];
762                 }
763
764                 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
765                 {
766                         if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
767                                 throw new FormatException ("Invalid format character.");
768
769                         // LAMESPEC: There is NO assurance that 'U' ALWAYS
770                         // euqals to 'F', but since we have to iterate all
771                         // the pattern strings, we cannot just use 
772                         // ToString("U", provider) here. I believe that the 
773                         // method's behavior cannot be formalized.
774                         bool adjustutc = false;
775                         switch (format) {
776                         case 'U':
777 //                      case 'r':
778 //                      case 'R':
779 //                      case 'u':
780                                 adjustutc = true;
781                                 break;
782                         }
783                         DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
784                         return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
785                 }
786
787                 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
788                 {
789                         string [] results = new string [patterns.Length];
790                         DateTime val = adjustutc ? ToUniversalTime () : this;
791                         for (int i = 0; i < results.Length; i++)
792                                 results [i] = val._ToString (patterns [i], dfi);
793                         return results;
794                 }
795
796 #if NET_2_0
797                 private void CheckDateTimeKind (DateTimeKind kind) {
798                         if ((kind != DateTimeKind.Unspecified) && (kind != DateTimeKind.Utc) && (kind != DateTimeKind.Local))
799                                 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
800                 }
801 #endif
802
803                 public override int GetHashCode ()
804                 {
805                         return (int) ticks.Ticks;
806                 }
807
808                 public TypeCode GetTypeCode ()
809                 {
810                         return TypeCode.DateTime;
811                 }
812
813                 public static bool IsLeapYear (int year)
814                 {
815                         return  ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
816                 }
817
818                 public static DateTime Parse (string s)
819                 {
820                         return Parse (s, null);
821                 }
822
823                 public static DateTime Parse (string s, IFormatProvider fp)
824                 {
825                         return Parse (s, fp, DateTimeStyles.AllowWhiteSpaces);
826                 }
827
828                 public static DateTime Parse (string s, IFormatProvider fp, DateTimeStyles styles)
829                 {
830                         // This method should try only expected patterns. 
831                         // Should not try extra patterns.
832                         // Right now we also try InvariantCulture, but I
833                         // confirmed in some cases this method rejects what
834                         // InvariantCulture supports (can be checked against
835                         // "th-TH" with Gregorian Calendar). So basically it
836                         // should not be done.
837                         // I think it should be CurrentCulture to be tested,
838                         // but right now we don't support all the supported
839                         // patterns for each culture, so try InvariantCulture
840                         // as a quick remedy.
841                         if (s == null)
842                                 throw new ArgumentNullException (Locale.GetText ("s is null"));
843                         DateTime result;
844
845                         if (fp == null)
846                                 fp = CultureInfo.CurrentCulture;
847                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
848
849                         bool longYear = false;
850
851                         // Try all the patterns
852                         if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear))
853                                 return result;
854
855                         // Try common formats.
856 //                      if (ParseExact (s, commonFormats, dfi, styles, out result, false, ref longYear))
857 //                              return result;
858
859                         // Try common formats with invariant culture
860                         if (ParseExact (s, commonFormats, DateTimeFormatInfo.InvariantInfo, styles, out result, false, ref longYear))
861                                 return result;
862
863                         if (longYear) {
864                                 throw new ArgumentOutOfRangeException ("year",
865                                         "Valid values are between 1 and 9999 inclusive");
866                         }
867
868                         throw new FormatException ("String was not recognized as a valid DateTime.");
869                 }
870
871                 public static DateTime ParseExact (string s, string format, IFormatProvider fp)
872                 {
873                         return ParseExact (s, format, fp, DateTimeStyles.None);
874                 }
875
876                 internal static int _ParseNumber (string s, int valuePos,
877                                                   int min_digits,
878                                                   int digits,
879                                                   bool leadingzero,
880                                                   bool sloppy_parsing,
881                                                   out int num_parsed)
882                 {
883                         int number = 0, i;
884
885                         if (sloppy_parsing)
886                                 leadingzero = false;
887
888                         if (!leadingzero) {
889                                 int real_digits = 0;
890                                 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
891                                         if (!Char.IsDigit (s[i]))
892                                                 break;
893
894                                         real_digits++;
895                                 }
896
897                                 digits = real_digits;
898                         }
899                         if (digits < min_digits) {
900                                 num_parsed = -1;
901                                 return 0;
902                         }
903
904                         if (s.Length - valuePos < digits) {
905                                 num_parsed = -1;
906                                 return 0;
907                         }
908
909                         for (i = valuePos; i < digits + valuePos; i++) {
910                                 char c = s[i];
911                                 if (!Char.IsDigit (c)) {
912                                         num_parsed = -1;
913                                         return 0;
914                                 }
915
916                                 number = number * 10 + (byte) (c - '0');
917                         }
918
919                         num_parsed = digits;
920                         return number;
921                 }
922
923                 internal static int _ParseEnum (string s, int sPos, string[] values, out int num_parsed)
924                 {
925                         int i;
926
927                         // FIXME: I know this is somehow lame code. Probably
928                         // it should iterate all the enum value and return
929                         // the longest match. However right now I don't see
930                         // anything but "1" and "10" - "12" that might match
931                         // two or more values. (They are only abbrev month
932                         // names, so do reverse order search). See bug #80094.
933                         for (i = values.Length - 1; i >= 0; i--) {
934                                 if (s.Length - sPos < values[i].Length)
935                                         continue;
936                                 else if (values [i].Length == 0)
937                                         continue;
938                                 String tmp = s.Substring (sPos, values[i].Length);
939                                 if (String.Compare (tmp, values[i], true) == 0) {
940                                         num_parsed = values[i].Length;
941                                         return i;
942                                 }
943                         }
944
945                         num_parsed = -1;
946                         return -1;
947                 }
948
949                 internal static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
950                 {
951                         if (maxlength <= 0)
952                                 maxlength = value.Length;
953
954                         if (String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
955                                 num_parsed = maxlength;
956                                 return true;
957                         }
958
959                         num_parsed = -1;
960                         return false;
961                 }
962
963                 private static bool _DoParse (string s, string format, bool exact,
964                                                out DateTime result,
965                                                DateTimeFormatInfo dfi,
966                                                DateTimeStyles style,
967                                                ref bool longYear)
968                 {
969                         bool useutc = false, use_localtime = true;
970                         bool use_invariant = false;
971                         bool sloppy_parsing = false;
972                         int valuePos = 0;
973                         if (format.Length == 1)
974                                 format = _GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
975                         else if (!exact && CultureInfo.InvariantCulture.CompareInfo.IndexOf (format, "GMT", CompareOptions.Ordinal) >= 0)
976                                 useutc = true;
977
978                         if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
979                                 format = format.TrimStart (null);
980
981                                 s = s.TrimStart (null); // it could be optimized, but will make little good.
982                         }
983
984                         if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
985                                 format = format.TrimEnd (null);
986                                 s = s.TrimEnd (null); // it could be optimized, but will make little good.
987                         }
988
989                         if (use_invariant)
990                                 dfi = DateTimeFormatInfo.InvariantInfo;
991
992                         if ((style & DateTimeStyles.AllowInnerWhite) != 0)
993                                 sloppy_parsing = true;
994
995                         string chars = format;
996                         int len = format.Length, pos = 0, num = 0;
997
998                         int day = -1, dayofweek = -1, month = -1, year = -1;
999                         int hour = -1, minute = -1, second = -1;
1000                         double fractionalSeconds = -1;
1001                         int ampm = -1;
1002                         int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1003
1004                         result = new DateTime (0);
1005                         while (pos+num < len)
1006                         {
1007                                 if (s.Length == valuePos)
1008                                         break;
1009
1010                                 if (chars[pos] == '\'') {
1011                                         num = 1;
1012                                         while (pos+num < len) {
1013                                                 if (chars[pos+num] == '\'')
1014                                                         break;
1015
1016                                                 if (valuePos == s.Length)
1017                                                         return false;
1018                                                 if (s [valuePos] != chars [pos + num])
1019                                                         return false;
1020                                                 valuePos++;
1021
1022                                                 num++;
1023                                         }
1024                                         if (pos+num > len)
1025                                                 return false;
1026
1027                                         pos += num + 1;
1028                                         num = 0;
1029                                         continue;
1030                                 } else if (chars[pos] == '"') {
1031                                         num = 1;
1032                                         while (pos+num < len) {
1033                                                 if (chars[pos+num] == '"')
1034                                                         break;
1035
1036                                                 if (valuePos == s.Length)
1037                                                         return false;
1038                                                 if (s [valuePos] != chars[pos+num])
1039                                                         return false;
1040                                                 valuePos++;
1041
1042                                                 num++;
1043                                         }
1044                                         if (pos+num > len)
1045                                                 return false;
1046
1047                                         pos += num + 1;
1048                                         num = 0;
1049                                         continue;
1050                                 } else if (chars[pos] == '\\') {
1051                                         pos += num + 1;
1052                                         num = 0;
1053                                         if (pos >= len)
1054                                                 return false;
1055
1056                                         if (s [valuePos] != chars [pos])
1057                                                 return false;
1058                                         valuePos++;
1059                                         pos++;
1060                                         continue;
1061                                 } else if (chars[pos] == '%') {
1062                                         pos++;
1063                                         continue;
1064                                 } else if (Char.IsWhiteSpace (s [valuePos]) ||
1065                                         s [valuePos] == ',' && Char.IsWhiteSpace (chars [pos])) {
1066                                         valuePos++;
1067                                         num = 0;
1068                                         if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1069                                                 if (!Char.IsWhiteSpace (chars[pos]))
1070                                                         return false;
1071                                                 pos++;
1072                                                 continue;
1073                                         }
1074
1075                                         int ws = valuePos;
1076                                         while (ws < s.Length) {
1077                                                 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1078                                                         ws++;
1079                                                 else
1080                                                         break;
1081                                         }
1082                                         valuePos = ws;
1083                                         ws = pos;
1084                                         while (ws < chars.Length) {
1085                                                 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1086                                                         ws++;
1087                                                 else
1088                                                         break;
1089                                         }
1090                                         pos = ws;
1091                                         continue;
1092                                 }
1093
1094                                 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1095                                         num++;
1096                                         continue;
1097                                 }
1098
1099
1100                                 int num_parsed = 0;
1101
1102                                 switch (chars[pos])
1103                                 {
1104                                 case 'd':
1105                                         if (day != -1)
1106                                                 return false;
1107                                         if (num == 0)
1108                                                 day = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
1109                                         else if (num == 1)
1110                                                 day = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
1111                                         else if (num == 2)
1112                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, out num_parsed);
1113                                         else
1114                                         {
1115                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, out num_parsed);
1116                                                 num = 3;
1117                                         }
1118                                         break;
1119                                 case 'M':
1120                                         if (month != -1)
1121                                                 return false;
1122                                         if (num == 0)
1123                                                 month = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
1124                                         else if (num == 1)
1125                                                 month = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1126                                         else if (num == 2)
1127                                                 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames , out num_parsed) + 1;
1128                                         else
1129                                         {
1130                                                 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, out num_parsed) + 1;
1131                                                 num = 3;
1132                                         }
1133                                         break;
1134                                 case 'y':
1135                                         if (year != -1)
1136                                                 return false;
1137
1138                                         if (num == 0) {
1139                                                 year = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
1140                                         } else if (num < 3) {
1141                                                 year = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
1142                                         } else {
1143                                                 year = _ParseNumber (s, valuePos,4, 4, false, sloppy_parsing, out num_parsed);
1144                                                 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1145                                                         int np = 0;
1146                                                         int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1147                                                         longYear = (ly > 9999);
1148                                                 }
1149                                                 num = 3;
1150                                         }
1151
1152                                         //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1153                                         if (num_parsed <= 2)
1154                                                 year += (year < 30) ? 2000 : 1900;
1155                                         break;
1156                                 case 'h':
1157                                         if (hour != -1)
1158                                                 return false;
1159                                         if (num == 0)
1160                                                 hour = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
1161                                         else
1162                                         {
1163                                                 hour = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
1164                                                 num = 1;
1165                                         }
1166
1167                                         if (hour > 12)
1168                                                 return false;
1169                                         if (hour == 12)
1170                                                 hour = 0;
1171
1172                                         break;
1173                                 case 'H':
1174                                         if ((hour != -1) || (ampm >= 0))
1175                                                 return false;
1176                                         if (num == 0)
1177                                                 hour = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
1178                                         else
1179                                         {
1180                                                 hour = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
1181                                                 num = 1;
1182                                         }
1183                                         if (hour >= 24)
1184                                                 return false;
1185
1186 //                                      ampm = -2;
1187                                         break;
1188                                 case 'm':
1189                                         if (minute != -1)
1190                                                 return false;
1191                                         if (num == 0)
1192                                                 minute = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
1193                                         else
1194                                         {
1195                                                 minute = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1196                                                 num = 1;
1197                                         }
1198                                         if (minute >= 60)
1199                                                 return false;
1200
1201                                         break;
1202                                 case 's':
1203                                         if (second != -1)
1204                                                 return false;
1205                                         if (num == 0)
1206                                                 second = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
1207                                         else
1208                                         {
1209                                                 second = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1210                                                 num = 1;
1211                                         }
1212                                         if (second >= 60)
1213                                                 return false;
1214
1215                                         break;
1216                                 case 'f':
1217                                         if (fractionalSeconds != -1)
1218                                                 return false;
1219                                         num = Math.Min (num, 6);
1220                                         double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, true, sloppy_parsing, out num_parsed);
1221                                         if (num_parsed == -1)
1222                                                 return false;
1223
1224                                         else
1225                                                 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1226                                         break;
1227                                 case 't':
1228                                         if (ampm != -1)
1229                                                 return false;
1230                                         if (num == 0)
1231                                         {
1232                                                 if (_ParseString (s, valuePos, 1, dfi.AMDesignator, out num_parsed))
1233                                                         ampm = 0;
1234                                                 else if (_ParseString (s, valuePos, 1, dfi.PMDesignator, out num_parsed))
1235                                                         ampm = 1;
1236                                                 else
1237                                                         return false;
1238                                         }
1239                                         else
1240                                         {
1241                                                 if (_ParseString (s, valuePos, 0, dfi.AMDesignator, out num_parsed))
1242                                                         ampm = 0;
1243                                                 else if (_ParseString (s, valuePos, 0, dfi.PMDesignator, out num_parsed))
1244                                                         ampm = 1;
1245                                                 else
1246                                                         return false;
1247                                                 num = 1;
1248                                         }
1249                                         break;
1250                                 case 'z':
1251                                         if (tzsign != -1)
1252                                                 return false;
1253                                         if (s [valuePos] == '+')
1254                                                 tzsign = 0;
1255                                         else if (s [valuePos] == '-')
1256                                                 tzsign = 1;
1257                                         else
1258                                                 return false;
1259                                         valuePos++;
1260                                         if (num == 0)
1261                                                 tzoffset = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
1262                                         else if (num == 1)
1263                                                 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1264                                         else
1265                                         {
1266                                                 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1267                                                 if (num_parsed < 0)
1268                                                         return false;
1269                                                 valuePos += num_parsed;
1270                                                 if (Char.IsDigit (s [valuePos]))
1271                                                         num_parsed = 0;
1272                                                 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1273                                                         return false;
1274                                                 valuePos += num_parsed;
1275                                                 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1276                                                 if (num_parsed < 0)
1277                                                         return false;
1278                                                 num = 2;
1279                                         }
1280                                         break;
1281
1282                                 // LAMESPEC: This should be part of UTCpattern
1283                                 // string and thus should not be considered here.
1284                                 //
1285                                 // Note that 'Z' is not defined as a pattern
1286                                 // character. Keep it for X509 certificate
1287                                 // verification. Also, "Z" != "'Z'" under MS.NET
1288                                 // ("'Z'" is just literal; handled above)
1289                                 case 'Z':
1290                                         if (s [valuePos] != 'Z')
1291                                                 return false;
1292                                         num = 0;
1293                                         num_parsed = 1;
1294                                         useutc = true;
1295                                         break;
1296
1297                                 case ':':
1298                                         if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1299                                                 return false;
1300                                         break;
1301                                 case '/':
1302                                         /* Accept any character for
1303                                          * DateSeparator, except
1304                                          * TimeSeparator, a digit or a
1305                                          * letter.  Not documented,
1306                                          * but seems to be MS
1307                                          * behaviour here.  See bug
1308                                          * 54047.
1309                                          */
1310                                         if (exact && s [valuePos] != '/')
1311                                                 return false;
1312
1313                                         if (_ParseString (s, valuePos, 0,
1314                                                           dfi.TimeSeparator,
1315                                                           out num_parsed) ||
1316                                             Char.IsDigit (s [valuePos]) ||
1317                                             Char.IsLetter (s [valuePos])) {
1318                                                 return(false);
1319                                         }
1320
1321                                         num = 0;
1322                                         if (num_parsed <= 0) {
1323                                                 num_parsed = 1;
1324                                         }
1325                                         
1326                                         break;
1327                                 default:
1328                                         if (s [valuePos] != chars[pos]) {
1329                                                 // FIXME: It is not sure, but
1330                                                 // IsLetter() is introduced 
1331                                                 // because we have to reject 
1332                                                 // "2002a02b25" but have to
1333                                                 // allow "2002$02$25". The same
1334                                                 // thing applies to '/' case.
1335                                                 if (exact ||
1336                                                         Char.IsDigit (s [valuePos]) ||
1337                                                         Char.IsLetter (s [valuePos]))
1338                                                         return false;
1339                                         }
1340                                         num = 0;
1341                                         num_parsed = 1;
1342                                         break;
1343                                 }
1344
1345                                 if (num_parsed < 0)
1346                                         return false;
1347
1348                                 valuePos += num_parsed;
1349
1350                                 if (!exact) {
1351                                         switch (chars [pos]) {
1352                                         case 'm':
1353                                         case 's':
1354                                         case 'f':
1355                                         case 'z':
1356                                                 if (s.Length > valuePos && s [valuePos] == 'Z'
1357                                                     && (pos + 1 == chars.Length
1358                                                     || chars [pos + 1] != 'Z')) {
1359                                                         useutc = true;
1360                                                         valuePos++;
1361                                                 }
1362                                                 break;
1363                                         }
1364                                 }
1365
1366                                 pos = pos + num + 1;
1367                                 num = 0;
1368                         }
1369
1370                         // possible empty value. Regarded as no match.
1371                         if (pos == 0)
1372                                 return false;
1373
1374                         if (pos < len)
1375                                 return false;
1376
1377                         if (s.Length != valuePos) // extraneous tail.
1378                                 return false;
1379
1380                         if (hour == -1)
1381                                 hour = 0;
1382                         if (minute == -1)
1383                                 minute = 0;
1384
1385                         if (second == -1)
1386                                 second = 0;
1387                         if (fractionalSeconds == -1)
1388                                 fractionalSeconds = 0;
1389
1390                         // If no date was given
1391                         if ((day == -1) && (month == -1) && (year == -1)) {
1392                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1393                                         day = 1;
1394                                         month = 1;
1395                                         year = 1;
1396                                 } else {
1397                                         day = Today.Day;
1398                                         month = Today.Month;
1399                                         year = Today.Year;
1400                                 }
1401                         }
1402
1403
1404                         if (day == -1)
1405                                 day = 1;
1406                         if (month == -1)
1407                                 month = 1;
1408                         if (year == -1) {
1409                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1410                                         year = 1;
1411                                 else
1412                                         year = Today.Year;
1413                         }
1414
1415                         if (ampm == 1)
1416                                 hour = hour + 12;
1417                         
1418                         // For anything out of range 
1419                         // return false
1420                         if ( year < 1 || year > 9999 || 
1421                         month < 1 || month >12  ||
1422                         day < 1 || day > DaysInMonth(year, month) ||
1423                         hour < 0 || hour > 23 ||
1424                         minute < 0 || minute > 59 ||
1425                         second < 0 || second > 59 )
1426                                 return false;
1427
1428                         result = new DateTime (year, month, day, hour, minute, second, 0);
1429                         result = result.AddSeconds(fractionalSeconds);
1430
1431                         if ((dayofweek != -1) && (dayofweek != (int) result.DayOfWeek))
1432                                 throw new FormatException (Locale.GetText ("String was not recognized as valid DateTime because the day of week was incorrect."));
1433
1434                         // If no timezone was specified, default to the local timezone.
1435                         TimeSpan utcoffset;
1436
1437                         if (useutc) {
1438                                 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1439                                         use_localtime = false;
1440                                 utcoffset = new TimeSpan (0, 0, 0);
1441                         } else if (tzsign == -1) {
1442                                 TimeZone tz = TimeZone.CurrentTimeZone;
1443                                 utcoffset = tz.GetUtcOffset (result);
1444                         } else {
1445                                 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1446                                         use_localtime = false;
1447
1448                                 if (tzoffmin == -1)
1449                                         tzoffmin = 0;
1450                                 if (tzoffset == -1)
1451                                         tzoffset = 0;
1452                                 if (tzsign == 1)
1453                                         tzoffset = -tzoffset;
1454
1455                                 utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
1456                         }
1457
1458                         long newticks = (result.ticks - utcoffset).Ticks;
1459
1460                         result = new DateTime (false, new TimeSpan (newticks));
1461                         if (use_localtime)
1462                                 result = result.ToLocalTime ();
1463 #if NET_2_0
1464                         else
1465                                 result.kind = DateTimeKind.Utc;
1466 #endif
1467
1468                         return true;
1469                 }
1470
1471
1472                 public static DateTime ParseExact (string s, string format,
1473                                                    IFormatProvider fp, DateTimeStyles style)
1474                 {
1475                         string[] formats;
1476
1477                         formats = new string [1];
1478                         formats[0] = format;
1479
1480                         return ParseExact (s, formats, fp, style);
1481                 }
1482
1483                 public static DateTime ParseExact (string s, string[] formats,
1484                                                    IFormatProvider fp,
1485                                                    DateTimeStyles style)
1486                 {
1487                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1488
1489                         if (s == null)
1490                                 throw new ArgumentNullException (Locale.GetText ("s is null"));
1491                         if (formats == null || formats.Length == 0)
1492                                 throw new ArgumentNullException (Locale.GetText ("format is null"));
1493
1494                         DateTime result;
1495                         bool longYear = false;
1496                         if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear))
1497                                 throw new FormatException ();
1498                         return result;
1499                 }
1500
1501 #if NET_2_0
1502                 public static bool TryParse (string s, out DateTime result)
1503                 {
1504                         try {
1505                                 result = Parse (s);
1506                         } catch {
1507                                 result = MinValue;
1508                                 return false;
1509                         }
1510                         return true;
1511                 }
1512                 
1513                 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1514                 {
1515                         try {
1516                                 result = Parse (s, provider, styles);
1517                         } catch {
1518                                 result = MinValue;
1519                                 return false;
1520                         }
1521                         return true;
1522                 }
1523                 
1524                 public static bool TryParseExact (string s, string format,
1525                                                   IFormatProvider fp,
1526                                                   DateTimeStyles style,
1527                                                   out DateTime result)
1528                 {
1529                         string[] formats;
1530
1531                         formats = new string [1];
1532                         formats[0] = format;
1533
1534                         return TryParseExact (s, formats, fp, style, out result);
1535                 }
1536
1537                 public static bool TryParseExact (string s, string[] formats,
1538                                                   IFormatProvider fp,
1539                                                   DateTimeStyles style,
1540                                                   out DateTime result)
1541                 {
1542                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1543
1544                         bool longYear = false;
1545                         return ParseExact (s, formats, dfi, style, out result, true, ref longYear);
1546                 }
1547 #endif
1548
1549                 private static bool ParseExact (string s, string [] formats,
1550                         DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1551                         bool exact, ref bool longYear)
1552                 {
1553                         int i;
1554                         for (i = 0; i < formats.Length; i++)
1555                         {
1556                                 DateTime result;
1557
1558                                 if (_DoParse (s, formats[i], exact, out result, dfi, style, ref longYear)) {
1559                                         ret = result;
1560                                         return true;
1561                                 }
1562                         }
1563                         ret = DateTime.MinValue;
1564                         return false;
1565                 }
1566                 
1567                 public TimeSpan Subtract(DateTime dt)
1568                 {   
1569                         return new TimeSpan(ticks.Ticks) - dt.ticks;
1570                 }
1571
1572                 public DateTime Subtract(TimeSpan ts)
1573                 {
1574                         TimeSpan newticks;
1575
1576                         newticks = (new TimeSpan (ticks.Ticks)) - ts;
1577                         DateTime ret = new DateTime(true,newticks);
1578 #if NET_2_0
1579                         ret.kind = kind;
1580 #endif
1581                         return ret;
1582                 }
1583
1584                 public long ToFileTime()
1585                 {
1586                         DateTime universalTime = ToUniversalTime();
1587                         
1588                         if (universalTime.Ticks < w32file_epoch) {
1589                                 throw new ArgumentOutOfRangeException("file time is not valid");
1590                         }
1591                         
1592                         return(universalTime.Ticks - w32file_epoch);
1593                 }
1594
1595 #if NET_1_1
1596                 public long ToFileTimeUtc()
1597                 {
1598                         if (Ticks < w32file_epoch) {
1599                                 throw new ArgumentOutOfRangeException("file time is not valid");
1600                         }
1601                         
1602                         return (Ticks - w32file_epoch);
1603                 }
1604 #endif
1605
1606                 public string ToLongDateString()
1607                 {
1608                         return ToString ("D");
1609                 }
1610
1611                 public string ToLongTimeString()
1612                 {
1613                         return ToString ("T");
1614                 }
1615
1616                 public double ToOADate ()
1617                 {
1618                         long t = this.Ticks;
1619                         // uninitialized DateTime case
1620                         if (t == 0)
1621                                 return 0;
1622                         // we can't reach minimum value
1623                         if (t < 31242239136000000)
1624                                 return OAMinValue + 0.001;
1625
1626                         TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1627                         double result = ts.TotalDays;
1628                         // t < 0 (where 599264352000000000 == 0.0d for OA)
1629                         if (t < 599264352000000000) {
1630                                 // negative days (int) but decimals are positive
1631                                 double d = Math.Ceiling (result);
1632                                 result = d - 2 - (result - d);
1633                         }
1634                         else {
1635                                 // we can't reach maximum value
1636                                 if (result >= OAMaxValue)
1637                                         result = OAMaxValue - 0.00000001d;
1638                         }
1639                         return result;
1640                 }
1641
1642                 public string ToShortDateString()
1643                 {
1644                         return ToString ("d");
1645                 }
1646
1647                 public string ToShortTimeString()
1648                 {
1649                         return ToString ("t");
1650                 }
1651                 
1652                 public override string ToString ()
1653                 {
1654                         return ToString ("G", null);
1655                 }
1656
1657                 public string ToString (IFormatProvider fp)
1658                 {
1659                         return ToString (null, fp);
1660                 }
1661
1662                 public string ToString (string format)
1663                 {
1664                         return ToString (format, null);
1665                 }
1666
1667                 internal static string _GetStandardPattern (char format, DateTimeFormatInfo dfi, out bool useutc, out bool use_invariant)
1668                 {
1669                         String pattern;
1670
1671                         useutc = false;
1672                         use_invariant = false;
1673
1674                         switch (format)
1675                         {
1676                         case 'd':
1677                                 pattern = dfi.ShortDatePattern;
1678                                 break;
1679                         case 'D':
1680                                 pattern = dfi.LongDatePattern;
1681                                 break;
1682                         case 'f':
1683                                 pattern = dfi.LongDatePattern + " " + dfi.ShortTimePattern;
1684                                 break;
1685                         case 'F':
1686                                 pattern = dfi.FullDateTimePattern;
1687                                 break;
1688                         case 'g':
1689                                 pattern = dfi.ShortDatePattern + " " + dfi.ShortTimePattern;
1690                                 break;
1691                         case 'G':
1692                                 pattern = dfi.ShortDatePattern + " " + dfi.LongTimePattern;
1693                                 break;
1694                         case 'm':
1695                         case 'M':
1696                                 pattern = dfi.MonthDayPattern;
1697                                 break;
1698                         case 'r':
1699                         case 'R':
1700                                 pattern = dfi.RFC1123Pattern;
1701                                 // commented by LP 09/jun/2002, rfc 1123 pattern is always in GMT
1702                                 // uncommented by AE 27/may/2004
1703 //                              useutc = true;
1704                                 use_invariant = true;
1705                                 break;
1706                         case 's':
1707                                 pattern = dfi.SortableDateTimePattern;
1708                                 break;
1709                         case 't':
1710                                 pattern = dfi.ShortTimePattern;
1711                                 break;
1712                         case 'T':
1713                                 pattern = dfi.LongTimePattern;
1714                                 break;
1715                         case 'u':
1716                                 pattern = dfi.UniversalSortableDateTimePattern;
1717                                 useutc = true;
1718                                 break;
1719                         case 'U':
1720 //                              pattern = dfi.LongDatePattern + " " + dfi.LongTimePattern;
1721                                 pattern = dfi.FullDateTimePattern;
1722                                 useutc = true;
1723                                 break;
1724                         case 'y':
1725                         case 'Y':
1726                                 pattern = dfi.YearMonthPattern;
1727                                 break;
1728                         default:
1729                                 pattern = null;
1730                                 break;
1731                         }
1732
1733                         return pattern;
1734                 }
1735
1736                 internal string _ToString (string format, DateTimeFormatInfo dfi)
1737                 {
1738                         // the length of the format is usually a good guess of the number
1739                         // of chars in the result. Might save us a few bytes sometimes
1740                         // Add + 10 for cases like mmmm dddd
1741                         StringBuilder result = new StringBuilder (format.Length + 10);
1742
1743                         // For some cases, the output should not use culture dependent calendar
1744                         DateTimeFormatInfo inv = DateTimeFormatInfo.InvariantInfo;
1745                         if (format == inv.RFC1123Pattern)
1746                                 dfi = inv;
1747                         else if (format == inv.UniversalSortableDateTimePattern)
1748                                 dfi = inv;
1749
1750                         int i = 0;
1751
1752                         while (i < format.Length) {
1753                                 int tokLen;
1754                                 char ch = format [i];
1755
1756                                 switch (ch) {
1757
1758                                 //
1759                                 // Time Formats
1760                                 //
1761                                 case 'h':
1762                                         // hour, [1, 12]
1763                                         tokLen = CountRepeat (format, i, ch);
1764
1765                                         int hr = this.Hour % 12;
1766                                         if (hr == 0)
1767                                                 hr = 12;
1768
1769                                         ZeroPad (result, hr, tokLen == 1 ? 1 : 2);
1770                                         break;
1771                                 case 'H':
1772                                         // hour, [0, 23]
1773                                         tokLen = CountRepeat (format, i, ch);
1774                                         ZeroPad (result, this.Hour, tokLen == 1 ? 1 : 2);
1775                                         break;
1776                                 case 'm':
1777                                         // minute, [0, 59]
1778                                         tokLen = CountRepeat (format, i, ch);
1779                                         ZeroPad (result, this.Minute, tokLen == 1 ? 1 : 2);
1780                                         break;
1781                                 case 's':
1782                                         // second [0, 29]
1783                                         tokLen = CountRepeat (format, i, ch);
1784                                         ZeroPad (result, this.Second, tokLen == 1 ? 1 : 2);
1785                                         break;
1786                                 case 'f':
1787                                         // fraction of second, to same number of
1788                                         // digits as there are f's
1789
1790                                         tokLen = CountRepeat (format, i, ch);
1791                                         if (tokLen > 7)
1792                                                 throw new FormatException ("Invalid Format String");
1793
1794                                         int dec = (int)((long)(this.Ticks % TimeSpan.TicksPerSecond) / (long) Math.Pow (10, 7 - tokLen));
1795                                         ZeroPad (result, dec, tokLen);
1796
1797                                         break;
1798                                 case 't':
1799                                         // AM/PM. t == first char, tt+ == full
1800                                         tokLen = CountRepeat (format, i, ch);
1801                                         string desig = this.Hour < 12 ? dfi.AMDesignator : dfi.PMDesignator;
1802
1803                                         if (tokLen == 1) {
1804                                                 if (desig.Length >= 1)
1805                                                         result.Append (desig [0]);
1806                                         }
1807                                         else
1808                                                 result.Append (desig);
1809
1810                                         break;
1811                                 case 'z':
1812                                         // timezone. t = +/-h; tt = +/-hh; ttt+=+/-hh:mm
1813                                         tokLen = CountRepeat (format, i, ch);
1814                                         TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
1815
1816                                         if (offset.Ticks >= 0)
1817                                                 result.Append ('+');
1818                                         else
1819                                                 result.Append ('-');
1820
1821                                         switch (tokLen) {
1822                                         case 1:
1823                                                 result.Append (Math.Abs (offset.Hours));
1824                                                 break;
1825                                         case 2:
1826                                                 result.Append (Math.Abs (offset.Hours).ToString ("00"));
1827                                                 break;
1828                                         default:
1829                                                 result.Append (Math.Abs (offset.Hours).ToString ("00"));
1830                                                 result.Append (':');
1831                                                 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
1832                                                 break;
1833                                         }
1834                                         break;
1835                                 //
1836                                 // Date tokens
1837                                 //
1838                                 case 'd':
1839                                         // day. d(d?) = day of month (leading 0 if two d's)
1840                                         // ddd = three leter day of week
1841                                         // dddd+ full day-of-week
1842                                         tokLen = CountRepeat (format, i, ch);
1843
1844                                         if (tokLen <= 2)
1845                                                 ZeroPad (result, dfi.Calendar.GetDayOfMonth (this), tokLen == 1 ? 1 : 2);
1846                                         else if (tokLen == 3)
1847                                                 result.Append (dfi.GetAbbreviatedDayName (dfi.Calendar.GetDayOfWeek (this)));
1848                                         else
1849                                                 result.Append (dfi.GetDayName (dfi.Calendar.GetDayOfWeek (this)));
1850
1851                                         break;
1852                                 case 'M':
1853                                         // Month.m(m?) = month # (with leading 0 if two mm)
1854                                         // mmm = 3 letter name
1855                                         // mmmm+ = full name
1856                                         tokLen = CountRepeat (format, i, ch);
1857                                         int month = dfi.Calendar.GetMonth(this);
1858                                         if (tokLen <= 2)
1859                                                 ZeroPad (result, month, tokLen);
1860                                         else if (tokLen == 3)
1861                                                 result.Append (dfi.GetAbbreviatedMonthName (month));
1862                                         else
1863                                                 result.Append (dfi.GetMonthName (month));
1864
1865                                         break;
1866                                 case 'y':
1867                                         // Year. y(y?) = two digit year, with leading 0 if yy
1868                                         // yyy+ full year, if yyy and yr < 1000, displayed as three digits
1869                                         tokLen = CountRepeat (format, i, ch);
1870
1871                                         if (tokLen <= 2)
1872                                                 ZeroPad (result, dfi.Calendar.GetYear (this) % 100, tokLen);
1873                                         else
1874                                                 ZeroPad (result, dfi.Calendar.GetYear (this), (tokLen == 3 ? 3 : 4));
1875
1876                                         break;
1877                                 case 'g':
1878                                         // Era name
1879                                         tokLen = CountRepeat (format, i, ch);
1880                                         result.Append (dfi.GetEraName (dfi.Calendar.GetEra (this)));
1881                                         break;
1882
1883                                 //
1884                                 // Other
1885                                 //
1886                                 case ':':
1887                                         result.Append (dfi.TimeSeparator);
1888                                         tokLen = 1;
1889                                         break;
1890                                 case '/':
1891                                         result.Append (dfi.DateSeparator);
1892                                         tokLen = 1;
1893                                         break;
1894                                 case '\'': case '"':
1895                                         tokLen = ParseQuotedString (format, i, result);
1896                                         break;
1897                                 case '%':
1898                                         if (i >= format.Length - 1)
1899                                                 throw new FormatException ("% at end of date time string");
1900                                         if (format [i + 1] == '%')
1901                                                 throw new FormatException ("%% in date string");
1902
1903                                         // Look for the next char
1904                                         tokLen = 1;
1905                                         break;
1906                                 case '\\':
1907                                         // C-Style escape
1908                                         if (i >= format.Length - 1)
1909                                                 throw new FormatException ("\\ at end of date time string");
1910
1911                                         result.Append (format [i + 1]);
1912                                         tokLen = 2;
1913
1914                                         break;
1915                                 default:
1916                                         // catch all
1917                                         result.Append (ch);
1918                                         tokLen = 1;
1919                                         break;
1920                                 }
1921                                 i += tokLen;
1922                         }
1923                         return result.ToString ();
1924                 }
1925                 
1926                 static int CountRepeat (string fmt, int p, char c)
1927                 {
1928                         int l = fmt.Length;
1929                         int i = p + 1;
1930                         while ((i < l) && (fmt [i] == c)) 
1931                                 i++;
1932                         
1933                         return i - p;
1934                 }
1935                 
1936                 static int ParseQuotedString (string fmt, int pos, StringBuilder output)
1937                 {
1938                         // pos == position of " or '
1939                         
1940                         int len = fmt.Length;
1941                         int start = pos;
1942                         char quoteChar = fmt [pos++];
1943                         
1944                         while (pos < len) {
1945                                 char ch = fmt [pos++];
1946                                 
1947                                 if (ch == quoteChar)
1948                                         return pos - start;
1949                                 
1950                                 if (ch == '\\') {
1951                                         // C-Style escape
1952                                         if (pos >= len)
1953                                                 throw new FormatException("Un-ended quote");
1954         
1955                                         output.Append (fmt [pos++]);
1956                                 } else {
1957                                         output.Append (ch);
1958                                 }
1959                         }
1960
1961                         throw new FormatException("Un-ended quote");
1962                 }
1963                 
1964                 static unsafe void ZeroPad (StringBuilder output, int digits, int len)
1965                 {
1966                         // more than enough for an int
1967                         char* buffer = stackalloc char [16];
1968                         int pos = 16;
1969                         
1970                         do {
1971                                 buffer [-- pos] = (char) ('0' + digits % 10);
1972                                 digits /= 10;
1973                                 len --;
1974                         } while (digits > 0);
1975                         
1976                         while (len -- > 0)
1977                                 buffer [-- pos] = '0';
1978                         
1979                         output.Append (new string (buffer, pos, 16 - pos));
1980                 }
1981
1982                 public string ToString (string format, IFormatProvider fp)
1983
1984                 {
1985                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
1986
1987                         if (format == null)
1988                                 format = "G";
1989
1990                         bool useutc = false, use_invariant = false;
1991
1992                         if (format.Length == 1) {
1993                                 char fchar = format [0];
1994                                 format = _GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
1995                         }
1996
1997                         // Don't convert UTC value. It just adds 'Z' for 
1998                         // 'u' format, for the same ticks.
1999                         return this._ToString (format, dfi);
2000                 }
2001
2002                 public DateTime ToLocalTime ()
2003                 {
2004                         return TimeZone.CurrentTimeZone.ToLocalTime (this);
2005                 }
2006
2007                 public DateTime ToUniversalTime()
2008                 {
2009                         return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2010                 }
2011
2012                 /*  OPERATORS */
2013
2014                 public static DateTime operator +(DateTime d, TimeSpan t)
2015                 {
2016                         DateTime ret = new DateTime (true, d.ticks + t);
2017 #if NET_2_0
2018                         ret.kind = d.kind;
2019 #endif
2020                         return ret;
2021                 }
2022
2023                 public static bool operator ==(DateTime d1, DateTime d2)
2024                 {
2025                         return (d1.ticks == d2.ticks);
2026                 }
2027
2028                 public static bool operator >(DateTime t1,DateTime t2)
2029                 {
2030                         return (t1.ticks > t2.ticks);
2031                 }
2032
2033                 public static bool operator >=(DateTime t1,DateTime t2)
2034                 {
2035                         return (t1.ticks >= t2.ticks);
2036                 }
2037
2038                 public static bool operator !=(DateTime d1, DateTime d2)
2039                 {
2040                         return (d1.ticks != d2.ticks);
2041                 }
2042
2043                 public static bool operator <(DateTime t1,      DateTime t2)
2044                 {
2045                         return (t1.ticks < t2.ticks );
2046                 }
2047
2048                 public static bool operator <=(DateTime t1,DateTime t2)
2049                 {
2050                         return (t1.ticks <= t2.ticks);
2051                 }
2052
2053                 public static TimeSpan operator -(DateTime d1,DateTime d2)
2054                 {
2055                         return new TimeSpan((d1.ticks - d2.ticks).Ticks);
2056                 }
2057
2058                 public static DateTime operator -(DateTime d,TimeSpan t)
2059                 {
2060                         DateTime ret = new DateTime (true, d.ticks - t);
2061 #if NET_2_0
2062                         ret.kind = d.kind;
2063 #endif
2064                         return ret;
2065                 }
2066
2067                 bool IConvertible.ToBoolean(IFormatProvider provider)
2068                 {
2069                         throw new InvalidCastException();
2070                 }
2071                 
2072                 byte IConvertible.ToByte(IFormatProvider provider)
2073                 {
2074                         throw new InvalidCastException();
2075
2076                 }
2077
2078                 char IConvertible.ToChar(IFormatProvider provider)
2079                 {
2080                         throw new InvalidCastException();
2081                 }
2082
2083                 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
2084                 {
2085                         return this;
2086                 } 
2087                 
2088                 decimal IConvertible.ToDecimal(IFormatProvider provider)
2089                 {
2090                          throw new InvalidCastException();
2091                 }
2092
2093                 double IConvertible.ToDouble(IFormatProvider provider)
2094                 {
2095                         throw new InvalidCastException();
2096                 }
2097
2098                 Int16 IConvertible.ToInt16(IFormatProvider provider)
2099                 {
2100                         throw new InvalidCastException();
2101                 }
2102
2103                 Int32 IConvertible.ToInt32(IFormatProvider provider)
2104                 {
2105                         throw new InvalidCastException();
2106                 }
2107
2108                 Int64 IConvertible.ToInt64(IFormatProvider provider)
2109                 {
2110                         throw new InvalidCastException();
2111                 }
2112
2113                 SByte IConvertible.ToSByte(IFormatProvider provider)
2114                 {
2115                         throw new InvalidCastException();
2116                 }
2117
2118                 Single IConvertible.ToSingle(IFormatProvider provider)
2119                 {
2120                         throw new InvalidCastException();
2121                 }
2122
2123                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2124                 {
2125                         if (conversionType == null)
2126                                 throw new ArgumentNullException ("conversionType");
2127
2128                         if (conversionType == typeof (DateTime))
2129                                 return this;
2130                         else if (conversionType == typeof (String))
2131                                 return this.ToString (provider);
2132                         else if (conversionType == typeof (Object))
2133                                 return this;
2134                         else
2135                                 throw new InvalidCastException();
2136                 }
2137
2138                 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
2139                 {
2140                         throw new InvalidCastException();
2141                 }
2142                 
2143                 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
2144                 {
2145                         throw new InvalidCastException();
2146                 }
2147
2148                 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
2149                 {
2150                         throw new InvalidCastException();
2151                 }
2152         }
2153 }