Bug #352210 fix, and add support for RoundtripKind parsing.
[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 ts)
546                 {
547                         DateTime ret = AddTicks (ts.Ticks);
548 #if NET_2_0
549                         ret.kind = kind;
550 #endif
551                         return ret;
552                 }
553
554                 public DateTime AddDays (double days)
555                 {
556                         return AddMilliseconds (Math.Round (days * 86400000));
557                 }
558                 
559                 public DateTime AddTicks (long t)
560                 {
561                         if ((t + ticks.Ticks) > MAX_VALUE_TICKS || (t + ticks.Ticks) < 0) {
562                                 throw new ArgumentOutOfRangeException();
563                         }
564                         DateTime ret = new DateTime (t + ticks.Ticks);
565 #if NET_2_0
566                         ret.kind = kind;
567 #endif
568                         return ret;
569                 }
570
571                 public DateTime AddHours (double hours)
572                 {
573                         return AddMilliseconds (hours * 3600000);
574                 }
575
576                 public DateTime AddMilliseconds (double ms)
577                 {
578                         if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
579                                         (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
580                                 throw new ArgumentOutOfRangeException();
581                         }
582                         long msticks = (long) (ms * 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 minutes)
600                 {
601                         return AddMilliseconds (minutes * 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 seconds)
635                 {
636                         return AddMilliseconds (seconds*1000);
637                 }
638
639                 public DateTime AddYears (int years )
640                 {
641                         return AddMonths(years * 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 v)
655                 {
656                         if ( v == null)
657                                 return 1;
658
659                         if (!(v is System.DateTime))
660                                 throw new ArgumentException (Locale.GetText (
661                                         "Value is not a System.DateTime"));
662
663                         return Compare (this, (DateTime) v);
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 o)
741                 {
742                         if (!(o is System.DateTime))
743                                 return false;
744
745                         return ((DateTime) o).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 fp)
882                 {
883                         return Parse (s, fp, DateTimeStyles.AllowWhiteSpaces);
884                 }
885
886                 public static DateTime Parse (string s, IFormatProvider fp, DateTimeStyles styles)
887                 {
888                         
889                         const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
890 #if !NET_2_0
891                         const string argumentYearRangeExceptionMessage = "Valid values are between 1 and 9999, inclusive.";
892 #endif
893                         
894                         if (s == null)
895                                 throw new ArgumentNullException (Locale.GetText ("s is null"));
896                         if (fp == null)
897                                 fp = CultureInfo.CurrentCulture;
898                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
899
900                         bool longYear = false;
901                         DateTime result;
902                         // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
903                         string[] allDateFormats = YearMonthDayFormats (dfi);
904                         for (int i = 0; i < allDateFormats.Length; i++) {
905                                 string firstPart = allDateFormats [i];
906                                 bool incompleteFormat = false;
907                                 if (_DoParse (s, firstPart, "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
908                                         return result;
909                                 if (!incompleteFormat)
910                                         continue;
911
912                                 for (int j = 0; j < ParseTimeFormats.Length; j++) {
913                                         if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
914                                                 return result;
915                                 }
916                         }
917                         string[] monthDayFormats = IsDayBeforeMonth (dfi) ? DayMonthShortFormats : MonthDayShortFormats;
918                         for (int i = 0; i < monthDayFormats.Length; i++) {
919                                 bool incompleteFormat = false;
920                                 if (_DoParse (s, monthDayFormats[i], "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
921                                         return result;
922                         }
923                         for (int j = 0; j < ParseTimeFormats.Length; j++) {
924                                 string firstPart = ParseTimeFormats [j];
925                                 bool incompleteFormat = false;
926                                 if (_DoParse (s, firstPart, "", false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
927                                         return result;
928                                 if (!incompleteFormat)
929                                         continue;
930
931                                 for (int i = 0; i < monthDayFormats.Length; i++) {
932                                         if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
933                                                 return result;
934                                 }
935                                 for (int i = 0; i < allDateFormats.Length; i++) {
936                                         string dateFormat = allDateFormats [i];
937                                         if (dateFormat[dateFormat.Length - 1] == 'T')
938                                                 continue; // T formats must be before the time part
939                                         if (_DoParse (s, firstPart, dateFormat, false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
940                                                 return result;
941                                 }
942                         }
943
944                         // Try as a last resort all the patterns
945                         if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear))
946                                 return result;
947
948 #if NET_2_0
949                         // .NET does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
950                         throw new FormatException (formatExceptionMessage);
951 #else
952                         if (longYear) {
953                                 throw new ArgumentOutOfRangeException ("year",
954                                         argumentYearRangeExceptionMessage);
955                         }
956
957                         throw new FormatException (formatExceptionMessage);
958 #endif
959                 }
960
961                 public static DateTime ParseExact (string s, string format, IFormatProvider fp)
962                 {
963                         return ParseExact (s, format, fp, DateTimeStyles.None);
964                 }
965
966                 private static bool IsDayBeforeMonth (DateTimeFormatInfo dfi)
967                 {
968                         int dayIndex = dfi.MonthDayPattern.IndexOf('d');
969                         int monthIndex = dfi.MonthDayPattern.IndexOf('M');
970                         if (dayIndex == -1 || monthIndex == -1)
971                                 throw new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
972
973                         return dayIndex < monthIndex;
974                 }
975
976                 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
977                 {
978                         int dayIndex = dfi.ShortDatePattern.IndexOf('d');
979                         int monthIndex = dfi.ShortDatePattern.IndexOf('M');
980                         int yearIndex = dfi.ShortDatePattern.IndexOf('y');
981                         if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1)
982                                 throw new FormatException (Locale.GetText("Order of year, month and date is not defined by {0}", dfi.ShortDatePattern));
983
984                         if (yearIndex < monthIndex)
985                                 if (monthIndex < dayIndex)
986                                         return ParseYearMonthDayFormats;
987                                 else if (yearIndex < dayIndex)
988                                         return ParseYearDayMonthFormats;
989                                 else
990                                         // The year cannot be between the date and the month
991                                         throw new FormatException (Locale.GetText("Order of date, year and month defined by {0} is not supported", dfi.ShortDatePattern));
992                         else if (dayIndex < monthIndex)
993                                 return ParseDayMonthYearFormats;
994                         else if (dayIndex < yearIndex)
995                                 return ParseMonthDayYearFormats;
996                         else
997                                 // The year cannot be between the month and the date
998                                 throw new FormatException (Locale.GetText("Order of month, year and date defined by {0} is not supported", dfi.ShortDatePattern));
999                 }
1000
1001                 private static int _ParseNumber (string s, int valuePos,
1002                                                  int min_digits,
1003                                                  int digits,
1004                                                  bool leadingzero,
1005                                                  bool sloppy_parsing,
1006                                                  out int num_parsed)
1007                 {
1008                         int number = 0, i;
1009
1010                         if (sloppy_parsing)
1011                                 leadingzero = false;
1012
1013                         if (!leadingzero) {
1014                                 int real_digits = 0;
1015                                 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
1016                                         if (!Char.IsDigit (s[i]))
1017                                                 break;
1018
1019                                         real_digits++;
1020                                 }
1021
1022                                 digits = real_digits;
1023                         }
1024                         if (digits < min_digits) {
1025                                 num_parsed = -1;
1026                                 return 0;
1027                         }
1028
1029                         if (s.Length - valuePos < digits) {
1030                                 num_parsed = -1;
1031                                 return 0;
1032                         }
1033
1034                         for (i = valuePos; i < digits + valuePos; i++) {
1035                                 char c = s[i];
1036                                 if (!Char.IsDigit (c)) {
1037                                         num_parsed = -1;
1038                                         return 0;
1039                                 }
1040
1041                                 number = number * 10 + (byte) (c - '0');
1042                         }
1043
1044                         num_parsed = digits;
1045                         return number;
1046                 }
1047
1048                 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1049                 {
1050                         // FIXME: I know this is somehow lame code. Probably
1051                         // it should iterate all the enum value and return
1052                         // the longest match. However right now I don't see
1053                         // anything but "1" and "10" - "12" that might match
1054                         // two or more values. (They are only abbrev month
1055                         // names, so do reverse order search). See bug #80094.
1056                         for (int i = values.Length - 1; i >= 0; i--) {
1057                                 if (!exact && invValues [i].Length > values[i].Length) {
1058                                         if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1059                                                 return i;
1060                                         if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1061                                                 return i;
1062                                 }
1063                                 else {
1064                                         if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1065                                                 return i;
1066                                         if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1067                                         return i;
1068                                 }
1069                         }
1070
1071                         num_parsed = -1;
1072                         return -1;
1073                 }
1074
1075                 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1076                 {
1077                         if (maxlength <= 0)
1078                                 maxlength = value.Length;
1079
1080                         if (sPos + maxlength <= s.Length && String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
1081                                 num_parsed = maxlength;
1082                                 return true;
1083                         }
1084
1085                         num_parsed = -1;
1086                         return false;
1087                 }
1088
1089                 // Note that in case of Parse (exact == false) we check both for AM/PM
1090                 // and the culture spcific AM/PM strings.
1091                 private static bool _ParseAmPm(string s,
1092                                                int valuePos,
1093                                                int num,
1094                                                DateTimeFormatInfo dfi,
1095                                                bool exact,
1096                                                out int num_parsed,
1097                                                ref int ampm)
1098                 {
1099                         num_parsed = -1;
1100                         if (ampm != -1)
1101                                 return false;
1102
1103                         if (!IsLetter (s, valuePos)) {
1104                                 if (dfi.AMDesignator != "")
1105                                         return false;
1106                                 if (exact)
1107                                         ampm = 0;
1108                                 num_parsed = 0;
1109                                 return true;
1110                         }
1111                         DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1112                         if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1113                             dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1114                                 ampm = 1;
1115                         else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1116                                  _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1117                                 if (exact || num_parsed != 0)
1118                                         ampm = 0;
1119                         }
1120                         else
1121                                 return false;
1122                         return true;
1123                 }
1124
1125                 // Note that in case of Parse (exact == false) we check both for ':'
1126                 // and the culture spcific TimeSperator
1127                 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1128                 {
1129                         return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1130                                !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1131                 }
1132
1133                 // Accept any character for DateSeparator, except TimeSeparator,
1134                 // a digit or a letter.
1135                 // Not documented, but seems to be MS behaviour here.  See bug 54047.
1136                 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1137                 {
1138                         num_parsed = -1;
1139                         if (exact && s [sPos] != '/')
1140                                 return false;
1141
1142                         if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1143                                 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1144                                 return(false);
1145
1146                         num_parsed = 1;
1147                         return true;
1148                 }
1149
1150                 private static bool IsLetter (string s, int pos)
1151                 {
1152                         return pos < s.Length && Char.IsLetter (s [pos]);
1153                 }
1154
1155                 // To implement better DateTime.Parse we use two format strings one
1156                 // for Date and one for Time. This allows us to define two different
1157                 // arrays of formats for Time and Dates and to combine them more or less
1158                 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1159                 private static bool _DoParse (string s,
1160                                               string firstPart,
1161                                               string secondPart,
1162                                               bool exact,
1163                                               out DateTime result,
1164                                               DateTimeFormatInfo dfi,
1165                                               DateTimeStyles style,
1166                                               bool firstPartIsDate,
1167                                               ref bool incompleteFormat,
1168                                               ref bool longYear)
1169                 {
1170                         bool useutc = false;
1171                         bool use_invariant = false;
1172                         bool sloppy_parsing = false;
1173 #if !NET_2_0
1174                         bool afterTimePart = firstPartIsDate && secondPart == "";
1175 #endif
1176                         bool flexibleTwoPartsParsing = !exact && secondPart != null;
1177                         incompleteFormat = false;
1178                         int valuePos = 0;
1179                         string format = firstPart;
1180                         bool afterTFormat = false;
1181                         DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1182                         if (format.Length == 1)
1183                                 format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
1184
1185                         result = new DateTime (0);
1186                         if (format == null)
1187                                 return false;
1188
1189                         if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1190                                 format = format.TrimStart (null);
1191
1192                                 s = s.TrimStart (null); // it could be optimized, but will make little good.
1193                         }
1194
1195                         if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1196                                 format = format.TrimEnd (null);
1197                                 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1198                         }
1199
1200                         if (use_invariant)
1201                                 dfi = invInfo;
1202
1203                         if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1204                                 sloppy_parsing = true;
1205
1206                         string chars = format;
1207                         int len = format.Length, pos = 0, num = 0;
1208                         if (len == 0)
1209                                 return false;
1210
1211                         int day = -1, dayofweek = -1, month = -1, year = -1;
1212                         int hour = -1, minute = -1, second = -1;
1213                         double fractionalSeconds = -1;
1214                         int ampm = -1;
1215                         int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1216                         bool isFirstPart = true;
1217
1218                         for (; ; )
1219                         {
1220                                 if (valuePos == s.Length)
1221                                         break;
1222
1223                                 int num_parsed = 0;
1224                                 if (flexibleTwoPartsParsing && pos + num == 0)
1225                                 {
1226                                         bool isLetter = IsLetter(s, valuePos);
1227 #if NET_2_0
1228                                         if (isLetter) {
1229 #else
1230                                         if (afterTimePart && isLetter) {
1231 #endif
1232                                                 if (s [valuePos] == 'Z')
1233                                                         num_parsed = 1;
1234                                                 else
1235                                                         _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1236                                                 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1237                                                         valuePos += num_parsed;
1238                                                         useutc = true;
1239                                                         continue;
1240                                                 }
1241                                         }
1242                                         if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1243                                                 if (IsLetter (s, valuePos + num_parsed))
1244                                                         ampm = -1;
1245                                                 else if (num_parsed > 0) {
1246                                                         valuePos += num_parsed;
1247                                                         continue;
1248                                                 }
1249                                         }
1250
1251                                         if (!afterTFormat && dayofweek == -1 && isLetter) {
1252                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1253                                                 if (dayofweek == -1)
1254                                                         dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1255                                                 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1256                                                         valuePos += num_parsed;
1257                                                         continue;
1258                                                 }
1259                                                 else
1260                                                         dayofweek = -1;
1261                                         }
1262
1263                                         if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1264                                                 valuePos += 1;
1265                                                 continue;
1266                                         }
1267                                         num_parsed = 0;
1268                                 }
1269
1270                                 if (pos + num >= len)
1271                                 {
1272                                         if (flexibleTwoPartsParsing && num == 0) {
1273                                                 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1274                                                 if (!isFirstPart && format == "")
1275                                                         break;
1276
1277                                                 pos = 0;
1278                                                 if (isFirstPart)
1279                                                         format = secondPart;
1280                                                 else
1281                                                         format = "";
1282                                                 chars = format;
1283                                                 len = chars.Length;
1284                                                 isFirstPart = false;
1285 #if !NET_2_0
1286                                                 if (!firstPartIsDate || format == "")
1287                                                         afterTimePart = true;
1288 #endif
1289                                                 continue;
1290                                         }
1291                                         break;
1292                                 }
1293
1294                                 bool leading_zeros = true;
1295
1296                                 if (chars[pos] == '\'') {
1297                                         num = 1;
1298                                         while (pos+num < len) {
1299                                                 if (chars[pos+num] == '\'')
1300                                                         break;
1301
1302                                                 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1303                                                         return false;
1304
1305                                                 valuePos++;
1306                                                 num++;
1307                                         }
1308
1309                                         pos += num + 1;
1310                                         num = 0;
1311                                         continue;
1312                                 } else if (chars[pos] == '"') {
1313                                         num = 1;
1314                                         while (pos+num < len) {
1315                                                 if (chars[pos+num] == '"')
1316                                                         break;
1317
1318                                                 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1319                                                         return false;
1320
1321                                                 valuePos++;
1322                                                 num++;
1323                                         }
1324
1325                                         pos += num + 1;
1326                                         num = 0;
1327                                         continue;
1328                                 } else if (chars[pos] == '\\') {
1329                                         pos += num + 1;
1330                                         num = 0;
1331                                         if (pos >= len)
1332                                                 return false;
1333                                         if (s [valuePos] != chars [pos])
1334                                                 return false;
1335
1336                                         valuePos++;
1337                                         pos++;
1338                                         continue;
1339                                 } else if (chars[pos] == '%') {
1340                                         pos++;
1341                                         continue;
1342                                 } else if (char.IsWhiteSpace (s [valuePos]) ||
1343                                         s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1344                                         valuePos++;
1345                                         num = 0;
1346                                         if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1347                                                 if (!Char.IsWhiteSpace (chars[pos]))
1348                                                         return false;
1349                                                 pos++;
1350                                                 continue;
1351                                         }
1352
1353                                         int ws = valuePos;
1354                                         while (ws < s.Length) {
1355                                                 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1356                                                         ws++;
1357                                                 else
1358                                                         break;
1359                                         }
1360                                         valuePos = ws;
1361                                         ws = pos;
1362                                         while (ws < chars.Length) {
1363                                                 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1364                                                         ws++;
1365                                                 else
1366                                                         break;
1367                                         }
1368                                         pos = ws;
1369                                         // A whitespace may match a '/' in the pattern.
1370                                         if (!exact && pos < chars.Length && chars[pos] == '/')
1371                                                 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1372                                                         pos++;
1373                                         continue;
1374                                 }
1375
1376                                 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1377                                         num++;
1378                                         continue;
1379                                 }
1380
1381                                 switch (chars[pos])
1382                                 {
1383                                 case 'd':
1384                                         if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1385                                                 return false;
1386                                         if (num == 0)
1387                                                 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1388                                         else if (num == 1)
1389                                                 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1390                                         else if (num == 2)
1391                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1392                                         else
1393                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1394                                         break;
1395                                 case 'M':
1396                                         if (month != -1)
1397                                                 return false;
1398
1399                                         if (flexibleTwoPartsParsing) {
1400                                                 num_parsed = -1;
1401                                                 if (num == 0 || num == 3)
1402                                                         month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1403                                                 if (num > 1 && num_parsed == -1)
1404                                                         month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1405                                                 if (num > 1 && num_parsed == -1)
1406                                                         month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1407                                                 break;
1408                                         }
1409
1410                                         if (num == 0)
1411                                                 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1412                                         else if (num == 1)
1413                                                 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1414                                         else if (num == 2)
1415                                                 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1416                                         else
1417                                                 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1418                                         break;
1419                                 case 'y':
1420                                         if (year != -1)
1421                                                 return false;
1422
1423                                         if (num == 0) {
1424                                                 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1425                                         } else if (num < 3) {
1426                                                 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1427                                         } else {
1428                                                 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1429                                                 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1430                                                         int np = 0;
1431                                                         int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1432                                                         longYear = (ly > 9999);
1433                                                 }
1434                                                 num = 3;
1435                                         }
1436
1437                                         //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1438                                         if (num_parsed <= 2)
1439                                                 year += (year < 30) ? 2000 : 1900;
1440                                         break;
1441                                 case 'h':
1442                                         if (hour != -1)
1443                                                 return false;
1444                                         if (num == 0)
1445                                                 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1446                                         else
1447                                                 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1448
1449                                         if (hour > 12)
1450                                                 return false;
1451                                         if (hour == 12)
1452                                                 hour = 0;
1453
1454                                         break;
1455                                 case 'H':
1456                                         if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1457                                                 return false;
1458                                         if (num == 0)
1459                                                 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1460                                         else
1461                                                 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1462
1463                                         if (hour >= 24)
1464                                                 return false;
1465
1466 //                                      ampm = -2;
1467                                         break;
1468                                 case 'm':
1469                                         if (minute != -1)
1470                                                 return false;
1471                                         if (num == 0)
1472                                                 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1473                                         else
1474                                                 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1475
1476                                         if (minute >= 60)
1477                                                 return false;
1478
1479                                         break;
1480                                 case 's':
1481                                         if (second != -1)
1482                                                 return false;
1483                                         if (num == 0)
1484                                                 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1485                                         else
1486                                                 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1487
1488                                         if (second >= 60)
1489                                                 return false;
1490
1491                                         break;
1492 #if NET_2_0
1493                                 case 'F':
1494                                         leading_zeros = false;
1495                                         goto case 'f';
1496 #endif
1497                                 case 'f':
1498                                         if (num > 6 || fractionalSeconds != -1)
1499                                                 return false;
1500                                         double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1501                                         if (num_parsed == -1)
1502                                                 return false;
1503                                         fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1504                                         break;
1505                                 case 't':
1506                                         if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1507                                                         return false;
1508                                         break;
1509                                 case 'z':
1510                                         if (tzsign != -1)
1511                                                 return false;
1512
1513                                         if (s [valuePos] == '+')
1514                                                 tzsign = 0;
1515                                         else if (s [valuePos] == '-')
1516                                                 tzsign = 1;
1517                                         else
1518                                                 return false;
1519                                         valuePos++;
1520
1521                                         if (num == 0)
1522                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1523                                         else if (num == 1)
1524                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1525                                         else {
1526                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1527                                                 valuePos += num_parsed;
1528                                                 if (num_parsed < 0)
1529                                                         return false;
1530
1531                                                 num_parsed = 0;
1532                                                 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1533                                                         _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1534                                                         valuePos += num_parsed;
1535                                                         tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1536                                                         if (num_parsed < 0)
1537                                                                 return false;
1538                                                 }
1539                                                 else if (!flexibleTwoPartsParsing)
1540                                                         return false;
1541                                                 else
1542                                                         num_parsed = 0;
1543                                         }
1544                                         break;
1545 #if NET_2_0
1546                                 case 'K':
1547                                         if (s [valuePos] == 'Z') {
1548                                                 valuePos++;
1549                                                 useutc = true;                                          
1550                                         }
1551                                         else if (s [valuePos] == '+' || s [valuePos] == '-') {
1552                                                 if (tzsign != -1)
1553                                                         return false;
1554                                                 if (s [valuePos] == '+')
1555                                                         tzsign = 0;
1556                                                 else if (s [valuePos] == '-')
1557                                                         tzsign = 1;
1558                                                 valuePos++;
1559
1560                                                 // zzz
1561                                                 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1562                                                 valuePos += num_parsed;
1563                                                 if (num_parsed < 0)
1564                                                         return false;
1565
1566                                                 if (Char.IsDigit (s [valuePos]))
1567                                                         num_parsed = 0;
1568                                                 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1569                                                         return false;
1570                                                 valuePos += num_parsed;
1571
1572                                                 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1573                                                 num = 2;
1574                                                 if (num_parsed < 0)
1575                                                         return false;
1576                                         }
1577                                         break;
1578 #endif
1579                                 // LAMESPEC: This should be part of UTCpattern
1580                                 // string and thus should not be considered here.
1581                                 //
1582                                 // Note that 'Z' is not defined as a pattern
1583                                 // character. Keep it for X509 certificate
1584                                 // verification. Also, "Z" != "'Z'" under MS.NET
1585                                 // ("'Z'" is just literal; handled above)
1586                                 case 'Z':
1587                                         if (s [valuePos] != 'Z')
1588                                                 return false;
1589                                         num = 0;
1590                                         num_parsed = 1;
1591                                         useutc = true;
1592                                         break;
1593                                 case 'G':
1594                                         if (s [valuePos] != 'G')
1595                                                 return false;
1596
1597                                         if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1598                                                 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1599                                                 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1600                                         {
1601                                                 useutc = true;
1602                                                 num = 2;
1603                                                 num_parsed = 3;
1604                                         }
1605                                         else {
1606                                                 num = 0;
1607                                                 num_parsed = 1;
1608                                         }
1609                                         break;
1610                                 case ':':
1611                                         if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1612                                                 return false;
1613                                         break;
1614                                 case '/':
1615                                         if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1616                                                 return false;
1617
1618                                         num = 0;
1619                                         break;
1620                                 default:
1621                                         if (s [valuePos] != chars [pos])
1622                                                         return false;
1623
1624                                         num = 0;
1625                                         num_parsed = 1;
1626                                         break;
1627                                 }
1628
1629                                 if (num_parsed < 0)
1630                                         return false;
1631
1632                                 valuePos += num_parsed;
1633
1634                                 if (!exact && !flexibleTwoPartsParsing) {
1635                                         switch (chars [pos]) {
1636                                         case 'm':
1637                                         case 's':
1638 #if NET_2_0
1639                                         case 'F':
1640 #endif
1641                                         case 'f':
1642                                         case 'z':
1643                                                 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1644                                                         (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1645                                                         useutc = true;
1646                                                         valuePos++;
1647                                                 }
1648                                                 break;
1649                                         }
1650                                 }
1651
1652                                 pos = pos + num + 1;
1653                                 num = 0;
1654                         }
1655
1656 #if NET_2_0
1657                         while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1658                                 pos++;
1659 #endif
1660                         if (pos < len)
1661                                 return false;
1662
1663                         if (s.Length > valuePos) // extraneous tail.
1664                         {
1665                                 if (valuePos == 0)
1666                                         return false;
1667
1668                                 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1669                                         return false;
1670                                 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1671                                         return false;
1672                                 incompleteFormat = true;
1673                                 return false;
1674                         }
1675
1676                         if (hour == -1)
1677                                 hour = 0;
1678                         if (minute == -1)
1679                                 minute = 0;
1680
1681                         if (second == -1)
1682                                 second = 0;
1683                         if (fractionalSeconds == -1)
1684                                 fractionalSeconds = 0;
1685
1686                         // If no date was given
1687                         if ((day == -1) && (month == -1) && (year == -1)) {
1688                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1689                                         day = 1;
1690                                         month = 1;
1691                                         year = 1;
1692                                 } else {
1693                                         day = DateTime.Today.Day;
1694                                         month = DateTime.Today.Month;
1695                                         year = DateTime.Today.Year;
1696                                 }
1697                         }
1698
1699                         if (day == -1)
1700                                 day = 1;
1701                         if (month == -1)
1702                                 month = 1;
1703                         if (year == -1) {
1704                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1705                                         year = 1;
1706                                 else
1707                                         year = DateTime.Today.Year;
1708                         }
1709
1710                         if (ampm == 0 && hour == 12)
1711                                 hour = 0;
1712
1713                         if (ampm == 1 && (!flexibleTwoPartsParsing || hour < 12))
1714                                 hour = hour + 12;
1715                         
1716                         // For anything out of range 
1717                         // return false
1718                         if (year < 1 || year > 9999 || 
1719                                 month < 1 || month >12  ||
1720                                 day < 1 || day > DateTime.DaysInMonth(year, month) ||
1721                                 hour < 0 || hour > 23 ||
1722                                 minute < 0 || minute > 59 ||
1723                                 second < 0 || second > 59)
1724                                 return false;
1725
1726                         result = new DateTime (year, month, day, hour, minute, second, 0);
1727                         result = result.AddSeconds(fractionalSeconds);
1728
1729                         if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1730                                 return false;
1731
1732                         bool kind_specified = true;
1733                         TimeSpan utcoffset;
1734
1735                         bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1736                         
1737                         if (tzsign != -1) {
1738                                 if (tzoffmin == -1)
1739                                         tzoffmin = 0;
1740                                 if (tzoffset == -1)
1741                                         tzoffset = 0;
1742                                 if (tzsign == 1)
1743                                         tzoffset = -tzoffset;
1744
1745                                 utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
1746                                 long newticks = (result.ticks - utcoffset).Ticks;
1747                                 if (newticks < 0)
1748                                         newticks += TimeSpan.TicksPerDay;
1749                                 result = new DateTime (false, new TimeSpan (newticks));
1750 #if NET_2_0
1751                                 result.kind = DateTimeKind.Utc;
1752                                 if ((style & DateTimeStyles.RoundtripKind) != 0)
1753                                         result = result.ToLocalTime ();
1754 #endif
1755                         }
1756 #if NET_2_0                                                     
1757                         else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1758                                 result.kind = DateTimeKind.Utc;
1759                         else if ((style & DateTimeStyles.AssumeLocal) != 0)
1760                                 result.kind = DateTimeKind.Local;                                               
1761
1762                         bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1763                         if (result.kind != DateTimeKind.Unspecified)
1764                         {                               
1765                                 if (adjustToUniversal)
1766                                         result = result.ToUniversalTime ();
1767                                 else if (adjustToLocal)
1768                                         result = result.ToLocalTime ();
1769                         }
1770 #else
1771                         if (!adjustToUniversal && (useutc || tzsign != -1))
1772                                 result = result.ToLocalTime ();
1773 #endif
1774                         return true;
1775                 }
1776
1777                 public static DateTime ParseExact (string s, string format,
1778                                                    IFormatProvider fp, DateTimeStyles style)
1779                 {
1780                         if (format == null)
1781                                 throw new ArgumentNullException ("format");
1782
1783                         string [] formats = new string [1];
1784                         formats[0] = format;
1785
1786                         return ParseExact (s, formats, fp, style);
1787                 }
1788
1789                 public static DateTime ParseExact (string s, string[] formats,
1790                                                    IFormatProvider fp,
1791                                                    DateTimeStyles style)
1792                 {
1793                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1794 #if NET_2_0
1795                         CheckStyle (style);
1796 #endif
1797                         if (s == null)
1798                                 throw new ArgumentNullException ("s");
1799                         if (formats == null)
1800                                 throw new ArgumentNullException ("formats");
1801                         if (formats.Length == 0)
1802                                 throw new FormatException ("Format specifier was invalid.");
1803
1804                         DateTime result;
1805                         bool longYear = false;
1806                         if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear))
1807                                 throw new FormatException ();
1808                         return result;
1809                 }               
1810
1811 #if NET_2_0
1812                 private static void CheckStyle (DateTimeStyles style)
1813                 {
1814                         if ( (style & DateTimeStyles.RoundtripKind) != 0)
1815                         {
1816                                 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1817                                          (style & DateTimeStyles.AssumeUniversal) != 0)
1818                                         throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, Asersal or AdjustToUniversal.", "style");
1819                         }
1820                         if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)                 
1821                                 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1822                 }
1823
1824                 public static bool TryParse (string s, out DateTime result)
1825                 {
1826                         try {
1827                                 result = Parse (s);
1828                         } catch {
1829                                 result = MinValue;
1830                                 return false;
1831                         }
1832                         return true;
1833                 }
1834                 
1835                 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1836                 {
1837                         try {
1838                                 result = Parse (s, provider, styles);
1839                         } catch {
1840                                 result = MinValue;
1841                                 return false;
1842                         }
1843                         return true;
1844                 }
1845                 
1846                 public static bool TryParseExact (string s, string format,
1847                                                   IFormatProvider fp,
1848                                                   DateTimeStyles style,
1849                                                   out DateTime result)
1850                 {
1851                         string[] formats;
1852
1853                         formats = new string [1];
1854                         formats[0] = format;
1855
1856                         return TryParseExact (s, formats, fp, style, out result);
1857                 }
1858
1859                 public static bool TryParseExact (string s, string[] formats,
1860                                                   IFormatProvider fp,
1861                                                   DateTimeStyles style,
1862                                                   out DateTime result)
1863                 {
1864                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1865
1866                         bool longYear = false;
1867                         return ParseExact (s, formats, dfi, style, out result, true, ref longYear);
1868                 }
1869 #endif
1870
1871                 private static bool ParseExact (string s, string [] formats,
1872                         DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1873                         bool exact, ref bool longYear)
1874                 {
1875                         int i;
1876                         bool incompleteFormat = false;
1877                         for (i = 0; i < formats.Length; i++)
1878                         {
1879                                 DateTime result;
1880                                 string format = formats[i];
1881                                 if (format == null || format == String.Empty)
1882                                         throw new FormatException ("Invalid Format String");
1883
1884                                 if (_DoParse (s, formats[i], null, exact, out result, dfi, style, false, ref incompleteFormat, ref longYear)) {
1885                                         ret = result;
1886                                         return true;
1887                                 }
1888                         }
1889                         ret = DateTime.MinValue;
1890                         return false;
1891                 }
1892                 
1893                 public TimeSpan Subtract(DateTime dt)
1894                 {
1895                         return new TimeSpan(ticks.Ticks) - dt.ticks;
1896                 }
1897
1898                 public DateTime Subtract(TimeSpan ts)
1899                 {
1900                         TimeSpan newticks;
1901
1902                         newticks = (new TimeSpan (ticks.Ticks)) - ts;
1903                         DateTime ret = new DateTime(true,newticks);
1904 #if NET_2_0
1905                         ret.kind = kind;
1906 #endif
1907                         return ret;
1908                 }
1909
1910                 public long ToFileTime()
1911                 {
1912                         DateTime universalTime = ToUniversalTime();
1913                         
1914                         if (universalTime.Ticks < w32file_epoch) {
1915                                 throw new ArgumentOutOfRangeException("file time is not valid");
1916                         }
1917                         
1918                         return(universalTime.Ticks - w32file_epoch);
1919                 }
1920
1921 #if NET_1_1
1922                 public long ToFileTimeUtc()
1923                 {
1924                         if (Ticks < w32file_epoch) {
1925                                 throw new ArgumentOutOfRangeException("file time is not valid");
1926                         }
1927                         
1928                         return (Ticks - w32file_epoch);
1929                 }
1930 #endif
1931
1932                 public string ToLongDateString()
1933                 {
1934                         return ToString ("D");
1935                 }
1936
1937                 public string ToLongTimeString()
1938                 {
1939                         return ToString ("T");
1940                 }
1941
1942                 public double ToOADate ()
1943                 {
1944                         long t = this.Ticks;
1945                         // uninitialized DateTime case
1946                         if (t == 0)
1947                                 return 0;
1948                         // we can't reach minimum value
1949                         if (t < 31242239136000000)
1950                                 return OAMinValue + 0.001;
1951
1952                         TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1953                         double result = ts.TotalDays;
1954                         // t < 0 (where 599264352000000000 == 0.0d for OA)
1955                         if (t < 599264352000000000) {
1956                                 // negative days (int) but decimals are positive
1957                                 double d = Math.Ceiling (result);
1958                                 result = d - 2 - (result - d);
1959                         }
1960                         else {
1961                                 // we can't reach maximum value
1962                                 if (result >= OAMaxValue)
1963                                         result = OAMaxValue - 0.00000001d;
1964                         }
1965                         return result;
1966                 }
1967
1968                 public string ToShortDateString()
1969                 {
1970                         return ToString ("d");
1971                 }
1972
1973                 public string ToShortTimeString()
1974                 {
1975                         return ToString ("t");
1976                 }
1977                 
1978                 public override string ToString ()
1979                 {
1980                         return ToString ("G", null);
1981                 }
1982
1983                 public string ToString (IFormatProvider fp)
1984                 {
1985                         return ToString (null, fp);
1986                 }
1987
1988                 public string ToString (string format)
1989                 {
1990                         return ToString (format, null);
1991                 }
1992         
1993                 public string ToString (string format, IFormatProvider fp)
1994                 {
1995                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
1996
1997                         if (format == null || format == String.Empty)
1998                                 format = "G";
1999
2000                         bool useutc = false, use_invariant = false;
2001
2002                         if (format.Length == 1) {
2003                                 char fchar = format [0];
2004                                 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2005                                 if (fchar == 'U')
2006                                         return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
2007 //                                      return ToUniversalTime()._ToString (format, dfi);
2008
2009                                 if (format == null)
2010                                         throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2011                         }
2012
2013                         // Don't convert UTC value. It just adds 'Z' for 
2014                         // 'u' format, for the same ticks.
2015                         return DateTimeUtils.ToString (this, format, dfi);
2016                 }
2017
2018                 public DateTime ToLocalTime ()
2019                 {
2020                         return TimeZone.CurrentTimeZone.ToLocalTime (this);
2021                 }
2022
2023                 public DateTime ToUniversalTime()
2024                 {
2025                         return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2026                 }
2027
2028                 /*  OPERATORS */
2029
2030                 public static DateTime operator +(DateTime d, TimeSpan t)
2031                 {
2032                         DateTime ret = new DateTime (true, d.ticks + t);
2033 #if NET_2_0
2034                         ret.kind = d.kind;
2035 #endif
2036                         return ret;
2037                 }
2038
2039                 public static bool operator ==(DateTime d1, DateTime d2)
2040                 {
2041                         return (d1.ticks == d2.ticks);
2042                 }
2043
2044                 public static bool operator >(DateTime t1,DateTime t2)
2045                 {
2046                         return (t1.ticks > t2.ticks);
2047                 }
2048
2049                 public static bool operator >=(DateTime t1,DateTime t2)
2050                 {
2051                         return (t1.ticks >= t2.ticks);
2052                 }
2053
2054                 public static bool operator !=(DateTime d1, DateTime d2)
2055                 {
2056                         return (d1.ticks != d2.ticks);
2057                 }
2058
2059                 public static bool operator <(DateTime t1,      DateTime t2)
2060                 {
2061                         return (t1.ticks < t2.ticks );
2062                 }
2063
2064                 public static bool operator <=(DateTime t1,DateTime t2)
2065                 {
2066                         return (t1.ticks <= t2.ticks);
2067                 }
2068
2069                 public static TimeSpan operator -(DateTime d1,DateTime d2)
2070                 {
2071                         return new TimeSpan((d1.ticks - d2.ticks).Ticks);
2072                 }
2073
2074                 public static DateTime operator -(DateTime d,TimeSpan t)
2075                 {
2076                         DateTime ret = new DateTime (true, d.ticks - t);
2077 #if NET_2_0
2078                         ret.kind = d.kind;
2079 #endif
2080                         return ret;
2081                 }
2082
2083                 bool IConvertible.ToBoolean(IFormatProvider provider)
2084                 {
2085                         throw new InvalidCastException();
2086                 }
2087                 
2088                 byte IConvertible.ToByte(IFormatProvider provider)
2089                 {
2090                         throw new InvalidCastException();
2091
2092                 }
2093
2094                 char IConvertible.ToChar(IFormatProvider provider)
2095                 {
2096                         throw new InvalidCastException();
2097                 }
2098
2099                 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
2100                 {
2101                         return this;
2102                 } 
2103                 
2104                 decimal IConvertible.ToDecimal(IFormatProvider provider)
2105                 {
2106                          throw new InvalidCastException();
2107                 }
2108
2109                 double IConvertible.ToDouble(IFormatProvider provider)
2110                 {
2111                         throw new InvalidCastException();
2112                 }
2113
2114                 Int16 IConvertible.ToInt16(IFormatProvider provider)
2115                 {
2116                         throw new InvalidCastException();
2117                 }
2118
2119                 Int32 IConvertible.ToInt32(IFormatProvider provider)
2120                 {
2121                         throw new InvalidCastException();
2122                 }
2123
2124                 Int64 IConvertible.ToInt64(IFormatProvider provider)
2125                 {
2126                         throw new InvalidCastException();
2127                 }
2128
2129                 SByte IConvertible.ToSByte(IFormatProvider provider)
2130                 {
2131                         throw new InvalidCastException();
2132                 }
2133
2134                 Single IConvertible.ToSingle(IFormatProvider provider)
2135                 {
2136                         throw new InvalidCastException();
2137                 }
2138
2139                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2140                 {
2141                         if (conversionType == null)
2142                                 throw new ArgumentNullException ("conversionType");
2143
2144                         if (conversionType == typeof (DateTime))
2145                                 return this;
2146                         else if (conversionType == typeof (String))
2147                                 return this.ToString (provider);
2148                         else if (conversionType == typeof (Object))
2149                                 return this;
2150                         else
2151                                 throw new InvalidCastException();
2152                 }
2153
2154                 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
2155                 {
2156                         throw new InvalidCastException();
2157                 }
2158                 
2159                 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
2160                 {
2161                         throw new InvalidCastException();
2162                 }
2163
2164                 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
2165                 {
2166                         throw new InvalidCastException();
2167                 }
2168         }
2169 }