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