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