Merge pull request #1238 from esdrubal/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 ? num + 1 : 3, num + 1, false, sloppy_parsing, out num_parsed);
1401                                                 longYear = (year > 9999);
1402                                         }
1403
1404                                         //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1405                                         if (num_parsed <= 2)
1406                                                 year += (year < 30) ? 2000 : 1900;
1407                                         break;
1408                                 case 'h':
1409                                         if (hour != -1)
1410                                                 return false;
1411                                         if (num == 0)
1412                                                 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1413                                         else
1414                                                 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1415
1416                                         if (hour > 12)
1417                                                 return false;
1418                                         if (hour == 12)
1419                                                 hour = 0;
1420
1421                                         break;
1422                                 case 'H':
1423                                         if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1424                                                 return false;
1425                                         if (num == 0)
1426                                                 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1427                                         else
1428                                                 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1429
1430                                         if (hour >= 24)
1431                                                 return false;
1432
1433                                         format_with_24_hours = true;
1434                                         break;
1435                                 case 'm':
1436                                         if (minute != -1)
1437                                                 return false;
1438                                         if (num == 0)
1439                                                 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1440                                         else
1441                                                 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1442
1443                                         if (minute >= 60)
1444                                                 return false;
1445
1446                                         break;
1447                                 case 's':
1448                                         if (second != -1)
1449                                                 return false;
1450                                         if (num == 0)
1451                                                 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1452                                         else
1453                                                 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1454
1455                                         if (second >= 60)
1456                                                 return false;
1457
1458                                         break;
1459                                 case 'F':
1460                                         leading_zeros = false;
1461                                         goto case 'f';
1462                                 case 'f':
1463                                         if (num > 6 || fractionalSeconds != -1)
1464                                                 return false;
1465                                         double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1466                                         if (num_parsed == -1)
1467                                                 return false;
1468                                         fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1469                                         break;
1470                                 case 't':
1471                                         if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1472                                                         return false;
1473                                         break;
1474                                 case 'z':
1475                                         if (tzsign != -1)
1476                                                 return false;
1477
1478                                         if (s [valuePos] == '+')
1479                                                 tzsign = 0;
1480                                         else if (s [valuePos] == '-')
1481                                                 tzsign = 1;
1482                                         else
1483                                                 return false;
1484                                         valuePos++;
1485
1486                                         if (num == 0)
1487                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1488                                         else if (num == 1)
1489                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1490                                         else {
1491                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1492                                                 valuePos += num_parsed;
1493                                                 if (num_parsed < 0)
1494                                                         return false;
1495
1496                                                 num_parsed = 0;
1497                                                 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1498                                                         _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1499                                                         valuePos += num_parsed;
1500                                                         tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1501                                                         if (num_parsed < 0)
1502                                                                 return false;
1503                                                 }
1504                                                 else if (!flexibleTwoPartsParsing)
1505                                                         return false;
1506                                                 else
1507                                                         num_parsed = 0;
1508                                         }
1509                                         break;
1510                                 case 'K':
1511                                         if (s [valuePos] == 'Z') {
1512                                                 valuePos++;
1513                                                 useutc = true;                                          
1514                                         }
1515                                         else if (s [valuePos] == '+' || s [valuePos] == '-') {
1516                                                 if (tzsign != -1)
1517                                                         return false;
1518                                                 if (s [valuePos] == '+')
1519                                                         tzsign = 0;
1520                                                 else if (s [valuePos] == '-')
1521                                                         tzsign = 1;
1522                                                 valuePos++;
1523
1524                                                 // zzz
1525                                                 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1526                                                 valuePos += num_parsed;
1527                                                 if (num_parsed < 0)
1528                                                         return false;
1529
1530                                                 if (Char.IsDigit (s [valuePos]))
1531                                                         num_parsed = 0;
1532                                                 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1533                                                         return false;
1534                                                 valuePos += num_parsed;
1535
1536                                                 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1537                                                 if (num_parsed < 0)
1538                                                         return false;
1539                                         }
1540                                         break;
1541
1542                                 // LAMESPEC: This should be part of UTCpattern
1543                                 // string and thus should not be considered here.
1544                                 //
1545                                 // Note that 'Z' is not defined as a pattern
1546                                 // character. Keep it for X509 certificate
1547                                 // verification. Also, "Z" != "'Z'" under MS.NET
1548                                 // ("'Z'" is just literal; handled above)
1549                                 case 'Z':
1550                                         if (s [valuePos] != 'Z')
1551                                                 return false;
1552                                         num = 0;
1553                                         num_parsed = 1;
1554                                         useutc = true;
1555                                         break;
1556                                 case 'G':
1557                                         if (s [valuePos] != 'G')
1558                                                 return false;
1559
1560                                         if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1561                                                 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1562                                                 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1563                                         {
1564                                                 useutc = true;
1565                                                 num = 2;
1566                                                 num_parsed = 3;
1567                                         }
1568                                         else {
1569                                                 num = 0;
1570                                                 num_parsed = 1;
1571                                         }
1572                                         break;
1573                                 case ':':
1574                                         if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1575                                                 return false;
1576                                         break;
1577                                 case '/':
1578                                         if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1579                                                 return false;
1580
1581                                         num = 0;
1582                                         break;
1583                                 case '.':
1584                                         if (s[valuePos] == '.') {
1585                                                 num = 0;
1586                                                 num_parsed = 1;
1587                                                 break;
1588                                         }
1589
1590                                         // '.FFF....' can be mapped to nothing
1591                                         if (pos + 1 < len && chars[pos + 1] == 'F') {
1592                                                 ++pos;
1593                                                 while (pos + 1 < len && chars[pos + 1] == 'F') {
1594                                                         ++pos;
1595                                                 }
1596
1597                                                 num = 0;
1598                                                 num_parsed = 0;
1599                                                 break;
1600                                         }
1601
1602                                         return false;
1603
1604                                 default:
1605                                         if (s [valuePos] != chars [pos])
1606                                                         return false;
1607
1608                                         num = 0;
1609                                         num_parsed = 1;
1610                                         break;
1611                                 }
1612
1613                                 if (num_parsed < 0)
1614                                         return false;
1615
1616                                 valuePos += num_parsed;
1617
1618                                 if (!exact && !flexibleTwoPartsParsing) {
1619                                         switch (chars [pos]) {
1620                                         case 'm':
1621                                         case 's':
1622                                         case 'F':
1623                                         case 'f':
1624                                         case 'z':
1625                                                 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1626                                                         (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1627                                                         useutc = true;
1628                                                         valuePos++;
1629                                                 }
1630                                                 break;
1631                                         }
1632                                 }
1633
1634                                 pos = pos + num + 1;
1635                                 num = 0;
1636                         }
1637
1638                         if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1639                                 pos++;
1640                                 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1641                                         pos++;
1642                         }
1643                         while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1644                                 pos++;
1645
1646                         if (pos < len)
1647                                 return false;
1648
1649                         if (s.Length > valuePos) // extraneous tail.
1650                         {
1651                                 if (valuePos == 0)
1652                                         return false;
1653
1654                                 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1655                                         return false;
1656                                 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1657                                         return false;
1658                                 incompleteFormat = true;
1659                                 return false;
1660                         }
1661
1662                         if (hour == -1)
1663                                 hour = 0;
1664                         if (minute == -1)
1665                                 minute = 0;
1666
1667                         if (second == -1)
1668                                 second = 0;
1669                         if (fractionalSeconds == -1)
1670                                 fractionalSeconds = 0;
1671
1672                         // If no date was given
1673                         if ((day == -1) && (month == -1) && (year == -1)) {
1674                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1675                                         day = 1;
1676                                         month = 1;
1677                                         year = 1;
1678                                 } else {
1679                                         day = DateTime.Today.Day;
1680                                         month = DateTime.Today.Month;
1681                                         year = DateTime.Today.Year;
1682                                 }
1683                         }
1684
1685                         if (day == -1)
1686                                 day = 1;
1687                         if (month == -1)
1688                                 month = 1;
1689                         if (year == -1) {
1690                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1691                                         year = 1;
1692                                 else
1693                                         year = DateTime.Today.Year;
1694                         }
1695                         
1696                         if (ampm == 0) { // AM designator
1697                                 if (hour >= 12 && format_with_24_hours && exact)
1698                                         return false;
1699                                 
1700                                 if (hour == 12)
1701                                         hour = 0;
1702                         } else if (ampm == 1) { // PM designator
1703                                 if (hour < 12) {
1704                                         if (format_with_24_hours && exact)
1705                                                 return false;
1706                                         
1707                                         hour += 12;
1708                                 }
1709                         }
1710                         
1711                         try {
1712                                 result = dfi.Calendar.ToDateTime (year, month, day, hour, minute, second, 0);
1713                         } catch {
1714                                 return false;
1715                         }
1716
1717                         result = result.AddSeconds(fractionalSeconds);
1718
1719                         if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1720                                 return false;
1721
1722                         if (tzsign == -1) {
1723                                 if (result != DateTime.MinValue) {
1724                                         try {
1725                                                 if ((style & DateTimeStyles.AssumeUniversal) != 0) {
1726                                                         dto = new DateTimeOffset (result, TimeSpan.Zero);
1727                                                 } else if ((style & DateTimeStyles.AssumeLocal) != 0) {
1728                                                         var offset = use_invariant ?
1729                                                                 TimeSpan.Zero :
1730                                                                 TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.Now);
1731                                                         dto = new DateTimeOffset (result, offset);
1732                                                 } else {
1733                                                         dto = new DateTimeOffset (result);
1734                                                 }
1735                                         } catch { } // We handle this error in DateTimeOffset.Parse
1736                                 }
1737                         } else {
1738                                 if (tzoffmin == -1)
1739                                         tzoffmin = 0;
1740                                 if (tzoffset == -1)
1741                                         tzoffset = 0;
1742                                 if (tzsign == 1) {
1743                                         tzoffset = -tzoffset;
1744                                         tzoffmin = -tzoffmin;
1745                                 }
1746                                 try {
1747                                         dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1748                                 } catch {} // We handle this error in DateTimeOffset.Parse
1749                         }
1750                         bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1751                         
1752                         if (tzsign != -1) {
1753                                 long newticks = (result - dto.Offset).Ticks;
1754                                 if (newticks < 0)
1755                                         newticks += TimeSpan.TicksPerDay;
1756                                 result = new DateTime (newticks, DateTimeKind.Utc);
1757                                 if ((style & DateTimeStyles.RoundtripKind) != 0)
1758                                         result = result.ToLocalTime ();
1759                         } else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1760                                 result.encoded |= ((long) DateTimeKind.Utc << KindShift);
1761                         else if ((style & DateTimeStyles.AssumeLocal) != 0)
1762                                 result.encoded |= ((long) DateTimeKind.Local << KindShift);
1763
1764                         bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1765                         if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
1766                                 if (adjustToUniversal)
1767                                         result = result.ToUniversalTime ();
1768                                 else if (adjustToLocal)
1769                                         result = result.ToLocalTime ();
1770                         }
1771                         return true;
1772                 }
1773                 
1774
1775                 public static DateTime ParseExact (string s, string format,
1776                                                    IFormatProvider provider, DateTimeStyles style)
1777                 {
1778                         if (format == null)
1779                                 throw new ArgumentNullException ("format");
1780
1781                         string [] formats = new string [1];
1782                         formats[0] = format;
1783
1784                         return ParseExact (s, formats, provider, style);
1785                 }
1786
1787                 public static DateTime ParseExact (string s, string[] formats,
1788                                                    IFormatProvider provider,
1789                                                    DateTimeStyles style)
1790                 {
1791                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1792                         CheckStyle (style);
1793                         if (s == null)
1794                                 throw new ArgumentNullException ("s");
1795                         if (formats == null)
1796                                 throw new ArgumentNullException ("formats");
1797                         if (formats.Length == 0)
1798                                 throw new FormatException ("Format specifier was invalid.");
1799
1800                         DateTime result;
1801                         DateTimeOffset dto;
1802                         bool longYear = false;
1803                         Exception e = null;
1804                         if (!CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, true, ref e))
1805                                 throw e;
1806                         return result;
1807                 }               
1808
1809                 private static void CheckStyle (DateTimeStyles style)
1810                 {
1811                         if ( (style & DateTimeStyles.RoundtripKind) != 0)
1812                         {
1813                                 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1814                                          (style & DateTimeStyles.AssumeUniversal) != 0)
1815                                         throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, AssumeUniversal or AdjustToUniversal.", "style");
1816                         }
1817                         if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)                 
1818                                 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1819                 }
1820
1821                 public static bool TryParse (string s, out DateTime result)
1822                 {
1823                         if (s != null){
1824                                 try {
1825                                         Exception exception = null;
1826                                         DateTimeOffset dto;
1827
1828                                         return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1829                                 } catch { }
1830                         }
1831                         result = MinValue;
1832                         return false;
1833                 }
1834                 
1835                 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1836                 {
1837                         if (s != null){
1838                                 try {
1839                                         Exception exception = null;
1840                                         DateTimeOffset dto;
1841                                         
1842                                         return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1843                                 } catch {}
1844                         } 
1845                         result = MinValue;
1846                         return false;
1847                 }
1848                 
1849                 public static bool TryParseExact (string s, string format,
1850                                                   IFormatProvider provider,
1851                                                   DateTimeStyles style,
1852                                                   out DateTime result)
1853                 {
1854                         string[] formats;
1855                         formats = new string [1];
1856                         formats[0] = format;
1857
1858                         return TryParseExact (s, formats, provider, style, out result);
1859                 }
1860
1861                 public static bool TryParseExact (string s, string[] formats,
1862                                                   IFormatProvider provider,
1863                                                   DateTimeStyles style,
1864                                                   out DateTime result)
1865                 {
1866                         try {
1867                                 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1868                                 DateTimeOffset dto;
1869
1870                                 bool longYear = false;
1871                                 Exception e = null;
1872                                 return CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, false, ref e);
1873                         } catch {
1874                                 result = MinValue;
1875                                 return false;
1876                         }
1877                 }
1878
1879                 internal static bool CoreParseExact (string s, string [] formats,
1880                                                 DateTimeFormatInfo dfi, DateTimeStyles style,
1881                                                 out DateTime ret, out DateTimeOffset dto,
1882                                                 bool exact, ref bool longYear,
1883                                                 bool setExceptionOnError, ref Exception exception,
1884                                                 bool dateTimeOffset = false)
1885                 {
1886                         dto = new DateTimeOffset (0, TimeSpan.Zero);
1887                         int i;
1888                         bool incompleteFormat = false;
1889                         for (i = 0; i < formats.Length; i++)
1890                         {
1891                                 DateTime result;
1892                                 string format = formats[i];
1893                                 if (format == null || format == String.Empty)
1894                                         break;
1895
1896                                 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear, dateTimeOffset)) {
1897                                         ret = result;
1898                                         return true;
1899                                 }
1900                         }
1901
1902                         if (setExceptionOnError)
1903                                 exception = new FormatException ("Invalid format string");
1904                         ret = DateTime.MinValue;
1905                         return false;
1906                 }
1907                 
1908                 public TimeSpan Subtract (DateTime value)
1909                 {
1910                         return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
1911                 }
1912
1913                 public DateTime Subtract(TimeSpan value)
1914                 {
1915                         long newticks;
1916
1917                         newticks = Ticks - value.Ticks;
1918                         if (newticks < 0 || newticks > MAX_VALUE_TICKS)
1919                                 throw new ArgumentOutOfRangeException ();
1920                         DateTime ret = new DateTime (newticks);
1921                         ret.encoded |= (encoded & KindMask);
1922                         return ret;
1923                 }
1924
1925                 public long ToFileTime()
1926                 {
1927                         DateTime universalTime = ToUniversalTime();
1928                         
1929                         if (universalTime.Ticks < w32file_epoch) {
1930                                 throw new ArgumentOutOfRangeException("file time is not valid");
1931                         }
1932                         
1933                         return(universalTime.Ticks - w32file_epoch);
1934                 }
1935
1936                 public long ToFileTimeUtc()
1937                 {
1938                         if (Kind == DateTimeKind.Local)
1939                                 return ToFileTime ();
1940
1941                         if (Ticks < w32file_epoch) {
1942                                 throw new ArgumentOutOfRangeException("file time is not valid");
1943                         }
1944                         
1945                         return (Ticks - w32file_epoch);
1946                 }
1947
1948                 public string ToLongDateString()
1949                 {
1950                         return ToString ("D");
1951                 }
1952
1953                 public string ToLongTimeString()
1954                 {
1955                         return ToString ("T");
1956                 }
1957
1958                 public double ToOADate ()
1959                 {
1960                         long t = this.Ticks;
1961                         // uninitialized DateTime case
1962                         if (t == 0)
1963                                 return 0;
1964                         // we can't reach minimum value
1965                         if (t < 31242239136000000)
1966                                 return OAMinValue + 0.001;
1967
1968                         TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1969                         double result = ts.TotalDays;
1970                         // t < 0 (where 599264352000000000 == 0.0d for OA)
1971                         if (t < 599264352000000000) {
1972                                 // negative days (int) but decimals are positive
1973                                 double d = Math.Ceiling (result);
1974                                 result = d - 2 - (result - d);
1975                         }
1976                         else {
1977                                 // we can't reach maximum value
1978                                 if (result >= OAMaxValue)
1979                                         result = OAMaxValue - 0.00000001d;
1980                         }
1981                         return result;
1982                 }
1983
1984                 public string ToShortDateString()
1985                 {
1986                         return ToString ("d");
1987                 }
1988
1989                 public string ToShortTimeString()
1990                 {
1991                         return ToString ("t");
1992                 }
1993                 
1994                 public override string ToString ()
1995                 {
1996                         return ToString ("G", null);
1997                 }
1998
1999                 public string ToString (IFormatProvider provider)
2000                 {
2001                         return ToString (null, provider);
2002                 }
2003
2004                 public string ToString (string format)
2005                 {
2006                         return ToString (format, null);
2007                 }
2008         
2009                 public string ToString (string format, IFormatProvider provider)
2010                 {
2011                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
2012
2013                         if (format == null || format == String.Empty)
2014                                 format = "G";
2015
2016                         if (format.Length == 1) {
2017                                 char fchar = format [0];
2018                                 bool use_invariant, useutc;
2019                                 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2020                                 if (fchar == 'U')
2021                                         return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
2022 //                                      return ToUniversalTime()._ToString (format, dfi);
2023
2024                                 if (format == null)
2025                                         throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2026
2027                                 if (use_invariant)
2028                                         dfi = DateTimeFormatInfo.InvariantInfo;
2029                         }
2030
2031                         // Don't convert UTC value. It just adds 'Z' for 
2032                         // 'u' format, for the same ticks.
2033                         return DateTimeUtils.ToString (this, format, dfi);
2034                 }
2035
2036                 public DateTime ToLocalTime ()
2037                 {
2038                         return TimeZone.CurrentTimeZone.ToLocalTime (this);
2039                 }
2040
2041                 public DateTime ToUniversalTime()
2042                 {
2043                         return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2044                 }
2045
2046                 /*  OPERATORS */
2047
2048                 public static DateTime operator +(DateTime d, TimeSpan t)
2049                 {
2050                         try {
2051                                 long res = checked ((d.encoded & TicksMask) + t.Ticks);
2052                                 if (res < 0 || res > MAX_VALUE_TICKS){
2053                                         throw new ArgumentOutOfRangeException ();
2054                                 }
2055                                 
2056                                 return new DateTime (res, d.Kind);
2057                         } catch (OverflowException){
2058                                 throw new ArgumentOutOfRangeException ();
2059                         }
2060                 }
2061
2062                 public static bool operator ==(DateTime d1, DateTime d2)
2063                 {
2064                         return ((d1.encoded & TicksMask) == (d2.encoded & TicksMask));
2065                 }
2066
2067                 public static bool operator >(DateTime t1,DateTime t2)
2068                 {
2069                         return ((t1.encoded & TicksMask) > (t2.encoded & TicksMask));
2070                 }
2071
2072                 public static bool operator >=(DateTime t1,DateTime t2)
2073                 {
2074                         return ((t1.encoded & TicksMask) >= (t2.encoded & TicksMask));
2075                 }
2076
2077                 public static bool operator !=(DateTime d1, DateTime d2)
2078                 {
2079                         return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
2080                 }
2081
2082                 public static bool operator <(DateTime t1, DateTime t2)
2083                 {
2084                         return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
2085                 }
2086
2087                 public static bool operator <=(DateTime t1, DateTime t2)
2088                 {
2089                         return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
2090                 }
2091
2092                 public static TimeSpan operator -(DateTime d1, DateTime d2)
2093                 {
2094                         return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
2095                 }
2096
2097                 public static DateTime operator -(DateTime d, TimeSpan t)
2098                 {
2099                         try {
2100                                 long res = checked ((d.encoded & TicksMask) - t.Ticks);
2101                                 if (res < 0 || res > MAX_VALUE_TICKS)
2102                                         throw new ArgumentOutOfRangeException ();
2103                                 return new DateTime (res, d.Kind);
2104                         } catch (OverflowException){
2105                                 throw new ArgumentOutOfRangeException ();
2106                         }
2107                 }
2108
2109                 bool IConvertible.ToBoolean (IFormatProvider provider)
2110                 {
2111                         throw new InvalidCastException();
2112                 }
2113                 
2114                 byte IConvertible.ToByte (IFormatProvider provider)
2115                 {
2116                         throw new InvalidCastException();
2117
2118                 }
2119
2120                 char IConvertible.ToChar (IFormatProvider provider)
2121                 {
2122                         throw new InvalidCastException();
2123                 }
2124
2125                 System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
2126                 {
2127                         return this;
2128                 } 
2129                 
2130                 decimal IConvertible.ToDecimal (IFormatProvider provider)
2131                 {
2132                          throw new InvalidCastException();
2133                 }
2134
2135                 double IConvertible.ToDouble (IFormatProvider provider)
2136                 {
2137                         throw new InvalidCastException();
2138                 }
2139
2140                 Int16 IConvertible.ToInt16 (IFormatProvider provider)
2141                 {
2142                         throw new InvalidCastException();
2143                 }
2144
2145                 Int32 IConvertible.ToInt32 (IFormatProvider provider)
2146                 {
2147                         throw new InvalidCastException();
2148                 }
2149
2150                 Int64 IConvertible.ToInt64 (IFormatProvider provider)
2151                 {
2152                         throw new InvalidCastException();
2153                 }
2154
2155                 SByte IConvertible.ToSByte (IFormatProvider provider)
2156                 {
2157                         throw new InvalidCastException();
2158                 }
2159
2160                 Single IConvertible.ToSingle (IFormatProvider provider)
2161                 {
2162                         throw new InvalidCastException();
2163                 }
2164
2165                 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2166                 {
2167                         if (targetType == null)
2168                                 throw new ArgumentNullException ("targetType");
2169
2170                         if (targetType == typeof (DateTime))
2171                                 return this;
2172                         else if (targetType == typeof (String))
2173                                 return this.ToString (provider);
2174                         else if (targetType == typeof (Object))
2175                                 return this;
2176                         else
2177                                 throw new InvalidCastException();
2178                 }
2179
2180                 UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
2181                 {
2182                         throw new InvalidCastException();
2183                 }
2184
2185                 UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
2186                 {
2187                         throw new InvalidCastException();
2188                 }
2189
2190                 UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
2191                 {
2192                         throw new InvalidCastException();
2193                 }
2194
2195                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2196                 {
2197                         long t = Ticks;
2198                         info.AddValue ("ticks", t);
2199
2200                         // This is the new .NET format, encodes the kind on the top bits
2201                         info.AddValue ("dateData", (UInt64)encoded);
2202                 }
2203                 
2204 #if MONOTOUCH
2205                 static DateTime () {
2206                         if (MonoTouchAOTHelper.FalseFlag) {
2207                                 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
2208                                 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();
2209                         }
2210                 }
2211 #endif
2212         }
2213 }