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