Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / corlib / System / TimeSpan.cs
1 //
2 // System.TimeSpan.cs
3 //
4 // Authors:
5 //   Duco Fijma (duco@lorentz.xs4all.nl)
6 //   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 //   Sebastien Pouliot  <sebastien@ximian.com>
8 //   Marek Safar (marek.safar@gmail.com)
9 //
10 // (C) 2001 Duco Fijma
11 // (C) 2004 Andreas Nahr
12 // Copyright (C) 2004 Novell (http://www.novell.com)
13 // Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System.Text;
36 using System.Threading;
37 using System.Globalization;
38
39 namespace System
40 {
41         [Serializable]
42         [System.Runtime.InteropServices.ComVisible (true)]
43         public struct TimeSpan : IComparable, IComparable<TimeSpan>, IEquatable <TimeSpan>
44 #if NET_4_0
45                                  , IFormattable
46 #endif
47         {
48 #if MONOTOUCH
49                 static TimeSpan () {
50                         if (MonoTouchAOTHelper.FalseFlag) {
51                                 var comparer = new System.Collections.Generic.GenericComparer <TimeSpan> ();
52                                 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <TimeSpan> ();
53                         }
54                 }
55 #endif
56                 public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
57                 public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
58                 public static readonly TimeSpan Zero = new TimeSpan (0L);
59
60                 public const long TicksPerDay = 864000000000L;
61                 public const long TicksPerHour = 36000000000L;
62                 public const long TicksPerMillisecond = 10000L;
63                 public const long TicksPerMinute = 600000000L;
64                 public const long TicksPerSecond = 10000000L;
65
66                 private long _ticks;
67
68                 public TimeSpan (long ticks)
69                 {
70                         _ticks = ticks;
71                 }
72
73                 public TimeSpan (int hours, int minutes, int seconds)
74                 {
75                         CalculateTicks (0, hours, minutes, seconds, 0, true, out _ticks);
76                 }
77
78                 public TimeSpan (int days, int hours, int minutes, int seconds)
79                 {
80                         CalculateTicks (days, hours, minutes, seconds, 0, true, out _ticks);
81                 }
82
83                 public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
84                 {
85                         CalculateTicks (days, hours, minutes, seconds, milliseconds, true, out _ticks);
86                 }
87
88                 internal static bool CalculateTicks (int days, int hours, int minutes, int seconds, int milliseconds, bool throwExc, out long result)
89                 {
90                         // there's no overflow checks for hours, minutes, ...
91                         // so big hours/minutes values can overflow at some point and change expected values
92                         int hrssec = (hours * 3600); // break point at (Int32.MaxValue - 596523)
93                         int minsec = (minutes * 60);
94                         long t = ((long)(hrssec + minsec + seconds) * 1000L + (long)milliseconds);
95                         t *= 10000;
96
97                         result = 0;
98
99                         bool overflow = false;
100                         // days is problematic because it can overflow but that overflow can be 
101                         // "legal" (i.e. temporary) (e.g. if other parameters are negative) or 
102                         // illegal (e.g. sign change).
103                         if (days > 0) {
104                                 long td = TicksPerDay * days;
105                                 if (t < 0) {
106                                         long ticks = t;
107                                         t += td;
108                                         // positive days -> total ticks should be lower
109                                         overflow = (ticks > t);
110                                 }
111                                 else {
112                                         t += td;
113                                         // positive + positive != negative result
114                                         overflow = (t < 0);
115                                 }
116                         }
117                         else if (days < 0) {
118                                 long td = TicksPerDay * days;
119                                 if (t <= 0) {
120                                         t += td;
121                                         // negative + negative != positive result
122                                         overflow = (t > 0);
123                                 }
124                                 else {
125                                         long ticks = t;
126                                         t += td;
127                                         // negative days -> total ticks should be lower
128                                         overflow = (t > ticks);
129                                 }
130                         }
131
132                         if (overflow) {
133                                 if (throwExc)
134                                         throw new ArgumentOutOfRangeException (Locale.GetText ("The timespan is too big or too small."));
135                                 return false;
136                         }
137
138                         result = t;
139                         return true;
140                 }
141
142                 public int Days {
143                         get {
144                                 return (int) (_ticks / TicksPerDay);
145                         }
146                 }
147
148                 public int Hours {
149                         get {
150                                 return (int) (_ticks % TicksPerDay / TicksPerHour);
151                         }
152                 }
153
154                 public int Milliseconds {
155                         get {
156                                 return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
157                         }
158                 }
159
160                 public int Minutes {
161                         get {
162                                 return (int) (_ticks % TicksPerHour / TicksPerMinute);
163                         }
164                 }
165
166                 public int Seconds {
167                         get {
168                                 return (int) (_ticks % TicksPerMinute / TicksPerSecond);
169                         }
170                 }
171
172                 public long Ticks {
173                         get {
174                                 return _ticks;
175                         }
176                 }
177
178                 public double TotalDays {
179                         get {
180                                 return (double) _ticks / TicksPerDay;
181                         }
182                 }
183
184                 public double TotalHours {
185                         get {
186                                 return (double) _ticks / TicksPerHour;
187                         }
188                 }
189
190                 public double TotalMilliseconds {
191                         get {
192                                 return (double) _ticks  / TicksPerMillisecond;
193                         }
194                 }
195
196                 public double TotalMinutes {
197                         get {
198                                 return (double) _ticks / TicksPerMinute;
199                         }
200                 }
201
202                 public double TotalSeconds {
203                         get {
204                                 return (double) _ticks / TicksPerSecond;
205                         }
206                 }
207
208                 public TimeSpan Add (TimeSpan ts)
209                 {
210                         try {
211                                 checked {
212                                         return new TimeSpan (_ticks + ts.Ticks);
213                                 }
214                         }
215                         catch (OverflowException) {
216                                 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
217                         }
218                 }
219
220                 public static int Compare (TimeSpan t1, TimeSpan t2)
221                 {
222                         if (t1._ticks < t2._ticks)
223                                 return -1;
224                         if (t1._ticks > t2._ticks)
225                                 return 1;
226                         return 0;
227                 }
228
229                 public int CompareTo (object value)
230                 {
231                         if (value == null)
232                                 return 1;
233
234                         if (!(value is TimeSpan)) {
235                                 throw new ArgumentException (Locale.GetText ("Argument has to be a TimeSpan."), "value");
236                         }
237
238                         return Compare (this, (TimeSpan) value);
239                 }
240
241                 public int CompareTo (TimeSpan value)
242                 {
243                         return Compare (this, value);
244                 }
245
246                 public bool Equals (TimeSpan obj)
247                 {
248                         return obj._ticks == _ticks;
249                 }
250
251                 public TimeSpan Duration ()
252                 {
253                         try {
254                                 checked {
255                                         return new TimeSpan (Math.Abs (_ticks));
256                                 }
257                         }
258                         catch (OverflowException) {
259                                 throw new OverflowException (Locale.GetText (
260                                         "This TimeSpan value is MinValue so you cannot get the duration."));
261                         }
262                 }
263
264                 public override bool Equals (object value)
265                 {
266                         if (!(value is TimeSpan))
267                                 return false;
268
269                         return _ticks == ((TimeSpan) value)._ticks;
270                 }
271
272                 public static bool Equals (TimeSpan t1, TimeSpan t2)
273                 {
274                         return t1._ticks == t2._ticks;
275                 }
276
277                 public static TimeSpan FromDays (double value)
278                 {
279                         return From (value, TicksPerDay);
280                 }
281
282                 public static TimeSpan FromHours (double value)
283                 {
284                         return From (value, TicksPerHour);
285                 }
286
287                 public static TimeSpan FromMinutes (double value)
288                 {
289                         return From (value, TicksPerMinute);
290                 }
291
292                 public static TimeSpan FromSeconds (double value)
293                 {
294                         return From (value, TicksPerSecond);
295                 }
296
297                 public static TimeSpan FromMilliseconds (double value)
298                 {
299                         return From (value, TicksPerMillisecond);
300                 }
301
302                 private static TimeSpan From (double value, long tickMultiplicator) 
303                 {
304                         if (Double.IsNaN (value))
305                                 throw new ArgumentException (Locale.GetText ("Value cannot be NaN."), "value");
306                         if (Double.IsNegativeInfinity (value) || Double.IsPositiveInfinity (value) ||
307                                 (value < MinValue.Ticks) || (value > MaxValue.Ticks))
308                                 throw new OverflowException (Locale.GetText ("Outside range [MinValue,MaxValue]"));
309
310                         try {
311                                 value = (value * (tickMultiplicator / TicksPerMillisecond));
312
313                                 checked {
314                                         long val = (long) Math.Round(value, MidpointRounding.AwayFromZero);
315                                         return new TimeSpan (val * TicksPerMillisecond);
316                                 }
317                         }
318                         catch (OverflowException) {
319                                 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
320                         }
321                 }
322
323                 public static TimeSpan FromTicks (long value)
324                 {
325                         return new TimeSpan (value);
326                 }
327
328                 public override int GetHashCode ()
329                 {
330                         return _ticks.GetHashCode ();
331                 }
332
333                 public TimeSpan Negate ()
334                 {
335                         if (_ticks == MinValue._ticks)
336                                 throw new OverflowException (Locale.GetText (
337                                         "This TimeSpan value is MinValue and cannot be negated."));
338                         return new TimeSpan (-_ticks);
339                 }
340
341                 public static TimeSpan Parse (string s)
342                 {
343                         if (s == null) {
344                                 throw new ArgumentNullException ("s");
345                         }
346
347                         TimeSpan result;
348                         Parser p = new Parser (s);
349                         p.Execute (false, out result);
350                         return result;
351                 }
352
353                 public static bool TryParse (string s, out TimeSpan result)
354                 {
355                         if (s == null) {
356                                 result = TimeSpan.Zero;
357                                 return false;
358                         }
359
360                         Parser p = new Parser (s);
361                         return p.Execute (true, out result);
362                 }
363
364 #if NET_4_0
365                 public static TimeSpan Parse (string input, IFormatProvider formatProvider)
366                 {
367                         if (input == null)
368                                 throw new ArgumentNullException ("input");
369
370                         TimeSpan result;
371                         Parser p = new Parser (input, formatProvider);
372                         p.Execute (false, out result);
373                         return result;
374                 }
375
376                 public static bool TryParse (string input, IFormatProvider formatProvider, out TimeSpan result)
377                 {
378                         if (string.IsNullOrEmpty (input)) {
379                                 result = TimeSpan.Zero;
380                                 return false;
381                         }
382
383                         Parser p = new Parser (input, formatProvider);
384                         return p.Execute (true, out result);
385                 }
386
387                 public static TimeSpan ParseExact (string input, string format, IFormatProvider formatProvider)
388                 {
389                         if (format == null)
390                                 throw new ArgumentNullException ("format");
391
392                         return ParseExact (input, new string [] { format }, formatProvider, TimeSpanStyles.None);
393                 }
394
395                 public static TimeSpan ParseExact (string input, string format, IFormatProvider formatProvider, TimeSpanStyles styles)
396                 {
397                         if (format == null)
398                                 throw new ArgumentNullException ("format");
399
400                         return ParseExact (input, new string [] { format }, formatProvider, styles);
401                 }
402
403                 public static TimeSpan ParseExact (string input, string [] formats, IFormatProvider formatProvider)
404                 {
405                         return ParseExact (input, formats, formatProvider, TimeSpanStyles.None);
406                 }
407
408                 public static TimeSpan ParseExact (string input, string [] formats, IFormatProvider formatProvider, TimeSpanStyles styles)
409                 {
410                         if (input == null)
411                                 throw new ArgumentNullException ("input");
412                         if (formats == null)
413                                 throw new ArgumentNullException ("formats");
414
415                         // All the errors found during the parsing process are reported as FormatException.
416                         TimeSpan result;
417                         if (!TryParseExact (input, formats, formatProvider, styles, out result))
418                                 throw new FormatException ("Invalid format.");
419
420                         return result;
421                 }
422
423                 public static bool TryParseExact (string input, string format, IFormatProvider formatProvider, out TimeSpan result)
424                 {
425                         return TryParseExact (input, new string [] { format }, formatProvider, TimeSpanStyles.None, out result);
426                 }
427
428                 public static bool TryParseExact (string input, string format, IFormatProvider formatProvider, TimeSpanStyles styles,
429                                 out TimeSpan result)
430                 {
431                         return TryParseExact (input, new string [] { format }, formatProvider, styles, out result);
432                 }
433
434                 public static bool TryParseExact (string input, string [] formats, IFormatProvider formatProvider, out TimeSpan result)
435                 {
436                         return TryParseExact (input, formats, formatProvider, TimeSpanStyles.None, out result);
437                 }
438
439                 public static bool TryParseExact (string input, string [] formats, IFormatProvider formatProvider, TimeSpanStyles styles,
440                         out TimeSpan result)
441                 {
442                         result = TimeSpan.Zero;
443
444                         if (input == null || formats == null || formats.Length == 0)
445                                 return false;
446
447                         Parser p = new Parser (input, formatProvider);
448                         p.Exact = true;
449
450                         foreach (string format in formats) {
451                                 if (format == null || format.Length == 0)
452                                         return false; // wrong format, return immediately.
453
454                                 switch (format) {
455                                         case "g":
456                                                 p.AllMembersRequired = false;
457                                                 p.CultureSensitive = true;
458                                                 p.UseColonAsDaySeparator = true;
459                                                 break;
460                                         case "G":
461                                                 p.AllMembersRequired = true;
462                                                 p.CultureSensitive = true;
463                                                 p.UseColonAsDaySeparator = true;
464                                                 break;
465                                         case "c":
466                                                 p.AllMembersRequired = false;
467                                                 p.CultureSensitive = false;
468                                                 p.UseColonAsDaySeparator = false;
469                                                 break;
470                                         default:
471                                                 // Single letter formats other than the defined ones are not accepted.
472                                                 if (format.Length == 1)
473                                                         return false;
474                                                 // custom format
475                                                 if (p.ExecuteWithFormat (format, styles, true, out result))
476                                                         return true;
477                                                 continue;
478                                 }
479
480                                 if (p.Execute (true, out result))
481                                         return true;
482                         }
483
484                         return false;
485                 }
486 #endif
487
488                 public TimeSpan Subtract (TimeSpan ts)
489                 {
490                         try {
491                                 checked {
492                                         return new TimeSpan (_ticks - ts.Ticks);
493                                 }
494                         }
495                         catch (OverflowException) {
496                                 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
497                         }
498                 }
499
500                 public override string ToString ()
501                 {
502                         StringBuilder sb = new StringBuilder (14);
503                         
504                         if (_ticks < 0)
505                                 sb.Append ('-');
506
507                         // We need to take absolute values of all components.
508                         // Can't handle negative timespans by negating the TimeSpan
509                         // as a whole. This would lead to an overflow for the 
510                         // degenerate case "TimeSpan.MinValue.ToString()".
511                         if (Days != 0) {
512                                 sb.Append (Math.Abs (Days));
513                                 sb.Append ('.');
514                         }
515
516                         sb.Append (Math.Abs (Hours).ToString ("D2"));
517                         sb.Append (':');
518                         sb.Append (Math.Abs (Minutes).ToString ("D2"));
519                         sb.Append (':');
520                         sb.Append (Math.Abs (Seconds).ToString ("D2"));
521
522                         int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
523                         if (fractional != 0) {
524                                 sb.Append ('.');
525                                 sb.Append (fractional.ToString ("D7"));
526                         }
527
528                         return sb.ToString ();
529                 }
530
531 #if NET_4_0
532                 public string ToString (string format)
533                 {
534                         return ToString (format, null);
535                 }
536
537                 public string ToString (string format, IFormatProvider formatProvider)
538                 {
539                         if (format == null || format.Length == 0 || format == "c" ||
540                                         format == "t" || format == "T") // Default version
541                                 return ToString ();
542
543                         if (format != "g" && format != "G")
544                                 return ToStringCustom (format); // custom formats ignore culture/formatProvider
545
546                         NumberFormatInfo number_info = null;
547                         if (formatProvider != null)
548                                 number_info = formatProvider.GetFormat (typeof (NumberFormatInfo)) as NumberFormatInfo;
549                         if (number_info == null)
550                                 number_info = Thread.CurrentThread.CurrentCulture.NumberFormat;
551
552                         string decimal_separator = number_info.NumberDecimalSeparator;
553                         int days, hours, minutes, seconds, milliseconds, fractional;
554
555                         days = Math.Abs (Days);
556                         hours = Math.Abs (Hours);
557                         minutes = Math.Abs (Minutes);
558                         seconds = Math.Abs (Seconds);
559                         milliseconds = Math.Abs (Milliseconds);
560                         fractional = (int) Math.Abs (_ticks % TicksPerSecond);
561
562                         // Set Capacity depending on whether it's long or shot format
563                         StringBuilder sb = new StringBuilder (format == "g" ? 16 : 32);
564                         if (_ticks < 0)
565                                 sb.Append ('-');
566
567                         switch (format) {
568                                 case "g": // short version
569                                         if (days != 0) {
570                                                 sb.Append (days.ToString ());
571                                                 sb.Append (':');
572                                         }
573                                         sb.Append (hours.ToString ());
574                                         sb.Append (':');
575                                         sb.Append (minutes.ToString ("D2"));
576                                         sb.Append (':');
577                                         sb.Append (seconds.ToString ("D2"));
578                                         if (milliseconds != 0) {
579                                                 sb.Append (decimal_separator);
580                                                 sb.Append (milliseconds.ToString ("D3"));
581                                         }
582                                         break;
583                                 case "G": // long version
584                                         sb.Append (days.ToString ("D1"));
585                                         sb.Append (':');
586                                         sb.Append (hours.ToString ("D2"));
587                                         sb.Append (':');
588                                         sb.Append (minutes.ToString ("D2"));
589                                         sb.Append (':');
590                                         sb.Append (seconds.ToString ("D2"));
591                                         sb.Append (decimal_separator);
592                                         sb.Append (fractional.ToString ("D7"));
593                                         break;
594                         }
595
596                         return sb.ToString ();
597                 }
598
599                 string ToStringCustom (string format)
600                 {
601                         // Single char formats are not accepted.
602                         if (format.Length < 2)
603                                 throw new FormatException ("The format is not recognized.");
604
605                         FormatParser parser = new FormatParser (format);
606                         FormatElement element;
607                         int value;
608
609                         StringBuilder sb = new StringBuilder (format.Length + 1);
610
611                         while (true) {
612                                 if (parser.AtEnd)
613                                         break;
614
615                                 element = parser.GetNextElement ();
616                                 switch (element.Type) {
617                                         case FormatElementType.Days:
618                                                 value = Math.Abs (Days);
619                                                 break;
620                                         case FormatElementType.Hours:
621                                                 value = Math.Abs (Hours);
622                                                 break;
623                                         case FormatElementType.Minutes:
624                                                 value = Math.Abs (Minutes);
625                                                 break;
626                                         case FormatElementType.Seconds:
627                                                 value = Math.Abs (Seconds);
628                                                 break;
629                                         case FormatElementType.Ticks:
630                                         case FormatElementType.TicksUppercase:
631                                                 value = Math.Abs (Milliseconds);
632                                                 if (value == 0) {
633                                                         if (element.Type == FormatElementType.Ticks)
634                                                                 break;
635
636                                                         continue;
637                                                 }
638
639                                                 int threshold = (int)Math.Pow (10, element.IntValue);
640                                                 while (value >= threshold)
641                                                         value /= 10;
642                                                 sb.Append (value.ToString ());
643                                                 continue;
644                                         case FormatElementType.EscapedChar:
645                                                 sb.Append (element.CharValue);
646                                                 continue;
647                                         case FormatElementType.Literal:
648                                                 sb.Append (element.StringValue);
649                                                 continue;
650                                         default:
651                                                 throw new FormatException ("The format is not recognized.");
652                                 }
653
654                                 sb.Append (value.ToString ("D" + element.IntValue.ToString ()));
655                         }
656
657                         return sb.ToString ();
658                 }
659 #endif
660
661                 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
662                 {
663                         return t1.Add (t2);
664                 }
665
666                 public static bool operator == (TimeSpan t1, TimeSpan t2)
667                 {
668                         return t1._ticks == t2._ticks;
669                 }
670
671                 public static bool operator > (TimeSpan t1, TimeSpan t2)
672                 {
673                         return t1._ticks > t2._ticks;
674                 }
675
676                 public static bool operator >= (TimeSpan t1, TimeSpan t2)
677                 {
678                         return t1._ticks >= t2._ticks;
679                 }
680
681                 public static bool operator != (TimeSpan t1, TimeSpan t2)
682                 {
683                         return t1._ticks != t2._ticks;
684                 }
685
686                 public static bool operator < (TimeSpan t1, TimeSpan t2)
687                 {
688                         return t1._ticks < t2._ticks;
689                 }
690
691                 public static bool operator <= (TimeSpan t1, TimeSpan t2)
692                 {
693                         return t1._ticks <= t2._ticks;
694                 }
695
696                 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
697                 {
698                         return t1.Subtract (t2);
699                 }
700
701                 public static TimeSpan operator - (TimeSpan t)
702                 {
703                         return t.Negate ();
704                 }
705
706                 public static TimeSpan operator + (TimeSpan t)
707                 {
708                         return t;
709                 }
710
711                 enum ParseError {
712                         None,
713                         Format,
714                         Overflow
715                 }
716
717                 // Class Parser implements parser for TimeSpan.Parse
718                 private class Parser
719                 {
720                         private string _src;
721                         private int _cur = 0;
722                         private int _length;
723                         ParseError parse_error;
724 #if NET_4_0
725                         bool parsed_ticks;
726                         NumberFormatInfo number_format;
727                         int parsed_numbers_count;
728                         bool parsed_days_separator;
729
730                         public bool Exact; // no fallback, strict pattern.
731                         public bool AllMembersRequired;
732                         public bool CultureSensitive = true;
733                         public bool UseColonAsDaySeparator = true;
734 #endif
735
736                         public Parser (string src)
737                         {
738                                 _src = src;
739                                 _length = _src.Length;
740 #if NET_4_0
741                                 number_format = GetNumberFormatInfo (null);
742 #endif
743                         }
744
745 #if NET_4_0
746                         // Reset state data, so we can execute another parse over the input.
747                         void Reset ()
748                         {
749                                 _cur = 0;
750                                 parse_error = ParseError.None;
751                                 parsed_ticks = parsed_days_separator = false;
752                                 parsed_numbers_count = 0;
753                         }
754
755                         public Parser (string src, IFormatProvider formatProvider) :
756                                 this (src)
757                         {
758                                 number_format = GetNumberFormatInfo (formatProvider);
759                         }
760
761                         static NumberFormatInfo GetNumberFormatInfo (IFormatProvider formatProvider)
762                         {
763                                 NumberFormatInfo format = null;
764                                 if (formatProvider != null)
765                                         format = formatProvider.GetFormat (typeof (NumberFormatInfo)) as NumberFormatInfo;
766                                 if (format == null)
767                                         format = Thread.CurrentThread.CurrentCulture.NumberFormat;
768
769                                 return format;
770                         }
771 #endif
772         
773                         public bool AtEnd {
774                                 get {
775                                         return _cur >= _length;
776                                 }
777                         }
778
779                         // All "Parse" functions throw a FormatException on syntax error.
780                         // Their return value is semantic value of the item parsed.
781
782                         // Range checking is spread over three different places:
783                         // 1) When parsing "int" values, an exception is thrown immediately
784                         //    when the value parsed exceeds the maximum value for an int.
785                         // 2) An explicit check is built in that checks for hours > 23 and
786                         //    for minutes and seconds > 59.
787                         // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
788                         //    or < MinValue is left to the TimeSpan constructor called.
789
790                         // Parse zero or more whitespace chars.
791                         private void ParseWhiteSpace ()
792                         {
793                                 while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
794                                         _cur++;
795                                 }
796                         }
797
798                         // Parse optional sign character.
799                         private bool ParseSign ()
800                         {
801                                 bool res = false;
802
803                                 if (!AtEnd && _src[_cur] == '-') {
804                                         res = true;
805                                         _cur++;
806                                 }
807
808                                 return res;
809                         }
810
811 #if NET_4_0
812                         // Used for custom formats parsing, where we may need to declare how
813                         // many digits we expect, as well as the maximum allowed.
814                         private int ParseIntExact (int digit_count, int max_digit_count)
815                         {
816                                 long res = 0;
817                                 int count = 0;
818
819                                 // We can have more than one preceding zero here.
820                                 while (!AtEnd && Char.IsDigit (_src, _cur)) {
821                                         res = res * 10 + _src [_cur] - '0';
822                                         if (res > Int32.MaxValue) {
823                                                 SetParseError (ParseError.Format);
824                                                 break;
825                                         }
826                                         _cur++;
827                                         count++;
828                                 }
829
830                                 // digit_count = 1 means we can use up to maximum count,
831                                 if (count == 0 || (digit_count > 1 && digit_count != count) ||
832                                                 count > max_digit_count)
833                                         SetParseError (ParseError.Format);
834
835                                 return (int)res;
836                         }
837 #endif
838
839                         // Parse simple int value
840                         private int ParseInt (bool optional)
841                         {
842                                 if (optional && AtEnd)
843                                         return 0;
844
845                                 long res = 0;
846                                 int count = 0;
847
848                                 while (!AtEnd && Char.IsDigit (_src, _cur)) {
849                                         res = res * 10 + _src[_cur] - '0';
850                                         if (res > Int32.MaxValue) {
851                                                 SetParseError (ParseError.Overflow);
852                                                 break;
853                                         }
854                                         _cur++;
855                                         count++;
856                                 }
857
858                                 if (!optional && (count == 0))
859                                         SetParseError (ParseError.Format);
860 #if NET_4_0
861                                 if (count > 0)
862                                         parsed_numbers_count++;
863 #endif
864
865                                 return (int)res;
866                         }
867
868 #if NET_4_0
869                         // This behaves pretty much like ParseOptDot, but we need to have it
870                         // as a separated routine for both days and decimal separators.
871                         private bool ParseOptDaysSeparator ()
872                         {
873                                 if (AtEnd)
874                                         return false;
875
876                                 if (_src[_cur] == '.') {
877                                         _cur++;
878                                         parsed_days_separator = true;
879                                         return true;
880                                 }
881                                 return false;
882                         }
883
884                         // Just as ParseOptDot, but for decimal separator
885                         private bool ParseOptDecimalSeparator ()
886                         {
887                                 if (AtEnd)
888                                         return false;
889
890                                 // we may need to provide compatibility with old versions using '.'
891                                 // for culture insensitve and non exact formats.
892                                 if (!Exact || !CultureSensitive)
893                                         if (_src [_cur] == '.') {
894                                                 _cur++;
895                                                 return true;
896                                         }
897
898                                 string decimal_separator = number_format.NumberDecimalSeparator;
899                                 if (CultureSensitive && String.Compare (_src, _cur, decimal_separator, 0, decimal_separator.Length) == 0) {
900                                         _cur += decimal_separator.Length;
901                                         return true;
902                                 }
903
904                                 return false;
905                         }
906
907                         private bool ParseLiteral (string value)
908                         {
909                                 if (!AtEnd && String.Compare (_src, _cur, value, 0, value.Length) == 0) {
910                                         _cur += value.Length;
911                                         return true;
912                                 }
913
914                                 return false;
915                         }
916
917                         private bool ParseChar (char c)
918                         {
919                                 if (!AtEnd && _src [_cur] == c) {
920                                         _cur++;
921                                         return true;
922                                 }
923
924                                 return false;
925                         }
926 #endif
927                         // Parse optional dot
928                         private bool ParseOptDot ()
929                         {
930                                 if (AtEnd)
931                                         return false;
932
933                                 if (_src[_cur] == '.') {
934                                         _cur++;
935                                         return true;
936                                 }
937                                 return false;
938                         }       
939
940                         private void ParseColon (bool optional)
941                         {
942                                 if (!AtEnd) {
943                                         if (_src[_cur] == ':')
944                                                 _cur++;
945                                         else if (!optional)
946                                                 SetParseError (ParseError.Format);
947                                 }
948                         }
949
950                         // Parse [1..7] digits, representing fractional seconds (ticks)
951                         // In 4.0 more than 7 digits will cause an OverflowException
952                         private long ParseTicks ()
953                         {
954                                 long mag = 1000000;
955                                 long res = 0;
956                                 bool digitseen = false;
957                                 
958                                 while (mag > 0 && !AtEnd && Char.IsDigit (_src, _cur)) {
959                                         res = res + (_src[_cur] - '0') * mag;
960                                         _cur++;
961                                         mag = mag / 10;
962                                         digitseen = true;
963                                 }
964
965                                 if (!digitseen)
966                                         SetParseError (ParseError.Format);
967 #if NET_4_0
968                                 else if (!AtEnd && Char.IsDigit (_src, _cur))
969                                         SetParseError (ParseError.Overflow);
970
971                                 parsed_ticks = true;
972 #endif
973
974                                 return res;
975                         }
976
977 #if NET_4_0
978                         // Used by custom formats parsing
979                         // digits_count = 0 for digits up to max_digits_count (optional), and other value to
980                         // force a precise number of digits.
981                         private long ParseTicksExact (int digits_count, int max_digits_count)
982                         {
983                                 long mag = 1000000;
984                                 long res = 0;
985                                 int count = 0;
986
987                                 while (mag > 0 && !AtEnd && Char.IsDigit (_src, _cur)) {
988                                         res = res + (_src [_cur] - '0') * mag;
989                                         _cur++;
990                                         count++;
991                                         mag = mag / 10;
992                                 }
993
994                                 if ((digits_count > 0 && count != digits_count) ||
995                                                 count > max_digits_count)
996                                         SetParseError (ParseError.Format);
997
998                                 return res;
999                         }
1000 #endif
1001
1002                         void SetParseError (ParseError error)
1003                         {
1004                                 // We preserve the very first error.
1005                                 if (parse_error != ParseError.None)
1006                                         return;
1007
1008                                 parse_error = error;
1009                         }
1010
1011 #if NET_4_0
1012                         bool CheckParseSuccess (bool tryParse)
1013 #else
1014                         bool CheckParseSuccess (int hours, int minutes, int seconds, bool tryParse)
1015 #endif
1016                         {
1017                                 // We always report the first error, but for 2.0 we need to give a higher
1018                                 // precence to per-element overflow (as opposed to int32 overflow).
1019 #if NET_4_0
1020                                 if (parse_error == ParseError.Overflow) {
1021 #else
1022                                 if (parse_error == ParseError.Overflow || hours > 23 || minutes > 59 || seconds > 59) {
1023 #endif
1024                                         if (tryParse)
1025                                                 return false;
1026                                         throw new OverflowException (
1027                                                 Locale.GetText ("Invalid time data."));
1028                                 }
1029
1030                                 if (parse_error == ParseError.Format) {
1031                                         if (tryParse)
1032                                                 return false;
1033                                         throw new FormatException (
1034                                                 Locale.GetText ("Invalid format for TimeSpan.Parse."));
1035                                 }
1036
1037                                 return true;
1038                         }
1039
1040 #if NET_4_0
1041                         // We are using a different parse approach in 4.0, due to some changes in the behaviour
1042                         // of the parse routines.
1043                         // The input string is documented as:
1044                         //      Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
1045                         //
1046                         // There are some special cases as part of 4.0, however:
1047                         // 1. ':' *can* be used as days separator, instead of '.', making valid the format 'dd:hh:mm:ss'
1048                         // 2. A input in the format 'hh:mm:ss' will end up assigned as 'dd.hh:mm' if the first int has a value
1049                         // exceeding the valid range for hours: 0-23.
1050                         // 3. The decimal separator can be retrieved from the current culture, as well as keeping support
1051                         // for the '.' value as part of keeping compatibility.
1052                         //
1053                         // So we take the approach to parse, if possible, 4 integers, and depending on both how many were
1054                         // actually parsed and what separators were read, assign the values to days/hours/minutes/seconds.
1055                         //
1056                         public bool Execute (bool tryParse, out TimeSpan result)
1057                         {
1058                                 bool sign;
1059                                 int value1, value2, value3, value4;
1060                                 int days, hours, minutes, seconds;
1061                                 long ticks = 0;
1062
1063                                 result = TimeSpan.Zero;
1064                                 value1 = value2 = value3 = value4 = 0;
1065                                 days = hours = minutes = seconds = 0;
1066
1067                                 Reset ();
1068
1069                                 ParseWhiteSpace ();
1070                                 sign = ParseSign ();
1071
1072                                 // Parse 4 integers, making only the first one non-optional.
1073                                 value1 = ParseInt (false);
1074                                 if (!ParseOptDaysSeparator ()) // Parse either day separator or colon
1075                                         ParseColon (false);
1076                                 int p = _cur;
1077                                 value2 = ParseInt (true);
1078                                 value3 = value4 = 0;
1079                                 if (p < _cur) {
1080                                         ParseColon (true);
1081                                         value3 = ParseInt (true);
1082                                         ParseColon (true);
1083                                         value4 = ParseInt (true);
1084                                 }
1085
1086                                 // We know the precise separator for ticks, so there's no need to guess.
1087                                 if (ParseOptDecimalSeparator ())
1088                                         ticks = ParseTicks ();
1089
1090                                 ParseWhiteSpace ();
1091
1092                                 if (!AtEnd)
1093                                         SetParseError (ParseError.Format);
1094
1095                                 if (Exact)
1096                                         // In Exact mode we cannot allow both ':' and '.' as day separator.
1097                                         if (UseColonAsDaySeparator && parsed_days_separator ||
1098                                                 AllMembersRequired && (parsed_numbers_count < 4 || !parsed_ticks))
1099                                                 SetParseError (ParseError.Format);
1100
1101                                 switch (parsed_numbers_count) {
1102                                         case 1:
1103                                                 days = value1;
1104                                                 break;
1105                                         case 2: // Two elements are valid only if they are *exactly* in the format: 'hh:mm'
1106                                                 if (parsed_days_separator)
1107                                                         SetParseError (ParseError.Format);
1108                                                 else {
1109                                                         hours = value1;
1110                                                         minutes = value2;
1111                                                 }
1112                                                 break;
1113                                         case 3: // Assign the first value to days if we parsed a day separator or the value
1114                                                 // is not in the valid range for hours.
1115                                                 if (parsed_days_separator || value1 > 23) {
1116                                                         days = value1;
1117                                                         hours = value2;
1118                                                         minutes = value3;
1119                                                 } else {
1120                                                         hours = value1;
1121                                                         minutes = value2;
1122                                                         seconds = value3;
1123                                                 }
1124                                                 break;
1125                                         case 4: // We are either on 'dd.hh:mm:ss' or 'dd:hh:mm:ss'
1126                                                 if (!UseColonAsDaySeparator && !parsed_days_separator)
1127                                                         SetParseError (ParseError.Format);
1128                                                 else {
1129                                                         days = value1;
1130                                                         hours = value2;
1131                                                         minutes = value3;
1132                                                         seconds = value4;
1133                                                 }
1134                                                 break;
1135                                 }
1136
1137                                 if (hours > 23 || minutes > 59 || seconds > 59)
1138                                         SetParseError (ParseError.Overflow);
1139
1140                                 if (!CheckParseSuccess (tryParse))
1141                                         return false;
1142
1143                                 long t;
1144                                 if (!TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0, false, out t))
1145                                         return false;
1146
1147                                 try {
1148                                         t = checked ((sign) ? (-t - ticks) : (t + ticks));
1149                                 } catch (OverflowException) {
1150                                         if (tryParse)
1151                                                 return false;
1152                                         throw;
1153                                 }
1154
1155                                 result = new TimeSpan (t);
1156                                 return true;
1157                         }
1158 #else
1159                         public bool Execute (bool tryParse, out TimeSpan result)
1160                         {
1161                                 bool sign;
1162                                 int days;
1163                                 int hours = 0;
1164                                 int minutes;
1165                                 int seconds;
1166                                 long ticks;
1167
1168                                 result = TimeSpan.Zero;
1169
1170                                 // documented as...
1171                                 // Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
1172                                 // ... but not entirely true as an lonely 
1173                                 // integer will be parsed as a number of days
1174                                 ParseWhiteSpace ();
1175                                 sign = ParseSign ();
1176                                 days = ParseInt (false);
1177                                 if (ParseOptDot ()) {
1178                                         hours = ParseInt (true);
1179                                 }
1180                                 else if (!AtEnd) {
1181                                         hours = days;
1182                                         days = 0;
1183                                 }
1184                                 ParseColon(false);
1185                                 int p = _cur;
1186                                 minutes = ParseInt (true);
1187                                 seconds = 0;
1188                                 if (p < _cur) {
1189                                         ParseColon (true);
1190                                         seconds = ParseInt (true);
1191                                 }
1192
1193                                 if ( ParseOptDot () ) {
1194                                         ticks = ParseTicks ();
1195                                 }
1196                                 else {
1197                                         ticks = 0;
1198                                 }
1199                                 ParseWhiteSpace ();
1200         
1201                                 if (!AtEnd)
1202                                         SetParseError (ParseError.Format);
1203
1204                                 if (!CheckParseSuccess (hours, minutes, seconds, tryParse))
1205                                         return false;
1206
1207                                 long t;
1208                                 if (!TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0, false, out t))
1209                                         return false;
1210
1211                                 try {
1212                                         t = checked ((sign) ? (-t - ticks) : (t + ticks));
1213                                 } catch (OverflowException) {
1214                                         if (tryParse)
1215                                                 return false;
1216                                         throw;
1217                                 }
1218
1219                                 result = new TimeSpan (t);
1220                                 return true;
1221                         }
1222 #endif
1223
1224 #if NET_4_0
1225                         public bool ExecuteWithFormat (string format, TimeSpanStyles style, bool tryParse, out TimeSpan result)
1226                         {
1227                                 int days, hours, minutes, seconds;
1228                                 long ticks;
1229                                 FormatElement format_element;
1230
1231                                 days = hours = minutes = seconds = -1;
1232                                 ticks = -1;
1233                                 result = TimeSpan.Zero;
1234                                 Reset ();
1235
1236                                 FormatParser format_parser = new FormatParser (format);
1237
1238                                 for (;;) {
1239                                         // We need to continue even if AtEnd == true, since we could have
1240                                         // a optional second element.
1241                                         if (parse_error != ParseError.None)
1242                                                 break;
1243                                         if (format_parser.AtEnd)
1244                                                 break;
1245
1246                                         format_element = format_parser.GetNextElement ();
1247                                         switch (format_element.Type) {
1248                                                 case FormatElementType.Days:
1249                                                         if (days != -1)
1250                                                                 goto case FormatElementType.Error;
1251                                                         days = ParseIntExact (format_element.IntValue, 8);
1252                                                         break;
1253                                                 case FormatElementType.Hours:
1254                                                         if (hours != -1)
1255                                                                 goto case FormatElementType.Error;
1256                                                         hours = ParseIntExact (format_element.IntValue, 2);
1257                                                         break;
1258                                                 case FormatElementType.Minutes:
1259                                                         if (minutes != -1)
1260                                                                 goto case FormatElementType.Error;
1261                                                         minutes = ParseIntExact (format_element.IntValue, 2);
1262                                                         break;
1263                                                 case FormatElementType.Seconds:
1264                                                         if (seconds != -1)
1265                                                                 goto case FormatElementType.Error;
1266                                                         seconds = ParseIntExact (format_element.IntValue, 2);
1267                                                         break;
1268                                                 case FormatElementType.Ticks:
1269                                                         if (ticks != -1)
1270                                                                 goto case FormatElementType.Error;
1271                                                         ticks = ParseTicksExact (format_element.IntValue,
1272                                                                         format_element.IntValue);
1273                                                         break;
1274                                                 case FormatElementType.TicksUppercase:
1275                                                         // Similar to Milliseconds, but optional and the
1276                                                         // number of F defines the max length, not the required one.
1277                                                         if (ticks != -1)
1278                                                                 goto case FormatElementType.Error;
1279                                                         ticks = ParseTicksExact (0, format_element.IntValue);
1280                                                         break;
1281                                                 case FormatElementType.Literal:
1282                                                         if (!ParseLiteral (format_element.StringValue))
1283                                                                 SetParseError (ParseError.Format);
1284                                                         break;
1285                                                 case FormatElementType.EscapedChar:
1286                                                         if (!ParseChar (format_element.CharValue))
1287                                                                 SetParseError (ParseError.Format);
1288                                                         break;
1289                                                 case FormatElementType.Error:
1290                                                         SetParseError (ParseError.Format);
1291                                                         break;
1292                                         }
1293                                 }
1294
1295                                 if (days == -1)
1296                                         days = 0;
1297                                 if (hours == -1)
1298                                         hours = 0;
1299                                 if (minutes == -1)
1300                                         minutes = 0;
1301                                 if (seconds == -1)
1302                                         seconds = 0;
1303                                 if (ticks == -1)
1304                                         ticks = 0;
1305
1306                                 if (!AtEnd || !format_parser.AtEnd)
1307                                         SetParseError (ParseError.Format);
1308                                 if (hours > 23 || minutes > 59 || seconds > 59)
1309                                         SetParseError (ParseError.Format);
1310
1311                                 if (!CheckParseSuccess (tryParse))
1312                                         return false;
1313
1314                                 long t;
1315                                 if (!TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0, false, out t))
1316                                         return false;
1317
1318                                 try {
1319                                         t = checked ((style == TimeSpanStyles.AssumeNegative) ? (-t - ticks) : (t + ticks));
1320                                 } catch (OverflowException) {
1321                                         if (tryParse)
1322                                                 return false;
1323                                         throw;
1324                                 }
1325
1326                                 result = new TimeSpan (t);
1327                                 return true;
1328                         }
1329 #endif
1330                 }
1331 #if NET_4_0
1332                 enum FormatElementType 
1333                 {
1334                         Days,
1335                         Hours,
1336                         Minutes,
1337                         Seconds,
1338                         Ticks, // 'f'
1339                         TicksUppercase, // 'F'
1340                         Literal,
1341                         EscapedChar,
1342                         Error,
1343                         End
1344                 }
1345
1346                 struct FormatElement
1347                 {
1348                         public FormatElement (FormatElementType type)
1349                         {
1350                                 Type = type;
1351                                 CharValue = (char)0;
1352                                 IntValue = 0;
1353                                 StringValue = null;
1354                         }
1355
1356                         public FormatElementType Type;
1357                         public char CharValue; // Used by EscapedChar
1358                         public string StringValue; // Used by Literal
1359                         public int IntValue; // Used by numerical elements.
1360                 }
1361
1362                 class FormatParser 
1363                 {
1364                         int cur;
1365                         string format;
1366
1367                         public FormatParser (string format)
1368                         {
1369                                 this.format = format;
1370                         }
1371
1372                         public bool AtEnd {
1373                                 get {
1374                                         return cur >= format.Length;
1375                                 }
1376                         }
1377
1378                         public FormatElement GetNextElement ()
1379                         {
1380                                 FormatElement element = new FormatElement ();
1381
1382                                 if (AtEnd)
1383                                         return new FormatElement (FormatElementType.End);
1384
1385                                 int count = 0;
1386                                 switch (format [cur]) {
1387                                         case 'd':
1388                                                 count = ParseChar ('d');
1389                                                 if (count > 8)
1390                                                         return new FormatElement (FormatElementType.Error);
1391                                                 element.Type = FormatElementType.Days;
1392                                                 element.IntValue = count;
1393                                                 break;
1394                                         case 'h':
1395                                                 count = ParseChar ('h');
1396                                                 if (count > 2)
1397                                                         return new FormatElement (FormatElementType.Error);
1398                                                 element.Type = FormatElementType.Hours;
1399                                                 element.IntValue = count;
1400                                                 break;
1401                                         case 'm':
1402                                                 count = ParseChar ('m');
1403                                                 if (count > 2)
1404                                                         return new FormatElement (FormatElementType.Error);
1405                                                 element.Type = FormatElementType.Minutes;
1406                                                 element.IntValue = count;
1407                                                 break;
1408                                         case 's':
1409                                                 count = ParseChar ('s');
1410                                                 if (count > 2)
1411                                                         return new FormatElement (FormatElementType.Error);
1412                                                 element.Type = FormatElementType.Seconds;
1413                                                 element.IntValue = count;
1414                                                 break;
1415                                         case 'f':
1416                                                 count = ParseChar ('f');
1417                                                 if (count > 7)
1418                                                         return new FormatElement (FormatElementType.Error);
1419                                                 element.Type = FormatElementType.Ticks;
1420                                                 element.IntValue = count;
1421                                                 break;
1422                                         case 'F':
1423                                                 count = ParseChar ('F');
1424                                                 if (count > 7)
1425                                                         return new FormatElement (FormatElementType.Error);
1426                                                 element.Type = FormatElementType.TicksUppercase;
1427                                                 element.IntValue = count;
1428                                                 break;
1429                                         case '%':
1430                                                 cur++;
1431                                                 if (AtEnd)
1432                                                         return new FormatElement (FormatElementType.Error);
1433                                                 if (format [cur] == 'd')
1434                                                         goto case 'd';
1435                                                 else if (format [cur] == 'h')
1436                                                         goto case 'h';
1437                                                 else if (format [cur] == 'm')
1438                                                         goto case 'm';
1439                                                 else if (format [cur] == 's')
1440                                                         goto case 's';
1441                                                 else if (format [cur] == 'f')
1442                                                         goto case 'f';
1443                                                 else if (format [cur] == 'F')
1444                                                         goto case 'F';
1445
1446                                                 return new FormatElement (FormatElementType.Error);
1447                                         case '\'':
1448                                                 string literal = ParseLiteral ();
1449                                                 if (literal == null)
1450                                                         return new FormatElement (FormatElementType.Error);
1451                                                 element.Type = FormatElementType.Literal;
1452                                                 element.StringValue = literal;
1453                                                 break;
1454                                         case '\\':
1455                                                 char escaped_char = ParseEscapedChar ();
1456                                                 if ((int)escaped_char == 0)
1457                                                         return new FormatElement (FormatElementType.Error);
1458                                                 element.Type = FormatElementType.EscapedChar;
1459                                                 element.CharValue = escaped_char;
1460                                                 break;
1461                                         default:
1462                                                 return new FormatElement (FormatElementType.Error);
1463                                 }
1464
1465                                 return element;
1466                         }
1467
1468                         int ParseChar (char c)
1469                         {
1470                                 int count = 0;
1471
1472                                 while (!AtEnd && format [cur] == c) {
1473                                         cur++;
1474                                         count++;
1475                                 }
1476
1477                                 return count;
1478                         }
1479
1480                         char ParseEscapedChar ()
1481                         {
1482                                 if (AtEnd || format [cur] != '\\')
1483                                         return (char)0;
1484
1485                                 cur++;
1486                                 if (AtEnd)
1487                                         return (char)0;
1488
1489                                 return format [cur++];
1490                         }
1491
1492                         string ParseLiteral ()
1493                         {
1494                                 int start;
1495                                 int count = 0;
1496
1497                                 if (AtEnd || format [cur] != '\'')
1498                                         return null;
1499
1500                                 start = ++cur;
1501                                 while (!AtEnd && format [cur] != '\'') {
1502                                         cur++;
1503                                         count++;
1504                                 }
1505
1506                                 if (!AtEnd && format [cur] == '\'') {
1507                                         cur++;
1508                                         return format.Substring (start, count);
1509                                 }
1510
1511                                 return null;
1512                         }
1513                 }
1514 #endif
1515
1516         }
1517 }