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