2009-06-12 Bill Holmes <billholmes54@gmail.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 GetTimeMonotonic ();
461
462                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
463                 internal static extern long GetNow ();
464
465                 //
466                 // To reduce the time consumed by DateTime.Now, we keep
467                 // the difference to map the system time into a local
468                 // time into `to_local_time_span', we record the timestamp
469                 // for this in `last_now'
470                 //
471                 static object to_local_time_span_object;
472                 static long last_now;
473                 
474                 public static DateTime Now 
475                 {
476                         get     
477                         {
478                                 long now = GetNow ();
479                                 DateTime dt = new DateTime (now);
480
481                                 if ((now - last_now) > TimeSpan.TicksPerMinute){
482                                         to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
483                                         last_now = now;
484
485                                 }
486
487                                 // This is boxed, so we avoid locking.
488                                 DateTime ret = dt + (TimeSpan) to_local_time_span_object;
489 #if NET_2_0
490                                 ret.kind = DateTimeKind.Local;
491 #endif
492                                 return ret;
493                         }
494                 }
495
496                 public long Ticks
497                 { 
498                         get     
499                         { 
500                                 return ticks.Ticks;
501                         }
502                 }
503         
504                 public static DateTime Today 
505                 {
506                         get {
507                                 DateTime now = Now;
508                                 DateTime today = new DateTime (now.Year, now.Month, now.Day);
509 #if NET_2_0
510                                 today.kind = now.kind;
511 #endif
512                                 return today;
513                         }
514                 }
515
516                 public static DateTime UtcNow 
517                 {
518                         get {
519 #if NET_2_0
520                                 return new DateTime (GetNow (), DateTimeKind.Utc);
521 #else
522                                 return new DateTime (GetNow ());
523 #endif
524                         }
525                 }
526
527                 public int Year 
528                 {
529                         get 
530                         { 
531                                 return FromTicks(Which.Year); 
532                         }
533                 }
534
535 #if NET_2_0
536                 public DateTimeKind Kind {
537                         get {
538                                 return kind;
539                         }
540                 }
541 #endif
542
543                 /* methods */
544
545                 public DateTime Add (TimeSpan value)
546                 {
547                         DateTime ret = AddTicks (value.Ticks);
548 #if NET_2_0
549                         ret.kind = kind;
550 #endif
551                         return ret;
552                 }
553
554                 public DateTime AddDays (double value)
555                 {
556                         return AddMilliseconds (Math.Round (value * 86400000));
557                 }
558                 
559                 public DateTime AddTicks (long value)
560                 {
561                         if ((value + ticks.Ticks) > MAX_VALUE_TICKS || (value + ticks.Ticks) < 0) {
562                                 throw new ArgumentOutOfRangeException();
563                         }
564                         DateTime ret = new DateTime (value + ticks.Ticks);
565 #if NET_2_0
566                         ret.kind = kind;
567 #endif
568                         return ret;
569                 }
570
571                 public DateTime AddHours (double value)
572                 {
573                         return AddMilliseconds (value * 3600000);
574                 }
575
576                 public DateTime AddMilliseconds (double value)
577                 {
578                         if ((value * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
579                                         (value * TimeSpan.TicksPerMillisecond) < long.MinValue) {
580                                 throw new ArgumentOutOfRangeException();
581                         }
582                         long msticks = (long) (value * TimeSpan.TicksPerMillisecond);
583
584                         return AddTicks (msticks);
585                 }
586
587                 // required to match MS implementation for OADate (OLE Automation)
588                 private DateTime AddRoundedMilliseconds (double ms)
589                 {
590                         if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
591                                 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
592                                 throw new ArgumentOutOfRangeException ();
593                         }
594                         long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
595
596                         return AddTicks (msticks);
597                 }
598
599                 public DateTime AddMinutes (double value)
600                 {
601                         return AddMilliseconds (value * 60000);
602                 }
603                 
604                 public DateTime AddMonths (int months)
605                 {
606                         int day, month, year,  maxday ;
607                         DateTime temp ;
608
609                         day = this.Day;
610                         month = this.Month + (months % 12);
611                         year = this.Year + months/12 ;
612                         
613                         if (month < 1)
614                         {
615                                 month = 12 + month ;
616                                 year -- ;
617                         }
618                         else if (month>12) 
619                         {
620                                 month = month -12;
621                                 year ++;
622                         }
623                         maxday = DaysInMonth(year, month);
624                         if (day > maxday)
625                                 day = maxday;
626
627                         temp = new DateTime (year, month, day);
628 #if NET_2_0
629                         temp.kind = kind;
630 #endif
631                         return  temp.Add (this.TimeOfDay);
632                 }
633
634                 public DateTime AddSeconds (double value)
635                 {
636                         return AddMilliseconds (value * 1000);
637                 }
638
639                 public DateTime AddYears (int value)
640                 {
641                         return AddMonths (value * 12);
642                 }
643
644                 public static int Compare (DateTime t1, DateTime t2)
645                 {
646                         if (t1.ticks < t2.ticks) 
647                                 return -1;
648                         else if (t1.ticks > t2.ticks) 
649                                 return 1;
650                         else
651                                 return 0;
652                 }
653
654                 public int CompareTo (object value)
655                 {
656                         if (value == null)
657                                 return 1;
658
659                         if (!(value is System.DateTime))
660                                 throw new ArgumentException (Locale.GetText (
661                                         "Value is not a System.DateTime"));
662
663                         return Compare (this, (DateTime) value);
664                 }
665
666 #if NET_2_0
667                 public bool IsDaylightSavingTime ()
668                 {
669                         if (kind == DateTimeKind.Utc)
670                                 return false;
671                         return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
672                 }
673
674                 public int CompareTo (DateTime value)
675                 {
676                         return Compare (this, value);
677                 }
678
679                 public bool Equals (DateTime value)
680                 {
681                         return value.ticks == ticks;
682                 }
683
684                 public long ToBinary ()
685                 {
686                         switch (kind) {
687                         case DateTimeKind.Utc:
688                                 return Ticks | 0x4000000000000000;
689                         case DateTimeKind.Local:
690                                 return (long) ((ulong) ToUniversalTime ().Ticks | 0x8000000000000000);
691                         default:
692                                 return Ticks;
693                         }
694                 }
695
696                 public static DateTime FromBinary (long dateData)
697                 {
698                         switch ((ulong)dateData >> 62) {
699                         case 1:
700                                 return new DateTime (dateData ^ 0x4000000000000000, DateTimeKind.Utc);
701                         case 0:
702                                 return new DateTime (dateData, DateTimeKind.Unspecified);
703                         default:
704                                 return new DateTime (dateData & 0x3fffffffffffffff, DateTimeKind.Utc).ToLocalTime ();
705                         }
706                 }
707
708                 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
709                 {
710                         return new DateTime (value.Ticks, kind);
711                 }
712
713 #else
714
715                 internal long ToBinary ()
716                 {
717                         return Ticks;
718                 }
719
720                 internal static DateTime FromBinary (long dateData)
721                 {
722                         return new DateTime (dateData & 0x3fffffffffffffff);
723                 }
724 #endif
725
726                 public static int DaysInMonth (int year, int month)
727                 {
728                         int[] days ;
729
730                         if (month < 1 || month >12)
731                                 throw new ArgumentOutOfRangeException ();
732
733                         if (year < 1 || year > 9999)
734                                 throw new ArgumentOutOfRangeException ();
735
736                         days = (IsLeapYear(year) ? daysmonthleap  : daysmonth);
737                         return days[month];                     
738                 }
739                 
740                 public override bool Equals (object value)
741                 {
742                         if (!(value is System.DateTime))
743                                 return false;
744
745                         return ((DateTime) value).ticks == ticks;
746                 }
747
748                 public static bool Equals (DateTime t1, DateTime t2 )
749                 {
750                         return (t1.ticks == t2.ticks );
751                 }
752
753                 public static DateTime FromFileTime (long fileTime) 
754                 {
755                         if (fileTime < 0)
756                                 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
757
758                         return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
759                 }
760
761 #if NET_1_1
762                 public static DateTime FromFileTimeUtc (long fileTime) 
763                 {
764                         if (fileTime < 0)
765                                 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
766
767                         return new DateTime (w32file_epoch + fileTime);
768                 }
769 #endif
770
771                 public static DateTime FromOADate (double d)
772                 {
773                         // An OLE Automation date is implemented as a floating-point number
774                         // whose value is the number of days from midnight, 30 December 1899.
775
776                         // d must be negative 657435.0 through positive 2958466.0.
777                         if ((d <= OAMinValue) || (d >= OAMaxValue))
778                                 throw new ArgumentException ("d", "[-657435,2958466]");
779
780                         DateTime dt = new DateTime (ticks18991230);
781                         if (d < 0.0d) {
782                                 Double days = Math.Ceiling (d);
783                                 // integer part is the number of days (negative)
784                                 dt = dt.AddRoundedMilliseconds (days * 86400000);
785                                 // but decimals are the number of hours (in days fractions) and positive
786                                 Double hours = (days - d);
787                                 dt = dt.AddRoundedMilliseconds (hours * 86400000);
788                         }
789                         else {
790                                 dt = dt.AddRoundedMilliseconds (d * 86400000);
791                         }
792
793                         return dt;
794                 }
795
796                 public string[] GetDateTimeFormats() 
797                 {
798                         return GetDateTimeFormats (CultureInfo.CurrentCulture);
799                 }
800
801                 public string[] GetDateTimeFormats(char format)
802                 {
803                         if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
804                                 throw new FormatException ("Invalid format character.");
805                         string[] result = new string[1];
806                         result[0] = this.ToString(format.ToString());
807                         return result;
808                 }
809                 
810                 public string[] GetDateTimeFormats(IFormatProvider provider)
811                 {
812                         DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
813 //                      return GetDateTimeFormats (info.GetAllDateTimePatterns ());
814                         ArrayList al = new ArrayList ();
815                         foreach (char c in "dDgGfFmMrRstTuUyY")
816                                 al.AddRange (GetDateTimeFormats (c, info));
817                         return al.ToArray (typeof (string)) as string [];
818                 }
819
820                 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
821                 {
822                         if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
823                                 throw new FormatException ("Invalid format character.");
824
825                         // LAMESPEC: There is NO assurance that 'U' ALWAYS
826                         // euqals to 'F', but since we have to iterate all
827                         // the pattern strings, we cannot just use 
828                         // ToString("U", provider) here. I believe that the 
829                         // method's behavior cannot be formalized.
830                         bool adjustutc = false;
831                         switch (format) {
832                         case 'U':
833 //                      case 'r':
834 //                      case 'R':
835 //                      case 'u':
836                                 adjustutc = true;
837                                 break;
838                         }
839                         DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
840                         return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
841                 }
842
843                 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
844                 {
845                         string [] results = new string [patterns.Length];
846                         DateTime val = adjustutc ? ToUniversalTime () : this;
847                         for (int i = 0; i < results.Length; i++)
848                                 results [i] = DateTimeUtils.ToString (val, patterns [i], dfi);
849                         return results;
850                 }
851
852 #if NET_2_0
853                 private void CheckDateTimeKind (DateTimeKind kind) {
854                         if ((kind != DateTimeKind.Unspecified) && (kind != DateTimeKind.Utc) && (kind != DateTimeKind.Local))
855                                 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
856                 }
857 #endif
858
859                 public override int GetHashCode ()
860                 {
861                         return (int) ticks.Ticks;
862                 }
863
864                 public TypeCode GetTypeCode ()
865                 {
866                         return TypeCode.DateTime;
867                 }
868
869                 public static bool IsLeapYear (int year)
870                 {
871                         if (year < 1 || year > 9999)
872                                 throw new ArgumentOutOfRangeException ();
873                         return  ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
874                 }
875
876                 public static DateTime Parse (string s)
877                 {
878                         return Parse (s, null);
879                 }
880
881                 public static DateTime Parse (string s, IFormatProvider provider)
882                 {
883                         return Parse (s, provider, DateTimeStyles.AllowWhiteSpaces);
884                 }
885
886                 public static DateTime Parse (string s, IFormatProvider provider, DateTimeStyles styles)
887                 {
888                         if (s == null)
889                                 throw new ArgumentNullException ("s");
890
891                         DateTime res;
892                         Exception exception = null;
893                         if (!CoreParse (s, provider, styles, out res, true, ref exception))
894                                 throw exception;
895                         
896                         return res;
897                 }
898
899                 const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
900                 
901                 internal static bool CoreParse (string s, IFormatProvider provider, DateTimeStyles styles,
902                                               out DateTime result, bool setExceptionOnError, ref Exception exception)
903                 {
904                         if (s == null || s.Length == 0) {
905                                 if (setExceptionOnError)
906                                         exception = new FormatException (formatExceptionMessage);
907                                 result = MinValue;
908                                 return false;
909                         }
910
911                         if (provider == null)
912                                 provider = CultureInfo.CurrentCulture;
913                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
914
915                         // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
916                         string[] allDateFormats = YearMonthDayFormats (dfi, setExceptionOnError, ref exception);
917                         if (allDateFormats == null){
918                                 result = MinValue;
919                                 return false;
920                         }
921                                         
922                         bool longYear = false;
923                         for (int i = 0; i < allDateFormats.Length; i++) {
924                                 string firstPart = allDateFormats [i];
925                                 bool incompleteFormat = false;
926                                 if (_DoParse (s, firstPart, "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
927                                         return true;
928
929                                 if (!incompleteFormat)
930                                         continue;
931
932                                 for (int j = 0; j < ParseTimeFormats.Length; j++) {
933                                         if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
934                                                 return true;
935                                 }
936                         }
937
938                         //
939                         // Month day formats
940                         //
941                         int dayIndex = dfi.MonthDayPattern.IndexOf('d');
942                         int monthIndex = dfi.MonthDayPattern.IndexOf('M');
943                         if (dayIndex == -1 || monthIndex == -1){
944                                 result = MinValue;
945                                 if (setExceptionOnError)
946                                         exception = new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
947                                 return false;
948                         }
949                         bool is_day_before_month = dayIndex < monthIndex;
950                         string[] monthDayFormats = is_day_before_month ? DayMonthShortFormats : MonthDayShortFormats;
951                         for (int i = 0; i < monthDayFormats.Length; i++) {
952                                 bool incompleteFormat = false;
953                                 if (_DoParse (s, monthDayFormats[i], "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
954                                         return true;
955                         }
956                         
957                         for (int j = 0; j < ParseTimeFormats.Length; j++) {
958                                 string firstPart = ParseTimeFormats [j];
959                                 bool incompleteFormat = false;
960                                 if (_DoParse (s, firstPart, "", false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
961                                         return true;
962                                 if (!incompleteFormat)
963                                         continue;
964
965                                 for (int i = 0; i < monthDayFormats.Length; i++) {
966                                         if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
967                                                 return true;
968                                 }
969                                 for (int i = 0; i < allDateFormats.Length; i++) {
970                                         string dateFormat = allDateFormats [i];
971                                         if (dateFormat[dateFormat.Length - 1] == 'T')
972                                                 continue; // T formats must be before the time part
973                                         if (_DoParse (s, firstPart, dateFormat, false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
974                                                 return true;
975                                 }
976                         }
977
978                         // Try as a last resort all the patterns
979                         if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear, setExceptionOnError, ref exception))
980                                 return true;
981
982                         if (!setExceptionOnError)
983                                 return false;
984                         
985 #if NET_2_0
986                         // .NET 2.x does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
987                         exception = new FormatException (formatExceptionMessage);
988 #else
989                         if (longYear)
990                                 exception = new ArgumentOutOfRangeException ("year", "Valid values are between 1 and 9999, inclusive.");
991                         else 
992                                 exception = new FormatException (formatExceptionMessage);
993 #endif
994                         return false;
995                 }
996
997                 public static DateTime ParseExact (string s, string format, IFormatProvider provider)
998                 {
999                         return ParseExact (s, format, provider, DateTimeStyles.None);
1000                 }
1001
1002                 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi, bool setExceptionOnError, ref Exception exc)
1003                 {
1004                         int dayIndex = dfi.ShortDatePattern.IndexOf('d');
1005                         int monthIndex = dfi.ShortDatePattern.IndexOf('M');
1006                         int yearIndex = dfi.ShortDatePattern.IndexOf('y');
1007                         if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1){
1008                                 if (setExceptionOnError)
1009                                         exc = new FormatException (Locale.GetText("Order of year, month and date is not defined by {0}", dfi.ShortDatePattern));
1010                                 return null;
1011                         }
1012
1013                         if (yearIndex < monthIndex)
1014                                 if (monthIndex < dayIndex)
1015                                         return ParseYearMonthDayFormats;
1016                                 else if (yearIndex < dayIndex)
1017                                         return ParseYearDayMonthFormats;
1018                                 else {
1019                                         // The year cannot be between the date and the month
1020                                         if (setExceptionOnError)
1021                                                 exc = new FormatException (Locale.GetText("Order of date, year and month defined by {0} is not supported", dfi.ShortDatePattern));
1022                                         return null;
1023                                 }
1024                         else if (dayIndex < monthIndex)
1025                                 return ParseDayMonthYearFormats;
1026                         else if (dayIndex < yearIndex)
1027                                 return ParseMonthDayYearFormats;
1028                         else {
1029                                 // The year cannot be between the month and the date
1030                                 if (setExceptionOnError)
1031                                         exc = new FormatException (Locale.GetText("Order of month, year and date defined by {0} is not supported", dfi.ShortDatePattern));
1032                                 return null;
1033                         }
1034                 }
1035
1036                 private static int _ParseNumber (string s, int valuePos,
1037                                                  int min_digits,
1038                                                  int digits,
1039                                                  bool leadingzero,
1040                                                  bool sloppy_parsing,
1041                                                  out int num_parsed)
1042                 {
1043                         int number = 0, i;
1044
1045                         if (sloppy_parsing)
1046                                 leadingzero = false;
1047
1048                         if (!leadingzero) {
1049                                 int real_digits = 0;
1050                                 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
1051                                         if (!Char.IsDigit (s[i]))
1052                                                 break;
1053
1054                                         real_digits++;
1055                                 }
1056
1057                                 digits = real_digits;
1058                         }
1059                         if (digits < min_digits) {
1060                                 num_parsed = -1;
1061                                 return 0;
1062                         }
1063
1064                         if (s.Length - valuePos < digits) {
1065                                 num_parsed = -1;
1066                                 return 0;
1067                         }
1068
1069                         for (i = valuePos; i < digits + valuePos; i++) {
1070                                 char c = s[i];
1071                                 if (!Char.IsDigit (c)) {
1072                                         num_parsed = -1;
1073                                         return 0;
1074                                 }
1075
1076                                 number = number * 10 + (byte) (c - '0');
1077                         }
1078
1079                         num_parsed = digits;
1080                         return number;
1081                 }
1082
1083                 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1084                 {
1085                         // FIXME: I know this is somehow lame code. Probably
1086                         // it should iterate all the enum value and return
1087                         // the longest match. However right now I don't see
1088                         // anything but "1" and "10" - "12" that might match
1089                         // two or more values. (They are only abbrev month
1090                         // names, so do reverse order search). See bug #80094.
1091                         for (int i = values.Length - 1; i >= 0; i--) {
1092                                 if (!exact && invValues [i].Length > values[i].Length) {
1093                                         if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1094                                                 return i;
1095                                         if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1096                                                 return i;
1097                                 }
1098                                 else {
1099                                         if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1100                                                 return i;
1101                                         if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1102                                         return i;
1103                                 }
1104                         }
1105
1106                         num_parsed = -1;
1107                         return -1;
1108                 }
1109
1110                 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1111                 {
1112                         if (maxlength <= 0)
1113                                 maxlength = value.Length;
1114
1115                         if (sPos + maxlength <= s.Length && String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
1116                                 num_parsed = maxlength;
1117                                 return true;
1118                         }
1119
1120                         num_parsed = -1;
1121                         return false;
1122                 }
1123
1124                 // Note that in case of Parse (exact == false) we check both for AM/PM
1125                 // and the culture spcific AM/PM strings.
1126                 private static bool _ParseAmPm(string s,
1127                                                int valuePos,
1128                                                int num,
1129                                                DateTimeFormatInfo dfi,
1130                                                bool exact,
1131                                                out int num_parsed,
1132                                                ref int ampm)
1133                 {
1134                         num_parsed = -1;
1135                         if (ampm != -1)
1136                                 return false;
1137
1138                         if (!IsLetter (s, valuePos)) {
1139                                 if (dfi.AMDesignator != "")
1140                                         return false;
1141                                 if (exact)
1142                                         ampm = 0;
1143                                 num_parsed = 0;
1144                                 return true;
1145                         }
1146                         DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1147                         if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1148                             dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1149                                 ampm = 1;
1150                         else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1151                                  _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1152                                 if (exact || num_parsed != 0)
1153                                         ampm = 0;
1154                         }
1155                         else
1156                                 return false;
1157                         return true;
1158                 }
1159
1160                 // Note that in case of Parse (exact == false) we check both for ':'
1161                 // and the culture spcific TimeSperator
1162                 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1163                 {
1164                         return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1165                                !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1166                 }
1167
1168                 // Accept any character for DateSeparator, except TimeSeparator,
1169                 // a digit or a letter.
1170                 // Not documented, but seems to be MS behaviour here.  See bug 54047.
1171                 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1172                 {
1173                         num_parsed = -1;
1174                         if (exact && s [sPos] != '/')
1175                                 return false;
1176
1177                         if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1178                                 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1179                                 return(false);
1180
1181                         num_parsed = 1;
1182                         return true;
1183                 }
1184
1185                 private static bool IsLetter (string s, int pos)
1186                 {
1187                         return pos < s.Length && Char.IsLetter (s [pos]);
1188                 }
1189
1190                 // To implement better DateTime.Parse we use two format strings one
1191                 // for Date and one for Time. This allows us to define two different
1192                 // arrays of formats for Time and Dates and to combine them more or less
1193                 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1194                 private static bool _DoParse (string s,
1195                                               string firstPart,
1196                                               string secondPart,
1197                                               bool exact,
1198                                               out DateTime result,
1199                                               DateTimeFormatInfo dfi,
1200                                               DateTimeStyles style,
1201                                               bool firstPartIsDate,
1202                                               ref bool incompleteFormat,
1203                                               ref bool longYear)
1204                 {
1205                         bool useutc = false;
1206                         bool use_invariant = false;
1207                         bool sloppy_parsing = false;
1208 #if !NET_2_0
1209                         bool afterTimePart = firstPartIsDate && secondPart == "";
1210 #endif
1211                         bool flexibleTwoPartsParsing = !exact && secondPart != null;
1212                         incompleteFormat = false;
1213                         int valuePos = 0;
1214                         string format = firstPart;
1215                         bool afterTFormat = false;
1216                         DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1217                         if (format.Length == 1)
1218                                 format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
1219
1220                         result = new DateTime (0);
1221                         if (format == null)
1222                                 return false;
1223
1224                         if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1225                                 format = format.TrimStart (null);
1226
1227                                 s = s.TrimStart (null); // it could be optimized, but will make little good.
1228                         }
1229
1230                         if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1231                                 format = format.TrimEnd (null);
1232                                 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1233                         }
1234
1235                         if (use_invariant)
1236                                 dfi = invInfo;
1237
1238                         if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1239                                 sloppy_parsing = true;
1240
1241                         string chars = format;
1242                         int len = format.Length, pos = 0, num = 0;
1243                         if (len == 0)
1244                                 return false;
1245
1246                         int day = -1, dayofweek = -1, month = -1, year = -1;
1247                         int hour = -1, minute = -1, second = -1;
1248                         double fractionalSeconds = -1;
1249                         int ampm = -1;
1250                         int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1251                         bool isFirstPart = true;
1252
1253                         for (; ; )
1254                         {
1255                                 if (valuePos == s.Length)
1256                                         break;
1257
1258                                 int num_parsed = 0;
1259                                 if (flexibleTwoPartsParsing && pos + num == 0)
1260                                 {
1261                                         bool isLetter = IsLetter(s, valuePos);
1262 #if NET_2_0
1263                                         if (isLetter) {
1264 #else
1265                                         if (afterTimePart && isLetter) {
1266 #endif
1267                                                 if (s [valuePos] == 'Z')
1268                                                         num_parsed = 1;
1269                                                 else
1270                                                         _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1271                                                 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1272                                                         valuePos += num_parsed;
1273                                                         useutc = true;
1274                                                         continue;
1275                                                 }
1276                                         }
1277                                         if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1278                                                 if (IsLetter (s, valuePos + num_parsed))
1279                                                         ampm = -1;
1280                                                 else if (num_parsed > 0) {
1281                                                         valuePos += num_parsed;
1282                                                         continue;
1283                                                 }
1284                                         }
1285
1286                                         if (!afterTFormat && dayofweek == -1 && isLetter) {
1287                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1288                                                 if (dayofweek == -1)
1289                                                         dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1290                                                 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1291                                                         valuePos += num_parsed;
1292                                                         continue;
1293                                                 }
1294                                                 else
1295                                                         dayofweek = -1;
1296                                         }
1297
1298                                         if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1299                                                 valuePos += 1;
1300                                                 continue;
1301                                         }
1302                                         num_parsed = 0;
1303                                 }
1304
1305                                 if (pos + num >= len)
1306                                 {
1307                                         if (flexibleTwoPartsParsing && num == 0) {
1308                                                 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1309                                                 if (!isFirstPart && format == "")
1310                                                         break;
1311
1312                                                 pos = 0;
1313                                                 if (isFirstPart)
1314                                                         format = secondPart;
1315                                                 else
1316                                                         format = "";
1317                                                 chars = format;
1318                                                 len = chars.Length;
1319                                                 isFirstPart = false;
1320 #if !NET_2_0
1321                                                 if (!firstPartIsDate || format == "")
1322                                                         afterTimePart = true;
1323 #endif
1324                                                 continue;
1325                                         }
1326                                         break;
1327                                 }
1328
1329                                 bool leading_zeros = true;
1330
1331                                 if (chars[pos] == '\'') {
1332                                         num = 1;
1333                                         while (pos+num < len) {
1334                                                 if (chars[pos+num] == '\'')
1335                                                         break;
1336
1337                                                 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1338                                                         return false;
1339
1340                                                 valuePos++;
1341                                                 num++;
1342                                         }
1343
1344                                         pos += num + 1;
1345                                         num = 0;
1346                                         continue;
1347                                 } else if (chars[pos] == '"') {
1348                                         num = 1;
1349                                         while (pos+num < len) {
1350                                                 if (chars[pos+num] == '"')
1351                                                         break;
1352
1353                                                 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1354                                                         return false;
1355
1356                                                 valuePos++;
1357                                                 num++;
1358                                         }
1359
1360                                         pos += num + 1;
1361                                         num = 0;
1362                                         continue;
1363                                 } else if (chars[pos] == '\\') {
1364                                         pos += num + 1;
1365                                         num = 0;
1366                                         if (pos >= len)
1367                                                 return false;
1368                                         if (s [valuePos] != chars [pos])
1369                                                 return false;
1370
1371                                         valuePos++;
1372                                         pos++;
1373                                         continue;
1374                                 } else if (chars[pos] == '%') {
1375                                         pos++;
1376                                         continue;
1377                                 } else if (char.IsWhiteSpace (s [valuePos]) ||
1378                                         s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1379                                         valuePos++;
1380                                         num = 0;
1381                                         if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1382                                                 if (!Char.IsWhiteSpace (chars[pos]))
1383                                                         return false;
1384                                                 pos++;
1385                                                 continue;
1386                                         }
1387
1388                                         int ws = valuePos;
1389                                         while (ws < s.Length) {
1390                                                 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1391                                                         ws++;
1392                                                 else
1393                                                         break;
1394                                         }
1395                                         valuePos = ws;
1396                                         ws = pos;
1397                                         while (ws < chars.Length) {
1398                                                 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1399                                                         ws++;
1400                                                 else
1401                                                         break;
1402                                         }
1403                                         pos = ws;
1404                                         // A whitespace may match a '/' in the pattern.
1405                                         if (!exact && pos < chars.Length && chars[pos] == '/')
1406                                                 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1407                                                         pos++;
1408                                         continue;
1409                                 }
1410
1411                                 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1412                                         num++;
1413                                         continue;
1414                                 }
1415
1416                                 switch (chars[pos])
1417                                 {
1418                                 case 'd':
1419                                         if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1420                                                 return false;
1421                                         if (num == 0)
1422                                                 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1423                                         else if (num == 1)
1424                                                 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1425                                         else if (num == 2)
1426                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1427                                         else
1428                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1429                                         break;
1430                                 case 'M':
1431                                         if (month != -1)
1432                                                 return false;
1433
1434                                         if (flexibleTwoPartsParsing) {
1435                                                 num_parsed = -1;
1436                                                 if (num == 0 || num == 3)
1437                                                         month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1438                                                 if (num > 1 && num_parsed == -1)
1439                                                         month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1440                                                 if (num > 1 && num_parsed == -1)
1441                                                         month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1442                                                 break;
1443                                         }
1444
1445                                         if (num == 0)
1446                                                 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1447                                         else if (num == 1)
1448                                                 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1449                                         else if (num == 2)
1450                                                 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1451                                         else
1452                                                 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1453                                         break;
1454                                 case 'y':
1455                                         if (year != -1)
1456                                                 return false;
1457
1458                                         if (num == 0) {
1459                                                 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1460                                         } else if (num < 3) {
1461                                                 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1462                                         } else {
1463                                                 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1464                                                 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1465                                                         int np = 0;
1466                                                         int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1467                                                         longYear = (ly > 9999);
1468                                                 }
1469                                                 num = 3;
1470                                         }
1471
1472                                         //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1473                                         if (num_parsed <= 2)
1474                                                 year += (year < 30) ? 2000 : 1900;
1475                                         break;
1476                                 case 'h':
1477                                         if (hour != -1)
1478                                                 return false;
1479                                         if (num == 0)
1480                                                 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1481                                         else
1482                                                 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1483
1484                                         if (hour > 12)
1485                                                 return false;
1486                                         if (hour == 12)
1487                                                 hour = 0;
1488
1489                                         break;
1490                                 case 'H':
1491                                         if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1492                                                 return false;
1493                                         if (num == 0)
1494                                                 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1495                                         else
1496                                                 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1497
1498                                         if (hour >= 24)
1499                                                 return false;
1500
1501 //                                      ampm = -2;
1502                                         break;
1503                                 case 'm':
1504                                         if (minute != -1)
1505                                                 return false;
1506                                         if (num == 0)
1507                                                 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1508                                         else
1509                                                 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1510
1511                                         if (minute >= 60)
1512                                                 return false;
1513
1514                                         break;
1515                                 case 's':
1516                                         if (second != -1)
1517                                                 return false;
1518                                         if (num == 0)
1519                                                 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1520                                         else
1521                                                 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1522
1523                                         if (second >= 60)
1524                                                 return false;
1525
1526                                         break;
1527 #if NET_2_0
1528                                 case 'F':
1529                                         leading_zeros = false;
1530                                         goto case 'f';
1531 #endif
1532                                 case 'f':
1533                                         if (num > 6 || fractionalSeconds != -1)
1534                                                 return false;
1535                                         double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1536                                         if (num_parsed == -1)
1537                                                 return false;
1538                                         fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1539                                         break;
1540                                 case 't':
1541                                         if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1542                                                         return false;
1543                                         break;
1544                                 case 'z':
1545                                         if (tzsign != -1)
1546                                                 return false;
1547
1548                                         if (s [valuePos] == '+')
1549                                                 tzsign = 0;
1550                                         else if (s [valuePos] == '-')
1551                                                 tzsign = 1;
1552                                         else
1553                                                 return false;
1554                                         valuePos++;
1555
1556                                         if (num == 0)
1557                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1558                                         else if (num == 1)
1559                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1560                                         else {
1561                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1562                                                 valuePos += num_parsed;
1563                                                 if (num_parsed < 0)
1564                                                         return false;
1565
1566                                                 num_parsed = 0;
1567                                                 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1568                                                         _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1569                                                         valuePos += num_parsed;
1570                                                         tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1571                                                         if (num_parsed < 0)
1572                                                                 return false;
1573                                                 }
1574                                                 else if (!flexibleTwoPartsParsing)
1575                                                         return false;
1576                                                 else
1577                                                         num_parsed = 0;
1578                                         }
1579                                         break;
1580 #if NET_2_0
1581                                 case 'K':
1582                                         if (s [valuePos] == 'Z') {
1583                                                 valuePos++;
1584                                                 useutc = true;                                          
1585                                         }
1586                                         else if (s [valuePos] == '+' || s [valuePos] == '-') {
1587                                                 if (tzsign != -1)
1588                                                         return false;
1589                                                 if (s [valuePos] == '+')
1590                                                         tzsign = 0;
1591                                                 else if (s [valuePos] == '-')
1592                                                         tzsign = 1;
1593                                                 valuePos++;
1594
1595                                                 // zzz
1596                                                 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1597                                                 valuePos += num_parsed;
1598                                                 if (num_parsed < 0)
1599                                                         return false;
1600
1601                                                 if (Char.IsDigit (s [valuePos]))
1602                                                         num_parsed = 0;
1603                                                 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1604                                                         return false;
1605                                                 valuePos += num_parsed;
1606
1607                                                 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1608                                                 num = 2;
1609                                                 if (num_parsed < 0)
1610                                                         return false;
1611                                         }
1612                                         break;
1613 #endif
1614                                 // LAMESPEC: This should be part of UTCpattern
1615                                 // string and thus should not be considered here.
1616                                 //
1617                                 // Note that 'Z' is not defined as a pattern
1618                                 // character. Keep it for X509 certificate
1619                                 // verification. Also, "Z" != "'Z'" under MS.NET
1620                                 // ("'Z'" is just literal; handled above)
1621                                 case 'Z':
1622                                         if (s [valuePos] != 'Z')
1623                                                 return false;
1624                                         num = 0;
1625                                         num_parsed = 1;
1626                                         useutc = true;
1627                                         break;
1628                                 case 'G':
1629                                         if (s [valuePos] != 'G')
1630                                                 return false;
1631
1632                                         if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1633                                                 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1634                                                 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1635                                         {
1636                                                 useutc = true;
1637                                                 num = 2;
1638                                                 num_parsed = 3;
1639                                         }
1640                                         else {
1641                                                 num = 0;
1642                                                 num_parsed = 1;
1643                                         }
1644                                         break;
1645                                 case ':':
1646                                         if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1647                                                 return false;
1648                                         break;
1649                                 case '/':
1650                                         if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1651                                                 return false;
1652
1653                                         num = 0;
1654                                         break;
1655                                 default:
1656                                         if (s [valuePos] != chars [pos])
1657                                                         return false;
1658
1659                                         num = 0;
1660                                         num_parsed = 1;
1661                                         break;
1662                                 }
1663
1664                                 if (num_parsed < 0)
1665                                         return false;
1666
1667                                 valuePos += num_parsed;
1668
1669                                 if (!exact && !flexibleTwoPartsParsing) {
1670                                         switch (chars [pos]) {
1671                                         case 'm':
1672                                         case 's':
1673 #if NET_2_0
1674                                         case 'F':
1675 #endif
1676                                         case 'f':
1677                                         case 'z':
1678                                                 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1679                                                         (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1680                                                         useutc = true;
1681                                                         valuePos++;
1682                                                 }
1683                                                 break;
1684                                         }
1685                                 }
1686
1687                                 pos = pos + num + 1;
1688                                 num = 0;
1689                         }
1690
1691 #if NET_2_0
1692                         if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1693                                 pos++;
1694                                 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1695                                         pos++;
1696                         }
1697                         while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1698                                 pos++;
1699 #endif
1700                         if (pos < len)
1701                                 return false;
1702
1703                         if (s.Length > valuePos) // extraneous tail.
1704                         {
1705                                 if (valuePos == 0)
1706                                         return false;
1707
1708                                 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1709                                         return false;
1710                                 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1711                                         return false;
1712                                 incompleteFormat = true;
1713                                 return false;
1714                         }
1715
1716                         if (hour == -1)
1717                                 hour = 0;
1718                         if (minute == -1)
1719                                 minute = 0;
1720
1721                         if (second == -1)
1722                                 second = 0;
1723                         if (fractionalSeconds == -1)
1724                                 fractionalSeconds = 0;
1725
1726                         // If no date was given
1727                         if ((day == -1) && (month == -1) && (year == -1)) {
1728                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1729                                         day = 1;
1730                                         month = 1;
1731                                         year = 1;
1732                                 } else {
1733                                         day = DateTime.Today.Day;
1734                                         month = DateTime.Today.Month;
1735                                         year = DateTime.Today.Year;
1736                                 }
1737                         }
1738
1739                         if (day == -1)
1740                                 day = 1;
1741                         if (month == -1)
1742                                 month = 1;
1743                         if (year == -1) {
1744                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1745                                         year = 1;
1746                                 else
1747                                         year = DateTime.Today.Year;
1748                         }
1749
1750                         if (ampm == 0 && hour == 12)
1751                                 hour = 0;
1752
1753                         if (ampm == 1 && (!flexibleTwoPartsParsing || hour < 12))
1754                                 hour = hour + 12;
1755                         
1756                         // For anything out of range 
1757                         // return false
1758                         if (year < 1 || year > 9999 || 
1759                                 month < 1 || month >12  ||
1760                                 day < 1 || day > DateTime.DaysInMonth(year, month) ||
1761                                 hour < 0 || hour > 23 ||
1762                                 minute < 0 || minute > 59 ||
1763                                 second < 0 || second > 59)
1764                                 return false;
1765
1766                         result = new DateTime (year, month, day, hour, minute, second, 0);
1767                         result = result.AddSeconds(fractionalSeconds);
1768
1769                         if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1770                                 return false;
1771
1772                         TimeSpan utcoffset;
1773
1774                         bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1775                         
1776                         if (tzsign != -1) {
1777                                 if (tzoffmin == -1)
1778                                         tzoffmin = 0;
1779                                 if (tzoffset == -1)
1780                                         tzoffset = 0;
1781                                 if (tzsign == 1)
1782                                         tzoffset = -tzoffset;
1783
1784                                 utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
1785                                 long newticks = (result.ticks - utcoffset).Ticks;
1786                                 if (newticks < 0)
1787                                         newticks += TimeSpan.TicksPerDay;
1788                                 result = new DateTime (false, new TimeSpan (newticks));
1789 #if NET_2_0
1790                                 result.kind = DateTimeKind.Utc;
1791                                 if ((style & DateTimeStyles.RoundtripKind) != 0)
1792                                         result = result.ToLocalTime ();
1793 #endif
1794                         }
1795 #if NET_2_0                                                     
1796                         else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1797                                 result.kind = DateTimeKind.Utc;
1798                         else if ((style & DateTimeStyles.AssumeLocal) != 0)
1799                                 result.kind = DateTimeKind.Local;                                               
1800
1801                         bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1802                         if (result.kind != DateTimeKind.Unspecified)
1803                         {                               
1804                                 if (adjustToUniversal)
1805                                         result = result.ToUniversalTime ();
1806                                 else if (adjustToLocal)
1807                                         result = result.ToLocalTime ();
1808                         }
1809 #else
1810                         if (!adjustToUniversal && (useutc || tzsign != -1))
1811                                 result = result.ToLocalTime ();
1812 #endif
1813                         return true;
1814                 }
1815
1816                 public static DateTime ParseExact (string s, string format,
1817                                                    IFormatProvider provider, DateTimeStyles style)
1818                 {
1819                         if (format == null)
1820                                 throw new ArgumentNullException ("format");
1821
1822                         string [] formats = new string [1];
1823                         formats[0] = format;
1824
1825                         return ParseExact (s, formats, provider, style);
1826                 }
1827
1828                 public static DateTime ParseExact (string s, string[] formats,
1829                                                    IFormatProvider provider,
1830                                                    DateTimeStyles style)
1831                 {
1832                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1833 #if NET_2_0
1834                         CheckStyle (style);
1835 #endif
1836                         if (s == null)
1837                                 throw new ArgumentNullException ("s");
1838                         if (formats == null)
1839                                 throw new ArgumentNullException ("formats");
1840                         if (formats.Length == 0)
1841                                 throw new FormatException ("Format specifier was invalid.");
1842
1843                         DateTime result;
1844                         bool longYear = false;
1845                         Exception e = null;
1846                         if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear, true, ref e))
1847                                 throw e;
1848                         return result;
1849                 }               
1850
1851 #if NET_2_0
1852                 private static void CheckStyle (DateTimeStyles style)
1853                 {
1854                         if ( (style & DateTimeStyles.RoundtripKind) != 0)
1855                         {
1856                                 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1857                                          (style & DateTimeStyles.AssumeUniversal) != 0)
1858                                         throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, Asersal or AdjustToUniversal.", "style");
1859                         }
1860                         if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)                 
1861                                 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1862                 }
1863
1864                 public static bool TryParse (string s, out DateTime result)
1865                 {
1866                         if (s != null){
1867                                 try {
1868                                         Exception exception = null;
1869
1870                                         return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, false, ref exception);
1871                                 } catch { }
1872                         }
1873                         result = MinValue;
1874                         return false;
1875                 }
1876                 
1877                 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1878                 {
1879                         if (s != null){
1880                                 try {
1881                                         Exception exception = null;
1882                                         
1883                                         return CoreParse (s, provider, styles, out result, false, ref exception);
1884                                 } catch {}
1885                         } 
1886                         result = MinValue;
1887                         return false;
1888                 }
1889                 
1890                 public static bool TryParseExact (string s, string format,
1891                                                   IFormatProvider provider,
1892                                                   DateTimeStyles style,
1893                                                   out DateTime result)
1894                 {
1895                         string[] formats;
1896                         formats = new string [1];
1897                         formats[0] = format;
1898
1899                         return TryParseExact (s, formats, provider, style, out result);
1900                 }
1901
1902                 public static bool TryParseExact (string s, string[] formats,
1903                                                   IFormatProvider provider,
1904                                                   DateTimeStyles style,
1905                                                   out DateTime result)
1906                 {
1907                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1908
1909                         bool longYear = false;
1910                         Exception e = null;
1911                         return ParseExact (s, formats, dfi, style, out result, true, ref longYear, false, ref e);
1912                 }
1913 #endif
1914
1915                 private static bool ParseExact (string s, string [] formats,
1916                                                 DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1917                                                 bool exact, ref bool longYear,
1918                                                 bool setExceptionOnError, ref Exception exception)
1919                 {
1920                         int i;
1921                         bool incompleteFormat = false;
1922                         for (i = 0; i < formats.Length; i++)
1923                         {
1924                                 DateTime result;
1925                                 string format = formats[i];
1926                                 if (format == null || format == String.Empty)
1927                                         break;
1928
1929                                 if (_DoParse (s, formats[i], null, exact, out result, dfi, style, false, ref incompleteFormat, ref longYear)) {
1930                                         ret = result;
1931                                         return true;
1932                                 }
1933                         }
1934
1935                         if (setExceptionOnError)
1936                                 exception = new FormatException ("Invalid format string");
1937                         ret = DateTime.MinValue;
1938                         return false;
1939                 }
1940                 
1941                 public TimeSpan Subtract (DateTime value)
1942                 {
1943                         return new TimeSpan (ticks.Ticks) - value.ticks;
1944                 }
1945
1946                 public DateTime Subtract(TimeSpan value)
1947                 {
1948                         TimeSpan newticks;
1949
1950                         newticks = (new TimeSpan (ticks.Ticks)) - value;
1951                         DateTime ret = new DateTime (true,newticks);
1952 #if NET_2_0
1953                         ret.kind = kind;
1954 #endif
1955                         return ret;
1956                 }
1957
1958                 public long ToFileTime()
1959                 {
1960                         DateTime universalTime = ToUniversalTime();
1961                         
1962                         if (universalTime.Ticks < w32file_epoch) {
1963                                 throw new ArgumentOutOfRangeException("file time is not valid");
1964                         }
1965                         
1966                         return(universalTime.Ticks - w32file_epoch);
1967                 }
1968
1969 #if NET_1_1
1970                 public long ToFileTimeUtc()
1971                 {
1972                         if (Ticks < w32file_epoch) {
1973                                 throw new ArgumentOutOfRangeException("file time is not valid");
1974                         }
1975                         
1976                         return (Ticks - w32file_epoch);
1977                 }
1978 #endif
1979
1980                 public string ToLongDateString()
1981                 {
1982                         return ToString ("D");
1983                 }
1984
1985                 public string ToLongTimeString()
1986                 {
1987                         return ToString ("T");
1988                 }
1989
1990                 public double ToOADate ()
1991                 {
1992                         long t = this.Ticks;
1993                         // uninitialized DateTime case
1994                         if (t == 0)
1995                                 return 0;
1996                         // we can't reach minimum value
1997                         if (t < 31242239136000000)
1998                                 return OAMinValue + 0.001;
1999
2000                         TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
2001                         double result = ts.TotalDays;
2002                         // t < 0 (where 599264352000000000 == 0.0d for OA)
2003                         if (t < 599264352000000000) {
2004                                 // negative days (int) but decimals are positive
2005                                 double d = Math.Ceiling (result);
2006                                 result = d - 2 - (result - d);
2007                         }
2008                         else {
2009                                 // we can't reach maximum value
2010                                 if (result >= OAMaxValue)
2011                                         result = OAMaxValue - 0.00000001d;
2012                         }
2013                         return result;
2014                 }
2015
2016                 public string ToShortDateString()
2017                 {
2018                         return ToString ("d");
2019                 }
2020
2021                 public string ToShortTimeString()
2022                 {
2023                         return ToString ("t");
2024                 }
2025                 
2026                 public override string ToString ()
2027                 {
2028                         return ToString ("G", null);
2029                 }
2030
2031                 public string ToString (IFormatProvider provider)
2032                 {
2033                         return ToString (null, provider);
2034                 }
2035
2036                 public string ToString (string format)
2037                 {
2038                         return ToString (format, null);
2039                 }
2040         
2041                 public string ToString (string format, IFormatProvider provider)
2042                 {
2043                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
2044
2045                         if (format == null || format == String.Empty)
2046                                 format = "G";
2047
2048                         bool useutc = false, use_invariant = false;
2049
2050                         if (format.Length == 1) {
2051                                 char fchar = format [0];
2052                                 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2053                                 if (fchar == 'U')
2054                                         return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
2055 //                                      return ToUniversalTime()._ToString (format, dfi);
2056
2057                                 if (format == null)
2058                                         throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2059                         }
2060
2061                         // Don't convert UTC value. It just adds 'Z' for 
2062                         // 'u' format, for the same ticks.
2063                         return DateTimeUtils.ToString (this, format, dfi);
2064                 }
2065
2066                 public DateTime ToLocalTime ()
2067                 {
2068                         return TimeZone.CurrentTimeZone.ToLocalTime (this);
2069                 }
2070
2071                 public DateTime ToUniversalTime()
2072                 {
2073                         return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2074                 }
2075
2076                 /*  OPERATORS */
2077
2078                 public static DateTime operator +(DateTime d, TimeSpan t)
2079                 {
2080                         DateTime ret = new DateTime (true, d.ticks + t);
2081 #if NET_2_0
2082                         ret.kind = d.kind;
2083 #endif
2084                         return ret;
2085                 }
2086
2087                 public static bool operator ==(DateTime d1, DateTime d2)
2088                 {
2089                         return (d1.ticks == d2.ticks);
2090                 }
2091
2092                 public static bool operator >(DateTime t1,DateTime t2)
2093                 {
2094                         return (t1.ticks > t2.ticks);
2095                 }
2096
2097                 public static bool operator >=(DateTime t1,DateTime t2)
2098                 {
2099                         return (t1.ticks >= t2.ticks);
2100                 }
2101
2102                 public static bool operator !=(DateTime d1, DateTime d2)
2103                 {
2104                         return (d1.ticks != d2.ticks);
2105                 }
2106
2107                 public static bool operator <(DateTime t1,      DateTime t2)
2108                 {
2109                         return (t1.ticks < t2.ticks );
2110                 }
2111
2112                 public static bool operator <=(DateTime t1,DateTime t2)
2113                 {
2114                         return (t1.ticks <= t2.ticks);
2115                 }
2116
2117                 public static TimeSpan operator -(DateTime d1,DateTime d2)
2118                 {
2119                         return new TimeSpan((d1.ticks - d2.ticks).Ticks);
2120                 }
2121
2122                 public static DateTime operator -(DateTime d,TimeSpan t)
2123                 {
2124                         DateTime ret = new DateTime (true, d.ticks - t);
2125 #if NET_2_0
2126                         ret.kind = d.kind;
2127 #endif
2128                         return ret;
2129                 }
2130
2131                 bool IConvertible.ToBoolean(IFormatProvider provider)
2132                 {
2133                         throw new InvalidCastException();
2134                 }
2135                 
2136                 byte IConvertible.ToByte(IFormatProvider provider)
2137                 {
2138                         throw new InvalidCastException();
2139
2140                 }
2141
2142                 char IConvertible.ToChar(IFormatProvider provider)
2143                 {
2144                         throw new InvalidCastException();
2145                 }
2146
2147                 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
2148                 {
2149                         return this;
2150                 } 
2151                 
2152                 decimal IConvertible.ToDecimal(IFormatProvider provider)
2153                 {
2154                          throw new InvalidCastException();
2155                 }
2156
2157                 double IConvertible.ToDouble(IFormatProvider provider)
2158                 {
2159                         throw new InvalidCastException();
2160                 }
2161
2162                 Int16 IConvertible.ToInt16(IFormatProvider provider)
2163                 {
2164                         throw new InvalidCastException();
2165                 }
2166
2167                 Int32 IConvertible.ToInt32(IFormatProvider provider)
2168                 {
2169                         throw new InvalidCastException();
2170                 }
2171
2172                 Int64 IConvertible.ToInt64(IFormatProvider provider)
2173                 {
2174                         throw new InvalidCastException();
2175                 }
2176
2177 #if ONLY_1_1
2178 #pragma warning disable 3019
2179                 [CLSCompliant (false)]
2180 #endif
2181                 SByte IConvertible.ToSByte(IFormatProvider provider)
2182                 {
2183                         throw new InvalidCastException();
2184                 }
2185 #if ONLY_1_1
2186 #pragma warning restore 3019
2187 #endif
2188
2189                 Single IConvertible.ToSingle(IFormatProvider provider)
2190                 {
2191                         throw new InvalidCastException();
2192                 }
2193
2194                 object IConvertible.ToType (Type type, IFormatProvider provider)
2195                 {
2196                         if (type == null)
2197                                 throw new ArgumentNullException ("type");
2198
2199                         if (type == typeof (DateTime))
2200                                 return this;
2201                         else if (type == typeof (String))
2202                                 return this.ToString (provider);
2203                         else if (type == typeof (Object))
2204                                 return this;
2205                         else
2206                                 throw new InvalidCastException();
2207                 }
2208
2209 #if ONLY_1_1
2210 #pragma warning disable 3019
2211                 [CLSCompliant (false)]
2212 #endif
2213                 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
2214                 {
2215                         throw new InvalidCastException();
2216                 }
2217 #if ONLY_1_1
2218 #pragma warning restore 3019
2219 #endif
2220
2221 #if ONLY_1_1
2222 #pragma warning disable 3019
2223                 [CLSCompliant (false)]
2224 #endif
2225                 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
2226                 {
2227                         throw new InvalidCastException();
2228                 }
2229 #if ONLY_1_1
2230 #pragma warning restore 3019
2231 #endif
2232
2233 #if ONLY_1_1
2234 #pragma warning disable 3019
2235                 [CLSCompliant (false)]
2236 #endif
2237                 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
2238                 {
2239                         throw new InvalidCastException();
2240                 }
2241 #if ONLY_1_1
2242 #pragma warning restore 3019
2243 #endif
2244         }
2245 }