//
// 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.
//
-using System.Globalization;
-
-namespace System {
-
-[Serializable]
-public struct TimeSpan : IComparable {
-
- private long _ticks;
-
- 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) {}
+using System.Text;
- internal TimeSpan (bool sign, int days, int hours, int minutes, int seconds, int milliseconds, long ticks)
+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
{
- checked {
- _ticks = TicksPerDay * days +
- TicksPerHour * hours +
- TicksPerMinute * minutes +
- TicksPerSecond * seconds +
- TicksPerMillisecond * milliseconds +
- ticks;
- if ( sign ) {
- _ticks = -_ticks;
- }
- }
- }
-
- public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
- public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
- 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);
-
- public int Days
- {
- get {
- return (int) TotalDays;
- }
- }
+ 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 int Hours
- {
- get {
- return (int) (_ticks % TicksPerDay / TicksPerHour);
- }
- }
+ 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 int Milliseconds
- {
- get
- {
- return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
- }
- }
+ private long _ticks;
- public int Minutes
- {
- get
+ public TimeSpan (long value)
{
- return (int) (_ticks % TicksPerHour / TicksPerMinute);
+ _ticks = value;
}
- }
- public int Seconds
- {
- get
+ public TimeSpan (int hours, int minutes, int seconds)
{
- return (int) (_ticks % TicksPerMinute / TicksPerSecond);
+ _ticks = CalculateTicks (0, hours, minutes, seconds, 0);
}
- }
- public long Ticks
- {
- get
+ public TimeSpan (int days, int hours, int minutes, int seconds)
{
- return _ticks;
+ _ticks = CalculateTicks (days, hours, minutes, seconds, 0);
}
- }
- public double TotalDays
- {
- get
+ public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
{
- return (double) _ticks / TicksPerDay;
+ _ticks = CalculateTicks (days, hours, minutes, seconds, milliseconds);
}
- }
- public double TotalHours
- {
- get
+ internal static long CalculateTicks (int days, int hours, int minutes, int seconds, int milliseconds)
{
- return (double) _ticks / TicksPerHour;
- }
- }
-
- public double TotalMilliseconds
- {
- get
- {
- return (double) _ticks / TicksPerMillisecond;
- }
- }
+ // 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);
+ }
+ }
- public double TotalMinutes
- {
- get {
- return (double) _ticks / TicksPerMinute;
- }
- }
+ if (overflow)
+ throw new ArgumentOutOfRangeException (Locale.GetText ("The timespan is too big or too small."));
- public double TotalSeconds
- {
- get {
- return (double) _ticks / TicksPerSecond;
+ return t;
}
- }
- public TimeSpan Add (TimeSpan ts)
- {
- checked {
- return new TimeSpan (_ticks + ts.Ticks);
+ public int Days {
+ get {
+ return (int) (_ticks / TicksPerDay);
+ }
}
- }
- public static int Compare (TimeSpan t1, TimeSpan t2)
- {
- if (t1._ticks < t2._ticks) {
- return -1;
- }
- else if (t1._ticks > t2._ticks) {
- return 1;
- }
- else {
- return 0;
+ public int Hours {
+ get {
+ return (int) (_ticks % TicksPerDay / TicksPerHour);
+ }
}
- }
-
- public int CompareTo (object value)
- {
- if (value == null )
- return 1;
- if (!(value is TimeSpan)) {
- throw new ArgumentException (Locale.GetText (
- "Argument of System.TimeSpan.CompareTo should be a TimeSpan"));
+ public int Milliseconds {
+ get {
+ return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
+ }
}
-
- return Compare(this, (TimeSpan) value);
- }
- public TimeSpan Duration ()
- {
- checked {
- return new TimeSpan (Math.Abs (_ticks));
+ public int Minutes {
+ get {
+ return (int) (_ticks % TicksPerHour / TicksPerMinute);
+ }
}
- }
- public override bool Equals (object value)
- {
- if (!(value is TimeSpan)) {
- return false;
+ public int Seconds {
+ get {
+ return (int) (_ticks % TicksPerMinute / TicksPerSecond);
+ }
}
- return Equals (this, (TimeSpan) value);
- }
-
- public static bool Equals (TimeSpan t1, TimeSpan t2)
- {
- 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;
+ public long Ticks {
+ get {
+ return _ticks;
+ }
}
- if (Double.IsPositiveInfinity (value)) {
- return MaxValue;
+ public double TotalDays {
+ get {
+ return (double) _ticks / TicksPerDay;
+ }
}
- return new TimeSpan ((int) value,0,0,0,0) + FromHours ((value - ((int) value)) * 24);
- }
-
- public static TimeSpan FromHours (double value)
- {
- if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
- return MinValue;
+ public double TotalHours {
+ get {
+ return (double) _ticks / TicksPerHour;
+ }
}
- if (Double.IsPositiveInfinity (value)) {
- return MaxValue;
+ public double TotalMilliseconds {
+ get {
+ return (double) _ticks / TicksPerMillisecond;
+ }
}
- return new TimeSpan ((int) value,0,0) + FromMinutes ((value - ((int) value)) * 60);
- }
-
- public static TimeSpan FromMinutes (double value)
- {
- if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
- return MinValue;
+ public double TotalMinutes {
+ get {
+ return (double) _ticks / TicksPerMinute;
+ }
}
- if (Double.IsPositiveInfinity (value)) {
- return MaxValue;
+ public double TotalSeconds {
+ get {
+ return (double) _ticks / TicksPerSecond;
+ }
}
- return new TimeSpan (0, (int) value, 0) + FromSeconds((value - ((int) value)) * 60);
- }
-
- public static TimeSpan FromSeconds (double value)
- {
- if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
- return MinValue;
+ public TimeSpan Add (TimeSpan ts)
+ {
+ try {
+ checked {
+ return new TimeSpan (_ticks + ts.Ticks);
+ }
+ }
+ catch (OverflowException) {
+ throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
+ }
}
- if (Double.IsPositiveInfinity (value)) {
- return MaxValue;
+ public static int Compare (TimeSpan t1, TimeSpan t2)
+ {
+ if (t1._ticks < t2._ticks)
+ return -1;
+ if (t1._ticks > t2._ticks)
+ return 1;
+ return 0;
}
- return new TimeSpan (0, 0, 0, (int) value) + FromMilliseconds((value - ((int) value)) * 1000);
+ public int CompareTo (object value)
+ {
+ if (value == null)
+ return 1;
- }
+ if (!(value is TimeSpan)) {
+ throw new ArgumentException (Locale.GetText ("Argument has to be a TimeSpan."), "value");
+ }
- public static TimeSpan FromMilliseconds (double value)
- {
- if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
- return MinValue;
+ return Compare (this, (TimeSpan) value);
}
- if (Double.IsPositiveInfinity (value)) {
- return MaxValue;
+#if NET_2_0
+ public int CompareTo (TimeSpan value)
+ {
+ return Compare (this, value);
}
- return new TimeSpan (0, 0, 0, 0, (int) value);
- }
-
- public static TimeSpan FromTicks (long value)
- {
- return new TimeSpan (value);
- }
-
- public override int GetHashCode ()
- {
- return _ticks.GetHashCode ();
- }
-
- public TimeSpan Negate ()
- {
- checked {
- return new TimeSpan (-_ticks);
+ public bool Equals (TimeSpan value)
+ {
+ return value._ticks == _ticks;
}
- }
+#endif
- public static TimeSpan Parse (string s)
- {
- if (s == null) {
- throw new ArgumentNullException (
- Locale.GetText ("null reference passed to TimeSpan.Parse"));
+ public TimeSpan Duration ()
+ {
+ 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."));
+ }
}
- Parser p = new Parser (s);
- return p.Execute ();
- }
+ public override bool Equals (object value)
+ {
+ if (!(value is TimeSpan))
+ return false;
- public TimeSpan Subtract (TimeSpan ts)
- {
- checked {
- return new TimeSpan (_ticks - ts.Ticks);
+ return _ticks == ((TimeSpan) value)._ticks;
}
- }
-
- public override string ToString ()
- {
- string res = "";
- if (_ticks < 0) {
- res += "-";
+ public static bool Equals (TimeSpan t1, TimeSpan t2)
+ {
+ return t1._ticks == t2._ticks;
}
- // 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) + "." ;
+ public static TimeSpan FromDays (double value)
+ {
+ return From (value, TicksPerDay);
}
- res += string.Format ("{0:00}:{1:00}:{2:00}", Math.Abs(Hours), Math.Abs(Minutes), Math.Abs(Seconds));
-
- int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
- if (fractional != 0) {
- res += string.Format (".{0:0000000}", fractional);
+ public static TimeSpan FromHours (double value)
+ {
+ return From (value, TicksPerHour);
}
-
- return res;
- }
-
- public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
- {
- return t1.Add (t2);
- }
-
- public static bool operator == (TimeSpan t1, TimeSpan t2)
- {
- return Compare (t1, t2) == 0;
- }
-
- public static bool operator > (TimeSpan t1, TimeSpan t2)
- {
- return Compare (t1, t2) == 1;
- }
- public static bool operator >= (TimeSpan t1, TimeSpan t2)
- {
- return Compare (t1, t2) != -1;
- }
-
- public static bool operator != (TimeSpan t1, TimeSpan t2)
- {
- return Compare (t1, t2) != 0;
- }
-
- public static bool operator < (TimeSpan t1, TimeSpan t2)
- {
- return Compare (t1, t2) == -1;
- }
+ public static TimeSpan FromMinutes (double value)
+ {
+ return From (value, TicksPerMinute);
+ }
- public static bool operator <= (TimeSpan t1, TimeSpan t2)
- {
- return Compare (t1, t2) != 1;
- }
+ public static TimeSpan FromSeconds (double value)
+ {
+ return From (value, TicksPerSecond);
+ }
- public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
- {
- return t1.Subtract (t2);
- }
+ public static TimeSpan FromMilliseconds (double value)
+ {
+ return From (value, TicksPerMillisecond);
+ }
- public static TimeSpan operator - (TimeSpan t)
- {
- return t.Negate ();
- }
+ 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));
+
+ 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 operator + (TimeSpan t)
- {
- return t;
- }
-}
+ public static TimeSpan FromTicks (long value)
+ {
+ return new TimeSpan (value);
+ }
-// Class Parser implements simple parser for TimeSpan::Parse
-internal class Parser {
+ public override int GetHashCode ()
+ {
+ return _ticks.GetHashCode ();
+ }
- private string _src;
- private int _cur;
- private int _length;
+ public TimeSpan Negate ()
+ {
+ if (_ticks == MinValue._ticks)
+ throw new OverflowException (Locale.GetText (
+ "This TimeSpan value is MinValue and cannot be negated."));
+ return new TimeSpan (-_ticks);
+ }
- public Parser (string src)
- {
- _src = src;
- Reset ();
- }
+ public static TimeSpan Parse (string s)
+ {
+ if (s == null) {
+ throw new ArgumentNullException ("s");
+ }
- public void Reset ()
- {
- _cur = 0;
- _length = _src.Length;
- }
+ Parser p = new Parser (s);
+ return p.Execute ();
+ }
- public bool AtEnd
- {
- get {
- return _cur >= _length;
+#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
- private void ThrowFormatException()
- {
- throw new FormatException (Locale.GetText ("Invalid format for TimeSpan.Parse"));
- }
+ public TimeSpan Subtract (TimeSpan ts)
+ {
+ try {
+ checked {
+ return new TimeSpan (_ticks - ts.Ticks);
+ }
+ }
+ catch (OverflowException) {
+ throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
+ }
+ }
- // All "Parse" functions throw a FormatException on syntax error.
- // Their return value is semantic value of the item parsed.
+ public override string ToString ()
+ {
+ 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) {
+ sb.Append (Math.Abs (Days));
+ sb.Append ('.');
+ }
- // 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.
+ 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) {
+ sb.Append ('.');
+ sb.Append (NumberFormatter.FormatDecimal (new NumberFormatter.NumberStore (fractional), 7, nfi));
+ }
- // Parse zero or more whitespace chars.
- private void ParseWhiteSpace ()
- {
- while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
- _cur++;
+ return sb.ToString ();
}
- }
-
- // Parse optional sign character.
- private bool ParseSign ()
- {
- bool res = false;
- if (!AtEnd && _src[_cur] == '-') {
- res = true;
- _cur++;
+ public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
+ {
+ return t1.Add (t2);
}
- return res;
- }
-
- // Parse simple int value
- private int ParseInt ()
- {
- int res = 0;
- int count = 0;
-
- while (!AtEnd && Char.IsDigit (_src, _cur)) {
- checked {
- res = res*10 + _src[_cur] - '0';
- }
- _cur++;
- count++;
- }
-
- if (count == 0) {
- ThrowFormatException ();
+ public static bool operator == (TimeSpan t1, TimeSpan t2)
+ {
+ return t1._ticks == t2._ticks;
}
- return res;
- }
+ public static bool operator > (TimeSpan t1, TimeSpan t2)
+ {
+ return t1._ticks > t2._ticks;
+ }
- // Parse optional dot
- private bool ParseOptDot ()
- {
- if (AtEnd) {
- return false;
+ public static bool operator >= (TimeSpan t1, TimeSpan t2)
+ {
+ return t1._ticks >= t2._ticks;
}
-
- if (_src[_cur] == '.') {
- _cur++;
- return true;
+
+ public static bool operator != (TimeSpan t1, TimeSpan t2)
+ {
+ return t1._ticks != t2._ticks;
}
- else {
- return false;
+
+ public static bool operator < (TimeSpan t1, TimeSpan t2)
+ {
+ return t1._ticks < t2._ticks;
}
- }
-
- // Parse NON-optional colon
- private void ParseColon ()
- {
- if (!AtEnd && _src[_cur] == ':') {
- _cur++;
+
+ public static bool operator <= (TimeSpan t1, TimeSpan t2)
+ {
+ return t1._ticks <= t2._ticks;
}
- else {
- ThrowFormatException ();
+
+ public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
+ {
+ return t1.Subtract (t2);
}
- }
- // 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;
+ public static TimeSpan operator - (TimeSpan t)
+ {
+ return t.Negate ();
}
- if (!digitseen) {
- ThrowFormatException ();
+ public static TimeSpan operator + (TimeSpan t)
+ {
+ return t;
}
- return res;
- }
+ // Class Parser implements parser for TimeSpan.Parse
+ private class Parser
+ {
+ private string _src;
+ private int _cur = 0;
+ private int _length;
+ private bool formatError;
+
+ public Parser (string src)
+ {
+ _src = src;
+ _length = _src.Length;
+ }
+
+ public bool AtEnd {
+ get {
+ return _cur >= _length;
+ }
+ }
- public TimeSpan Execute ()
- {
- bool sign;
- int days;
- int hours;
- int minutes;
- int seconds;
- long ticks;
+ // 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.
+
+ // Parse zero or more whitespace chars.
+ private void ParseWhiteSpace ()
+ {
+ while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
+ _cur++;
+ }
+ }
- // 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 ();
+ // Parse optional sign character.
+ private bool ParseSign ()
+ {
+ bool res = false;
- if ( !AtEnd ) {
- ThrowFormatException ();
- }
+ if (!AtEnd && _src[_cur] == '-') {
+ res = true;
+ _cur++;
+ }
- if ( hours > 23 || minutes > 59 || seconds > 59 ) {
- throw new OverflowException (Locale.GetText (
- "Value outside range in TimeSpan.Parse" ));
- }
+ return res;
+ }
- TimeSpan ts = new TimeSpan (sign, days, hours, minutes, seconds, 0, ticks);
+ // Parse simple int value
+ private int ParseInt (bool optional)
+ {
+ if (optional && AtEnd)
+ return 0;
- return ts;
- }
+ int res = 0;
+ int count = 0;
-}
+ while (!AtEnd && Char.IsDigit (_src, _cur)) {
+ checked {
+ res = res * 10 + _src[_cur] - '0';
+ }
+ _cur++;
+ count++;
+ }
-}
+ if (count == 0)
+ formatError = true;
+
+ return res;
+ }
+
+ // Parse optional dot
+ private bool ParseOptDot ()
+ {
+ if (AtEnd)
+ return false;
+
+ if (_src[_cur] == '.') {
+ _cur++;
+ return true;
+ }
+ return false;
+ }
+
+ // Parse optional (LAMESPEC) colon
+ private void ParseOptColon ()
+ {
+ if (!AtEnd) {
+ if (_src[_cur] == ':')
+ _cur++;
+ else
+ formatError = true;
+ }
+ }
+ // 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)
+ formatError = true;
+
+ return res;
+ }
+ 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;
+
+ // 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."));
+ }
+
+ long t = TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0);
+ t = checked ((sign) ? (-t - ticks) : (t + ticks));
+ return new TimeSpan (t);
+ }
+ }
+ }
+}