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