2007-11-14 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mcs / class / corlib / System / TimeSpan.cs
index eea8266e5b10745c0254bb89d7e697f12e3819c8..9ae9da02a9444e0b7cb81e033bc606f478aafafa 100644 (file)
 //
 // System.TimeSpan.cs
 //
-// author:
+// Authors:
 //   Duco Fijma (duco@lorentz.xs4all.nl)
+//   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
+//   Sebastien Pouliot  <sebastien@ximian.com>
 //
-//   (C) 2001 Duco Fijma
+// (C) 2001 Duco Fijma
+// (C) 2004 Andreas Nahr
+// Copyright (C) 2004 Novell (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-// version: 07312001
-
-using System.Globalization;
-
-namespace System {
-       
-       public struct TimeSpan :  IComparable  {
-               private long _ticks;
-
-               // Ctors
-
-               public TimeSpan (long value) { _ticks = value; }
-               public TimeSpan (int hours, int minutes, int seconds) 
-                       : this(false, 0, hours, minutes, seconds, 0, 0) {}
-               public TimeSpan (int days, int hours, int minutes, int seconds) 
-                       : this(false, days, hours, minutes, seconds, 0, 0) {}
-               public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
-                       : this(false, days, hours, minutes, seconds, milliseconds, 0) {}
-
-               internal TimeSpan (bool sign, int days, int hours, int minutes, int seconds, int milliseconds, long ticks)
-               {
-                       checked {
-                               _ticks = TicksPerDay * days + 
-                                       TicksPerHour * hours +
-                                       TicksPerMinute * minutes +
-                                       TicksPerSecond * seconds +
-                                       TicksPerMillisecond * milliseconds +
-                                       ticks;
-                               if ( sign ) {
-                                       _ticks = -_ticks;
-                               }
-                       }
-               }
-               
-               // Fields
 
+using System.Text;
+
+namespace System
+{
+       [Serializable]
+#if NET_2_0
+       [System.Runtime.InteropServices.ComVisible (true)]
+#endif
+       public struct TimeSpan : IComparable
+#if NET_2_0
+               , IComparable<TimeSpan>, IEquatable <TimeSpan>
+#endif
+       {
                public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
                public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
+               public static readonly TimeSpan Zero = new TimeSpan (0L);
+
                public const long TicksPerDay = 864000000000L;
                public const long TicksPerHour = 36000000000L;
                public const long TicksPerMillisecond = 10000L;
                public const long TicksPerMinute = 600000000L;
                public const long TicksPerSecond = 10000000L;
-               public static readonly TimeSpan Zero = new TimeSpan (0L);
 
-               // Properties
+               private long _ticks;
+
+               public TimeSpan (long value)
+               {
+                       _ticks = value;
+               }
+
+               public TimeSpan (int hours, int minutes, int seconds)
+               {
+                       _ticks = CalculateTicks (0, hours, minutes, seconds, 0);
+               }
+
+               public TimeSpan (int days, int hours, int minutes, int seconds)
+               {
+                       _ticks = CalculateTicks (days, hours, minutes, seconds, 0);
+               }
 
-               public int Days
+               public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
                {
+                       _ticks = CalculateTicks (days, hours, minutes, seconds, milliseconds);
+               }
+
+               internal static long CalculateTicks (int days, int hours, int minutes, int seconds, int milliseconds)
+               {
+                       // there's no overflow checks for hours, minutes, ...
+                       // so big hours/minutes values can overflow at some point and change expected values
+                       int hrssec = (hours * 3600); // break point at (Int32.MaxValue - 596523)
+                       int minsec = (minutes * 60);
+                       long t = ((long)(hrssec + minsec + seconds) * 1000L + (long)milliseconds);
+                       t *= 10000;
+
+                       bool overflow = false;
+                       // days is problematic because it can overflow but that overflow can be 
+                       // "legal" (i.e. temporary) (e.g. if other parameters are negative) or 
+                       // illegal (e.g. sign change).
+                       if (days > 0) {
+                               long td = TicksPerDay * days;
+                               if (t < 0) {
+                                       long ticks = t;
+                                       t += td;
+                                       // positive days -> total ticks should be lower
+                                       overflow = (ticks > t);
+                               }
+                               else {
+                                       t += td;
+                                       // positive + positive != negative result
+                                       overflow = (t < 0);
+                               }
+                       }
+                       else if (days < 0) {
+                               long td = TicksPerDay * days;
+                               if (t <= 0) {
+                                       t += td;
+                                       // negative + negative != positive result
+                                       overflow = (t > 0);
+                               }
+                               else {
+                                       long ticks = t;
+                                       t += td;
+                                       // negative days -> total ticks should be lower
+                                       overflow = (t > ticks);
+                               }
+                       }
+
+                       if (overflow)
+                               throw new ArgumentOutOfRangeException (Locale.GetText ("The timespan is too big or too small."));
+
+                       return t;
+               }
+
+               public int Days {
                        get {
-                               return (int) TotalDays;
+                               return (int) (_ticks / TicksPerDay);
                        }
                }
 
-               public int Hours
-               {
+               public int Hours {
                        get {
                                return (int) (_ticks % TicksPerDay / TicksPerHour);
                        }
                }
 
-               public int Milliseconds
-               {
-                       get
-                       {
+               public int Milliseconds {
+                       get {
                                return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
                        }
                }
 
-               public int Minutes
-               {
-                       get
-                       {
+               public int Minutes {
+                       get {
                                return (int) (_ticks % TicksPerHour / TicksPerMinute);
                        }
                }
 
-               public int Seconds
-               {
-                       get
-                       {
+               public int Seconds {
+                       get {
                                return (int) (_ticks % TicksPerMinute / TicksPerSecond);
                        }
                }
 
-               public long Ticks
-               { 
-                       get
-                       {
+               public long Ticks {
+                       get {
                                return _ticks;
                        }
                }
 
-               public double TotalDays
-               {
-                       get
-                       {
+               public double TotalDays {
+                       get {
                                return (double) _ticks / TicksPerDay;
                        }
                }
 
-               public double TotalHours
-               {
-                       get
-                       {
+               public double TotalHours {
+                       get {
                                return (double) _ticks / TicksPerHour;
                        }
                }
 
-               public double TotalMilliseconds
-               {
-                       get
-                       {
+               public double TotalMilliseconds {
+                       get {
                                return (double) _ticks  / TicksPerMillisecond;
                        }
                }
 
-               public double TotalMinutes
-               {
+               public double TotalMinutes {
                        get {
                                return (double) _ticks / TicksPerMinute;
                        }
                }
 
-               public double TotalSeconds
-               {
+               public double TotalSeconds {
                        get {
                                return (double) _ticks / TicksPerSecond;
                        }
                }
 
-               // Methods
-
                public TimeSpan Add (TimeSpan ts)
                {
-                       checked {
-                               return new TimeSpan (_ticks + ts.Ticks);
+                       try {
+                               checked {
+                                       return new TimeSpan (_ticks + ts.Ticks);
+                               }
+                       }
+                       catch (OverflowException) {
+                               throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
                        }
                }
 
                public static int Compare (TimeSpan t1, TimeSpan t2)
                {
-                       if (t1._ticks < t2._ticks) {
+                       if (t1._ticks < t2._ticks)
                                return -1;
-                       }
-                       else if (t1._ticks > t2._ticks) {
+                       if (t1._ticks > t2._ticks)
                                return 1;
-                       }
-                       else {
-                               return 0;
-                       }
+                       return 0;
                }
 
                public int CompareTo (object value)
                {
-                       if (value == null )
+                       if (value == null)
                                return 1;
 
                        if (!(value is TimeSpan)) {
-                               throw new ArgumentException (Locale.GetText (
-                                       "Argument of System.TimeSpan.CompareTo should be a TimeSpan"));
+                               throw new ArgumentException (Locale.GetText ("Argument has to be a TimeSpan."), "value");
                        }
-               
-                       return Compare(this, (TimeSpan) value);
+
+                       return Compare (this, (TimeSpan) value);
+               }
+
+#if NET_2_0
+               public int CompareTo (TimeSpan value)
+               {
+                       return Compare (this, value);
+               }
+
+               public bool Equals (TimeSpan value)
+               {
+                       return value._ticks == _ticks;
                }
+#endif
 
                public TimeSpan Duration ()
                {
-                       checked {
-                               return new TimeSpan (Math.Abs (_ticks));
+                       try {
+                               checked {
+                                       return new TimeSpan (Math.Abs (_ticks));
+                               }
+                       }
+                       catch (OverflowException) {
+                               throw new OverflowException (Locale.GetText (
+                                       "This TimeSpan value is MinValue so you cannot get the duration."));
                        }
                }
 
                public override bool Equals (object value)
                {
-                       if (!(value is TimeSpan)) {
+                       if (!(value is TimeSpan))
                                return false;
-                       }
-                       return Equals (this, (TimeSpan) value);
+
+                       return _ticks == ((TimeSpan) value)._ticks;
                }
 
                public static bool Equals (TimeSpan t1, TimeSpan t2)
@@ -192,73 +260,50 @@ namespace System {
                        return t1._ticks == t2._ticks;
                }
 
-               // Implementing FromDays -> FromHours -> FromMinutes -> FromSeconds ->
-               // FromMilliseconds as done here is probably not the most efficient
-               // way. 
                public static TimeSpan FromDays (double value)
                {
-                       if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
-                               return MinValue;
-                       }
-
-                       if (Double.IsPositiveInfinity (value)) {
-                               return MaxValue;
-                       }
-
-                       return new TimeSpan ((int) value,0,0,0,0) + FromHours ((value - ((int) value)) * 24);
+                       return From (value, TicksPerDay);
                }
 
                public static TimeSpan FromHours (double value)
                {
-                       if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
-                               return MinValue;
-                       }
-
-                       if (Double.IsPositiveInfinity (value)) {
-                               return MaxValue;
-                       }
-
-                       return new TimeSpan ((int) value,0,0) + FromMinutes ((value - ((int) value)) * 60);
+                       return From (value, TicksPerHour);
                }
 
                public static TimeSpan FromMinutes (double value)
                {
-                       if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
-                               return MinValue;
-                       }
-
-                       if (Double.IsPositiveInfinity (value)) {
-                               return MaxValue;
-                       }
-
-                       return new TimeSpan (0, (int) value, 0) + FromSeconds((value - ((int) value)) * 60);
+                       return From (value, TicksPerMinute);
                }
 
                public static TimeSpan FromSeconds (double value)
                {
-                       if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
-                               return MinValue;
-                       }
-
-                       if (Double.IsPositiveInfinity (value)) {
-                               return MaxValue;
-                       }
-
-                       return new TimeSpan (0, 0, 0, (int) value) + FromMilliseconds((value - ((int) value)) * 1000);
-
+                       return From (value, TicksPerSecond);
                }
 
                public static TimeSpan FromMilliseconds (double value)
                {
-                       if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
-                               return MinValue;
-                       }
+                       return From (value, TicksPerMillisecond);
+               }
 
-                       if (Double.IsPositiveInfinity (value)) {
-                               return MaxValue;
-                       }
+               private static TimeSpan From (double value, long tickMultiplicator) 
+               {
+                       if (Double.IsNaN (value))
+                               throw new ArgumentException (Locale.GetText ("Value cannot be NaN."), "value");
+                       if (Double.IsNegativeInfinity (value) || Double.IsPositiveInfinity (value) ||
+                               (value < MinValue.Ticks) || (value > MaxValue.Ticks))
+                               throw new OverflowException (Locale.GetText ("Outside range [MinValue,MaxValue]"));
+
+                       try {
+                               value = (value * (tickMultiplicator / TicksPerMillisecond));
 
-                       return new TimeSpan (0, 0, 0, 0, (int) value);
+                               checked {
+                                       long val = (long) Math.Round(value);
+                                       return new TimeSpan (val * TicksPerMillisecond);
+                               }
+                       }
+                       catch (OverflowException) {
+                               throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
+                       }
                }
 
                public static TimeSpan FromTicks (long value)
@@ -273,53 +318,77 @@ namespace System {
 
                public TimeSpan Negate ()
                {
-                       checked {
-                               return new TimeSpan (-_ticks);
-                       }
+                       if (_ticks == MinValue._ticks)
+                               throw new OverflowException (Locale.GetText (
+                                       "This TimeSpan value is MinValue and cannot be negated."));
+                       return new TimeSpan (-_ticks);
                }
 
                public static TimeSpan Parse (string s)
                {
                        if (s == null) {
-                               throw new ArgumentNullException (
-                                       Locale.GetText ("null reference passed to TimeSpan.Parse"));
+                               throw new ArgumentNullException ("s");
                        }
 
-                       Parser p = new Parser (s); 
+                       Parser p = new Parser (s);
                        return p.Execute ();
                }
 
+#if NET_2_0
+               public static bool TryParse (string s, out TimeSpan result)
+               {
+                       try {
+                               result = Parse (s);
+                               return true;
+                       } catch {
+                               result = TimeSpan.Zero;
+                               return false;
+                       }
+               }
+#endif
+
                public TimeSpan Subtract (TimeSpan ts)
                {
-                       checked {
-                               return new TimeSpan (_ticks - ts.Ticks);
+                       try {
+                               checked {
+                                       return new TimeSpan (_ticks - ts.Ticks);
+                               }
+                       }
+                       catch (OverflowException) {
+                               throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
                        }
                }
 
                public override string ToString ()
                {
-                       string res = "";        
-
-                       if (_ticks < 0) {
-                               res += "-";
-                       }
+                       StringBuilder sb = new StringBuilder (14);
+                       
+                       if (_ticks < 0)
+                               sb.Append ('-');
 
                        // We need to take absolute values of all components.
                        // Can't handle negative timespans by negating the TimeSpan
                        // as a whole. This would lead to an overflow for the 
                        // degenerate case "TimeSpan.MinValue.ToString()".
                        if (Days != 0) {
-                               res += Math.Abs (Days) + "." ;
+                               sb.Append (Math.Abs (Days));
+                               sb.Append ('.');
                        }
 
-                       res += string.Format ("{0:00}:{1:00}:{2:00}", Math.Abs(Hours), Math.Abs(Minutes), Math.Abs(Seconds));
+                       System.Globalization.NumberFormatInfo nfi = System.Globalization.CultureInfo.CurrentCulture.NumberFormat;
+                       sb.Append (NumberFormatter.FormatDecimal (new NumberFormatter.NumberStore ((int)Math.Abs (Hours)), 2, nfi));
+                       sb.Append (':');
+                       sb.Append (NumberFormatter.FormatDecimal (new NumberFormatter.NumberStore ((int)Math.Abs (Minutes)), 2, nfi));
+                       sb.Append (':');
+                       sb.Append (NumberFormatter.FormatDecimal (new NumberFormatter.NumberStore ((int)Math.Abs (Seconds)), 2, nfi));
 
                        int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
                        if (fractional != 0) {
-                               res += string.Format (".{0:0000000}", fractional);
+                               sb.Append ('.');
+                               sb.Append (NumberFormatter.FormatDecimal (new NumberFormatter.NumberStore (fractional), 7, nfi));
                        }
-                       return res;
+
+                       return sb.ToString ();
                }
 
                public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
@@ -329,32 +398,32 @@ namespace System {
 
                public static bool operator == (TimeSpan t1, TimeSpan t2)
                {
-                       return Compare (t1, t2) == 0;
+                       return t1._ticks == t2._ticks;
                }
 
                public static bool operator > (TimeSpan t1, TimeSpan t2)
                {
-                       return Compare (t1, t2) == 1;
+                       return t1._ticks > t2._ticks;
                }
 
                public static bool operator >= (TimeSpan t1, TimeSpan t2)
                {
-                       return Compare (t1, t2) != -1;
+                       return t1._ticks >= t2._ticks;
                }
 
                public static bool operator != (TimeSpan t1, TimeSpan t2)
                {
-                       return Compare (t1, t2) != 0;
+                       return t1._ticks != t2._ticks;
                }
 
                public static bool operator < (TimeSpan t1, TimeSpan t2)
                {
-                       return Compare (t1, t2) == -1;
+                       return t1._ticks < t2._ticks;
                }
 
                public static bool operator <= (TimeSpan t1, TimeSpan t2)
                {
-                       return Compare (t1, t2) != 1;
+                       return t1._ticks <= t2._ticks;
                }
 
                public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
@@ -371,186 +440,178 @@ namespace System {
                {
                        return t;
                }
-       }
-
-       // Class Parser implements simple parser for TimeSpan::Parse
-       internal class Parser {
-
-               private string _src;
-               private int _cur;
-               private int _length;
-       
-               public Parser (string src)
-               {
-                       _src = src;
-                       Reset ();
-               }
 
-               public void Reset ()
+               // Class Parser implements parser for TimeSpan.Parse
+               private class Parser
                {
-                       _cur = 0;
-                       _length = _src.Length;
-               }
+                       private string _src;
+                       private int _cur = 0;
+                       private int _length;
+                       private bool formatError;
 
-               public bool AtEnd
-               {
-                       get {
-                               return _cur >= _length;
+                       public Parser (string src)
+                       {
+                               _src = src;
+                               _length = _src.Length;
+                       }
+       
+                       public bool AtEnd {
+                               get {
+                                       return _cur >= _length;
+                               }
                        }
-               }
-
-               private void ThrowFormatException() 
-               {
-                       throw new FormatException (Locale.GetText ("Invalid format for TimeSpan.Parse"));
-               }
 
-               // All "Parse" functions throw a FormatException on syntax error.
-               // Their return value is semantic value of the item parsed.
+                       // All "Parse" functions throw a FormatException on syntax error.
+                       // Their return value is semantic value of the item parsed.
 
-               // Range checking is spread over three different places:
-               // 1) When parsing "int" values, an exception is thrown immediately
-               //    when the value parsed exceeds the maximum value for an int.
-               // 2) An explicit check is built in that checks for hours > 23 and
-               //    for minutes and seconds > 59.
-               // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
-               //    or < MinValue is left to the TimeSpan constructor called.
+                       // Range checking is spread over three different places:
+                       // 1) When parsing "int" values, an exception is thrown immediately
+                       //    when the value parsed exceeds the maximum value for an int.
+                       // 2) An explicit check is built in that checks for hours > 23 and
+                       //    for minutes and seconds > 59.
+                       // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
+                       //    or < MinValue is left to the TimeSpan constructor called.
 
-               // Parse zero or more whitespace chars.
-               private void ParseWhiteSpace ()
-               {
-                       while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
-                               _cur++; 
+                       // Parse zero or more whitespace chars.
+                       private void ParseWhiteSpace ()
+                       {
+                               while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
+                                       _cur++;
+                               }
                        }
-               }
 
-               // Parse optional sign character.
-               private bool ParseSign () 
-               {
-                       bool res = false;
+                       // Parse optional sign character.
+                       private bool ParseSign ()
+                       {
+                               bool res = false;
+
+                               if (!AtEnd && _src[_cur] == '-') {
+                                       res = true;
+                                       _cur++;
+                               }
 
-                       if (!AtEnd && _src[_cur] == '-') { 
-                               res = true;
-                               _cur++;
+                               return res;
                        }
 
-                       return res;
-               }
+                       // Parse simple int value
+                       private int ParseInt (bool optional)
+                       {
+                               if (optional && AtEnd)
+                                       return 0;
+
+                               int res = 0;
+                               int count = 0;
+
+                               while (!AtEnd && Char.IsDigit (_src, _cur)) {
+                                       checked {
+                                               res = res * 10 + _src[_cur] - '0';
+                                       }
+                                       _cur++;
+                                       count++;
+                               }
 
-               // Parse simple int value
-               private int ParseInt ()
-               {
-                       int res = 0;
-                       int count = 0;
+                               if (count == 0)
+                                       formatError = true;
 
-                       while (!AtEnd && Char.IsDigit (_src, _cur)) {
-                               checked {
-                                       res = res*10 + _src[_cur] - '0';
-                               }
-                               _cur++;
-                               count++;
-                       }
-                       
-                       if (count == 0) {
-                               ThrowFormatException ();
+                               return res;
                        }
 
-                       return res;
-               }
+                       // Parse optional dot
+                       private bool ParseOptDot ()
+                       {
+                               if (AtEnd)
+                                       return false;
 
-               // Parse optional dot
-               private bool ParseOptDot ()
-               {
-                       if (AtEnd) {
-                               return false;
-                       }
-                       
-                       if (_src[_cur] == '.') {
-                               _cur++;
-                               return true;
-                       }
-                       else {
+                               if (_src[_cur] == '.') {
+                                       _cur++;
+                                       return true;
+                               }
                                return false;
-                       }
-               }       
-               
-               // Parse NON-optional colon 
-               private void ParseColon ()
-               {
-                       if (!AtEnd && _src[_cur] == ':') {
-                               _cur++;
-                       }
-                       else {
-                               ThrowFormatException ();
-                       }
-               }
-
-               // Parse [1..7] digits, representing fractional seconds (ticks)
-               private long ParseTicks ()
-               {
-                       long mag = 1000000;
-                       long res = 0;
-                       bool digitseen = false;
-                       
-                       while ( mag > 0 && !AtEnd && Char.IsDigit (_src, _cur) ) {
-                               res = res + (_src[_cur] - '0') * mag;
-                               _cur++;
-                               mag = mag / 10;
-                               digitseen = true;
-                       }
+                       }       
 
-                       if (!digitseen) {
-                               ThrowFormatException ();
+                       // Parse optional (LAMESPEC) colon
+                       private void ParseOptColon ()
+                       {
+                               if (!AtEnd) {
+                                       if (_src[_cur] == ':')
+                                               _cur++;
+                                       else 
+                                               formatError = true;
+                               }
                        }
 
-                       return res;
-               }
-
-               public TimeSpan Execute ()
-               {
-                       bool sign;
-                       int days;
-                       int hours;
-                       int minutes;
-                       int seconds;
-                       long ticks;
+                       // Parse [1..7] digits, representing fractional seconds (ticks)
+                       private long ParseTicks ()
+                       {
+                               long mag = 1000000;
+                               long res = 0;
+                               bool digitseen = false;
+                               
+                               while (mag > 0 && !AtEnd && Char.IsDigit (_src, _cur)) {
+                                       res = res + (_src[_cur] - '0') * mag;
+                                       _cur++;
+                                       mag = mag / 10;
+                                       digitseen = true;
+                               }
 
-                       // Parse [ws][dd.]hh:mm:ss[.ff][ws]
-                       ParseWhiteSpace ();
-                       sign = ParseSign ();
-                       days = ParseInt ();
-                       if (ParseOptDot ()) {
-                               hours = ParseInt ();
-                       }
-                       else {
-                               hours = days;
-                               days = 0;
-                       }
-                       ParseColon();
-                       minutes = ParseInt ();
-                       ParseColon();
-                       seconds = ParseInt ();
-                       if ( ParseOptDot () ) {
-                               ticks = ParseTicks ();
-                       }       
-                       else {
-                               ticks = 0;
-                       }
-                       ParseWhiteSpace ();
+                               if (!digitseen)
+                                       formatError = true;
 
-                       if ( !AtEnd ) {
-                               ThrowFormatException ();
+                               return res;
                        }
 
-                       if ( hours > 23 || minutes > 59 || seconds > 59 ) {
-                               throw new OverflowException (Locale.GetText (
-                                       "Value outside range in TimeSpan.Parse" ));
-                       }
+                       public TimeSpan Execute ()
+                       {
+                               bool sign;
+                               int days;
+                               int hours = 0;
+                               int minutes;
+                               int seconds;
+                               long ticks;
+
+                               // documented as...
+                               // Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
+                               // ... but not entirely true as an lonely 
+                               // integer will be parsed as a number of days
+                               ParseWhiteSpace ();
+                               sign = ParseSign ();
+                               days = ParseInt (false);
+                               if (ParseOptDot ()) {
+                                       hours = ParseInt (true);
+                               }
+                               else if (!AtEnd) {
+                                       hours = days;
+                                       days = 0;
+                               }
+                               ParseOptColon();
+                               minutes = ParseInt (true);
+                               ParseOptColon ();
+                               seconds = ParseInt (true);
+                               if ( ParseOptDot () ) {
+                                       ticks = ParseTicks ();
+                               }
+                               else {
+                                       ticks = 0;
+                               }
+                               ParseWhiteSpace ();
+       
+                               if (!AtEnd)
+                                       formatError = true;
 
-                       TimeSpan ts = new TimeSpan (sign, days, hours, minutes, seconds, 0, ticks);
+                               // Overflow has presceance over FormatException
+                               if (hours > 23 || minutes > 59 || seconds > 59) {
+                                       throw new OverflowException (
+                                               Locale.GetText ("Invalid time data."));
+                               }
+                               else if (formatError) {
+                                       throw new FormatException (
+                                               Locale.GetText ("Invalid format for TimeSpan.Parse."));
+                               }
 
-                       return ts;
-               }       
+                               long t = TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0);
+                               t = checked ((sign) ? (-t - ticks) : (t + ticks));
+                               return new TimeSpan (t);
+                       }
+               }
        }
 }
-
-