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