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