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