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