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