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