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