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