0f96e57ea605634561557aae7381bc947b72016d
[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 & 0x3fffffffffffffff, DateTimeKind.Local);
702                         }
703                 }
704
705                 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
706                 {
707                         return new DateTime (value.Ticks, kind);
708                 }
709
710 #else
711
712                 internal long ToBinary ()
713                 {
714                         return Ticks;
715                 }
716
717                 internal static DateTime FromBinary (long dateData)
718                 {
719                         return new DateTime (dateData & 0x3fffffffffffffff);
720                 }
721 #endif
722
723                 public static int DaysInMonth (int year, int month)
724                 {
725                         int[] days ;
726
727                         if (month < 1 || month >12)
728                                 throw new ArgumentOutOfRangeException ();
729
730                         if (year < 1 || year > 9999)
731                                 throw new ArgumentOutOfRangeException ();
732
733                         days = (IsLeapYear(year) ? daysmonthleap  : daysmonth);
734                         return days[month];                     
735                 }
736                 
737                 public override bool Equals (object o)
738                 {
739                         if (!(o is System.DateTime))
740                                 return false;
741
742                         return ((DateTime) o).ticks == ticks;
743                 }
744
745                 public static bool Equals (DateTime t1, DateTime t2 )
746                 {
747                         return (t1.ticks == t2.ticks );
748                 }
749
750                 public static DateTime FromFileTime (long fileTime) 
751                 {
752                         if (fileTime < 0)
753                                 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
754
755                         return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
756                 }
757
758 #if NET_1_1
759                 public static DateTime FromFileTimeUtc (long fileTime) 
760                 {
761                         if (fileTime < 0)
762                                 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
763
764                         return new DateTime (w32file_epoch + fileTime);
765                 }
766 #endif
767
768                 public static DateTime FromOADate (double d)
769                 {
770                         // An OLE Automation date is implemented as a floating-point number
771                         // whose value is the number of days from midnight, 30 December 1899.
772
773                         // d must be negative 657435.0 through positive 2958466.0.
774                         if ((d <= OAMinValue) || (d >= OAMaxValue))
775                                 throw new ArgumentException ("d", "[-657435,2958466]");
776
777                         DateTime dt = new DateTime (ticks18991230);
778                         if (d < 0.0d) {
779                                 Double days = Math.Ceiling (d);
780                                 // integer part is the number of days (negative)
781                                 dt = dt.AddRoundedMilliseconds (days * 86400000);
782                                 // but decimals are the number of hours (in days fractions) and positive
783                                 Double hours = (days - d);
784                                 dt = dt.AddRoundedMilliseconds (hours * 86400000);
785                         }
786                         else {
787                                 dt = dt.AddRoundedMilliseconds (d * 86400000);
788                         }
789
790                         return dt;
791                 }
792
793                 public string[] GetDateTimeFormats() 
794                 {
795                         return GetDateTimeFormats (CultureInfo.CurrentCulture);
796                 }
797
798                 public string[] GetDateTimeFormats(char format)
799                 {
800                         if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
801                                 throw new FormatException ("Invalid format character.");
802                         string[] result = new string[1];
803                         result[0] = this.ToString(format.ToString());
804                         return result;
805                 }
806                 
807                 public string[] GetDateTimeFormats(IFormatProvider provider)
808                 {
809                         DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
810 //                      return GetDateTimeFormats (info.GetAllDateTimePatterns ());
811                         ArrayList al = new ArrayList ();
812                         foreach (char c in "dDgGfFmMrRstTuUyY")
813                                 al.AddRange (GetDateTimeFormats (c, info));
814                         return al.ToArray (typeof (string)) as string [];
815                 }
816
817                 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
818                 {
819                         if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
820                                 throw new FormatException ("Invalid format character.");
821
822                         // LAMESPEC: There is NO assurance that 'U' ALWAYS
823                         // euqals to 'F', but since we have to iterate all
824                         // the pattern strings, we cannot just use 
825                         // ToString("U", provider) here. I believe that the 
826                         // method's behavior cannot be formalized.
827                         bool adjustutc = false;
828                         switch (format) {
829                         case 'U':
830 //                      case 'r':
831 //                      case 'R':
832 //                      case 'u':
833                                 adjustutc = true;
834                                 break;
835                         }
836                         DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
837                         return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
838                 }
839
840                 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
841                 {
842                         string [] results = new string [patterns.Length];
843                         DateTime val = adjustutc ? ToUniversalTime () : this;
844                         for (int i = 0; i < results.Length; i++)
845                                 results [i] = val._ToString (patterns [i], dfi);
846                         return results;
847                 }
848
849 #if NET_2_0
850                 private void CheckDateTimeKind (DateTimeKind kind) {
851                         if ((kind != DateTimeKind.Unspecified) && (kind != DateTimeKind.Utc) && (kind != DateTimeKind.Local))
852                                 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
853                 }
854 #endif
855
856                 public override int GetHashCode ()
857                 {
858                         return (int) ticks.Ticks;
859                 }
860
861                 public TypeCode GetTypeCode ()
862                 {
863                         return TypeCode.DateTime;
864                 }
865
866                 public static bool IsLeapYear (int year)
867                 {
868                         if (year < 1 || year > 9999)
869                                 throw new ArgumentOutOfRangeException ();
870                         return  ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
871                 }
872
873                 public static DateTime Parse (string s)
874                 {
875                         return Parse (s, null);
876                 }
877
878                 public static DateTime Parse (string s, IFormatProvider fp)
879                 {
880                         return Parse (s, fp, DateTimeStyles.AllowWhiteSpaces);
881                 }
882
883                 public static DateTime Parse (string s, IFormatProvider fp, DateTimeStyles styles)
884                 {
885                         
886                         const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
887 #if !NET_2_0
888                         const string argumentYearRangeExceptionMessage = "Valid values are between 1 and 9999, inclusive.";
889 #endif
890                         
891                         if (s == null)
892                                 throw new ArgumentNullException (Locale.GetText ("s is null"));
893                         if (fp == null)
894                                 fp = CultureInfo.CurrentCulture;
895                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
896
897                         bool longYear = false;
898                         DateTime result;
899                         // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
900                         string[] allDateFormats = YearMonthDayFormats (dfi);
901                         for (int i = 0; i < allDateFormats.Length; i++) {
902                                 string firstPart = allDateFormats [i];
903                                 bool incompleteFormat = false;
904                                 if (_DoParse (s, firstPart, "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
905                                         return result;
906                                 if (!incompleteFormat)
907                                         continue;
908
909                                 for (int j = 0; j < ParseTimeFormats.Length; j++) {
910                                         if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
911                                                 return result;
912                                 }
913                         }
914                         string[] monthDayFormats = IsDayBeforeMonth (dfi) ? DayMonthShortFormats : MonthDayShortFormats;
915                         for (int i = 0; i < monthDayFormats.Length; i++) {
916                                 bool incompleteFormat = false;
917                                 if (_DoParse (s, monthDayFormats[i], "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
918                                         return result;
919                         }
920                         for (int j = 0; j < ParseTimeFormats.Length; j++) {
921                                 string firstPart = ParseTimeFormats [j];
922                                 bool incompleteFormat = false;
923                                 if (_DoParse (s, firstPart, "", false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
924                                         return result;
925                                 if (!incompleteFormat)
926                                         continue;
927
928                                 for (int i = 0; i < monthDayFormats.Length; i++) {
929                                         if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
930                                                 return result;
931                                 }
932                                 for (int i = 0; i < allDateFormats.Length; i++) {
933                                         string dateFormat = allDateFormats [i];
934                                         if (dateFormat[dateFormat.Length - 1] == 'T')
935                                                 continue; // T formats must be before the time part
936                                         if (_DoParse (s, firstPart, dateFormat, false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
937                                                 return result;
938                                 }
939                         }
940
941                         // Try as a last resort all the patterns
942                         if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear))
943                                 return result;
944
945 #if NET_2_0
946                         // .NET does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
947                         throw new FormatException (formatExceptionMessage);
948 #else
949                         if (longYear) {
950                                 throw new ArgumentOutOfRangeException ("year",
951                                         argumentYearRangeExceptionMessage);
952                         }
953
954                         throw new FormatException (formatExceptionMessage);
955 #endif
956                 }
957
958                 public static DateTime ParseExact (string s, string format, IFormatProvider fp)
959                 {
960                         return ParseExact (s, format, fp, DateTimeStyles.None);
961                 }
962
963                 private static bool IsDayBeforeMonth (DateTimeFormatInfo dfi)
964                 {
965                         int dayIndex = dfi.MonthDayPattern.IndexOf('d');
966                         int monthIndex = dfi.MonthDayPattern.IndexOf('M');
967                         if (dayIndex == -1 || monthIndex == -1)
968                                 throw new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
969
970                         return dayIndex < monthIndex;
971                 }
972
973                 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
974                 {
975                         int dayIndex = dfi.ShortDatePattern.IndexOf('d');
976                         int monthIndex = dfi.ShortDatePattern.IndexOf('M');
977                         int yearIndex = dfi.ShortDatePattern.IndexOf('y');
978                         if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1)
979                                 throw new FormatException (Locale.GetText("Order of year, month and date is not defined by {0}", dfi.ShortDatePattern));
980
981                         if (yearIndex < monthIndex)
982                                 if (monthIndex < dayIndex)
983                                         return ParseYearMonthDayFormats;
984                                 else if (yearIndex < dayIndex)
985                                         return ParseYearDayMonthFormats;
986                                 else
987                                         // The year cannot be between the date and the month
988                                         throw new FormatException (Locale.GetText("Order of date, year and month defined by {0} is not supported", dfi.ShortDatePattern));
989                         else if (dayIndex < monthIndex)
990                                 return ParseDayMonthYearFormats;
991                         else if (dayIndex < yearIndex)
992                                 return ParseMonthDayYearFormats;
993                         else
994                                 // The year cannot be between the month and the date
995                                 throw new FormatException (Locale.GetText("Order of month, year and date defined by {0} is not supported", dfi.ShortDatePattern));
996                 }
997
998                 private static int _ParseNumber (string s, int valuePos,
999                                                  int min_digits,
1000                                                  int digits,
1001                                                  bool leadingzero,
1002                                                  bool sloppy_parsing,
1003                                                  out int num_parsed)
1004                 {
1005                         int number = 0, i;
1006
1007                         if (sloppy_parsing)
1008                                 leadingzero = false;
1009
1010                         if (!leadingzero) {
1011                                 int real_digits = 0;
1012                                 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
1013                                         if (!Char.IsDigit (s[i]))
1014                                                 break;
1015
1016                                         real_digits++;
1017                                 }
1018
1019                                 digits = real_digits;
1020                         }
1021                         if (digits < min_digits) {
1022                                 num_parsed = -1;
1023                                 return 0;
1024                         }
1025
1026                         if (s.Length - valuePos < digits) {
1027                                 num_parsed = -1;
1028                                 return 0;
1029                         }
1030
1031                         for (i = valuePos; i < digits + valuePos; i++) {
1032                                 char c = s[i];
1033                                 if (!Char.IsDigit (c)) {
1034                                         num_parsed = -1;
1035                                         return 0;
1036                                 }
1037
1038                                 number = number * 10 + (byte) (c - '0');
1039                         }
1040
1041                         num_parsed = digits;
1042                         return number;
1043                 }
1044
1045                 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1046                 {
1047                         // FIXME: I know this is somehow lame code. Probably
1048                         // it should iterate all the enum value and return
1049                         // the longest match. However right now I don't see
1050                         // anything but "1" and "10" - "12" that might match
1051                         // two or more values. (They are only abbrev month
1052                         // names, so do reverse order search). See bug #80094.
1053                         for (int i = values.Length - 1; i >= 0; i--) {
1054                                 if (!exact && invValues [i].Length > values[i].Length) {
1055                                         if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1056                                                 return i;
1057                                         if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1058                                                 return i;
1059                                 }
1060                                 else {
1061                                         if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1062                                                 return i;
1063                                         if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1064                                         return i;
1065                                 }
1066                         }
1067
1068                         num_parsed = -1;
1069                         return -1;
1070                 }
1071
1072                 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1073                 {
1074                         if (maxlength <= 0)
1075                                 maxlength = value.Length;
1076
1077                         if (sPos + maxlength <= s.Length && String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
1078                                 num_parsed = maxlength;
1079                                 return true;
1080                         }
1081
1082                         num_parsed = -1;
1083                         return false;
1084                 }
1085
1086                 // Note that in case of Parse (exact == false) we check both for AM/PM
1087                 // and the culture spcific AM/PM strings.
1088                 private static bool _ParseAmPm(string s,
1089                                                int valuePos,
1090                                                int num,
1091                                                DateTimeFormatInfo dfi,
1092                                                bool exact,
1093                                                out int num_parsed,
1094                                                ref int ampm)
1095                 {
1096                         num_parsed = -1;
1097                         if (ampm != -1)
1098                                 return false;
1099
1100                         if (!IsLetter (s, valuePos)) {
1101                                 if (dfi.AMDesignator != "")
1102                                         return false;
1103                                 if (exact)
1104                                         ampm = 0;
1105                                 num_parsed = 0;
1106                                 return true;
1107                         }
1108                         DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1109                         if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1110                             dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1111                                 ampm = 1;
1112                         else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1113                                  _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1114                                 if (exact || num_parsed != 0)
1115                                         ampm = 0;
1116                         }
1117                         else
1118                                 return false;
1119                         return true;
1120                 }
1121
1122                 // Note that in case of Parse (exact == false) we check both for ':'
1123                 // and the culture spcific TimeSperator
1124                 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1125                 {
1126                         return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1127                                !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1128                 }
1129
1130                 // Accept any character for DateSeparator, except TimeSeparator,
1131                 // a digit or a letter.
1132                 // Not documented, but seems to be MS behaviour here.  See bug 54047.
1133                 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1134                 {
1135                         num_parsed = -1;
1136                         if (exact && s [sPos] != '/')
1137                                 return false;
1138
1139                         if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1140                                 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1141                                 return(false);
1142
1143                         num_parsed = 1;
1144                         return true;
1145                 }
1146
1147                 private static bool IsLetter (string s, int pos)
1148                 {
1149                         return pos < s.Length && Char.IsLetter (s [pos]);
1150                 }
1151
1152                 // To implement better DateTime.Parse we use two format strings one
1153                 // for Date and one for Time. This allows us to define two different
1154                 // arrays of formats for Time and Dates and to combine them more or less
1155                 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1156                 private static bool _DoParse (string s,
1157                                               string firstPart,
1158                                               string secondPart,
1159                                               bool exact,
1160                                               out DateTime result,
1161                                               DateTimeFormatInfo dfi,
1162                                               DateTimeStyles style,
1163                                               bool firstPartIsDate,
1164                                               ref bool incompleteFormat,
1165                                               ref bool longYear)
1166                 {
1167 #if NET_2_0
1168                         DateTimeKind explicit_kind = DateTimeKind.Unspecified;
1169 #endif
1170                         bool useutc = false, use_localtime = true;
1171                         bool use_invariant = false;
1172                         bool sloppy_parsing = false;
1173                         bool afterTimePart = firstPartIsDate && secondPart == "";
1174                         bool flexibleTwoPartsParsing = !exact && secondPart != null;
1175                         incompleteFormat = false;
1176                         int valuePos = 0;
1177                         string format = firstPart;
1178                         bool afterTFormat = false;
1179                         DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1180                         if (format.Length == 1) {
1181                                 if (format == "u")
1182                                         use_localtime = false;
1183                                 format = _GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
1184                         }
1185                         else if (!exact && CultureInfo.InvariantCulture.CompareInfo.IndexOf (format, "GMT", CompareOptions.Ordinal) >= 0)
1186                                 useutc = true;
1187 #if NET_2_0
1188                         if ((style & DateTimeStyles.AssumeUniversal) != 0)
1189                                 useutc = true;
1190 #endif
1191
1192                         result = new DateTime (0);
1193                         if (format == null)
1194                                 return false;
1195
1196                         if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1197                                 format = format.TrimStart (null);
1198
1199                                 s = s.TrimStart (null); // it could be optimized, but will make little good.
1200                         }
1201
1202                         if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1203                                 format = format.TrimEnd (null);
1204                                 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1205                         }
1206
1207                         if (use_invariant)
1208                                 dfi = invInfo;
1209
1210                         if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1211                                 sloppy_parsing = true;
1212
1213                         string chars = format;
1214                         int len = format.Length, pos = 0, num = 0;
1215                         if (len == 0)
1216                                 return false;
1217
1218                         int day = -1, dayofweek = -1, month = -1, year = -1;
1219                         int hour = -1, minute = -1, second = -1;
1220                         double fractionalSeconds = -1;
1221                         int ampm = -1;
1222                         int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1223                         bool isFirstPart = true;
1224
1225                         for (; ; )
1226                         {
1227                                 if (valuePos == s.Length)
1228                                         break;
1229
1230                                 int num_parsed = 0;
1231                                 if (flexibleTwoPartsParsing && pos + num == 0)
1232                                 {
1233                                         bool isLetter = IsLetter(s, valuePos);
1234                                         if (afterTimePart && isLetter) {
1235                                                 if (s [valuePos] == 'Z')
1236                                                         num_parsed = 1;
1237                                                 else
1238                                                         _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1239                                                 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1240                                                         valuePos += num_parsed;
1241                                                         useutc = true;
1242                                                         continue;
1243                                                 }
1244                                         }
1245                                         if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1246                                                 if (IsLetter (s, valuePos + num_parsed))
1247                                                         ampm = -1;
1248                                                 else if (num_parsed > 0) {
1249                                                         valuePos += num_parsed;
1250                                                         continue;
1251                                                 }
1252                                         }
1253
1254                                         if (!afterTFormat && dayofweek == -1 && isLetter) {
1255                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1256                                                 if (dayofweek == -1)
1257                                                         dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1258                                                 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1259                                                         valuePos += num_parsed;
1260                                                         continue;
1261                                                 }
1262                                                 else
1263                                                         dayofweek = -1;
1264                                         }
1265
1266                                         if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1267                                                 valuePos += 1;
1268                                                 continue;
1269                                         }
1270                                         num_parsed = 0;
1271                                 }
1272
1273                                 if (pos + num >= len)
1274                                 {
1275                                         if (flexibleTwoPartsParsing && num == 0) {
1276                                                 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1277                                                 if (!isFirstPart && format == "")
1278                                                         break;
1279
1280                                                 pos = 0;
1281                                                 if (isFirstPart)
1282                                                         format = secondPart;
1283                                                 else
1284                                                         format = "";
1285                                                 chars = format;
1286                                                 len = chars.Length;
1287                                                 isFirstPart = false;
1288                                                 if (!firstPartIsDate || format == "")
1289                                                         afterTimePart = true;
1290                                                 continue;
1291                                         }
1292                                         break;
1293                                 }
1294
1295                                 bool leading_zeros = true;
1296
1297                                 if (chars[pos] == '\'') {
1298                                         num = 1;
1299                                         while (pos+num < len) {
1300                                                 if (chars[pos+num] == '\'')
1301                                                         break;
1302
1303                                                 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1304                                                         return false;
1305
1306                                                 valuePos++;
1307                                                 num++;
1308                                         }
1309
1310                                         pos += num + 1;
1311                                         num = 0;
1312                                         continue;
1313                                 } else if (chars[pos] == '"') {
1314                                         num = 1;
1315                                         while (pos+num < len) {
1316                                                 if (chars[pos+num] == '"')
1317                                                         break;
1318
1319                                                 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1320                                                         return false;
1321
1322                                                 valuePos++;
1323                                                 num++;
1324                                         }
1325
1326                                         pos += num + 1;
1327                                         num = 0;
1328                                         continue;
1329                                 } else if (chars[pos] == '\\') {
1330                                         pos += num + 1;
1331                                         num = 0;
1332                                         if (pos >= len)
1333                                                 return false;
1334                                         if (s [valuePos] != chars [pos])
1335                                                 return false;
1336
1337                                         valuePos++;
1338                                         pos++;
1339                                         continue;
1340                                 } else if (chars[pos] == '%') {
1341                                         pos++;
1342                                         continue;
1343                                 } else if (char.IsWhiteSpace (s [valuePos]) ||
1344                                         s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1345                                         valuePos++;
1346                                         num = 0;
1347                                         if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1348                                                 if (!Char.IsWhiteSpace (chars[pos]))
1349                                                         return false;
1350                                                 pos++;
1351                                                 continue;
1352                                         }
1353
1354                                         int ws = valuePos;
1355                                         while (ws < s.Length) {
1356                                                 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1357                                                         ws++;
1358                                                 else
1359                                                         break;
1360                                         }
1361                                         valuePos = ws;
1362                                         ws = pos;
1363                                         while (ws < chars.Length) {
1364                                                 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1365                                                         ws++;
1366                                                 else
1367                                                         break;
1368                                         }
1369                                         pos = ws;
1370                                         // A whitespace may match a '/' in the pattern.
1371                                         if (!exact && pos < chars.Length && chars[pos] == '/')
1372                                                 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1373                                                         pos++;
1374                                         continue;
1375                                 }
1376
1377                                 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1378                                         num++;
1379                                         continue;
1380                                 }
1381
1382                                 switch (chars[pos])
1383                                 {
1384                                 case 'd':
1385                                         if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1386                                                 return false;
1387                                         if (num == 0)
1388                                                 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1389                                         else if (num == 1)
1390                                                 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1391                                         else if (num == 2)
1392                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1393                                         else
1394                                                 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1395                                         break;
1396                                 case 'M':
1397                                         if (month != -1)
1398                                                 return false;
1399
1400                                         if (flexibleTwoPartsParsing) {
1401                                                 num_parsed = -1;
1402                                                 if (num == 0 || num == 3)
1403                                                         month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1404                                                 if (num > 1 && num_parsed == -1)
1405                                                         month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1406                                                 if (num > 1 && num_parsed == -1)
1407                                                         month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1408                                                 break;
1409                                         }
1410
1411                                         if (num == 0)
1412                                                 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1413                                         else if (num == 1)
1414                                                 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1415                                         else if (num == 2)
1416                                                 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1417                                         else
1418                                                 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1419                                         break;
1420                                 case 'y':
1421                                         if (year != -1)
1422                                                 return false;
1423
1424                                         if (num == 0) {
1425                                                 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1426                                         } else if (num < 3) {
1427                                                 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1428                                         } else {
1429                                                 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1430                                                 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1431                                                         int np = 0;
1432                                                         int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1433                                                         longYear = (ly > 9999);
1434                                                 }
1435                                                 num = 3;
1436                                         }
1437
1438                                         //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1439                                         if (num_parsed <= 2)
1440                                                 year += (year < 30) ? 2000 : 1900;
1441                                         break;
1442                                 case 'h':
1443                                         if (hour != -1)
1444                                                 return false;
1445                                         if (num == 0)
1446                                                 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1447                                         else
1448                                                 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1449
1450                                         if (hour > 12)
1451                                                 return false;
1452                                         if (hour == 12)
1453                                                 hour = 0;
1454
1455                                         break;
1456                                 case 'H':
1457                                         if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1458                                                 return false;
1459                                         if (num == 0)
1460                                                 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1461                                         else
1462                                                 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1463
1464                                         if (hour >= 24)
1465                                                 return false;
1466
1467 //                                      ampm = -2;
1468                                         break;
1469                                 case 'm':
1470                                         if (minute != -1)
1471                                                 return false;
1472                                         if (num == 0)
1473                                                 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1474                                         else
1475                                                 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1476
1477                                         if (minute >= 60)
1478                                                 return false;
1479
1480                                         break;
1481                                 case 's':
1482                                         if (second != -1)
1483                                                 return false;
1484                                         if (num == 0)
1485                                                 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1486                                         else
1487                                                 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1488
1489                                         if (second >= 60)
1490                                                 return false;
1491
1492                                         break;
1493 #if NET_2_0
1494                                 case 'F':
1495                                         leading_zeros = false;
1496                                         goto case 'f';
1497 #endif
1498                                 case 'f':
1499                                         if (num > 6 || fractionalSeconds != -1)
1500                                                 return false;
1501                                         double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1502                                         if (num_parsed == -1)
1503                                                 return false;
1504                                         fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1505                                         break;
1506                                 case 't':
1507                                         if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1508                                                         return false;
1509                                         break;
1510                                 case 'z':
1511                                         if (tzsign != -1)
1512                                                 return false;
1513
1514                                         if (s [valuePos] == '+')
1515                                                 tzsign = 0;
1516                                         else if (s [valuePos] == '-')
1517                                                 tzsign = 1;
1518                                         else
1519                                                 return false;
1520                                         valuePos++;
1521
1522                                         if (num == 0)
1523                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1524                                         else if (num == 1)
1525                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1526                                         else {
1527                                                 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1528                                                 valuePos += num_parsed;
1529                                                 if (num_parsed < 0)
1530                                                         return false;
1531
1532                                                 num_parsed = 0;
1533                                                 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1534                                                         _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1535                                                         valuePos += num_parsed;
1536                                                         tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1537                                                         if (num_parsed < 0)
1538                                                                 return false;
1539                                                 }
1540                                                 else if (!flexibleTwoPartsParsing)
1541                                                         return false;
1542                                                 else
1543                                                         num_parsed = 0;
1544                                         }
1545                                         break;
1546 #if NET_2_0
1547                                 case 'K':
1548                                         if (s [valuePos] == 'Z') {
1549                                                 valuePos++;
1550                                                 explicit_kind = DateTimeKind.Utc;
1551                                         }
1552                                         else if (s [valuePos] == '+' || s [valuePos] == '-') {
1553                                                 if (tzsign != -1)
1554                                                         return false;
1555                                                 if (s [valuePos] == '+')
1556                                                         tzsign = 0;
1557                                                 else if (s [valuePos] == '-')
1558                                                         tzsign = 1;
1559                                                 valuePos++;
1560
1561                                                 // zzz
1562                                                 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1563                                                 valuePos += num_parsed;
1564                                                 if (num_parsed < 0)
1565                                                         return false;
1566
1567                                                 if (Char.IsDigit (s [valuePos]))
1568                                                         num_parsed = 0;
1569                                                 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1570                                                         return false;
1571                                                 valuePos += num_parsed;
1572
1573                                                 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1574                                                 num = 2;
1575                                                 explicit_kind = DateTimeKind.Local;
1576                                                 if (num_parsed < 0)
1577                                                         return false;
1578                                         }
1579                                         break;
1580 #endif
1581                                 // LAMESPEC: This should be part of UTCpattern
1582                                 // string and thus should not be considered here.
1583                                 //
1584                                 // Note that 'Z' is not defined as a pattern
1585                                 // character. Keep it for X509 certificate
1586                                 // verification. Also, "Z" != "'Z'" under MS.NET
1587                                 // ("'Z'" is just literal; handled above)
1588                                 case 'Z':
1589                                         if (s [valuePos] != 'Z')
1590                                                 return false;
1591                                         num = 0;
1592                                         num_parsed = 1;
1593                                         useutc = true;
1594                                         break;
1595
1596                                 case ':':
1597                                         if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1598                                                 return false;
1599                                         break;
1600                                 case '/':
1601                                         if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1602                                                 return false;
1603
1604                                         num = 0;
1605                                         break;
1606                                 default:
1607                                         if (s [valuePos] != chars [pos])
1608                                                         return false;
1609
1610                                         num = 0;
1611                                         num_parsed = 1;
1612                                         break;
1613                                 }
1614
1615                                 if (num_parsed < 0)
1616                                         return false;
1617
1618                                 valuePos += num_parsed;
1619
1620                                 if (!exact && !flexibleTwoPartsParsing) {
1621                                         switch (chars [pos]) {
1622                                         case 'm':
1623                                         case 's':
1624 #if NET_2_0
1625                                         case 'F':
1626 #endif
1627                                         case 'f':
1628                                         case 'z':
1629                                                 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1630                                                         (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1631                                                         useutc = true;
1632                                                         valuePos++;
1633                                                 }
1634                                                 break;
1635                                         }
1636                                 }
1637
1638                                 pos = pos + num + 1;
1639                                 num = 0;
1640                         }
1641
1642                         if (pos < len)
1643                                 return false;
1644
1645                         if (s.Length > valuePos) // extraneous tail.
1646                         {
1647                                 if (valuePos == 0)
1648                                         return false;
1649
1650                                 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1651                                         return false;
1652                                 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1653                                         return false;
1654                                 incompleteFormat = true;
1655                                 return false;
1656                         }
1657
1658                         if (hour == -1)
1659                                 hour = 0;
1660                         if (minute == -1)
1661                                 minute = 0;
1662
1663                         if (second == -1)
1664                                 second = 0;
1665                         if (fractionalSeconds == -1)
1666                                 fractionalSeconds = 0;
1667
1668                         // If no date was given
1669                         if ((day == -1) && (month == -1) && (year == -1)) {
1670                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1671                                         day = 1;
1672                                         month = 1;
1673                                         year = 1;
1674                                 } else {
1675                                         day = DateTime.Today.Day;
1676                                         month = DateTime.Today.Month;
1677                                         year = DateTime.Today.Year;
1678                                 }
1679                         }
1680
1681                         if (day == -1)
1682                                 day = 1;
1683                         if (month == -1)
1684                                 month = 1;
1685                         if (year == -1) {
1686                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1687                                         year = 1;
1688                                 else
1689                                         year = DateTime.Today.Year;
1690                         }
1691
1692                         if (ampm == 0 && hour == 12)
1693                                 hour = 0;
1694
1695                         if (ampm == 1 && (!flexibleTwoPartsParsing || hour < 12))
1696                                 hour = hour + 12;
1697                         
1698                         // For anything out of range 
1699                         // return false
1700                         if (year < 1 || year > 9999 || 
1701                                 month < 1 || month >12  ||
1702                                 day < 1 || day > DateTime.DaysInMonth(year, month) ||
1703                                 hour < 0 || hour > 23 ||
1704                                 minute < 0 || minute > 59 ||
1705                                 second < 0 || second > 59)
1706                                 return false;
1707
1708                         result = new DateTime (year, month, day, hour, minute, second, 0);
1709                         result = result.AddSeconds(fractionalSeconds);
1710
1711                         if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1712                                 return false;
1713
1714                         // If no timezone was specified, default to the local timezone.
1715                         TimeSpan utcoffset;
1716
1717                         if (useutc) {
1718                                 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1719                                         use_localtime = false;
1720                                 utcoffset = new TimeSpan (0, 0, 0);
1721                         } else if (tzsign == -1) {
1722                                 TimeZone tz = TimeZone.CurrentTimeZone;
1723                                 utcoffset = tz.GetUtcOffset (result);
1724                         } else {
1725                                 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1726                                         use_localtime = false;
1727
1728                                 if (tzoffmin == -1)
1729                                         tzoffmin = 0;
1730                                 if (tzoffset == -1)
1731                                         tzoffset = 0;
1732                                 if (tzsign == 1)
1733                                         tzoffset = -tzoffset;
1734
1735                                 utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
1736                         }
1737
1738                         long newticks = (result.ticks - utcoffset).Ticks;
1739
1740                         result = new DateTime (false, new TimeSpan (newticks));
1741 #if NET_2_0
1742                         if (explicit_kind != DateTimeKind.Unspecified)
1743                                 result.kind = explicit_kind;
1744                         else if (use_localtime)
1745                                 result = result.ToLocalTime ();
1746                         else
1747                                 result.kind = DateTimeKind.Utc;
1748 #else
1749                         if (use_localtime)
1750                                 result = result.ToLocalTime ();
1751 #endif
1752
1753                         return true;
1754                 }
1755
1756                 public static DateTime ParseExact (string s, string format,
1757                                                    IFormatProvider fp, DateTimeStyles style)
1758                 {
1759                         if (format == null)
1760                                 throw new ArgumentNullException ("format");
1761
1762                         string [] formats = new string [1];
1763                         formats[0] = format;
1764
1765                         return ParseExact (s, formats, fp, style);
1766                 }
1767
1768                 public static DateTime ParseExact (string s, string[] formats,
1769                                                    IFormatProvider fp,
1770                                                    DateTimeStyles style)
1771                 {
1772                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1773
1774                         if (s == null)
1775                                 throw new ArgumentNullException ("s");
1776                         if (formats == null)
1777                                 throw new ArgumentNullException ("formats");
1778                         if (formats.Length == 0)
1779                                 throw new FormatException ("Format specifier was invalid.");
1780
1781                         DateTime result;
1782                         bool longYear = false;
1783                         if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear))
1784                                 throw new FormatException ();
1785                         return result;
1786                 }
1787
1788 #if NET_2_0
1789                 public static bool TryParse (string s, out DateTime result)
1790                 {
1791                         try {
1792                                 result = Parse (s);
1793                         } catch {
1794                                 result = MinValue;
1795                                 return false;
1796                         }
1797                         return true;
1798                 }
1799                 
1800                 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1801                 {
1802                         try {
1803                                 result = Parse (s, provider, styles);
1804                         } catch {
1805                                 result = MinValue;
1806                                 return false;
1807                         }
1808                         return true;
1809                 }
1810                 
1811                 public static bool TryParseExact (string s, string format,
1812                                                   IFormatProvider fp,
1813                                                   DateTimeStyles style,
1814                                                   out DateTime result)
1815                 {
1816                         string[] formats;
1817
1818                         formats = new string [1];
1819                         formats[0] = format;
1820
1821                         return TryParseExact (s, formats, fp, style, out result);
1822                 }
1823
1824                 public static bool TryParseExact (string s, string[] formats,
1825                                                   IFormatProvider fp,
1826                                                   DateTimeStyles style,
1827                                                   out DateTime result)
1828                 {
1829                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1830
1831                         bool longYear = false;
1832                         return ParseExact (s, formats, dfi, style, out result, true, ref longYear);
1833                 }
1834 #endif
1835
1836                 private static bool ParseExact (string s, string [] formats,
1837                         DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1838                         bool exact, ref bool longYear)
1839                 {
1840                         int i;
1841                         bool incompleteFormat = false;
1842                         for (i = 0; i < formats.Length; i++)
1843                         {
1844                                 DateTime result;
1845                                 string format = formats[i];
1846                                 if (format == null || format == String.Empty)
1847                                         throw new FormatException ("Invalid Format String");
1848
1849                                 if (_DoParse (s, formats[i], null, exact, out result, dfi, style, false, ref incompleteFormat, ref longYear)) {
1850                                         ret = result;
1851                                         return true;
1852                                 }
1853                         }
1854                         ret = DateTime.MinValue;
1855                         return false;
1856                 }
1857                 
1858                 public TimeSpan Subtract(DateTime dt)
1859                 {
1860                         return new TimeSpan(ticks.Ticks) - dt.ticks;
1861                 }
1862
1863                 public DateTime Subtract(TimeSpan ts)
1864                 {
1865                         TimeSpan newticks;
1866
1867                         newticks = (new TimeSpan (ticks.Ticks)) - ts;
1868                         DateTime ret = new DateTime(true,newticks);
1869 #if NET_2_0
1870                         ret.kind = kind;
1871 #endif
1872                         return ret;
1873                 }
1874
1875                 public long ToFileTime()
1876                 {
1877                         DateTime universalTime = ToUniversalTime();
1878                         
1879                         if (universalTime.Ticks < w32file_epoch) {
1880                                 throw new ArgumentOutOfRangeException("file time is not valid");
1881                         }
1882                         
1883                         return(universalTime.Ticks - w32file_epoch);
1884                 }
1885
1886 #if NET_1_1
1887                 public long ToFileTimeUtc()
1888                 {
1889                         if (Ticks < w32file_epoch) {
1890                                 throw new ArgumentOutOfRangeException("file time is not valid");
1891                         }
1892                         
1893                         return (Ticks - w32file_epoch);
1894                 }
1895 #endif
1896
1897                 public string ToLongDateString()
1898                 {
1899                         return ToString ("D");
1900                 }
1901
1902                 public string ToLongTimeString()
1903                 {
1904                         return ToString ("T");
1905                 }
1906
1907                 public double ToOADate ()
1908                 {
1909                         long t = this.Ticks;
1910                         // uninitialized DateTime case
1911                         if (t == 0)
1912                                 return 0;
1913                         // we can't reach minimum value
1914                         if (t < 31242239136000000)
1915                                 return OAMinValue + 0.001;
1916
1917                         TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1918                         double result = ts.TotalDays;
1919                         // t < 0 (where 599264352000000000 == 0.0d for OA)
1920                         if (t < 599264352000000000) {
1921                                 // negative days (int) but decimals are positive
1922                                 double d = Math.Ceiling (result);
1923                                 result = d - 2 - (result - d);
1924                         }
1925                         else {
1926                                 // we can't reach maximum value
1927                                 if (result >= OAMaxValue)
1928                                         result = OAMaxValue - 0.00000001d;
1929                         }
1930                         return result;
1931                 }
1932
1933                 public string ToShortDateString()
1934                 {
1935                         return ToString ("d");
1936                 }
1937
1938                 public string ToShortTimeString()
1939                 {
1940                         return ToString ("t");
1941                 }
1942                 
1943                 public override string ToString ()
1944                 {
1945                         return ToString ("G", null);
1946                 }
1947
1948                 public string ToString (IFormatProvider fp)
1949                 {
1950                         return ToString (null, fp);
1951                 }
1952
1953                 public string ToString (string format)
1954                 {
1955                         return ToString (format, null);
1956                 }
1957
1958                 internal static string _GetStandardPattern (char format, DateTimeFormatInfo dfi, out bool useutc, out bool use_invariant)
1959                 {
1960                         String pattern;
1961
1962                         useutc = false;
1963                         use_invariant = false;
1964
1965                         switch (format)
1966                         {
1967                         case 'd':
1968                                 pattern = dfi.ShortDatePattern;
1969                                 break;
1970                         case 'D':
1971                                 pattern = dfi.LongDatePattern;
1972                                 break;
1973                         case 'f':
1974                                 pattern = dfi.LongDatePattern + " " + dfi.ShortTimePattern;
1975                                 break;
1976                         case 'F':
1977                                 pattern = dfi.FullDateTimePattern;
1978                                 break;
1979                         case 'g':
1980                                 pattern = dfi.ShortDatePattern + " " + dfi.ShortTimePattern;
1981                                 break;
1982                         case 'G':
1983                                 pattern = dfi.ShortDatePattern + " " + dfi.LongTimePattern;
1984                                 break;
1985                         case 'm':
1986                         case 'M':
1987                                 pattern = dfi.MonthDayPattern;
1988                                 break;
1989 #if NET_2_0
1990                         case 'o':
1991                                 pattern = dfi.RoundtripPattern;
1992                                 break;
1993 #endif
1994                         case 'r':
1995                         case 'R':
1996                                 pattern = dfi.RFC1123Pattern;
1997                                 // commented by LP 09/jun/2002, rfc 1123 pattern is always in GMT
1998                                 // uncommented by AE 27/may/2004
1999 //                              useutc = true;
2000                                 use_invariant = true;
2001                                 break;
2002                         case 's':
2003                                 pattern = dfi.SortableDateTimePattern;
2004                                 break;
2005                         case 't':
2006                                 pattern = dfi.ShortTimePattern;
2007                                 break;
2008                         case 'T':
2009                                 pattern = dfi.LongTimePattern;
2010                                 break;
2011                         case 'u':
2012                                 pattern = dfi.UniversalSortableDateTimePattern;
2013                                 useutc = true;
2014                                 break;
2015                         case 'U':
2016 //                              pattern = dfi.LongDatePattern + " " + dfi.LongTimePattern;
2017                                 pattern = dfi.FullDateTimePattern;
2018                                 useutc = true;
2019                                 break;
2020                         case 'y':
2021                         case 'Y':
2022                                 pattern = dfi.YearMonthPattern;
2023                                 break;
2024                         default:
2025                                 pattern = null;
2026                                 break;
2027 //                              throw new FormatException (String.Format ("Invalid format pattern: '{0}'", format));
2028                         }
2029
2030                         return pattern;
2031                 }
2032
2033                 internal string _ToString (string format, DateTimeFormatInfo dfi)
2034                 {
2035                         // the length of the format is usually a good guess of the number
2036                         // of chars in the result. Might save us a few bytes sometimes
2037                         // Add + 10 for cases like mmmm dddd
2038                         StringBuilder result = new StringBuilder (format.Length + 10);
2039
2040                         // For some cases, the output should not use culture dependent calendar
2041                         DateTimeFormatInfo inv = DateTimeFormatInfo.InvariantInfo;
2042                         if (format == inv.RFC1123Pattern)
2043                                 dfi = inv;
2044                         else if (format == inv.UniversalSortableDateTimePattern)
2045                                 dfi = inv;
2046
2047                         int i = 0;
2048
2049                         while (i < format.Length) {
2050                                 int tokLen;
2051                                 bool omitZeros = false;
2052                                 char ch = format [i];
2053
2054                                 switch (ch) {
2055
2056                                 //
2057                                 // Time Formats
2058                                 //
2059                                 case 'h':
2060                                         // hour, [1, 12]
2061                                         tokLen = CountRepeat (format, i, ch);
2062
2063                                         int hr = this.Hour % 12;
2064                                         if (hr == 0)
2065                                                 hr = 12;
2066
2067                                         ZeroPad (result, hr, tokLen == 1 ? 1 : 2);
2068                                         break;
2069                                 case 'H':
2070                                         // hour, [0, 23]
2071                                         tokLen = CountRepeat (format, i, ch);
2072                                         ZeroPad (result, this.Hour, tokLen == 1 ? 1 : 2);
2073                                         break;
2074                                 case 'm':
2075                                         // minute, [0, 59]
2076                                         tokLen = CountRepeat (format, i, ch);
2077                                         ZeroPad (result, this.Minute, tokLen == 1 ? 1 : 2);
2078                                         break;
2079                                 case 's':
2080                                         // second [0, 29]
2081                                         tokLen = CountRepeat (format, i, ch);
2082                                         ZeroPad (result, this.Second, tokLen == 1 ? 1 : 2);
2083                                         break;
2084 #if NET_2_0
2085                                 case 'F':
2086                                         omitZeros = true;
2087                                         goto case 'f';
2088 #endif
2089                                 case 'f':
2090                                         // fraction of second, to same number of
2091                                         // digits as there are f's
2092
2093                                         tokLen = CountRepeat (format, i, ch);
2094                                         if (tokLen > 7)
2095                                                 throw new FormatException ("Invalid Format String");
2096
2097                                         int dec = (int)((long)(this.Ticks % TimeSpan.TicksPerSecond) / (long) Math.Pow (10, 7 - tokLen));
2098                                         int startLen = result.Length;
2099                                         ZeroPad (result, dec, tokLen);
2100
2101                                         if (omitZeros) {
2102                                                 while (result.Length > startLen && result [result.Length - 1] == '0')
2103                                                         result.Length--;
2104                                                 // when the value was 0, then trim even preceding '.' (!) It is fixed character.
2105                                                 if (dec == 0 && startLen > 0 && result [startLen - 1] == '.')
2106                                                         result.Length--;
2107                                         }
2108
2109                                         break;
2110                                 case 't':
2111                                         // AM/PM. t == first char, tt+ == full
2112                                         tokLen = CountRepeat (format, i, ch);
2113                                         string desig = this.Hour < 12 ? dfi.AMDesignator : dfi.PMDesignator;
2114
2115                                         if (tokLen == 1) {
2116                                                 if (desig.Length >= 1)
2117                                                         result.Append (desig [0]);
2118                                         }
2119                                         else
2120                                                 result.Append (desig);
2121
2122                                         break;
2123                                 case 'z':
2124                                         // timezone. t = +/-h; tt = +/-hh; ttt+=+/-hh:mm
2125                                         tokLen = CountRepeat (format, i, ch);
2126                                         TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
2127
2128                                         if (offset.Ticks >= 0)
2129                                                 result.Append ('+');
2130                                         else
2131                                                 result.Append ('-');
2132
2133                                         switch (tokLen) {
2134                                         case 1:
2135                                                 result.Append (Math.Abs (offset.Hours));
2136                                                 break;
2137                                         case 2:
2138                                                 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2139                                                 break;
2140                                         default:
2141                                                 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2142                                                 result.Append (':');
2143                                                 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
2144                                                 break;
2145                                         }
2146                                         break;
2147 #if NET_2_0
2148                                 case 'K': // 'Z' (UTC) or zzz (Local)
2149                                         tokLen = 1;
2150                                         switch (kind) {
2151                                         case DateTimeKind.Utc:
2152                                                 result.Append ('Z');
2153                                                 break;
2154                                         case DateTimeKind.Local:
2155                                                 offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
2156                                                 if (offset.Ticks >= 0)
2157                                                         result.Append ('+');
2158                                                 else
2159                                                         result.Append ('-');
2160                                                 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2161                                                 result.Append (':');
2162                                                 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
2163                                                 break;
2164                                         }
2165                                         break;
2166 #endif
2167                                 //
2168                                 // Date tokens
2169                                 //
2170                                 case 'd':
2171                                         // day. d(d?) = day of month (leading 0 if two d's)
2172                                         // ddd = three leter day of week
2173                                         // dddd+ full day-of-week
2174                                         tokLen = CountRepeat (format, i, ch);
2175
2176                                         if (tokLen <= 2)
2177                                                 ZeroPad (result, dfi.Calendar.GetDayOfMonth (this), tokLen == 1 ? 1 : 2);
2178                                         else if (tokLen == 3)
2179                                                 result.Append (dfi.GetAbbreviatedDayName (dfi.Calendar.GetDayOfWeek (this)));
2180                                         else
2181                                                 result.Append (dfi.GetDayName (dfi.Calendar.GetDayOfWeek (this)));
2182
2183                                         break;
2184                                 case 'M':
2185                                         // Month.m(m?) = month # (with leading 0 if two mm)
2186                                         // mmm = 3 letter name
2187                                         // mmmm+ = full name
2188                                         tokLen = CountRepeat (format, i, ch);
2189                                         int month = dfi.Calendar.GetMonth(this);
2190                                         if (tokLen <= 2)
2191                                                 ZeroPad (result, month, tokLen);
2192                                         else if (tokLen == 3)
2193                                                 result.Append (dfi.GetAbbreviatedMonthName (month));
2194                                         else
2195                                                 result.Append (dfi.GetMonthName (month));
2196
2197                                         break;
2198                                 case 'y':
2199                                         // Year. y(y?) = two digit year, with leading 0 if yy
2200                                         // yyy+ full year with leading zeros if needed.
2201                                         tokLen = CountRepeat (format, i, ch);
2202
2203                                         if (tokLen <= 2)
2204                                                 ZeroPad (result, dfi.Calendar.GetYear (this) % 100, tokLen);
2205                                         else
2206                                                 ZeroPad (result, dfi.Calendar.GetYear (this), tokLen);
2207                                         break;
2208
2209                                 case 'g':
2210                                         // Era name
2211                                         tokLen = CountRepeat (format, i, ch);
2212                                         result.Append (dfi.GetEraName (dfi.Calendar.GetEra (this)));
2213                                         break;
2214
2215                                 //
2216                                 // Other
2217                                 //
2218                                 case ':':
2219                                         result.Append (dfi.TimeSeparator);
2220                                         tokLen = 1;
2221                                         break;
2222                                 case '/':
2223                                         result.Append (dfi.DateSeparator);
2224                                         tokLen = 1;
2225                                         break;
2226                                 case '\'': case '"':
2227                                         tokLen = ParseQuotedString (format, i, result);
2228                                         break;
2229                                 case '%':
2230                                         if (i >= format.Length - 1)
2231                                                 throw new FormatException ("% at end of date time string");
2232                                         if (format [i + 1] == '%')
2233                                                 throw new FormatException ("%% in date string");
2234
2235                                         // Look for the next char
2236                                         tokLen = 1;
2237                                         break;
2238                                 case '\\':
2239                                         // C-Style escape
2240                                         if (i >= format.Length - 1)
2241                                                 throw new FormatException ("\\ at end of date time string");
2242
2243                                         result.Append (format [i + 1]);
2244                                         tokLen = 2;
2245
2246                                         break;
2247                                 default:
2248                                         // catch all
2249                                         result.Append (ch);
2250                                         tokLen = 1;
2251                                         break;
2252                                 }
2253                                 i += tokLen;
2254                         }
2255                         return result.ToString ();
2256                 }
2257                 
2258                 static int CountRepeat (string fmt, int p, char c)
2259                 {
2260                         int l = fmt.Length;
2261                         int i = p + 1;
2262                         while ((i < l) && (fmt [i] == c)) 
2263                                 i++;
2264                         
2265                         return i - p;
2266                 }
2267                 
2268                 static int ParseQuotedString (string fmt, int pos, StringBuilder output)
2269                 {
2270                         // pos == position of " or '
2271                         
2272                         int len = fmt.Length;
2273                         int start = pos;
2274                         char quoteChar = fmt [pos++];
2275                         
2276                         while (pos < len) {
2277                                 char ch = fmt [pos++];
2278                                 
2279                                 if (ch == quoteChar)
2280                                         return pos - start;
2281                                 
2282                                 if (ch == '\\') {
2283                                         // C-Style escape
2284                                         if (pos >= len)
2285                                                 throw new FormatException("Un-ended quote");
2286         
2287                                         output.Append (fmt [pos++]);
2288                                 } else {
2289                                         output.Append (ch);
2290                                 }
2291                         }
2292
2293                         throw new FormatException("Un-ended quote");
2294                 }
2295                 
2296                 static unsafe void ZeroPad (StringBuilder output, int digits, int len)
2297                 {
2298                         // more than enough for an int
2299                         char* buffer = stackalloc char [16];
2300                         int pos = 16;
2301                         
2302                         do {
2303                                 buffer [-- pos] = (char) ('0' + digits % 10);
2304                                 digits /= 10;
2305                                 len --;
2306                         } while (digits > 0);
2307                         
2308                         while (len -- > 0)
2309                                 buffer [-- pos] = '0';
2310                         
2311                         output.Append (new string (buffer, pos, 16 - pos));
2312                 }
2313
2314                 public string ToString (string format, IFormatProvider fp)
2315                 {
2316                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
2317
2318                         if (format == null || format == String.Empty)
2319                                 format = "G";
2320
2321                         bool useutc = false, use_invariant = false;
2322
2323                         if (format.Length == 1) {
2324                                 char fchar = format [0];
2325                                 format = _GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2326                                 if (fchar == 'U')
2327                                         return ToUniversalTime()._ToString (format, dfi);
2328
2329                                 if (format == null)
2330                                         throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2331                         }
2332
2333                         // Don't convert UTC value. It just adds 'Z' for 
2334                         // 'u' format, for the same ticks.
2335                         return this._ToString (format, dfi);
2336                 }
2337
2338                 public DateTime ToLocalTime ()
2339                 {
2340                         return TimeZone.CurrentTimeZone.ToLocalTime (this);
2341                 }
2342
2343                 public DateTime ToUniversalTime()
2344                 {
2345                         return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2346                 }
2347
2348                 /*  OPERATORS */
2349
2350                 public static DateTime operator +(DateTime d, TimeSpan t)
2351                 {
2352                         DateTime ret = new DateTime (true, d.ticks + t);
2353 #if NET_2_0
2354                         ret.kind = d.kind;
2355 #endif
2356                         return ret;
2357                 }
2358
2359                 public static bool operator ==(DateTime d1, DateTime d2)
2360                 {
2361                         return (d1.ticks == d2.ticks);
2362                 }
2363
2364                 public static bool operator >(DateTime t1,DateTime t2)
2365                 {
2366                         return (t1.ticks > t2.ticks);
2367                 }
2368
2369                 public static bool operator >=(DateTime t1,DateTime t2)
2370                 {
2371                         return (t1.ticks >= t2.ticks);
2372                 }
2373
2374                 public static bool operator !=(DateTime d1, DateTime d2)
2375                 {
2376                         return (d1.ticks != d2.ticks);
2377                 }
2378
2379                 public static bool operator <(DateTime t1,      DateTime t2)
2380                 {
2381                         return (t1.ticks < t2.ticks );
2382                 }
2383
2384                 public static bool operator <=(DateTime t1,DateTime t2)
2385                 {
2386                         return (t1.ticks <= t2.ticks);
2387                 }
2388
2389                 public static TimeSpan operator -(DateTime d1,DateTime d2)
2390                 {
2391                         return new TimeSpan((d1.ticks - d2.ticks).Ticks);
2392                 }
2393
2394                 public static DateTime operator -(DateTime d,TimeSpan t)
2395                 {
2396                         DateTime ret = new DateTime (true, d.ticks - t);
2397 #if NET_2_0
2398                         ret.kind = d.kind;
2399 #endif
2400                         return ret;
2401                 }
2402
2403                 bool IConvertible.ToBoolean(IFormatProvider provider)
2404                 {
2405                         throw new InvalidCastException();
2406                 }
2407                 
2408                 byte IConvertible.ToByte(IFormatProvider provider)
2409                 {
2410                         throw new InvalidCastException();
2411
2412                 }
2413
2414                 char IConvertible.ToChar(IFormatProvider provider)
2415                 {
2416                         throw new InvalidCastException();
2417                 }
2418
2419                 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
2420                 {
2421                         return this;
2422                 } 
2423                 
2424                 decimal IConvertible.ToDecimal(IFormatProvider provider)
2425                 {
2426                          throw new InvalidCastException();
2427                 }
2428
2429                 double IConvertible.ToDouble(IFormatProvider provider)
2430                 {
2431                         throw new InvalidCastException();
2432                 }
2433
2434                 Int16 IConvertible.ToInt16(IFormatProvider provider)
2435                 {
2436                         throw new InvalidCastException();
2437                 }
2438
2439                 Int32 IConvertible.ToInt32(IFormatProvider provider)
2440                 {
2441                         throw new InvalidCastException();
2442                 }
2443
2444                 Int64 IConvertible.ToInt64(IFormatProvider provider)
2445                 {
2446                         throw new InvalidCastException();
2447                 }
2448
2449                 SByte IConvertible.ToSByte(IFormatProvider provider)
2450                 {
2451                         throw new InvalidCastException();
2452                 }
2453
2454                 Single IConvertible.ToSingle(IFormatProvider provider)
2455                 {
2456                         throw new InvalidCastException();
2457                 }
2458
2459                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2460                 {
2461                         if (conversionType == null)
2462                                 throw new ArgumentNullException ("conversionType");
2463
2464                         if (conversionType == typeof (DateTime))
2465                                 return this;
2466                         else if (conversionType == typeof (String))
2467                                 return this.ToString (provider);
2468                         else if (conversionType == typeof (Object))
2469                                 return this;
2470                         else
2471                                 throw new InvalidCastException();
2472                 }
2473
2474                 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
2475                 {
2476                         throw new InvalidCastException();
2477                 }
2478                 
2479                 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
2480                 {
2481                         throw new InvalidCastException();
2482                 }
2483
2484                 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
2485                 {
2486                         throw new InvalidCastException();
2487                 }
2488         }
2489 }