Created DateTime.CoreParseExact so to be used by DateTimeOffset.
[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 (CoreParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, out dto, false, ref longYear, setExceptionOnError, ref exception))
932                                 return true;
933
934                         if (CoreParseExact (s, ExoticAndNonStandardFormats, dfi, styles, out result, out dto, 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                                               bool dateTimeOffset = false)
1147                 {
1148                         bool useutc = false;
1149                         bool use_invariant = false;
1150                         bool sloppy_parsing = false;
1151                         dto = new DateTimeOffset (0, TimeSpan.Zero);
1152                         bool flexibleTwoPartsParsing = !exact && secondPart != null;
1153                         incompleteFormat = false;
1154                         int valuePos = 0;
1155                         string format = firstPart;
1156                         bool afterTFormat = false;
1157                         DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1158                         if (format.Length == 1)
1159                                 format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant, dateTimeOffset);
1160
1161                         result = new DateTime (0);
1162                         if (format == null)
1163                                 return false;
1164
1165                         if (s == null)
1166                                 return false;
1167                                 
1168                         if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1169                                 format = format.TrimStart (null);
1170
1171                                 s = s.TrimStart (null); // it could be optimized, but will make little good.
1172                         }
1173
1174                         if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1175                                 format = format.TrimEnd (null);
1176                                 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1177                         }
1178
1179                         if (use_invariant)
1180                                 dfi = invInfo;
1181
1182                         if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1183                                 sloppy_parsing = true;
1184
1185                         string chars = format;
1186                         int len = format.Length, pos = 0, num = 0;
1187                         if (len == 0)
1188                                 return false;
1189
1190                         int day = -1, dayofweek = -1, month = -1, year = -1;
1191                         int hour = -1, minute = -1, second = -1;
1192                         double fractionalSeconds = -1;
1193                         int ampm = -1;
1194                         int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1195                         bool isFirstPart = true;
1196                         bool format_with_24_hours = false;
1197
1198                         for (; ; )
1199                         {
1200                                 if (valuePos == s.Length)
1201                                         break;
1202
1203                                 int num_parsed = 0;
1204                                 if (flexibleTwoPartsParsing && pos + num == 0)
1205                                 {
1206                                         bool isLetter = IsLetter(s, valuePos);
1207                                         if (isLetter) {
1208                                                 if (s [valuePos] == 'Z')
1209                                                         num_parsed = 1;
1210                                                 else
1211                                                         _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1212                                                 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1213                                                         valuePos += num_parsed;
1214                                                         useutc = true;
1215                                                         continue;
1216                                                 }
1217                                         }
1218                                         if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1219                                                 if (IsLetter (s, valuePos + num_parsed))
1220                                                         ampm = -1;
1221                                                 else if (num_parsed > 0) {
1222                                                         valuePos += num_parsed;
1223                                                         continue;
1224                                                 }
1225                                         }
1226
1227                                         if (!afterTFormat && dayofweek == -1 && isLetter) {
1228                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1229                                                 if (dayofweek == -1)
1230                                                         dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1231                                                 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1232                                                         valuePos += num_parsed;
1233                                                         continue;
1234                                                 }
1235                                                 else
1236                                                         dayofweek = -1;
1237                                         }
1238
1239                                         if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1240                                                 valuePos += 1;
1241                                                 continue;
1242                                         }
1243                                         num_parsed = 0;
1244                                 }
1245
1246                                 if (pos + num >= len)
1247                                 {
1248                                         if (flexibleTwoPartsParsing && num == 0) {
1249                                                 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1250                                                 if (!isFirstPart && format == "")
1251                                                         break;
1252
1253                                                 pos = 0;
1254                                                 if (isFirstPart)
1255                                                         format = secondPart;
1256                                                 else
1257                                                         format = "";
1258                                                 chars = format;
1259                                                 len = chars.Length;
1260                                                 isFirstPart = false;
1261                                                 continue;
1262                                         }
1263                                         break;
1264                                 }
1265
1266                                 bool leading_zeros = true;
1267
1268                                 if (chars[pos] == '\'') {
1269                                         num = 1;
1270                                         while (pos+num < len) {
1271                                                 if (chars[pos+num] == '\'')
1272                                                         break;
1273
1274                                                 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1275                                                         return false;
1276
1277                                                 valuePos++;
1278                                                 num++;
1279                                         }
1280
1281                                         pos += num + 1;
1282                                         num = 0;
1283                                         continue;
1284                                 } else if (chars[pos] == '"') {
1285                                         num = 1;
1286                                         while (pos+num < len) {
1287                                                 if (chars[pos+num] == '"')
1288                                                         break;
1289
1290                                                 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1291                                                         return false;
1292
1293                                                 valuePos++;
1294                                                 num++;
1295                                         }
1296
1297                                         pos += num + 1;
1298                                         num = 0;
1299                                         continue;
1300                                 } else if (chars[pos] == '\\') {
1301                                         pos += num + 1;
1302                                         num = 0;
1303                                         if (pos >= len)
1304                                                 return false;
1305                                         if (s [valuePos] != chars [pos])
1306                                                 return false;
1307
1308                                         valuePos++;
1309                                         pos++;
1310                                         continue;
1311                                 } else if (chars[pos] == '%') {
1312                                         pos++;
1313                                         continue;
1314                                 } else if (char.IsWhiteSpace (s [valuePos]) ||
1315                                         s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1316                                         valuePos++;
1317                                         num = 0;
1318                                         if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1319                                                 if (!Char.IsWhiteSpace (chars[pos]))
1320                                                         return false;
1321                                                 pos++;
1322                                                 continue;
1323                                         }
1324
1325                                         int ws = valuePos;
1326                                         while (ws < s.Length) {
1327                                                 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1328                                                         ws++;
1329                                                 else
1330                                                         break;
1331                                         }
1332                                         valuePos = ws;
1333                                         ws = pos;
1334                                         while (ws < chars.Length) {
1335                                                 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1336                                                         ws++;
1337                                                 else
1338                                                         break;
1339                                         }
1340                                         pos = ws;
1341                                         // A whitespace may match a '/' in the pattern.
1342                                         if (!exact && pos < chars.Length && chars[pos] == '/')
1343                                                 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1344                                                         pos++;
1345                                         continue;
1346                                 }
1347
1348                                 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1349                                         num++;
1350                                         continue;
1351                                 }
1352
1353                                 switch (chars[pos])
1354                                 {
1355                                 case 'd':
1356                                         if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1357                                                 return false;
1358                                         if (num == 0)
1359                                                 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1360                                         else if (num == 1)
1361                                                 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1362                                         else if (num == 2)
1363                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1364                                         else
1365                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1366                                         break;
1367                                 case 'M':
1368                                         if (month != -1)
1369                                                 return false;
1370
1371                                         if (flexibleTwoPartsParsing) {
1372                                                 num_parsed = -1;
1373                                                 if (num == 0 || num == 3)
1374                                                         month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1375                                                 if (num > 1 && num_parsed == -1)
1376                                                         month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1377                                                 if (num > 1 && num_parsed == -1)
1378                                                         month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1379                                                 break;
1380                                         }
1381
1382                                         if (num == 0)
1383                                                 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1384                                         else if (num == 1)
1385                                                 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1386                                         else if (num == 2)
1387                                                 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1388                                         else
1389                                                 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1390                                         break;
1391                                 case 'y':
1392                                         if (year != -1)
1393                                                 return false;
1394
1395                                         if (num == 0) {
1396                                                 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1397                                         } else if (num < 3) {
1398                                                 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1399                                         } else {
1400                                                 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1401                                                 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1402                                                         int np = 0;
1403                                                         int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1404                                                         longYear = (ly > 9999);
1405                                                 }
1406                                                 num = 3;
1407                                         }
1408
1409                                         //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1410                                         if (num_parsed <= 2)
1411                                                 year += (year < 30) ? 2000 : 1900;
1412                                         break;
1413                                 case 'h':
1414                                         if (hour != -1)
1415                                                 return false;
1416                                         if (num == 0)
1417                                                 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1418                                         else
1419                                                 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1420
1421                                         if (hour > 12)
1422                                                 return false;
1423                                         if (hour == 12)
1424                                                 hour = 0;
1425
1426                                         break;
1427                                 case 'H':
1428                                         if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1429                                                 return false;
1430                                         if (num == 0)
1431                                                 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1432                                         else
1433                                                 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1434
1435                                         if (hour >= 24)
1436                                                 return false;
1437
1438                                         format_with_24_hours = true;
1439                                         break;
1440                                 case 'm':
1441                                         if (minute != -1)
1442                                                 return false;
1443                                         if (num == 0)
1444                                                 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1445                                         else
1446                                                 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1447
1448                                         if (minute >= 60)
1449                                                 return false;
1450
1451                                         break;
1452                                 case 's':
1453                                         if (second != -1)
1454                                                 return false;
1455                                         if (num == 0)
1456                                                 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1457                                         else
1458                                                 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1459
1460                                         if (second >= 60)
1461                                                 return false;
1462
1463                                         break;
1464                                 case 'F':
1465                                         leading_zeros = false;
1466                                         goto case 'f';
1467                                 case 'f':
1468                                         if (num > 6 || fractionalSeconds != -1)
1469                                                 return false;
1470                                         double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1471                                         if (num_parsed == -1)
1472                                                 return false;
1473                                         fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1474                                         break;
1475                                 case 't':
1476                                         if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1477                                                         return false;
1478                                         break;
1479                                 case 'z':
1480                                         if (tzsign != -1)
1481                                                 return false;
1482
1483                                         if (s [valuePos] == '+')
1484                                                 tzsign = 0;
1485                                         else if (s [valuePos] == '-')
1486                                                 tzsign = 1;
1487                                         else
1488                                                 return false;
1489                                         valuePos++;
1490
1491                                         if (num == 0)
1492                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1493                                         else if (num == 1)
1494                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1495                                         else {
1496                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1497                                                 valuePos += num_parsed;
1498                                                 if (num_parsed < 0)
1499                                                         return false;
1500
1501                                                 num_parsed = 0;
1502                                                 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1503                                                         _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1504                                                         valuePos += num_parsed;
1505                                                         tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1506                                                         if (num_parsed < 0)
1507                                                                 return false;
1508                                                 }
1509                                                 else if (!flexibleTwoPartsParsing)
1510                                                         return false;
1511                                                 else
1512                                                         num_parsed = 0;
1513                                         }
1514                                         break;
1515                                 case 'K':
1516                                         if (s [valuePos] == 'Z') {
1517                                                 valuePos++;
1518                                                 useutc = true;                                          
1519                                         }
1520                                         else if (s [valuePos] == '+' || s [valuePos] == '-') {
1521                                                 if (tzsign != -1)
1522                                                         return false;
1523                                                 if (s [valuePos] == '+')
1524                                                         tzsign = 0;
1525                                                 else if (s [valuePos] == '-')
1526                                                         tzsign = 1;
1527                                                 valuePos++;
1528
1529                                                 // zzz
1530                                                 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1531                                                 valuePos += num_parsed;
1532                                                 if (num_parsed < 0)
1533                                                         return false;
1534
1535                                                 if (Char.IsDigit (s [valuePos]))
1536                                                         num_parsed = 0;
1537                                                 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1538                                                         return false;
1539                                                 valuePos += num_parsed;
1540
1541                                                 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1542                                                 num = 2;
1543                                                 if (num_parsed < 0)
1544                                                         return false;
1545                                         }
1546                                         break;
1547
1548                                 // LAMESPEC: This should be part of UTCpattern
1549                                 // string and thus should not be considered here.
1550                                 //
1551                                 // Note that 'Z' is not defined as a pattern
1552                                 // character. Keep it for X509 certificate
1553                                 // verification. Also, "Z" != "'Z'" under MS.NET
1554                                 // ("'Z'" is just literal; handled above)
1555                                 case 'Z':
1556                                         if (s [valuePos] != 'Z')
1557                                                 return false;
1558                                         num = 0;
1559                                         num_parsed = 1;
1560                                         useutc = true;
1561                                         break;
1562                                 case 'G':
1563                                         if (s [valuePos] != 'G')
1564                                                 return false;
1565
1566                                         if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1567                                                 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1568                                                 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1569                                         {
1570                                                 useutc = true;
1571                                                 num = 2;
1572                                                 num_parsed = 3;
1573                                         }
1574                                         else {
1575                                                 num = 0;
1576                                                 num_parsed = 1;
1577                                         }
1578                                         break;
1579                                 case ':':
1580                                         if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1581                                                 return false;
1582                                         break;
1583                                 case '/':
1584                                         if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1585                                                 return false;
1586
1587                                         num = 0;
1588                                         break;
1589                                 case '.':
1590                                         if (s[valuePos] == '.') {
1591                                                 num = 0;
1592                                                 num_parsed = 1;
1593                                                 break;
1594                                         }
1595
1596                                         // '.FFF....' can be mapped to nothing
1597                                         if (pos + 1 < len && chars[pos + 1] == 'F') {
1598                                                 ++pos;
1599                                                 while (pos + 1 < len && chars[pos + 1] == 'F') {
1600                                                         ++pos;
1601                                                 }
1602
1603                                                 num = 0;
1604                                                 num_parsed = 0;
1605                                                 break;
1606                                         }
1607
1608                                         return false;
1609
1610                                 default:
1611                                         if (s [valuePos] != chars [pos])
1612                                                         return false;
1613
1614                                         num = 0;
1615                                         num_parsed = 1;
1616                                         break;
1617                                 }
1618
1619                                 if (num_parsed < 0)
1620                                         return false;
1621
1622                                 valuePos += num_parsed;
1623
1624                                 if (!exact && !flexibleTwoPartsParsing) {
1625                                         switch (chars [pos]) {
1626                                         case 'm':
1627                                         case 's':
1628                                         case 'F':
1629                                         case 'f':
1630                                         case 'z':
1631                                                 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1632                                                         (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1633                                                         useutc = true;
1634                                                         valuePos++;
1635                                                 }
1636                                                 break;
1637                                         }
1638                                 }
1639
1640                                 pos = pos + num + 1;
1641                                 num = 0;
1642                         }
1643
1644                         if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1645                                 pos++;
1646                                 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1647                                         pos++;
1648                         }
1649                         while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1650                                 pos++;
1651
1652                         if (pos < len)
1653                                 return false;
1654
1655                         if (s.Length > valuePos) // extraneous tail.
1656                         {
1657                                 if (valuePos == 0)
1658                                         return false;
1659
1660                                 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1661                                         return false;
1662                                 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1663                                         return false;
1664                                 incompleteFormat = true;
1665                                 return false;
1666                         }
1667
1668                         if (hour == -1)
1669                                 hour = 0;
1670                         if (minute == -1)
1671                                 minute = 0;
1672
1673                         if (second == -1)
1674                                 second = 0;
1675                         if (fractionalSeconds == -1)
1676                                 fractionalSeconds = 0;
1677
1678                         // If no date was given
1679                         if ((day == -1) && (month == -1) && (year == -1)) {
1680                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1681                                         day = 1;
1682                                         month = 1;
1683                                         year = 1;
1684                                 } else {
1685                                         day = DateTime.Today.Day;
1686                                         month = DateTime.Today.Month;
1687                                         year = DateTime.Today.Year;
1688                                 }
1689                         }
1690
1691                         if (day == -1)
1692                                 day = 1;
1693                         if (month == -1)
1694                                 month = 1;
1695                         if (year == -1) {
1696                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1697                                         year = 1;
1698                                 else
1699                                         year = DateTime.Today.Year;
1700                         }
1701                         
1702                         if (ampm == 0) { // AM designator
1703                                 if (hour >= 12 && format_with_24_hours && exact)
1704                                         return false;
1705                                 
1706                                 if (hour == 12)
1707                                         hour = 0;
1708                         } else if (ampm == 1) { // PM designator
1709                                 if (hour < 12) {
1710                                         if (format_with_24_hours && exact)
1711                                                 return false;
1712                                         
1713                                         hour += 12;
1714                                 }
1715                         }
1716                         
1717                         try {
1718                                 result = dfi.Calendar.ToDateTime (year, month, day, hour, minute, second, 0);
1719                         } catch {
1720                                 return false;
1721                         }
1722
1723                         result = result.AddSeconds(fractionalSeconds);
1724
1725                         if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1726                                 return false;
1727
1728                         if (tzsign == -1) {
1729                                 if (result != DateTime.MinValue) {
1730                                         try {
1731                                                 dto = new DateTimeOffset (result);
1732                                         } catch { } // We handle this error in DateTimeOffset.Parse
1733                                 }
1734                         } else {
1735                                 if (tzoffmin == -1)
1736                                         tzoffmin = 0;
1737                                 if (tzoffset == -1)
1738                                         tzoffset = 0;
1739                                 if (tzsign == 1) {
1740                                         tzoffset = -tzoffset;
1741                                         tzoffmin = -tzoffmin;
1742                                 }
1743                                 try {
1744                                         dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1745                                 } catch {} // We handle this error in DateTimeOffset.Parse
1746                         }
1747                         bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1748                         
1749                         if (tzsign != -1) {
1750                                 long newticks = (result - dto.Offset).Ticks;
1751                                 if (newticks < 0)
1752                                         newticks += TimeSpan.TicksPerDay;
1753                                 result = new DateTime (newticks, DateTimeKind.Utc);
1754                                 if ((style & DateTimeStyles.RoundtripKind) != 0)
1755                                         result = result.ToLocalTime ();
1756                         } else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1757                                 result.encoded |= ((long) DateTimeKind.Utc << KindShift);
1758                         else if ((style & DateTimeStyles.AssumeLocal) != 0)
1759                                 result.encoded |= ((long) DateTimeKind.Local << KindShift);
1760
1761                         bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1762                         if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
1763                                 if (adjustToUniversal)
1764                                         result = result.ToUniversalTime ();
1765                                 else if (adjustToLocal)
1766                                         result = result.ToLocalTime ();
1767                         }
1768                         return true;
1769                 }
1770                 
1771
1772                 public static DateTime ParseExact (string s, string format,
1773                                                    IFormatProvider provider, DateTimeStyles style)
1774                 {
1775                         if (format == null)
1776                                 throw new ArgumentNullException ("format");
1777
1778                         string [] formats = new string [1];
1779                         formats[0] = format;
1780
1781                         return ParseExact (s, formats, provider, style);
1782                 }
1783
1784                 public static DateTime ParseExact (string s, string[] formats,
1785                                                    IFormatProvider provider,
1786                                                    DateTimeStyles style)
1787                 {
1788                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1789                         CheckStyle (style);
1790                         if (s == null)
1791                                 throw new ArgumentNullException ("s");
1792                         if (formats == null)
1793                                 throw new ArgumentNullException ("formats");
1794                         if (formats.Length == 0)
1795                                 throw new FormatException ("Format specifier was invalid.");
1796
1797                         DateTime result;
1798                         DateTimeOffset dto;
1799                         bool longYear = false;
1800                         Exception e = null;
1801                         if (!CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, true, ref e))
1802                                 throw e;
1803                         return result;
1804                 }               
1805
1806                 private static void CheckStyle (DateTimeStyles style)
1807                 {
1808                         if ( (style & DateTimeStyles.RoundtripKind) != 0)
1809                         {
1810                                 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1811                                          (style & DateTimeStyles.AssumeUniversal) != 0)
1812                                         throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, AssumeUniversal or AdjustToUniversal.", "style");
1813                         }
1814                         if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)                 
1815                                 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1816                 }
1817
1818                 public static bool TryParse (string s, out DateTime result)
1819                 {
1820                         if (s != null){
1821                                 try {
1822                                         Exception exception = null;
1823                                         DateTimeOffset dto;
1824
1825                                         return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1826                                 } catch { }
1827                         }
1828                         result = MinValue;
1829                         return false;
1830                 }
1831                 
1832                 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1833                 {
1834                         if (s != null){
1835                                 try {
1836                                         Exception exception = null;
1837                                         DateTimeOffset dto;
1838                                         
1839                                         return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1840                                 } catch {}
1841                         } 
1842                         result = MinValue;
1843                         return false;
1844                 }
1845                 
1846                 public static bool TryParseExact (string s, string format,
1847                                                   IFormatProvider provider,
1848                                                   DateTimeStyles style,
1849                                                   out DateTime result)
1850                 {
1851                         string[] formats;
1852                         formats = new string [1];
1853                         formats[0] = format;
1854
1855                         return TryParseExact (s, formats, provider, style, out result);
1856                 }
1857
1858                 public static bool TryParseExact (string s, string[] formats,
1859                                                   IFormatProvider provider,
1860                                                   DateTimeStyles style,
1861                                                   out DateTime result)
1862                 {
1863                         try {
1864                                 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1865                                 DateTimeOffset dto;
1866
1867                                 bool longYear = false;
1868                                 Exception e = null;
1869                                 return CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, false, ref e);
1870                         } catch {
1871                                 result = MinValue;
1872                                 return false;
1873                         }
1874                 }
1875
1876                 internal static bool CoreParseExact (string s, string [] formats,
1877                                                 DateTimeFormatInfo dfi, DateTimeStyles style,
1878                                                 out DateTime ret, out DateTimeOffset dto,
1879                                                 bool exact, ref bool longYear,
1880                                                 bool setExceptionOnError, ref Exception exception,
1881                                                 bool dateTimeOffset = false)
1882                 {
1883                         int i;
1884                         bool incompleteFormat = false;
1885                         for (i = 0; i < formats.Length; i++)
1886                         {
1887                                 DateTime result;
1888                                 string format = formats[i];
1889                                 if (format == null || format == String.Empty)
1890                                         break;
1891
1892                                 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear, dateTimeOffset)) {
1893                                         ret = result;
1894                                         return true;
1895                                 }
1896                         }
1897
1898                         if (setExceptionOnError)
1899                                 exception = new FormatException ("Invalid format string");
1900                         ret = DateTime.MinValue;
1901                         return false;
1902                 }
1903                 
1904                 public TimeSpan Subtract (DateTime value)
1905                 {
1906                         return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
1907                 }
1908
1909                 public DateTime Subtract(TimeSpan value)
1910                 {
1911                         long newticks;
1912
1913                         newticks = Ticks - value.Ticks;
1914                         if (newticks < 0 || newticks > MAX_VALUE_TICKS)
1915                                 throw new ArgumentOutOfRangeException ();
1916                         DateTime ret = new DateTime (newticks);
1917                         ret.encoded |= (encoded & KindMask);
1918                         return ret;
1919                 }
1920
1921                 public long ToFileTime()
1922                 {
1923                         DateTime universalTime = ToUniversalTime();
1924                         
1925                         if (universalTime.Ticks < w32file_epoch) {
1926                                 throw new ArgumentOutOfRangeException("file time is not valid");
1927                         }
1928                         
1929                         return(universalTime.Ticks - w32file_epoch);
1930                 }
1931
1932                 public long ToFileTimeUtc()
1933                 {
1934                         if (Kind == DateTimeKind.Local)
1935                                 return ToFileTime ();
1936
1937                         if (Ticks < w32file_epoch) {
1938                                 throw new ArgumentOutOfRangeException("file time is not valid");
1939                         }
1940                         
1941                         return (Ticks - w32file_epoch);
1942                 }
1943
1944                 public string ToLongDateString()
1945                 {
1946                         return ToString ("D");
1947                 }
1948
1949                 public string ToLongTimeString()
1950                 {
1951                         return ToString ("T");
1952                 }
1953
1954                 public double ToOADate ()
1955                 {
1956                         long t = this.Ticks;
1957                         // uninitialized DateTime case
1958                         if (t == 0)
1959                                 return 0;
1960                         // we can't reach minimum value
1961                         if (t < 31242239136000000)
1962                                 return OAMinValue + 0.001;
1963
1964                         TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1965                         double result = ts.TotalDays;
1966                         // t < 0 (where 599264352000000000 == 0.0d for OA)
1967                         if (t < 599264352000000000) {
1968                                 // negative days (int) but decimals are positive
1969                                 double d = Math.Ceiling (result);
1970                                 result = d - 2 - (result - d);
1971                         }
1972                         else {
1973                                 // we can't reach maximum value
1974                                 if (result >= OAMaxValue)
1975                                         result = OAMaxValue - 0.00000001d;
1976                         }
1977                         return result;
1978                 }
1979
1980                 public string ToShortDateString()
1981                 {
1982                         return ToString ("d");
1983                 }
1984
1985                 public string ToShortTimeString()
1986                 {
1987                         return ToString ("t");
1988                 }
1989                 
1990                 public override string ToString ()
1991                 {
1992                         return ToString ("G", null);
1993                 }
1994
1995                 public string ToString (IFormatProvider provider)
1996                 {
1997                         return ToString (null, provider);
1998                 }
1999
2000                 public string ToString (string format)
2001                 {
2002                         return ToString (format, null);
2003                 }
2004         
2005                 public string ToString (string format, IFormatProvider provider)
2006                 {
2007                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
2008
2009                         if (format == null || format == String.Empty)
2010                                 format = "G";
2011
2012                         if (format.Length == 1) {
2013                                 char fchar = format [0];
2014                                 bool use_invariant, useutc;
2015                                 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2016                                 if (fchar == 'U')
2017                                         return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
2018 //                                      return ToUniversalTime()._ToString (format, dfi);
2019
2020                                 if (format == null)
2021                                         throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2022
2023                                 if (use_invariant)
2024                                         dfi = DateTimeFormatInfo.InvariantInfo;
2025                         }
2026
2027                         // Don't convert UTC value. It just adds 'Z' for 
2028                         // 'u' format, for the same ticks.
2029                         return DateTimeUtils.ToString (this, format, dfi);
2030                 }
2031
2032                 public DateTime ToLocalTime ()
2033                 {
2034                         return TimeZone.CurrentTimeZone.ToLocalTime (this);
2035                 }
2036
2037                 public DateTime ToUniversalTime()
2038                 {
2039                         return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2040                 }
2041
2042                 /*  OPERATORS */
2043
2044                 public static DateTime operator +(DateTime d, TimeSpan t)
2045                 {
2046                         try {
2047                                 long res = checked ((d.encoded & TicksMask) + t.Ticks);
2048                                 if (res < 0 || res > MAX_VALUE_TICKS){
2049                                         throw new ArgumentOutOfRangeException ();
2050                                 }
2051                                 
2052                                 return new DateTime (res, d.Kind);
2053                         } catch (OverflowException){
2054                                 throw new ArgumentOutOfRangeException ();
2055                         }
2056                 }
2057
2058                 public static bool operator ==(DateTime d1, DateTime d2)
2059                 {
2060                         return ((d1.encoded & TicksMask) == (d2.encoded & TicksMask));
2061                 }
2062
2063                 public static bool operator >(DateTime t1,DateTime t2)
2064                 {
2065                         return ((t1.encoded & TicksMask) > (t2.encoded & TicksMask));
2066                 }
2067
2068                 public static bool operator >=(DateTime t1,DateTime t2)
2069                 {
2070                         return ((t1.encoded & TicksMask) >= (t2.encoded & TicksMask));
2071                 }
2072
2073                 public static bool operator !=(DateTime d1, DateTime d2)
2074                 {
2075                         return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
2076                 }
2077
2078                 public static bool operator <(DateTime t1, DateTime t2)
2079                 {
2080                         return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
2081                 }
2082
2083                 public static bool operator <=(DateTime t1, DateTime t2)
2084                 {
2085                         return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
2086                 }
2087
2088                 public static TimeSpan operator -(DateTime d1, DateTime d2)
2089                 {
2090                         return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
2091                 }
2092
2093                 public static DateTime operator -(DateTime d, TimeSpan t)
2094                 {
2095                         try {
2096                                 long res = checked ((d.encoded & TicksMask) - t.Ticks);
2097                                 if (res < 0 || res > MAX_VALUE_TICKS)
2098                                         throw new ArgumentOutOfRangeException ();
2099                                 return new DateTime (res, d.Kind);
2100                         } catch (OverflowException){
2101                                 throw new ArgumentOutOfRangeException ();
2102                         }
2103                 }
2104
2105                 bool IConvertible.ToBoolean (IFormatProvider provider)
2106                 {
2107                         throw new InvalidCastException();
2108                 }
2109                 
2110                 byte IConvertible.ToByte (IFormatProvider provider)
2111                 {
2112                         throw new InvalidCastException();
2113
2114                 }
2115
2116                 char IConvertible.ToChar (IFormatProvider provider)
2117                 {
2118                         throw new InvalidCastException();
2119                 }
2120
2121                 System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
2122                 {
2123                         return this;
2124                 } 
2125                 
2126                 decimal IConvertible.ToDecimal (IFormatProvider provider)
2127                 {
2128                          throw new InvalidCastException();
2129                 }
2130
2131                 double IConvertible.ToDouble (IFormatProvider provider)
2132                 {
2133                         throw new InvalidCastException();
2134                 }
2135
2136                 Int16 IConvertible.ToInt16 (IFormatProvider provider)
2137                 {
2138                         throw new InvalidCastException();
2139                 }
2140
2141                 Int32 IConvertible.ToInt32 (IFormatProvider provider)
2142                 {
2143                         throw new InvalidCastException();
2144                 }
2145
2146                 Int64 IConvertible.ToInt64 (IFormatProvider provider)
2147                 {
2148                         throw new InvalidCastException();
2149                 }
2150
2151                 SByte IConvertible.ToSByte (IFormatProvider provider)
2152                 {
2153                         throw new InvalidCastException();
2154                 }
2155
2156                 Single IConvertible.ToSingle (IFormatProvider provider)
2157                 {
2158                         throw new InvalidCastException();
2159                 }
2160
2161                 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2162                 {
2163                         if (targetType == null)
2164                                 throw new ArgumentNullException ("targetType");
2165
2166                         if (targetType == typeof (DateTime))
2167                                 return this;
2168                         else if (targetType == typeof (String))
2169                                 return this.ToString (provider);
2170                         else if (targetType == typeof (Object))
2171                                 return this;
2172                         else
2173                                 throw new InvalidCastException();
2174                 }
2175
2176                 UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
2177                 {
2178                         throw new InvalidCastException();
2179                 }
2180
2181                 UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
2182                 {
2183                         throw new InvalidCastException();
2184                 }
2185
2186                 UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
2187                 {
2188                         throw new InvalidCastException();
2189                 }
2190
2191                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2192                 {
2193                         long t = Ticks;
2194                         info.AddValue ("ticks", t);
2195
2196                         // This is the new .NET format, encodes the kind on the top bits
2197                         info.AddValue ("dateData", (UInt64)encoded);
2198                 }
2199                 
2200 #if MONOTOUCH
2201                 static DateTime () {
2202                         if (MonoTouchAOTHelper.FalseFlag) {
2203                                 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
2204                                 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();
2205                         }
2206                 }
2207 #endif
2208         }
2209 }