2acbd6dd683b2b5cb5f00078b232898cefd9f75f
[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
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Collections;
36 using System.Globalization;
37 using System.Runtime.CompilerServices;
38 using System.Runtime.InteropServices;
39 using System.Text;
40
41 namespace System
42 {
43         /// <summary>
44         /// The DateTime structure represents dates and time ranging from
45         /// 1-1-0001 12:00:00 AM to 31-12-9999 23:59:00 Common Era.
46         /// </summary>
47         /// 
48         [Serializable]
49         [StructLayout (LayoutKind.Auto)]
50         public struct DateTime : IFormattable, IConvertible,
51 #if NET_2_0
52                 IComparable, IComparable<DateTime>
53 #else
54                 IComparable
55 #endif
56         {
57                 private TimeSpan ticks;
58
59                 private const int dp400 = 146097;
60                 private const int dp100 = 36524;
61                 private const int dp4 = 1461;
62
63                 // w32 file time starts counting from 1/1/1601 00:00 GMT
64                 // which is the constant ticks from the .NET epoch
65                 private const long w32file_epoch = 504911232000000000L;
66
67                 //private const long MAX_VALUE_TICKS = 3155378975400000000L;
68                 // -- Microsoft .NET has this value.
69                 private const long MAX_VALUE_TICKS = 3155378975999999999L;
70
71                 //
72                 // The UnixEpoch, it begins on Jan 1, 1970 at 0:0:0, expressed
73                 // in Ticks
74                 //
75                 internal const long UnixEpoch = 621355968000000000L;
76
77                 // for OLE Automation dates
78                 private const long ticks18991230 = 599264352000000000L;
79                 private const double OAMinValue = -657435.0d;
80                 private const double OAMaxValue = 2958466.0d;
81
82                 public static readonly DateTime MaxValue = new DateTime (false, MAX_VALUE_TICKS);
83                 public static readonly DateTime MinValue = new DateTime (false, 0);
84
85                 private static readonly string[] commonFormats = {
86                         // For compatibility with MS's CLR, this format (which
87                         // doesn't have a one-letter equivalent) is parsed
88                         // too. It's important because it's used in XML
89                         // serialization.
90
91                         // Note that those format should be tried only for
92                         // invalid patterns; 
93
94                         // FIXME: SOME OF those patterns looks tried against 
95                         // the current culture, since some patterns fail in 
96                         // some culture.
97
98                         "yyyy-MM-dd",
99                         "yyyy-MM-ddTHH:mm:sszzz",
100                         "yyyy-MM-ddTHH:mm:ss.fffffff",
101                         "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
102                         // UTC / allow any separator
103                         "yyyy/MM/ddTHH:mm:ssZ",
104                         "yyyy/M/dZ",
105                         // bug #58938
106                         "yyyy/M/d HH:mm:ss",
107                         // bug #47720
108                         "yyyy/MM/dd HH:mm:ss 'GMT'",
109                         // Close to RFC1123, but without 'GMT'
110                         "ddd, d MMM yyyy HH:mm:ss",
111                         // use UTC ('Z'; not literal "'Z'")
112                         // FIXME: 1078(af-ZA) and 1079(ka-GE) reject it
113                         "yyyy/MM/dd HH':'mm':'ssZ", 
114
115                         // DayOfTheWeek, dd full_month_name yyyy
116                         // FIXME: 1054(th-TH) rejects it
117                         "dddd, dd MMMM yyyy",
118                         // DayOfTheWeek, dd yyyy. This works for every locales.
119                         "MMMM dd, yyyy",
120 #if NET_1_1
121                         // X509Certificate pattern is accepted by Parse() *in every culture*
122                         "yyyyMMddHHmmssZ",
123 #endif
124                         // In Parse() the 'r' equivalent pattern is first parsed as universal time
125                         "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'",
126 /*
127                         // Full date and time
128                         "F", "G", "r", "s", "u", "U",
129                         // Full date and time, but no seconds
130                         "f", "g",
131                         // Only date
132                         "d", "D",
133                         // Only time
134                         "T", "t",
135                         // Only date, but no year
136                         "m",
137                         // Only date, but no day
138                         "y" 
139 */
140                 };
141
142                 private enum Which 
143                 {
144                         Day,
145                         DayYear,
146                         Month,
147                         Year
148                 };
149         
150                 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };        
151                 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };    
152
153                 private static int AbsoluteDays (int year, int month, int day)
154                 {
155                         int[] days;
156                         int temp = 0, m=1 ;
157
158
159
160
161                 
162                         days = (IsLeapYear(year) ? daysmonthleap  : daysmonth);
163                         
164                         while (m < month)
165                                 temp += days[m++];
166                         return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
167                 }
168
169                 private int FromTicks(Which what)
170                 {
171                         int num400, num100, num4, numyears; 
172                         int M =1;
173
174                         int[] days = daysmonth;
175                         int totaldays = this.ticks.Days;
176
177                         num400 = (totaldays / dp400);
178                         totaldays -=  num400 * dp400;
179                 
180                         num100 = (totaldays / dp100);
181                         if (num100 == 4)   // leap
182                                 num100 = 3;
183                         totaldays -= (num100 * dp100);
184
185                         num4 = totaldays / dp4;
186                         totaldays -= (num4 * dp4);
187
188                         numyears = totaldays / 365 ;
189
190                         if (numyears == 4)  //leap
191                                 numyears =3 ;
192                         if (what == Which.Year )
193                                 return num400*400 + num100*100 + num4*4 + numyears + 1;
194
195                         totaldays -= (numyears * 365) ;
196                         if (what == Which.DayYear )
197                                 return totaldays + 1;
198                         
199                         if  ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
200                                 days = daysmonthleap;
201                                 
202                         while (totaldays >= days[M])
203                                 totaldays -= days[M++];
204
205                         if (what == Which.Month )
206                                 return M;
207
208                         return totaldays +1; 
209                 }
210
211
212                 // Constructors
213                 
214                 /// <summary>
215                 /// Constructs a DateTime for specified ticks
216                 /// </summary>
217                 /// 
218                 public DateTime (long newticks)
219                         // `local' must default to false here to avoid
220                         // a recursion loop.
221                         : this (false, newticks) {}
222
223                 internal DateTime (bool local, long newticks)
224                 {
225                         ticks = new TimeSpan (newticks);
226                         if (local) {
227                                 TimeZone tz = TimeZone.CurrentTimeZone;
228
229                                 TimeSpan utcoffset = tz.GetUtcOffset (this);
230
231                                 ticks = ticks + utcoffset;
232                         }
233                         if (ticks.Ticks < MinValue.Ticks || ticks.Ticks > MaxValue.Ticks)
234                             throw new ArgumentOutOfRangeException ();
235                 }
236
237                 public DateTime (int year, int month, int day)
238                         : this (year, month, day,0,0,0,0) {}
239
240                 public DateTime (int year, int month, int day, int hour, int minute, int second)
241                         : this (year, month, day, hour, minute, second, 0)      {}
242
243                 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
244                         {
245                         if ( year < 1 || year > 9999 || 
246                                 month < 1 || month >12  ||
247                                 day < 1 || day > DaysInMonth(year, month) ||
248                                 hour < 0 || hour > 23 ||
249                                 minute < 0 || minute > 59 ||
250                                 second < 0 || second > 59 ||
251                                 millisecond < 0 || millisecond > 999)
252                                 throw new ArgumentOutOfRangeException ("Parameters describe an " +
253                                                                         "unrepresentable DateTime.");
254
255                         ticks = new TimeSpan (AbsoluteDays(year,month,day), hour, minute, second, millisecond);
256                 }
257
258                 public DateTime (int year, int month, int day, Calendar calendar)
259                         : this (year, month, day, 0, 0, 0, 0, calendar) {}
260
261                 
262                 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
263                         : this (year, month, day, hour, minute, second, 0, calendar)    {}
264
265
266                 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
267                         : this (year, month, day, hour, minute, second, millisecond) 
268                 {
269                         if (calendar == null)
270                                 throw new ArgumentNullException();
271                 }
272
273                 internal DateTime (bool check, TimeSpan value)
274                 {
275                         if (check && (value.Ticks < MinValue.Ticks || value.Ticks > MaxValue.Ticks))
276                             throw new ArgumentOutOfRangeException ();
277
278                         ticks = value;
279                 }
280
281                 /* Properties  */
282
283                 public DateTime Date 
284                 {
285                         get     
286                         { 
287                                 return new DateTime (Year, Month, Day);
288                         }
289                 }
290         
291                 public int Month 
292                 {
293                         get     
294                         { 
295                                 return FromTicks(Which.Month); 
296                         }
297                 }
298
299                
300                 public int Day
301                 {
302                         get 
303                         { 
304                                 return FromTicks(Which.Day); 
305                         }
306                 }
307
308                 public DayOfWeek DayOfWeek 
309                 {
310                         get 
311                         { 
312                                 return ( (DayOfWeek) ((ticks.Days+1) % 7) ); 
313                         }
314                 }
315
316                 public int DayOfYear 
317                 {
318                         get 
319                         { 
320                                 return FromTicks(Which.DayYear); 
321                         }
322                 }
323
324                 public TimeSpan TimeOfDay 
325                 {
326                         get     
327                         { 
328                                 return new TimeSpan(ticks.Ticks % TimeSpan.TicksPerDay );
329                         }
330                         
331                 }
332
333                 public int Hour 
334                 {
335                         get 
336                         { 
337                                 return ticks.Hours;
338                         }
339                 }
340
341                 public int Minute 
342                 {
343                         get 
344                         { 
345                                 return ticks.Minutes;
346                         }
347                 }
348
349                 public int Second 
350                 {
351                         get     
352                         { 
353                                 return ticks.Seconds;
354                         }
355                 }
356
357                 public int Millisecond 
358                 {
359                         get 
360                         { 
361                                 return ticks.Milliseconds;
362                         }
363                 }
364                 
365                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
366                 internal static extern long GetNow ();
367
368                 public static DateTime Now 
369                 {
370                         get     
371                         {
372                                 return new DateTime (true, GetNow ());
373                         }
374                 }
375
376                 public long Ticks
377                 { 
378                         get     
379                         { 
380                                 return ticks.Ticks;
381                         }
382                 }
383         
384                 public static DateTime Today 
385                 {
386                         get {
387                                 DateTime now = Now;
388                                 return new DateTime (now.Year, now.Month, now.Day);
389                         }
390                 }
391
392                 public static DateTime UtcNow 
393                 {
394                         get {
395                                 return new DateTime (GetNow ());
396                         }
397                 }
398
399                 public int Year 
400                 {
401                         get 
402                         { 
403                                 return FromTicks(Which.Year); 
404                         }
405                 }
406
407                 /* methods */
408
409                 public DateTime Add (TimeSpan ts)
410                 {
411                         return AddTicks (ts.Ticks);
412                 }
413
414                 public DateTime AddDays (double days)
415                 {
416                         return AddMilliseconds (Math.Round (days * 86400000));
417                 }
418                 
419                 public DateTime AddTicks (long t)
420                 {
421                         if ((t + ticks.Ticks) > MAX_VALUE_TICKS || (t + ticks.Ticks) < 0) {
422                                 throw new ArgumentOutOfRangeException();
423                         }
424                         return new DateTime (t + ticks.Ticks);
425                 }
426
427                 public DateTime AddHours (double hours)
428                 {
429                         return AddMilliseconds (hours * 3600000);
430                 }
431
432                 public DateTime AddMilliseconds (double ms)
433                 {
434                         if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
435                                         (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
436                                 throw new ArgumentOutOfRangeException();
437                         }
438                         long msticks = (long) (ms * TimeSpan.TicksPerMillisecond);
439
440                         return AddTicks (msticks);
441                 }
442
443                 // required to match MS implementation for OADate (OLE Automation)
444                 private DateTime AddRoundedMilliseconds (double ms)
445                 {
446                         if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
447                                 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
448                                 throw new ArgumentOutOfRangeException ();
449                         }
450                         long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
451
452                         return AddTicks (msticks);
453                 }
454
455                 public DateTime AddMinutes (double minutes)
456                 {
457                         return AddMilliseconds (minutes * 60000);
458                 }
459                 
460                 public DateTime AddMonths (int months)
461                 {
462                         int day, month, year,  maxday ;
463                         DateTime temp ;
464
465                         day = this.Day;
466                         month = this.Month + (months % 12);
467                         year = this.Year + months/12 ;
468                         
469                         if (month < 1)
470                         {
471                                 month = 12 + month ;
472                                 year -- ;
473                         }
474                         else if (month>12) 
475                         {
476                                 month = month -12;
477                                 year ++;
478                         }
479                         maxday = DaysInMonth(year, month);
480                         if (day > maxday)
481                                 day = maxday;
482
483                         temp = new DateTime (year, month, day);
484                         return  temp.Add (this.TimeOfDay);
485                 }
486
487                 public DateTime AddSeconds (double seconds)
488                 {
489                         return AddMilliseconds (seconds*1000);
490                 }
491
492                 public DateTime AddYears (int years )
493                 {
494                         return AddMonths(years * 12);
495                 }
496
497                 public static int Compare (DateTime t1, DateTime t2)
498                 {
499                         if (t1.ticks < t2.ticks) 
500                                 return -1;
501                         else if (t1.ticks > t2.ticks) 
502                                 return 1;
503                         else
504                                 return 0;
505                 }
506
507                 public int CompareTo (object v)
508                 {
509                         if ( v == null)
510                                 return 1;
511
512                         if (!(v is System.DateTime))
513                                 throw new ArgumentException (Locale.GetText (
514                                         "Value is not a System.DateTime"));
515
516                         return Compare (this, (DateTime) v);
517                 }
518
519 #if NET_2_0
520                 public int CompareTo (DateTime value)
521                 {
522                         return Compare (this, value);
523                 }
524
525                 public bool Equals (DateTime value)
526                 {
527                         return value.ticks == ticks;
528                 }
529 #endif
530
531                 public static int DaysInMonth (int year, int month)
532                 {
533                         int[] days ;
534
535                         if (month < 1 || month >12)
536                                 throw new ArgumentOutOfRangeException ();
537
538                         days = (IsLeapYear(year) ? daysmonthleap  : daysmonth);
539                         return days[month];                     
540                 }
541                 
542                 public override bool Equals (object o)
543                 {
544                         if (!(o is System.DateTime))
545                                 return false;
546
547                         return ((DateTime) o).ticks == ticks;
548                 }
549
550                 public static bool Equals (DateTime t1, DateTime t2 )
551                 {
552                         return (t1.ticks == t2.ticks );
553                 }
554
555                 public static DateTime FromFileTime (long fileTime) 
556                 {
557                         if (fileTime < 0)
558                                 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
559
560                         return new DateTime (true, w32file_epoch + fileTime);
561                 }
562
563 #if NET_1_1
564                 public static DateTime FromFileTimeUtc (long fileTime) 
565                 {
566                         if (fileTime < 0)
567                                 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
568
569                         return new DateTime (false, w32file_epoch + fileTime);
570                 }
571 #endif
572
573                 public static DateTime FromOADate (double d)
574                 {
575                         // An OLE Automation date is implemented as a floating-point number
576                         // whose value is the number of days from midnight, 30 December 1899.
577
578                         // d must be negative 657435.0 through positive 2958466.0.
579                         if ((d <= OAMinValue) || (d >= OAMaxValue))
580                                 throw new ArgumentException ("d", "[-657435,2958466]");
581
582                         DateTime dt = new DateTime (ticks18991230);
583                         if (d < 0.0d) {
584                                 Double days = Math.Ceiling (d);
585                                 // integer part is the number of days (negative)
586                                 dt = dt.AddRoundedMilliseconds (days * 86400000);
587                                 // but decimals are the number of hours (in days fractions) and positive
588                                 Double hours = (days - d);
589                                 dt = dt.AddRoundedMilliseconds (hours * 86400000);
590                         }
591                         else {
592                                 dt = dt.AddRoundedMilliseconds (d * 86400000);
593                         }
594
595                         return dt;
596                 }
597
598                 public string[] GetDateTimeFormats() 
599                 {
600                         return GetDateTimeFormats (CultureInfo.CurrentCulture);
601                 }
602
603                 public string[] GetDateTimeFormats(char format)
604                 {
605                         if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
606                                 throw new FormatException ("Invalid format character.");
607                         string[] result = new string[1];
608                         result[0] = this.ToString(format.ToString());
609                         return result;
610                 }
611                 
612                 public string[] GetDateTimeFormats(IFormatProvider provider)
613                 {
614                         DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
615 //                      return GetDateTimeFormats (info.GetAllDateTimePatterns ());
616                         ArrayList al = new ArrayList ();
617                         foreach (char c in "dDgGfFmMrRstTuUyY")
618                                 al.AddRange (GetDateTimeFormats (c, info));
619                         return al.ToArray (typeof (string)) as string [];
620                 }
621
622                 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
623                 {
624                         if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
625                                 throw new FormatException ("Invalid format character.");
626
627                         // LAMESPEC: There is NO assurance that 'U' ALWAYS
628                         // euqals to 'F', but since we have to iterate all
629                         // the pattern strings, we cannot just use 
630                         // ToString("U", provider) here. I believe that the 
631                         // method's behavior cannot be formalized.
632                         bool adjustutc = false;
633                         switch (format) {
634                         case 'U':
635 //                      case 'r':
636 //                      case 'R':
637 //                      case 'u':
638                                 adjustutc = true;
639                                 break;
640                         }
641                         DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
642                         return GetDateTimeFormats (adjustutc, info.GetAllDateTimePatterns (format), info);
643                 }
644
645                 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
646                 {
647                         string [] results = new string [patterns.Length];
648                         DateTime val = adjustutc ? ToUniversalTime () : this;
649                         for (int i = 0; i < results.Length; i++)
650                                 results [i] = val._ToString (patterns [i], dfi);
651                         return results;
652                 }
653
654                 public override int GetHashCode ()
655                 {
656                         return (int) ticks.Ticks;
657                 }
658
659                 public TypeCode GetTypeCode ()
660                 {
661                         return TypeCode.DateTime;
662                 }
663
664                 public static bool IsLeapYear (int year)
665                 {
666                         return  ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
667                 }
668
669                 public static DateTime Parse (string s)
670                 {
671                         return Parse (s, null);
672                 }
673
674                 public static DateTime Parse (string s, IFormatProvider fp)
675                 {
676                         return Parse (s, fp, DateTimeStyles.AllowWhiteSpaces);
677                 }
678
679                 [MonoTODO ("see the comments inline")]
680                 public static DateTime Parse (string s, IFormatProvider fp, DateTimeStyles styles)
681                 {
682                         // This method should try only expected patterns. 
683                         // Should not try extra patterns.
684                         // Right now we also try InvariantCulture, but I
685                         // confirmed in some cases this method rejects what
686                         // InvariantCulture supports (can be checked against
687                         // "th-TH" with Gregorian Calendar). So basically it
688                         // should not be done.
689                         // I think it should be CurrentCulture to be tested,
690                         // but right now we don't support all the supported
691                         // patterns for each culture, so try InvariantCulture
692                         // as a quick remedy.
693                         if (s == null)
694                                 throw new ArgumentNullException (Locale.GetText ("s is null"));
695                         DateTime result;
696
697                         if (fp == null)
698                                 fp = CultureInfo.CurrentCulture;
699                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
700
701                         bool longYear = false;
702                         // Try common formats.
703                         if (ParseExact (s, commonFormats, dfi, styles, out result, false, ref longYear))
704                                 return result;
705
706                         // Try common formats, also with invariant culture
707                         if (ParseExact (s, commonFormats, DateTimeFormatInfo.InvariantInfo, styles, out result, false, ref longYear))
708                                 return result;
709
710                         // Next, try all the patterns
711                         string [] patterns = new string [] {"d", "D", "g", "G", "f", "F", "m", "M", "r", "R", "s", "t", "T", "u", "U", "y", "Y"};
712
713                         if (ParseExact (s, patterns, dfi, styles, out result, false, ref longYear))
714                                 return result;
715
716                         if (longYear) {
717                                 throw new ArgumentOutOfRangeException ("year",
718                                         "Valid values are between 1 and 9999 inclusive");
719                         }
720
721                         throw new FormatException ("String was not recognized as a valid DateTime.");
722                 }
723
724                 public static DateTime ParseExact (string s, string format, IFormatProvider fp)
725                 {
726                         return ParseExact (s, format, fp, DateTimeStyles.None);
727                 }
728
729                 internal static int _ParseNumber (string s, int valuePos,
730                                                   int digits,
731                                                   bool leadingzero,
732                                                   bool sloppy_parsing,
733                                                   bool next_not_digit,
734                                                   out int num_parsed)
735                 {
736                         int number = 0, i;
737
738                         if (sloppy_parsing)
739                                 leadingzero = false;
740
741                         if (!leadingzero) {
742                                 int real_digits = 0;
743                                 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
744                                         if (!Char.IsDigit (s[i]))
745                                                 break;
746
747                                         real_digits++;
748                                 }
749
750                                 digits = real_digits;
751                         }
752
753                         if (s.Length - valuePos < digits) {
754                                 num_parsed = -1;
755                                 return 0;
756                         }
757
758                         if (s.Length - valuePos > digits &&
759                             next_not_digit &&
760                             Char.IsDigit (s[digits + valuePos])) {
761                                 /* More digits left over */
762                                 num_parsed = -1;
763                                 return(0);
764                         }
765
766                         for (i = valuePos; i < digits + valuePos; i++) {
767                                 char c = s[i];
768                                 if (!Char.IsDigit (c)) {
769                                         num_parsed = -1;
770                                         return 0;
771                                 }
772
773                                 number = number * 10 + (byte) (c - '0');
774                         }
775
776                         num_parsed = digits;
777                         return number;
778                 }
779
780                 internal static int _ParseEnum (string s, int sPos, string[] values, out int num_parsed)
781                 {
782                         int i;
783
784                         for (i = 0; i < values.Length; i++) {
785                                 if (s.Length - sPos < values[i].Length)
786                                         continue;
787                                 else if (values [i].Length == 0)
788                                         continue;
789                                 String tmp = s.Substring (sPos, values[i].Length);
790                                 if (String.Compare (tmp, values[i], true) == 0) {
791                                         num_parsed = values[i].Length;
792                                         return i;
793                                 }
794                         }
795
796                         num_parsed = -1;
797                         return -1;
798                 }
799
800                 internal static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
801                 {
802                         if (maxlength <= 0)
803                                 maxlength = value.Length;
804
805                         if (String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
806                                 num_parsed = maxlength;
807                                 return true;
808                         }
809
810                         num_parsed = -1;
811                         return false;
812                 }
813
814                 private static bool _DoParse (string s, string format, bool exact,
815                                                out DateTime result,
816                                                DateTimeFormatInfo dfi,
817                                                DateTimeStyles style,
818                                                ref bool longYear)
819                 {
820                         bool useutc = false, use_localtime = true;
821                         bool use_invariant = false;
822                         bool sloppy_parsing = false;
823                         int valuePos = 0;
824                         if (format.Length == 1)
825                                 format = _GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
826                         else if (!exact && format.IndexOf ("GMT") >= 0)
827                                 useutc = true;
828
829                         if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
830                                 format = format.TrimStart (null);
831
832                                 s = s.TrimStart (null); // it could be optimized, but will make little good.
833                         }
834
835                         if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
836                                 format = format.TrimEnd (null);
837                                 s = s.TrimEnd (null); // it could be optimized, but will make little good.
838                         }
839
840                         if (use_invariant)
841                                 dfi = DateTimeFormatInfo.InvariantInfo;
842
843                         if ((style & DateTimeStyles.AllowInnerWhite) != 0)
844                                 sloppy_parsing = true;
845
846                         string chars = format;
847                         int len = format.Length, pos = 0, num = 0;
848
849                         int day = -1, dayofweek = -1, month = -1, year = -1;
850                         int hour = -1, minute = -1, second = -1;
851                         double fractionalSeconds = -1;
852                         int ampm = -1;
853                         int tzsign = -1, tzoffset = -1, tzoffmin = -1;
854                         bool next_not_digit;
855
856                         result = new DateTime (0);
857                         while (pos+num < len)
858                         {
859                                 if (s.Length == valuePos)
860                                         break;
861
862                                 if (chars[pos] == '\'') {
863                                         num = 1;
864                                         while (pos+num < len) {
865                                                 if (chars[pos+num] == '\'')
866                                                         break;
867
868                                                 if (valuePos == s.Length)
869                                                         return false;
870                                                 if (s [valuePos] != chars [pos + num])
871                                                         return false;
872                                                 valuePos++;
873
874                                                 num++;
875                                         }
876                                         if (pos+num > len)
877                                                 return false;
878
879                                         pos += num + 1;
880                                         num = 0;
881                                         continue;
882                                 } else if (chars[pos] == '"') {
883                                         num = 1;
884                                         while (pos+num < len) {
885                                                 if (chars[pos+num] == '"')
886                                                         break;
887
888                                                 if (valuePos == s.Length)
889                                                         return false;
890                                                 if (s [valuePos] != chars[pos+num])
891                                                         return false;
892                                                 valuePos++;
893
894                                                 num++;
895                                         }
896                                         if (pos+num > len)
897                                                 return false;
898
899                                         pos += num + 1;
900                                         num = 0;
901                                         continue;
902                                 } else if (chars[pos] == '\\') {
903                                         if (pos+1 >= len)
904                                                 return false;
905
906                                         if (s [valuePos] != chars [pos + num])
907                                                 return false;
908                                         valuePos++;
909                                         if (valuePos == s.Length)
910                                                 return false;
911
912                                         pos++;
913                                         continue;
914                                 } else if (chars[pos] == '%') {
915                                         pos++;
916                                         continue;
917                                 } else if (Char.IsWhiteSpace (s [valuePos])) {
918                                         valuePos++;
919
920                                         if (Char.IsWhiteSpace (chars[pos])) {
921                                                 pos++;
922                                                 continue;
923                                         }
924
925                                         if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0)
926                                                 return false;
927                                         int ws = valuePos;
928                                         while (ws < s.Length) {
929                                                 if (Char.IsWhiteSpace (s [ws]))
930                                                         ws++;
931                                                 else
932                                                         break;
933                                         }
934                                         valuePos = ws;
935                                 }
936
937
938                                 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
939                                         num++;
940                                         continue;
941                                 }
942
943                                 int num_parsed = 0;
944
945                                 if (pos+num+1 < len) {
946                                         char next_char = chars[pos+num+1];
947                                         
948                                         next_not_digit = !(next_char == 'd' ||
949                                                            next_char == 'M' ||
950                                                            next_char == 'y' ||
951                                                            next_char == 'h' ||
952                                                            next_char == 'H' ||
953                                                            next_char == 'm' ||
954                                                            next_char == 's' ||
955                                                            next_char == 'f' ||
956                                                            next_char == 'z' ||
957                                                            next_char == '"' ||
958                                                            next_char == '\'' ||
959                                                            Char.IsDigit (next_char));
960                                 } else {
961                                         next_not_digit = true;
962                                 }
963                                 
964                                 switch (chars[pos])
965                                 {
966                                 case 'd':
967                                         if (day != -1)
968                                                 return false;
969                                         if (num == 0)
970                                                 day = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
971                                         else if (num == 1)
972                                                 day = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
973                                         else if (num == 2)
974                                                 dayofweek = _ParseEnum (s, valuePos, dfi.AbbreviatedDayNames, out num_parsed);
975                                         else
976                                         {
977                                                 dayofweek = _ParseEnum (s, valuePos, dfi.DayNames, out num_parsed);
978                                                 num = 3;
979                                         }
980                                         break;
981                                 case 'M':
982                                         if (month != -1)
983                                                 return false;
984                                         if (num == 0)
985                                                 month = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
986                                         else if (num == 1)
987                                                 month = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
988                                         else if (num == 2)
989                                                 month = _ParseEnum (s, valuePos, dfi.AbbreviatedMonthNames , out num_parsed) + 1;
990                                         else
991                                         {
992                                                 month = _ParseEnum (s, valuePos, dfi.MonthNames, out num_parsed) + 1;
993                                                 num = 3;
994                                         }
995                                         break;
996                                 case 'y':
997                                         if (year != -1)
998                                                 return false;
999
1000                                         if (num == 0) {
1001                                                 year = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1002                                         } else if (num < 3) {
1003                                                 year = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1004                                         } else {
1005                                                 year = _ParseNumber (s, valuePos, 4, false, sloppy_parsing, next_not_digit, out num_parsed);
1006                                                 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1007                                                         int np = 0;
1008                                                         int ly = _ParseNumber (s, valuePos, 5, false, sloppy_parsing, next_not_digit, out np);
1009                                                         longYear = (ly > 9999);
1010                                                 }
1011                                                 num = 3;
1012                                         }
1013
1014                                         //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1015                                         if (num_parsed <= 2)
1016                                                 year += (year < 30) ? 2000 : 1900;
1017                                         break;
1018                                 case 'h':
1019                                         if (hour != -1)
1020                                                 return false;
1021                                         if (num == 0)
1022                                                 hour = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1023                                         else
1024                                         {
1025                                                 hour = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1026                                                 num = 1;
1027                                         }
1028
1029                                         if (hour > 12)
1030                                                 return false;
1031                                         if (hour == 12)
1032                                                 hour = 0;
1033
1034                                         break;
1035                                 case 'H':
1036                                         if ((hour != -1) || (ampm >= 0))
1037                                                 return false;
1038                                         if (num == 0)
1039                                                 hour = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1040                                         else
1041                                         {
1042                                                 hour = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1043                                                 num = 1;
1044                                         }
1045                                         if (hour >= 24)
1046                                                 return false;
1047
1048                                         ampm = -2;
1049                                         break;
1050                                 case 'm':
1051                                         if (minute != -1)
1052                                                 return false;
1053                                         if (num == 0)
1054                                                 minute = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1055                                         else
1056                                         {
1057                                                 minute = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1058                                                 num = 1;
1059                                         }
1060                                         if (minute >= 60)
1061                                                 return false;
1062
1063                                         break;
1064                                 case 's':
1065                                         if (second != -1)
1066                                                 return false;
1067                                         if (num == 0)
1068                                                 second = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1069                                         else
1070                                         {
1071                                                 second = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1072                                                 num = 1;
1073                                         }
1074                                         if (second >= 60)
1075                                                 return false;
1076
1077                                         break;
1078                                 case 'f':
1079                                         if (fractionalSeconds != -1)
1080                                                 return false;
1081                                         num = Math.Min (num, 6);
1082                                         double decimalNumber = (double) _ParseNumber (s, valuePos, num+1, true, sloppy_parsing, next_not_digit, out num_parsed);
1083                                         if (num_parsed == -1)
1084                                                 return false;
1085
1086                                         else
1087                                                 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1088                                         break;
1089                                 case 't':
1090                                         if (ampm != -1)
1091                                                 return false;
1092                                         if (num == 0)
1093                                         {
1094                                                 if (_ParseString (s, valuePos, 1, dfi.AMDesignator, out num_parsed))
1095                                                         ampm = 0;
1096                                                 else if (_ParseString (s, valuePos, 1, dfi.PMDesignator, out num_parsed))
1097                                                         ampm = 1;
1098                                                 else
1099                                                         return false;
1100                                         }
1101                                         else
1102                                         {
1103                                                 if (_ParseString (s, valuePos, 0, dfi.AMDesignator, out num_parsed))
1104                                                         ampm = 0;
1105                                                 else if (_ParseString (s, valuePos, 0, dfi.PMDesignator, out num_parsed))
1106                                                         ampm = 1;
1107                                                 else
1108                                                         return false;
1109                                                 num = 1;
1110                                         }
1111                                         break;
1112                                 case 'z':
1113                                         if (tzsign != -1)
1114                                                 return false;
1115                                         if (s [valuePos] == '+')
1116                                                 tzsign = 0;
1117                                         else if (s [valuePos] == '-')
1118                                                 tzsign = 1;
1119                                         else
1120                                                 return false;
1121                                         valuePos++;
1122                                         if (num == 0)
1123                                                 tzoffset = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1124                                         else if (num == 1)
1125                                                 tzoffset = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, false, out num_parsed);
1126                                         else
1127                                         {
1128                                                 tzoffset = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1129                                                 if (num_parsed < 0)
1130                                                         return false;
1131                                                 valuePos += num_parsed;
1132                                                 if (Char.IsDigit (s [valuePos]))
1133                                                         num_parsed = 0;
1134                                                 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1135                                                         return false;
1136                                                 valuePos += num_parsed;
1137                                                 tzoffmin = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, false, out num_parsed);
1138                                                 if (num_parsed < 0)
1139                                                         return false;
1140                                                 num = 2;
1141                                         }
1142                                         break;
1143
1144                                 // LAMESPEC: This should be part of UTCpattern
1145                                 // string and thus should not be considered here.
1146                                 //
1147                                 // Note that 'Z' is not defined as a pattern
1148                                 // character. Keep it for X509 certificate
1149                                 // verification. Also, "Z" != "'Z'" under MS.NET
1150                                 // ("'Z'" is just literal; handled above)
1151                                 case 'Z':
1152                                         if (s [valuePos] != 'Z')
1153                                                 return false;
1154                                         num = 0;
1155                                         num_parsed = 1;
1156                                         useutc = true;
1157                                         break;
1158
1159                                 case ':':
1160                                         if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1161                                                 return false;
1162                                         break;
1163                                 case '/':
1164                                         /* Accept any character for
1165                                          * DateSeparator, except
1166                                          * TimeSeparator, a digit or a
1167                                          * letter.  Not documented,
1168                                          * but seems to be MS
1169                                          * behaviour here.  See bug
1170                                          * 54047.
1171                                          */
1172                                         if (exact && s [valuePos] != '/')
1173                                                 return false;
1174
1175                                         if (_ParseString (s, valuePos, 0,
1176                                                           dfi.TimeSeparator,
1177                                                           out num_parsed) ||
1178                                             Char.IsDigit (s [valuePos]) ||
1179                                             Char.IsLetter (s [valuePos])) {
1180                                                 return(false);
1181                                         }
1182
1183                                         num = 0;
1184                                         if (num_parsed <= 0) {
1185                                                 num_parsed = 1;
1186                                         }
1187                                         
1188                                         break;
1189                                 default:
1190                                         if (s [valuePos] != chars[pos]) {
1191                                                 // FIXME: It is not sure, but
1192                                                 // IsLetter() is introduced 
1193                                                 // because we have to reject 
1194                                                 // "2002a02b25" but have to
1195                                                 // allow "2002$02$25". The same
1196                                                 // thing applies to '/' case.
1197                                                 if (exact ||
1198                                                         Char.IsDigit (s [valuePos]) ||
1199                                                         Char.IsLetter (s [valuePos]))
1200                                                         return false;
1201                                         }
1202                                         num = 0;
1203                                         num_parsed = 1;
1204                                         break;
1205                                 }
1206
1207                                 if (num_parsed < 0)
1208                                         return false;
1209
1210                                 valuePos += num_parsed;
1211
1212                                 if (!exact) {
1213                                         switch (chars [pos]) {
1214                                         case 'm':
1215                                         case 's':
1216                                         case 'f':
1217                                         case 'z':
1218                                                 if (s.Length > valuePos && s [valuePos] == 'Z'
1219                                                     && (pos + 1 == chars.Length
1220                                                     || chars [pos + 1] != 'Z')) {
1221                                                         useutc = true;
1222                                                         valuePos++;
1223                                                 }
1224                                                 break;
1225                                         }
1226                                 }
1227
1228                                 pos = pos + num + 1;
1229                                 num = 0;
1230                         }
1231
1232                         if (exact && pos < len)
1233                                 return false;
1234
1235                         if (s.Length != valuePos) // extraneous tail.
1236                                 return false;
1237
1238                         if (hour == -1)
1239                                 hour = 0;
1240                         if (minute == -1)
1241                                 minute = 0;
1242
1243                         if (second == -1)
1244                                 second = 0;
1245                         if (fractionalSeconds == -1)
1246                                 fractionalSeconds = 0;
1247
1248                         // If no date was given
1249                         if ((day == -1) && (month == -1) && (year == -1)) {
1250                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1251                                         day = 1;
1252                                         month = 1;
1253                                         year = 1;
1254                                 } else {
1255                                         day = Today.Day;
1256                                         month = Today.Month;
1257                                         year = Today.Year;
1258                                 }
1259                         }
1260
1261
1262                         if (day == -1)
1263                                 day = 1;
1264                         if (month == -1)
1265                                 month = 1;
1266                         if (year == -1) {
1267                                 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1268                                         year = 1;
1269                                 else
1270                                         year = Today.Year;
1271                         }
1272
1273                         if (ampm == 1)
1274                                 hour = hour + 12;
1275                         
1276                         // For anything out of range 
1277                         // return false
1278                         if ( year < 1 || year > 9999 || 
1279                         month < 1 || month >12  ||
1280                         day < 1 || day > DaysInMonth(year, month) ||
1281                         hour < 0 || hour > 23 ||
1282                         minute < 0 || minute > 59 ||
1283                         second < 0 || second > 59 )
1284                                 return false;
1285
1286                         result = new DateTime (year, month, day, hour, minute, second, 0);
1287                         result = result.AddSeconds(fractionalSeconds);
1288
1289 //Console.WriteLine ("**** Parsed as {1} {0} {2}", new object [] {useutc ? "[u]" : "", format, use_localtime ? "[lt]" : ""});
1290                         if ((dayofweek != -1) && (dayofweek != (int) result.DayOfWeek))
1291                                 throw new FormatException (Locale.GetText ("String was not recognized as valid DateTime because the day of week was incorrect."));
1292
1293                         // If no timezone was specified, default to the local timezone.
1294                         TimeSpan utcoffset;
1295
1296                         if (useutc)
1297                                 utcoffset = new TimeSpan (0, 0, 0);
1298                         else if (tzsign == -1) {
1299                                 TimeZone tz = TimeZone.CurrentTimeZone;
1300                                 utcoffset = tz.GetUtcOffset (result);
1301                         } else {
1302                                 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1303                                         use_localtime = false;
1304
1305                                 if (tzoffmin == -1)
1306                                         tzoffmin = 0;
1307                                 if (tzoffset == -1)
1308                                         tzoffset = 0;
1309                                 if (tzsign == 1)
1310                                         tzoffset = -tzoffset;
1311
1312                                 utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
1313                         }
1314
1315                         long newticks = (result.ticks - utcoffset).Ticks;
1316
1317                         result = new DateTime (use_localtime, newticks);
1318
1319                         return true;
1320                 }
1321
1322
1323                 public static DateTime ParseExact (string s, string format,
1324                                                    IFormatProvider fp, DateTimeStyles style)
1325                 {
1326                         string[] formats;
1327
1328                         formats = new string [1];
1329                         formats[0] = format;
1330
1331                         return ParseExact (s, formats, fp, style);
1332                 }
1333
1334                 public static DateTime ParseExact (string s, string[] formats,
1335                                                    IFormatProvider fp,
1336                                                    DateTimeStyles style)
1337                 {
1338                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1339
1340                         if (s == null)
1341                                 throw new ArgumentNullException (Locale.GetText ("s is null"));
1342                         if (formats == null || formats.Length == 0)
1343                                 throw new ArgumentNullException (Locale.GetText ("format is null"));
1344
1345                         DateTime result;
1346                         bool longYear = false;
1347                         if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear))
1348                                 throw new FormatException ();
1349                         return result;
1350                 }
1351                 
1352                 private static bool ParseExact (string s, string [] formats,
1353                         DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1354                         bool exact, ref bool longYear)
1355                 {
1356                         int i;
1357                         for (i = 0; i < formats.Length; i++)
1358                         {
1359                                 DateTime result;
1360
1361                                 if (_DoParse (s, formats[i], exact, out result, dfi, style, ref longYear)) {
1362                                         ret = result;
1363                                         return true;
1364                                 }
1365                         }
1366                         ret = DateTime.MinValue;
1367                         return false;
1368                 }
1369                 
1370                 public TimeSpan Subtract(DateTime dt)
1371                 {   
1372                         return new TimeSpan(ticks.Ticks) - dt.ticks;
1373                 }
1374
1375                 public DateTime Subtract(TimeSpan ts)
1376                 {
1377                         TimeSpan newticks;
1378
1379                         newticks = (new TimeSpan (ticks.Ticks)) - ts;
1380                         return new DateTime(true,newticks);
1381                 }
1382
1383                 public long ToFileTime()
1384                 {
1385                         DateTime universalTime = ToUniversalTime();
1386                         
1387                         if (universalTime.Ticks < w32file_epoch) {
1388                                 throw new ArgumentOutOfRangeException("file time is not valid");
1389                         }
1390                         
1391                         return(universalTime.Ticks - w32file_epoch);
1392                 }
1393
1394 #if NET_1_1
1395                 public long ToFileTimeUtc()
1396                 {
1397                         if (Ticks < w32file_epoch) {
1398                                 throw new ArgumentOutOfRangeException("file time is not valid");
1399                         }
1400                         
1401                         return (Ticks - w32file_epoch);
1402                 }
1403 #endif
1404
1405                 public string ToLongDateString()
1406                 {
1407                         return ToString ("D");
1408                 }
1409
1410                 public string ToLongTimeString()
1411                 {
1412                         return ToString ("T");
1413                 }
1414
1415                 public double ToOADate ()
1416                 {
1417                         long t = this.Ticks;
1418                         // uninitialized DateTime case
1419                         if (t == 0)
1420                                 return 0;
1421                         // we can't reach minimum value
1422                         if (t < 31242239136000000)
1423                                 return OAMinValue + 0.001;
1424
1425                         TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1426                         double result = ts.TotalDays;
1427                         // t < 0 (where 599264352000000000 == 0.0d for OA)
1428                         if (t < 599264352000000000) {
1429                                 // negative days (int) but decimals are positive
1430                                 double d = Math.Ceiling (result);
1431                                 result = d - 2 - (result - d);
1432                         }
1433                         else {
1434                                 // we can't reach maximum value
1435                                 if (result >= OAMaxValue)
1436                                         result = OAMaxValue - 0.00000001d;
1437                         }
1438                         return result;
1439                 }
1440
1441                 public string ToShortDateString()
1442                 {
1443                         return ToString ("d");
1444                 }
1445
1446                 public string ToShortTimeString()
1447                 {
1448                         return ToString ("t");
1449                 }
1450                 
1451                 public override string ToString ()
1452                 {
1453                         return ToString ("G", null);
1454                 }
1455
1456                 public string ToString (IFormatProvider fp)
1457                 {
1458                         return ToString (null, fp);
1459                 }
1460
1461                 public string ToString (string format)
1462                 {
1463                         return ToString (format, null);
1464                 }
1465
1466                 internal static string _GetStandardPattern (char format, DateTimeFormatInfo dfi, out bool useutc, out bool use_invariant)
1467                 {
1468                         String pattern;
1469
1470                         useutc = false;
1471                         use_invariant = false;
1472
1473                         switch (format)
1474                         {
1475                         case 'd':
1476                                 pattern = dfi.ShortDatePattern;
1477                                 break;
1478                         case 'D':
1479                                 pattern = dfi.LongDatePattern;
1480                                 break;
1481                         case 'f':
1482                                 pattern = dfi.LongDatePattern + " " + dfi.ShortTimePattern;
1483                                 break;
1484                         case 'F':
1485                                 pattern = dfi.FullDateTimePattern;
1486                                 break;
1487                         case 'g':
1488                                 pattern = dfi.ShortDatePattern + " " + dfi.ShortTimePattern;
1489                                 break;
1490                         case 'G':
1491                                 pattern = dfi.ShortDatePattern + " " + dfi.LongTimePattern;
1492                                 break;
1493                         case 'm':
1494                         case 'M':
1495                                 pattern = dfi.MonthDayPattern;
1496                                 break;
1497                         case 'r':
1498                         case 'R':
1499                                 pattern = dfi.RFC1123Pattern;
1500                                 // commented by LP 09/jun/2002, rfc 1123 pattern is always in GMT
1501                                 // uncommented by AE 27/may/2004
1502 //                              useutc = true;
1503                                 use_invariant = true;
1504                                 break;
1505                         case 's':
1506                                 pattern = dfi.SortableDateTimePattern;
1507                                 break;
1508                         case 't':
1509                                 pattern = dfi.ShortTimePattern;
1510                                 break;
1511                         case 'T':
1512                                 pattern = dfi.LongTimePattern;
1513                                 break;
1514                         case 'u':
1515                                 pattern = dfi.UniversalSortableDateTimePattern;
1516                                 useutc = true;
1517                                 break;
1518                         case 'U':
1519 //                              pattern = dfi.LongDatePattern + " " + dfi.LongTimePattern;
1520                                 pattern = dfi.FullDateTimePattern;
1521                                 useutc = true;
1522                                 break;
1523                         case 'y':
1524                         case 'Y':
1525                                 pattern = dfi.YearMonthPattern;
1526                                 break;
1527                         default:
1528                                 pattern = null;
1529                                 break;
1530                         }
1531
1532                         return pattern;
1533                 }
1534
1535                 internal string _ToString (string format, DateTimeFormatInfo dfi)
1536                 {
1537                         // the length of the format is usually a good guess of the number
1538                         // of chars in the result. Might save us a few bytes sometimes
1539                         // Add + 10 for cases like mmmm dddd
1540                         StringBuilder result = new StringBuilder (format.Length + 10);
1541
1542                         // For some cases, the output should not use culture dependent calendar
1543                         DateTimeFormatInfo inv = DateTimeFormatInfo.InvariantInfo;
1544                         if (format == inv.RFC1123Pattern)
1545                                 dfi = inv;
1546                         else if (format == inv.UniversalSortableDateTimePattern)
1547                                 dfi = inv;
1548
1549                         int i = 0;
1550
1551                         while (i < format.Length) {
1552                                 int tokLen;
1553                                 char ch = format [i];
1554
1555                                 switch (ch) {
1556
1557                                 //
1558                                 // Time Formats
1559                                 //
1560                                 case 'h':
1561                                         // hour, [1, 12]
1562                                         tokLen = CountRepeat (format, i, ch);
1563
1564                                         int hr = this.Hour % 12;
1565                                         if (hr == 0)
1566                                                 hr = 12;
1567
1568                                         ZeroPad (result, hr, tokLen == 1 ? 1 : 2);
1569                                         break;
1570                                 case 'H':
1571                                         // hour, [0, 23]
1572                                         tokLen = CountRepeat (format, i, ch);
1573                                         ZeroPad (result, this.Hour, tokLen == 1 ? 1 : 2);
1574                                         break;
1575                                 case 'm':
1576                                         // minute, [0, 59]
1577                                         tokLen = CountRepeat (format, i, ch);
1578                                         ZeroPad (result, this.Minute, tokLen == 1 ? 1 : 2);
1579                                         break;
1580                                 case 's':
1581                                         // second [0, 29]
1582                                         tokLen = CountRepeat (format, i, ch);
1583                                         ZeroPad (result, this.Second, tokLen == 1 ? 1 : 2);
1584                                         break;
1585                                 case 'f':
1586                                         // fraction of second, to same number of
1587                                         // digits as there are f's
1588
1589                                         tokLen = CountRepeat (format, i, ch);
1590                                         if (tokLen > 7)
1591                                                 throw new FormatException ("Invalid Format String");
1592
1593                                         int dec = (int)((long)(this.Ticks % TimeSpan.TicksPerSecond) / (long) Math.Pow (10, 7 - tokLen));
1594                                         ZeroPad (result, dec, tokLen);
1595
1596                                         break;
1597                                 case 't':
1598                                         // AM/PM. t == first char, tt+ == full
1599                                         tokLen = CountRepeat (format, i, ch);
1600                                         string desig = this.Hour < 12 ? dfi.AMDesignator : dfi.PMDesignator;
1601
1602                                         if (tokLen == 1) {
1603                                                 if (desig.Length >= 1)
1604                                                         result.Append (desig [0]);
1605                                         }
1606                                         else
1607                                                 result.Append (desig);
1608
1609                                         break;
1610                                 case 'z':
1611                                         // timezone. t = +/-h; tt = +/-hh; ttt+=+/-hh:mm
1612                                         tokLen = CountRepeat (format, i, ch);
1613                                         TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
1614
1615                                         if (offset.Ticks >= 0)
1616                                                 result.Append ('+');
1617                                         else
1618                                                 result.Append ('-');
1619
1620                                         switch (tokLen) {
1621                                         case 1:
1622                                                 result.Append (Math.Abs (offset.Hours));
1623                                                 break;
1624                                         case 2:
1625                                                 result.Append (Math.Abs (offset.Hours).ToString ("00"));
1626                                                 break;
1627                                         default:
1628                                                 result.Append (Math.Abs (offset.Hours).ToString ("00"));
1629                                                 result.Append (':');
1630                                                 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
1631                                                 break;
1632                                         }
1633                                         break;
1634                                 //
1635                                 // Date tokens
1636                                 //
1637                                 case 'd':
1638                                         // day. d(d?) = day of month (leading 0 if two d's)
1639                                         // ddd = three leter day of week
1640                                         // dddd+ full day-of-week
1641                                         tokLen = CountRepeat (format, i, ch);
1642
1643                                         if (tokLen <= 2)
1644                                                 ZeroPad (result, dfi.Calendar.GetDayOfMonth (this), tokLen == 1 ? 1 : 2);
1645                                         else if (tokLen == 3)
1646                                                 result.Append (dfi.GetAbbreviatedDayName (dfi.Calendar.GetDayOfWeek (this)));
1647                                         else
1648                                                 result.Append (dfi.GetDayName (dfi.Calendar.GetDayOfWeek (this)));
1649
1650                                         break;
1651                                 case 'M':
1652                                         // Month.m(m?) = month # (with leading 0 if two mm)
1653                                         // mmm = 3 letter name
1654                                         // mmmm+ = full name
1655                                         tokLen = CountRepeat (format, i, ch);
1656                                         int month = dfi.Calendar.GetMonth(this);
1657                                         if (tokLen <= 2)
1658                                                 ZeroPad (result, month, tokLen);
1659                                         else if (tokLen == 3)
1660                                                 result.Append (dfi.GetAbbreviatedMonthName (month));
1661                                         else
1662                                                 result.Append (dfi.GetMonthName (month));
1663
1664                                         break;
1665                                 case 'y':
1666                                         // Year. y(y?) = two digit year, with leading 0 if yy
1667                                         // yyy+ full year, if yyy and yr < 1000, displayed as three digits
1668                                         tokLen = CountRepeat (format, i, ch);
1669
1670                                         if (tokLen <= 2)
1671                                                 ZeroPad (result, dfi.Calendar.GetYear (this) % 100, tokLen);
1672                                         else
1673                                                 ZeroPad (result, dfi.Calendar.GetYear (this), (tokLen == 3 ? 3 : 4));
1674
1675                                         break;
1676                                 case 'g':
1677                                         // Era name
1678                                         tokLen = CountRepeat (format, i, ch);
1679                                         result.Append (dfi.GetEraName (dfi.Calendar.GetEra (this)));
1680                                         break;
1681
1682                                 //
1683                                 // Other
1684                                 //
1685                                 case ':':
1686                                         result.Append (dfi.TimeSeparator);
1687                                         tokLen = 1;
1688                                         break;
1689                                 case '/':
1690                                         result.Append (dfi.DateSeparator);
1691                                         tokLen = 1;
1692                                         break;
1693                                 case '\'': case '"':
1694                                         tokLen = ParseQuotedString (format, i, result);
1695                                         break;
1696                                 case '%':
1697                                         if (i >= format.Length - 1)
1698                                                 throw new FormatException ("% at end of date time string");
1699                                         if (format [i + 1] == '%')
1700                                                 throw new FormatException ("%% in date string");
1701
1702                                         // Look for the next char
1703                                         tokLen = 1;
1704                                         break;
1705                                 case '\\':
1706                                         // C-Style escape
1707                                         if (i >= format.Length - 1)
1708                                                 throw new FormatException ("\\ at end of date time string");
1709
1710                                         result.Append (format [i + 1]);
1711                                         tokLen = 2;
1712
1713                                         break;
1714                                 default:
1715                                         // catch all
1716                                         result.Append (ch);
1717                                         tokLen = 1;
1718                                         break;
1719                                 }
1720                                 i += tokLen;
1721                         }
1722                         return result.ToString ();
1723                 }
1724                 
1725                 static int CountRepeat (string fmt, int p, char c)
1726                 {
1727                         int l = fmt.Length;
1728                         int i = p + 1;
1729                         while ((i < l) && (fmt [i] == c)) 
1730                                 i++;
1731                         
1732                         return i - p;
1733                 }
1734                 
1735                 static int ParseQuotedString (string fmt, int pos, StringBuilder output)
1736                 {
1737                         // pos == position of " or '
1738                         
1739                         int len = fmt.Length;
1740                         int start = pos;
1741                         char quoteChar = fmt [pos++];
1742                         
1743                         while (pos < len) {
1744                                 char ch = fmt [pos++];
1745                                 
1746                                 if (ch == quoteChar)
1747                                         return pos - start;
1748                                 
1749                                 if (ch == '\\') {
1750                                         // C-Style escape
1751                                         if (pos >= len)
1752                                                 throw new FormatException("Un-ended quote");
1753         
1754                                         output.Append (fmt [pos++]);
1755                                 } else {
1756                                         output.Append (ch);
1757                                 }
1758                         }
1759
1760                         throw new FormatException("Un-ended quote");
1761                 }
1762                 
1763                 static unsafe void ZeroPad (StringBuilder output, int digits, int len)
1764                 {
1765                         // more than enough for an int
1766                         char* buffer = stackalloc char [16];
1767                         int pos = 16;
1768                         
1769                         do {
1770                                 buffer [-- pos] = (char) ('0' + digits % 10);
1771                                 digits /= 10;
1772                                 len --;
1773                         } while (digits > 0);
1774                         
1775                         while (len -- > 0)
1776                                 buffer [-- pos] = '0';
1777                         
1778                         output.Append (new string (buffer, pos, 16 - pos));
1779                 }
1780
1781                 public string ToString (string format, IFormatProvider fp)
1782
1783                 {
1784                         DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
1785
1786                         if (format == null)
1787                                 format = "G";
1788
1789                         bool useutc = false, use_invariant = false;
1790
1791                         if (format.Length == 1) {
1792                                 char fchar = format [0];
1793                                 format = _GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
1794                         }
1795
1796                         // Don't convert UTC value. It just adds 'Z' for 
1797                         // 'u' format, for the same ticks.
1798                         return this._ToString (format, dfi);
1799                 }
1800
1801                 public DateTime ToLocalTime()
1802                 {
1803                         TimeZone tz = TimeZone.CurrentTimeZone;
1804
1805                         TimeSpan offset = tz.GetUtcOffset (this);
1806
1807                         if (offset.Ticks > 0) {
1808                                 if (DateTime.MaxValue - offset < this)
1809                                         return DateTime.MaxValue;
1810                         } else if (offset.Ticks < 0) {
1811                                 // MS.NET fails to check validity here 
1812                                 // - it may throw ArgumentOutOfRangeException
1813                                 /*
1814                                 if (DateTime.MinValue - offset > this)
1815                                         return DateTime.MinValue;
1816                                 */
1817                         }
1818                         
1819                         DateTime lt = new DateTime(true, ticks+offset);
1820                         TimeSpan ltoffset = tz.GetUtcOffset(lt);
1821                         if(ltoffset != offset)
1822                                 lt = lt.Add(ltoffset.Subtract(offset));
1823
1824                         return lt;
1825                 }
1826
1827                 public DateTime ToUniversalTime()
1828                 {
1829                         TimeZone tz = TimeZone.CurrentTimeZone;
1830
1831                         TimeSpan offset = tz.GetUtcOffset (this);
1832
1833                         if (offset.Ticks < 0) {
1834                                 if (DateTime.MaxValue + offset < this)
1835                                         return DateTime.MaxValue;
1836                         } else if (offset.Ticks > 0) {
1837                                 if (DateTime.MinValue + offset > this)
1838                                         return DateTime.MinValue;
1839                         }
1840
1841                         return new DateTime (false, ticks - offset);
1842                 }
1843
1844                 /*  OPERATORS */
1845
1846                 public static DateTime operator +(DateTime d, TimeSpan t)
1847                 {
1848                         return new DateTime (true, d.ticks + t);
1849                 }
1850
1851                 public static bool operator ==(DateTime d1, DateTime d2)
1852                 {
1853                         return (d1.ticks == d2.ticks);
1854                 }
1855
1856                 public static bool operator >(DateTime t1,DateTime t2)
1857                 {
1858                         return (t1.ticks > t2.ticks);
1859                 }
1860
1861                 public static bool operator >=(DateTime t1,DateTime t2)
1862                 {
1863                         return (t1.ticks >= t2.ticks);
1864                 }
1865
1866                 public static bool operator !=(DateTime d1, DateTime d2)
1867                 {
1868                         return (d1.ticks != d2.ticks);
1869                 }
1870
1871                 public static bool operator <(DateTime t1,      DateTime t2)
1872                 {
1873                         return (t1.ticks < t2.ticks );
1874                 }
1875
1876                 public static bool operator <=(DateTime t1,DateTime t2)
1877                 {
1878                         return (t1.ticks <= t2.ticks);
1879                 }
1880
1881                 public static TimeSpan operator -(DateTime d1,DateTime d2)
1882                 {
1883                         return new TimeSpan((d1.ticks - d2.ticks).Ticks);
1884                 }
1885
1886                 public static DateTime operator -(DateTime d,TimeSpan t)
1887                 {
1888                         return new DateTime (true, d.ticks - t);
1889                 }
1890
1891                 bool IConvertible.ToBoolean(IFormatProvider provider)
1892                 {
1893                         throw new InvalidCastException();
1894                 }
1895                 
1896                 byte IConvertible.ToByte(IFormatProvider provider)
1897                 {
1898                         throw new InvalidCastException();
1899
1900                 }
1901
1902                 char IConvertible.ToChar(IFormatProvider provider)
1903                 {
1904                         throw new InvalidCastException();
1905                 }
1906
1907                 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
1908                 {
1909                         return this;
1910                 } 
1911                 
1912                 decimal IConvertible.ToDecimal(IFormatProvider provider)
1913                 {
1914                          throw new InvalidCastException();
1915                 }
1916
1917                 double IConvertible.ToDouble(IFormatProvider provider)
1918                 {
1919                         throw new InvalidCastException();
1920                 }
1921
1922                 Int16 IConvertible.ToInt16(IFormatProvider provider)
1923                 {
1924                         throw new InvalidCastException();
1925                 }
1926
1927                 Int32 IConvertible.ToInt32(IFormatProvider provider)
1928                 {
1929                         throw new InvalidCastException();
1930                 }
1931
1932                 Int64 IConvertible.ToInt64(IFormatProvider provider)
1933                 {
1934                         throw new InvalidCastException();
1935                 }
1936
1937                 SByte IConvertible.ToSByte(IFormatProvider provider)
1938                 {
1939                         throw new InvalidCastException();
1940                 }
1941
1942                 Single IConvertible.ToSingle(IFormatProvider provider)
1943                 {
1944                         throw new InvalidCastException();
1945                 }
1946
1947                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1948                 {
1949                         if (conversionType == null)
1950                                 throw new ArgumentNullException ("conversionType");
1951
1952                         if (conversionType == typeof (DateTime))
1953                                 return this;
1954                         else if (conversionType == typeof (String))
1955                                 return this.ToString (provider);
1956                         else if (conversionType == typeof (Object))
1957                                 return this;
1958                         else
1959                                 throw new InvalidCastException();
1960                 }
1961
1962                 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
1963                 {
1964                         throw new InvalidCastException();
1965                 }
1966                 
1967                 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
1968                 {
1969                         throw new InvalidCastException();
1970                 }
1971
1972                 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
1973                 {
1974                         throw new InvalidCastException();
1975                 }
1976         }
1977 }
1978
1979 namespace System
1980 {
1981         public enum DayOfWeek
1982         {
1983                 Sunday,
1984                 Monday,
1985                 Tuesday,
1986                 Wednesday,
1987                 Thursday,
1988                 Friday,
1989                 Saturday
1990         }
1991 }