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