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