2007-11-03 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / corlib / System / DateTime.cs
1 //
2 // System.DateTime.cs
3 //
4 // author:
5 //   Marcel Narings (marcel@narings.nl)
6 //   Martin Baulig (martin@gnome.org)
7 //   Atsushi Enomoto (atsushi@ximian.com)
8 //
9 //   (C) 2001 Marcel Narings
10 // Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Collections;
33 using System.Globalization;
34 using System.Runtime.CompilerServices;
35 using System.Runtime.InteropServices;
36 using System.Text;
37
38 namespace System
39 {
40         /// <summary>
41         /// The DateTime structure represents dates and time ranging from
42         /// 1-1-0001 12:00:00 AM to 31-12-9999 23:59:00 Common Era.
43         /// </summary>
44         /// 
45         [Serializable]
46         [StructLayout (LayoutKind.Auto)]
47         public struct DateTime : IFormattable, IConvertible, IComparable
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) ToUniversalTime ().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.Utc).ToLocalTime ();
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                                                 useutc = true;
1551                                                 explicit_kind = DateTimeKind.Utc;
1552                                         }
1553                                         else if (s [valuePos] == '+' || s [valuePos] == '-') {
1554                                                 if (tzsign != -1)
1555                                                         return false;
1556                                                 if (s [valuePos] == '+')
1557                                                         tzsign = 0;
1558                                                 else if (s [valuePos] == '-')
1559                                                         tzsign = 1;
1560                                                 valuePos++;
1561
1562                                                 // zzz
1563                                                 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1564                                                 valuePos += num_parsed;
1565                                                 if (num_parsed < 0)
1566                                                         return false;
1567
1568                                                 if (Char.IsDigit (s [valuePos]))
1569                                                         num_parsed = 0;
1570                                                 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1571                                                         return false;
1572                                                 valuePos += num_parsed;
1573
1574                                                 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1575                                                 num = 2;
1576                                                 explicit_kind = DateTimeKind.Local;
1577                                                 if (num_parsed < 0)
1578                                                         return false;
1579                                         }
1580                                         break;
1581 #endif
1582                                 // LAMESPEC: This should be part of UTCpattern
1583                                 // string and thus should not be considered here.
1584                                 //
1585                                 // Note that 'Z' is not defined as a pattern
1586                                 // character. Keep it for X509 certificate
1587                                 // verification. Also, "Z" != "'Z'" under MS.NET
1588                                 // ("'Z'" is just literal; handled above)
1589                                 case 'Z':
1590                                         if (s [valuePos] != 'Z')
1591                                                 return false;
1592                                         num = 0;
1593                                         num_parsed = 1;
1594                                         useutc = true;
1595                                         break;
1596
1597                                 case ':':
1598                                         if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1599                                                 return false;
1600                                         break;
1601                                 case '/':
1602                                         if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1603                                                 return false;
1604
1605                                         num = 0;
1606                                         break;
1607                                 default:
1608                                         if (s [valuePos] != chars [pos])
1609                                                         return false;
1610
1611                                         num = 0;
1612                                         num_parsed = 1;
1613                                         break;
1614                                 }
1615
1616                                 if (num_parsed < 0)
1617                                         return false;
1618
1619                                 valuePos += num_parsed;
1620
1621                                 if (!exact && !flexibleTwoPartsParsing) {
1622                                         switch (chars [pos]) {
1623                                         case 'm':
1624                                         case 's':
1625 #if NET_2_0
1626                                         case 'F':
1627 #endif
1628                                         case 'f':
1629                                         case 'z':
1630                                                 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1631                                                         (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1632                                                         useutc = true;
1633                                                         valuePos++;
1634                                                 }
1635                                                 break;
1636                                         }
1637                                 }
1638
1639                                 pos = pos + num + 1;
1640                                 num = 0;
1641                         }
1642
1643 #if NET_2_0
1644                         while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1645                                 pos++;
1646 #endif
1647                         if (pos < len)
1648                                 return false;
1649
1650                         if (s.Length > valuePos) // extraneous tail.
1651                         {
1652                                 if (valuePos == 0)
1653                                         return false;
1654
1655                                 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1656                                         return false;
1657                                 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1658                                         return false;
1659                                 incompleteFormat = true;
1660                                 return false;
1661                         }
1662
1663                         if (hour == -1)
1664                                 hour = 0;
1665                         if (minute == -1)
1666                                 minute = 0;
1667
1668                         if (second == -1)
1669                                 second = 0;
1670                         if (fractionalSeconds == -1)
1671                                 fractionalSeconds = 0;
1672
1673                         // If no date was given
1674                         if ((day == -1) && (month == -1) && (year == -1)) {
1675                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1676                                         day = 1;
1677                                         month = 1;
1678                                         year = 1;
1679                                 } else {
1680                                         day = DateTime.Today.Day;
1681                                         month = DateTime.Today.Month;
1682                                         year = DateTime.Today.Year;
1683                                 }
1684                         }
1685
1686                         if (day == -1)
1687                                 day = 1;
1688                         if (month == -1)
1689                                 month = 1;
1690                         if (year == -1) {
1691                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1692                                         year = 1;
1693                                 else
1694                                         year = DateTime.Today.Year;
1695                         }
1696
1697                         if (ampm == 0 && hour == 12)
1698                                 hour = 0;
1699
1700                         if (ampm == 1 && (!flexibleTwoPartsParsing || hour < 12))
1701                                 hour = hour + 12;
1702                         
1703                         // For anything out of range 
1704                         // return false
1705                         if (year < 1 || year > 9999 || 
1706                                 month < 1 || month >12  ||
1707                                 day < 1 || day > DateTime.DaysInMonth(year, month) ||
1708                                 hour < 0 || hour > 23 ||
1709                                 minute < 0 || minute > 59 ||
1710                                 second < 0 || second > 59)
1711                                 return false;
1712
1713                         result = new DateTime (year, month, day, hour, minute, second, 0);
1714                         result = result.AddSeconds(fractionalSeconds);
1715
1716                         if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1717                                 return false;
1718
1719                         // If no timezone was specified, default to the local timezone.
1720                         TimeSpan utcoffset;
1721
1722                         if (useutc) {
1723                                 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1724                                         use_localtime = false;
1725                                 utcoffset = new TimeSpan (0, 0, 0);
1726                         } else if (tzsign == -1) {
1727                                 TimeZone tz = TimeZone.CurrentTimeZone;
1728                                 utcoffset = tz.GetUtcOffset (result);
1729                         } else {
1730                                 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1731                                         use_localtime = false;
1732
1733                                 if (tzoffmin == -1)
1734                                         tzoffmin = 0;
1735                                 if (tzoffset == -1)
1736                                         tzoffset = 0;
1737                                 if (tzsign == 1)
1738                                         tzoffset = -tzoffset;
1739
1740                                 utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
1741                         }
1742
1743                         long newticks = (result.ticks - utcoffset).Ticks;
1744
1745                         result = new DateTime (false, new TimeSpan (newticks));
1746 #if NET_2_0
1747                         if (explicit_kind != DateTimeKind.Unspecified)
1748                                 result.kind = explicit_kind;
1749                         else if (use_localtime)
1750                                 result = result.ToLocalTime ();
1751                         else
1752                                 result.kind = DateTimeKind.Utc;
1753 #else
1754                         if (use_localtime)
1755                                 result = result.ToLocalTime ();
1756 #endif
1757
1758                         return true;
1759                 }
1760
1761                 public static DateTime ParseExact (string s, string format,
1762                                                    IFormatProvider fp, DateTimeStyles style)
1763                 {
1764                         if (format == null)
1765                                 throw new ArgumentNullException ("format");
1766
1767                         string [] formats = new string [1];
1768                         formats[0] = format;
1769
1770                         return ParseExact (s, formats, fp, style);
1771                 }
1772
1773                 public static DateTime ParseExact (string s, string[] formats,
1774                                                    IFormatProvider fp,
1775                                                    DateTimeStyles style)
1776                 {
1777                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1778
1779                         if (s == null)
1780                                 throw new ArgumentNullException ("s");
1781                         if (formats == null)
1782                                 throw new ArgumentNullException ("formats");
1783                         if (formats.Length == 0)
1784                                 throw new FormatException ("Format specifier was invalid.");
1785
1786                         DateTime result;
1787                         bool longYear = false;
1788                         if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear))
1789                                 throw new FormatException ();
1790                         return result;
1791                 }
1792
1793 #if NET_2_0
1794                 public static bool TryParse (string s, out DateTime result)
1795                 {
1796                         try {
1797                                 result = Parse (s);
1798                         } catch {
1799                                 result = MinValue;
1800                                 return false;
1801                         }
1802                         return true;
1803                 }
1804                 
1805                 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1806                 {
1807                         try {
1808                                 result = Parse (s, provider, styles);
1809                         } catch {
1810                                 result = MinValue;
1811                                 return false;
1812                         }
1813                         return true;
1814                 }
1815                 
1816                 public static bool TryParseExact (string s, string format,
1817                                                   IFormatProvider fp,
1818                                                   DateTimeStyles style,
1819                                                   out DateTime result)
1820                 {
1821                         string[] formats;
1822
1823                         formats = new string [1];
1824                         formats[0] = format;
1825
1826                         return TryParseExact (s, formats, fp, style, out result);
1827                 }
1828
1829                 public static bool TryParseExact (string s, string[] formats,
1830                                                   IFormatProvider fp,
1831                                                   DateTimeStyles style,
1832                                                   out DateTime result)
1833                 {
1834                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1835
1836                         bool longYear = false;
1837                         return ParseExact (s, formats, dfi, style, out result, true, ref longYear);
1838                 }
1839 #endif
1840
1841                 private static bool ParseExact (string s, string [] formats,
1842                         DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1843                         bool exact, ref bool longYear)
1844                 {
1845                         int i;
1846                         bool incompleteFormat = false;
1847                         for (i = 0; i < formats.Length; i++)
1848                         {
1849                                 DateTime result;
1850                                 string format = formats[i];
1851                                 if (format == null || format == String.Empty)
1852                                         throw new FormatException ("Invalid Format String");
1853
1854                                 if (_DoParse (s, formats[i], null, exact, out result, dfi, style, false, ref incompleteFormat, ref longYear)) {
1855                                         ret = result;
1856                                         return true;
1857                                 }
1858                         }
1859                         ret = DateTime.MinValue;
1860                         return false;
1861                 }
1862                 
1863                 public TimeSpan Subtract(DateTime dt)
1864                 {
1865                         return new TimeSpan(ticks.Ticks) - dt.ticks;
1866                 }
1867
1868                 public DateTime Subtract(TimeSpan ts)
1869                 {
1870                         TimeSpan newticks;
1871
1872                         newticks = (new TimeSpan (ticks.Ticks)) - ts;
1873                         DateTime ret = new DateTime(true,newticks);
1874 #if NET_2_0
1875                         ret.kind = kind;
1876 #endif
1877                         return ret;
1878                 }
1879
1880                 public long ToFileTime()
1881                 {
1882                         DateTime universalTime = ToUniversalTime();
1883                         
1884                         if (universalTime.Ticks < w32file_epoch) {
1885                                 throw new ArgumentOutOfRangeException("file time is not valid");
1886                         }
1887                         
1888                         return(universalTime.Ticks - w32file_epoch);
1889                 }
1890
1891 #if NET_1_1
1892                 public long ToFileTimeUtc()
1893                 {
1894                         if (Ticks < w32file_epoch) {
1895                                 throw new ArgumentOutOfRangeException("file time is not valid");
1896                         }
1897                         
1898                         return (Ticks - w32file_epoch);
1899                 }
1900 #endif
1901
1902                 public string ToLongDateString()
1903                 {
1904                         return ToString ("D");
1905                 }
1906
1907                 public string ToLongTimeString()
1908                 {
1909                         return ToString ("T");
1910                 }
1911
1912                 public double ToOADate ()
1913                 {
1914                         long t = this.Ticks;
1915                         // uninitialized DateTime case
1916                         if (t == 0)
1917                                 return 0;
1918                         // we can't reach minimum value
1919                         if (t < 31242239136000000)
1920                                 return OAMinValue + 0.001;
1921
1922                         TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1923                         double result = ts.TotalDays;
1924                         // t < 0 (where 599264352000000000 == 0.0d for OA)
1925                         if (t < 599264352000000000) {
1926                                 // negative days (int) but decimals are positive
1927                                 double d = Math.Ceiling (result);
1928                                 result = d - 2 - (result - d);
1929                         }
1930                         else {
1931                                 // we can't reach maximum value
1932                                 if (result >= OAMaxValue)
1933                                         result = OAMaxValue - 0.00000001d;
1934                         }
1935                         return result;
1936                 }
1937
1938                 public string ToShortDateString()
1939                 {
1940                         return ToString ("d");
1941                 }
1942
1943                 public string ToShortTimeString()
1944                 {
1945                         return ToString ("t");
1946                 }
1947                 
1948                 public override string ToString ()
1949                 {
1950                         return ToString ("G", null);
1951                 }
1952
1953                 public string ToString (IFormatProvider fp)
1954                 {
1955                         return ToString (null, fp);
1956                 }
1957
1958                 public string ToString (string format)
1959                 {
1960                         return ToString (format, null);
1961                 }
1962
1963                 internal static string _GetStandardPattern (char format, DateTimeFormatInfo dfi, out bool useutc, out bool use_invariant)
1964                 {
1965                         String pattern;
1966
1967                         useutc = false;
1968                         use_invariant = false;
1969
1970                         switch (format)
1971                         {
1972                         case 'd':
1973                                 pattern = dfi.ShortDatePattern;
1974                                 break;
1975                         case 'D':
1976                                 pattern = dfi.LongDatePattern;
1977                                 break;
1978                         case 'f':
1979                                 pattern = dfi.LongDatePattern + " " + dfi.ShortTimePattern;
1980                                 break;
1981                         case 'F':
1982                                 pattern = dfi.FullDateTimePattern;
1983                                 break;
1984                         case 'g':
1985                                 pattern = dfi.ShortDatePattern + " " + dfi.ShortTimePattern;
1986                                 break;
1987                         case 'G':
1988                                 pattern = dfi.ShortDatePattern + " " + dfi.LongTimePattern;
1989                                 break;
1990                         case 'm':
1991                         case 'M':
1992                                 pattern = dfi.MonthDayPattern;
1993                                 break;
1994 #if NET_2_0
1995                         case 'o':
1996                                 pattern = dfi.RoundtripPattern;
1997                                 break;
1998 #endif
1999                         case 'r':
2000                         case 'R':
2001                                 pattern = dfi.RFC1123Pattern;
2002                                 // commented by LP 09/jun/2002, rfc 1123 pattern is always in GMT
2003                                 // uncommented by AE 27/may/2004
2004 //                              useutc = true;
2005                                 use_invariant = true;
2006                                 break;
2007                         case 's':
2008                                 pattern = dfi.SortableDateTimePattern;
2009                                 break;
2010                         case 't':
2011                                 pattern = dfi.ShortTimePattern;
2012                                 break;
2013                         case 'T':
2014                                 pattern = dfi.LongTimePattern;
2015                                 break;
2016                         case 'u':
2017                                 pattern = dfi.UniversalSortableDateTimePattern;
2018                                 useutc = true;
2019                                 break;
2020                         case 'U':
2021 //                              pattern = dfi.LongDatePattern + " " + dfi.LongTimePattern;
2022                                 pattern = dfi.FullDateTimePattern;
2023                                 useutc = true;
2024                                 break;
2025                         case 'y':
2026                         case 'Y':
2027                                 pattern = dfi.YearMonthPattern;
2028                                 break;
2029                         default:
2030                                 pattern = null;
2031                                 break;
2032 //                              throw new FormatException (String.Format ("Invalid format pattern: '{0}'", format));
2033                         }
2034
2035                         return pattern;
2036                 }
2037
2038                 internal string _ToString (string format, DateTimeFormatInfo dfi)
2039                 {
2040                         // the length of the format is usually a good guess of the number
2041                         // of chars in the result. Might save us a few bytes sometimes
2042                         // Add + 10 for cases like mmmm dddd
2043                         StringBuilder result = new StringBuilder (format.Length + 10);
2044
2045                         // For some cases, the output should not use culture dependent calendar
2046                         DateTimeFormatInfo inv = DateTimeFormatInfo.InvariantInfo;
2047                         if (format == inv.RFC1123Pattern)
2048                                 dfi = inv;
2049                         else if (format == inv.UniversalSortableDateTimePattern)
2050                                 dfi = inv;
2051
2052                         int i = 0;
2053
2054                         while (i < format.Length) {
2055                                 int tokLen;
2056                                 bool omitZeros = false;
2057                                 char ch = format [i];
2058
2059                                 switch (ch) {
2060
2061                                 //
2062                                 // Time Formats
2063                                 //
2064                                 case 'h':
2065                                         // hour, [1, 12]
2066                                         tokLen = CountRepeat (format, i, ch);
2067
2068                                         int hr = this.Hour % 12;
2069                                         if (hr == 0)
2070                                                 hr = 12;
2071
2072                                         ZeroPad (result, hr, tokLen == 1 ? 1 : 2);
2073                                         break;
2074                                 case 'H':
2075                                         // hour, [0, 23]
2076                                         tokLen = CountRepeat (format, i, ch);
2077                                         ZeroPad (result, this.Hour, tokLen == 1 ? 1 : 2);
2078                                         break;
2079                                 case 'm':
2080                                         // minute, [0, 59]
2081                                         tokLen = CountRepeat (format, i, ch);
2082                                         ZeroPad (result, this.Minute, tokLen == 1 ? 1 : 2);
2083                                         break;
2084                                 case 's':
2085                                         // second [0, 29]
2086                                         tokLen = CountRepeat (format, i, ch);
2087                                         ZeroPad (result, this.Second, tokLen == 1 ? 1 : 2);
2088                                         break;
2089 #if NET_2_0
2090                                 case 'F':
2091                                         omitZeros = true;
2092                                         goto case 'f';
2093 #endif
2094                                 case 'f':
2095                                         // fraction of second, to same number of
2096                                         // digits as there are f's
2097
2098                                         tokLen = CountRepeat (format, i, ch);
2099                                         if (tokLen > 7)
2100                                                 throw new FormatException ("Invalid Format String");
2101
2102                                         int dec = (int)((long)(this.Ticks % TimeSpan.TicksPerSecond) / (long) Math.Pow (10, 7 - tokLen));
2103                                         int startLen = result.Length;
2104                                         ZeroPad (result, dec, tokLen);
2105
2106                                         if (omitZeros) {
2107                                                 while (result.Length > startLen && result [result.Length - 1] == '0')
2108                                                         result.Length--;
2109                                                 // when the value was 0, then trim even preceding '.' (!) It is fixed character.
2110                                                 if (dec == 0 && startLen > 0 && result [startLen - 1] == '.')
2111                                                         result.Length--;
2112                                         }
2113
2114                                         break;
2115                                 case 't':
2116                                         // AM/PM. t == first char, tt+ == full
2117                                         tokLen = CountRepeat (format, i, ch);
2118                                         string desig = this.Hour < 12 ? dfi.AMDesignator : dfi.PMDesignator;
2119
2120                                         if (tokLen == 1) {
2121                                                 if (desig.Length >= 1)
2122                                                         result.Append (desig [0]);
2123                                         }
2124                                         else
2125                                                 result.Append (desig);
2126
2127                                         break;
2128                                 case 'z':
2129                                         // timezone. t = +/-h; tt = +/-hh; ttt+=+/-hh:mm
2130                                         tokLen = CountRepeat (format, i, ch);
2131                                         TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
2132
2133                                         if (offset.Ticks >= 0)
2134                                                 result.Append ('+');
2135                                         else
2136                                                 result.Append ('-');
2137
2138                                         switch (tokLen) {
2139                                         case 1:
2140                                                 result.Append (Math.Abs (offset.Hours));
2141                                                 break;
2142                                         case 2:
2143                                                 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2144                                                 break;
2145                                         default:
2146                                                 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2147                                                 result.Append (':');
2148                                                 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
2149                                                 break;
2150                                         }
2151                                         break;
2152 #if NET_2_0
2153                                 case 'K': // 'Z' (UTC) or zzz (Local)
2154                                         tokLen = 1;
2155                                         switch (kind) {
2156                                         case DateTimeKind.Utc:
2157                                                 result.Append ('Z');
2158                                                 break;
2159                                         case DateTimeKind.Local:
2160                                                 offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
2161                                                 if (offset.Ticks >= 0)
2162                                                         result.Append ('+');
2163                                                 else
2164                                                         result.Append ('-');
2165                                                 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2166                                                 result.Append (':');
2167                                                 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
2168                                                 break;
2169                                         }
2170                                         break;
2171 #endif
2172                                 //
2173                                 // Date tokens
2174                                 //
2175                                 case 'd':
2176                                         // day. d(d?) = day of month (leading 0 if two d's)
2177                                         // ddd = three leter day of week
2178                                         // dddd+ full day-of-week
2179                                         tokLen = CountRepeat (format, i, ch);
2180
2181                                         if (tokLen <= 2)
2182                                                 ZeroPad (result, dfi.Calendar.GetDayOfMonth (this), tokLen == 1 ? 1 : 2);
2183                                         else if (tokLen == 3)
2184                                                 result.Append (dfi.GetAbbreviatedDayName (dfi.Calendar.GetDayOfWeek (this)));
2185                                         else
2186                                                 result.Append (dfi.GetDayName (dfi.Calendar.GetDayOfWeek (this)));
2187
2188                                         break;
2189                                 case 'M':
2190                                         // Month.m(m?) = month # (with leading 0 if two mm)
2191                                         // mmm = 3 letter name
2192                                         // mmmm+ = full name
2193                                         tokLen = CountRepeat (format, i, ch);
2194                                         int month = dfi.Calendar.GetMonth(this);
2195                                         if (tokLen <= 2)
2196                                                 ZeroPad (result, month, tokLen);
2197                                         else if (tokLen == 3)
2198                                                 result.Append (dfi.GetAbbreviatedMonthName (month));
2199                                         else
2200                                                 result.Append (dfi.GetMonthName (month));
2201
2202                                         break;
2203                                 case 'y':
2204                                         // Year. y(y?) = two digit year, with leading 0 if yy
2205                                         // yyy+ full year with leading zeros if needed.
2206                                         tokLen = CountRepeat (format, i, ch);
2207
2208                                         if (tokLen <= 2)
2209                                                 ZeroPad (result, dfi.Calendar.GetYear (this) % 100, tokLen);
2210                                         else
2211                                                 ZeroPad (result, dfi.Calendar.GetYear (this), tokLen);
2212                                         break;
2213
2214                                 case 'g':
2215                                         // Era name
2216                                         tokLen = CountRepeat (format, i, ch);
2217                                         result.Append (dfi.GetEraName (dfi.Calendar.GetEra (this)));
2218                                         break;
2219
2220                                 //
2221                                 // Other
2222                                 //
2223                                 case ':':
2224                                         result.Append (dfi.TimeSeparator);
2225                                         tokLen = 1;
2226                                         break;
2227                                 case '/':
2228                                         result.Append (dfi.DateSeparator);
2229                                         tokLen = 1;
2230                                         break;
2231                                 case '\'': case '"':
2232                                         tokLen = ParseQuotedString (format, i, result);
2233                                         break;
2234                                 case '%':
2235                                         if (i >= format.Length - 1)
2236                                                 throw new FormatException ("% at end of date time string");
2237                                         if (format [i + 1] == '%')
2238                                                 throw new FormatException ("%% in date string");
2239
2240                                         // Look for the next char
2241                                         tokLen = 1;
2242                                         break;
2243                                 case '\\':
2244                                         // C-Style escape
2245                                         if (i >= format.Length - 1)
2246                                                 throw new FormatException ("\\ at end of date time string");
2247
2248                                         result.Append (format [i + 1]);
2249                                         tokLen = 2;
2250
2251                                         break;
2252                                 default:
2253                                         // catch all
2254                                         result.Append (ch);
2255                                         tokLen = 1;
2256                                         break;
2257                                 }
2258                                 i += tokLen;
2259                         }
2260                         return result.ToString ();
2261                 }
2262                 
2263                 static int CountRepeat (string fmt, int p, char c)
2264                 {
2265                         int l = fmt.Length;
2266                         int i = p + 1;
2267                         while ((i < l) && (fmt [i] == c)) 
2268                                 i++;
2269                         
2270                         return i - p;
2271                 }
2272                 
2273                 static int ParseQuotedString (string fmt, int pos, StringBuilder output)
2274                 {
2275                         // pos == position of " or '
2276                         
2277                         int len = fmt.Length;
2278                         int start = pos;
2279                         char quoteChar = fmt [pos++];
2280                         
2281                         while (pos < len) {
2282                                 char ch = fmt [pos++];
2283                                 
2284                                 if (ch == quoteChar)
2285                                         return pos - start;
2286                                 
2287                                 if (ch == '\\') {
2288                                         // C-Style escape
2289                                         if (pos >= len)
2290                                                 throw new FormatException("Un-ended quote");
2291         
2292                                         output.Append (fmt [pos++]);
2293                                 } else {
2294                                         output.Append (ch);
2295                                 }
2296                         }
2297
2298                         throw new FormatException("Un-ended quote");
2299                 }
2300                 
2301                 static unsafe void ZeroPad (StringBuilder output, int digits, int len)
2302                 {
2303                         // more than enough for an int
2304                         char* buffer = stackalloc char [16];
2305                         int pos = 16;
2306                         
2307                         do {
2308                                 buffer [-- pos] = (char) ('0' + digits % 10);
2309                                 digits /= 10;
2310                                 len --;
2311                         } while (digits > 0);
2312                         
2313                         while (len -- > 0)
2314                                 buffer [-- pos] = '0';
2315                         
2316                         output.Append (new string (buffer, pos, 16 - pos));
2317                 }
2318
2319                 public string ToString (string format, IFormatProvider fp)
2320                 {
2321                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
2322
2323                         if (format == null || format == String.Empty)
2324                                 format = "G";
2325
2326                         bool useutc = false, use_invariant = false;
2327
2328                         if (format.Length == 1) {
2329                                 char fchar = format [0];
2330                                 format = _GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2331                                 if (fchar == 'U')
2332                                         return ToUniversalTime()._ToString (format, dfi);
2333
2334                                 if (format == null)
2335                                         throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2336                         }
2337
2338                         // Don't convert UTC value. It just adds 'Z' for 
2339                         // 'u' format, for the same ticks.
2340                         return this._ToString (format, dfi);
2341                 }
2342
2343                 public DateTime ToLocalTime ()
2344                 {
2345                         return TimeZone.CurrentTimeZone.ToLocalTime (this);
2346                 }
2347
2348                 public DateTime ToUniversalTime()
2349                 {
2350                         return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2351                 }
2352
2353                 /*  OPERATORS */
2354
2355                 public static DateTime operator +(DateTime d, TimeSpan t)
2356                 {
2357                         DateTime ret = new DateTime (true, d.ticks + t);
2358 #if NET_2_0
2359                         ret.kind = d.kind;
2360 #endif
2361                         return ret;
2362                 }
2363
2364                 public static bool operator ==(DateTime d1, DateTime d2)
2365                 {
2366                         return (d1.ticks == d2.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 t1,DateTime t2)
2375                 {
2376                         return (t1.ticks >= t2.ticks);
2377                 }
2378
2379                 public static bool operator !=(DateTime d1, DateTime d2)
2380                 {
2381                         return (d1.ticks != d2.ticks);
2382                 }
2383
2384                 public static bool operator <(DateTime t1,      DateTime t2)
2385                 {
2386                         return (t1.ticks < t2.ticks );
2387                 }
2388
2389                 public static bool operator <=(DateTime t1,DateTime t2)
2390                 {
2391                         return (t1.ticks <= t2.ticks);
2392                 }
2393
2394                 public static TimeSpan operator -(DateTime d1,DateTime d2)
2395                 {
2396                         return new TimeSpan((d1.ticks - d2.ticks).Ticks);
2397                 }
2398
2399                 public static DateTime operator -(DateTime d,TimeSpan t)
2400                 {
2401                         DateTime ret = new DateTime (true, d.ticks - t);
2402 #if NET_2_0
2403                         ret.kind = d.kind;
2404 #endif
2405                         return ret;
2406                 }
2407
2408                 bool IConvertible.ToBoolean(IFormatProvider provider)
2409                 {
2410                         throw new InvalidCastException();
2411                 }
2412                 
2413                 byte IConvertible.ToByte(IFormatProvider provider)
2414                 {
2415                         throw new InvalidCastException();
2416
2417                 }
2418
2419                 char IConvertible.ToChar(IFormatProvider provider)
2420                 {
2421                         throw new InvalidCastException();
2422                 }
2423
2424                 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
2425                 {
2426                         return this;
2427                 } 
2428                 
2429                 decimal IConvertible.ToDecimal(IFormatProvider provider)
2430                 {
2431                          throw new InvalidCastException();
2432                 }
2433
2434                 double IConvertible.ToDouble(IFormatProvider provider)
2435                 {
2436                         throw new InvalidCastException();
2437                 }
2438
2439                 Int16 IConvertible.ToInt16(IFormatProvider provider)
2440                 {
2441                         throw new InvalidCastException();
2442                 }
2443
2444                 Int32 IConvertible.ToInt32(IFormatProvider provider)
2445                 {
2446                         throw new InvalidCastException();
2447                 }
2448
2449                 Int64 IConvertible.ToInt64(IFormatProvider provider)
2450                 {
2451                         throw new InvalidCastException();
2452                 }
2453
2454                 SByte IConvertible.ToSByte(IFormatProvider provider)
2455                 {
2456                         throw new InvalidCastException();
2457                 }
2458
2459                 Single IConvertible.ToSingle(IFormatProvider provider)
2460                 {
2461                         throw new InvalidCastException();
2462                 }
2463
2464                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2465                 {
2466                         if (conversionType == null)
2467                                 throw new ArgumentNullException ("conversionType");
2468
2469                         if (conversionType == typeof (DateTime))
2470                                 return this;
2471                         else if (conversionType == typeof (String))
2472                                 return this.ToString (provider);
2473                         else if (conversionType == typeof (Object))
2474                                 return this;
2475                         else
2476                                 throw new InvalidCastException();
2477                 }
2478
2479                 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
2480                 {
2481                         throw new InvalidCastException();
2482                 }
2483                 
2484                 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
2485                 {
2486                         throw new InvalidCastException();
2487                 }
2488
2489                 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
2490                 {
2491                         throw new InvalidCastException();
2492                 }
2493         }
2494 }