5 // Duco Fijma (duco@lorentz.xs4all.nl)
6 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
9 // (C) 2004 Andreas Nahr
17 public struct TimeSpan : IComparable
19 public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
20 public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
21 public static readonly TimeSpan Zero = new TimeSpan (0L);
23 public const long TicksPerDay = 864000000000L;
24 public const long TicksPerHour = 36000000000L;
25 public const long TicksPerMillisecond = 10000L;
26 public const long TicksPerMinute = 600000000L;
27 public const long TicksPerSecond = 10000000L;
31 public TimeSpan (long value)
36 public TimeSpan (int hours, int minutes, int seconds)
37 : this (0, hours, minutes, seconds, 0)
41 public TimeSpan (int days, int hours, int minutes, int seconds)
42 : this (days, hours, minutes, seconds, 0)
46 public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
50 _ticks = TicksPerDay * days +
51 TicksPerHour * hours +
52 TicksPerMinute * minutes +
53 TicksPerSecond * seconds +
54 TicksPerMillisecond * milliseconds;
58 throw new ArgumentOutOfRangeException (Locale.GetText ("The timespan is too big or too small."));
62 private TimeSpan (bool sign, int days, int hours, int minutes, int seconds, long ticks)
66 _ticks = TicksPerDay * days +
67 TicksPerHour * hours +
68 TicksPerMinute * minutes +
69 TicksPerSecond * seconds +
77 throw new ArgumentOutOfRangeException (Locale.GetText ("The timespan is too big or too small."));
83 return (int) (_ticks / TicksPerDay);
89 return (int) (_ticks % TicksPerDay / TicksPerHour);
93 public int Milliseconds {
95 return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
101 return (int) (_ticks % TicksPerHour / TicksPerMinute);
107 return (int) (_ticks % TicksPerMinute / TicksPerSecond);
117 public double TotalDays {
119 return (double) _ticks / TicksPerDay;
123 public double TotalHours {
125 return (double) _ticks / TicksPerHour;
129 public double TotalMilliseconds {
131 return (double) _ticks / TicksPerMillisecond;
135 public double TotalMinutes {
137 return (double) _ticks / TicksPerMinute;
141 public double TotalSeconds {
143 return (double) _ticks / TicksPerSecond;
147 public TimeSpan Add (TimeSpan ts)
151 return new TimeSpan (_ticks + ts.Ticks);
155 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
159 public static int Compare (TimeSpan t1, TimeSpan t2)
161 if (t1._ticks < t2._ticks)
163 if (t1._ticks > t2._ticks)
168 public int CompareTo (object value)
173 if (!(value is TimeSpan)) {
174 throw new ArgumentException (Locale.GetText ("Argument has to be a TimeSpan."), "value");
177 return Compare (this, (TimeSpan) value);
180 public TimeSpan Duration ()
184 return new TimeSpan (Math.Abs (_ticks));
188 throw new OverflowException (Locale.GetText (
189 "This TimeSpan value is MinValue so you cannot get the duration."));
193 public override bool Equals (object value)
195 if (!(value is TimeSpan))
198 return _ticks == ((TimeSpan) value)._ticks;
201 public static bool Equals (TimeSpan t1, TimeSpan t2)
203 return t1._ticks == t2._ticks;
206 public static TimeSpan FromDays (double value)
208 return FromMilliseconds (value * (TicksPerDay / TicksPerMillisecond));
211 public static TimeSpan FromHours (double value)
213 return FromMilliseconds (value * (TicksPerHour / TicksPerMillisecond));
216 public static TimeSpan FromMinutes (double value)
218 return FromMilliseconds (value * (TicksPerMinute / TicksPerMillisecond));
221 public static TimeSpan FromSeconds (double value)
223 return FromMilliseconds (value * (TicksPerSecond / TicksPerMillisecond));
226 public static TimeSpan FromMilliseconds (double value)
228 if (Double.IsNaN (value))
229 throw new ArgumentException (Locale.GetText ("Value cannot be NaN."), "value");
230 if (Double.IsNegativeInfinity (value))
232 if (Double.IsPositiveInfinity (value))
237 long val = (long) Math.Round(value);
238 return new TimeSpan (val * TicksPerMillisecond);
242 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
246 public static TimeSpan FromTicks (long value)
248 return new TimeSpan (value);
251 public override int GetHashCode ()
253 return _ticks.GetHashCode ();
256 public TimeSpan Negate ()
258 if (_ticks == MinValue._ticks)
259 throw new OverflowException (Locale.GetText (
260 "This TimeSpan value is MinValue and cannot be negated."));
261 return new TimeSpan (-_ticks);
264 public static TimeSpan Parse (string s)
267 throw new ArgumentNullException ("s");
270 Parser p = new Parser (s);
274 public TimeSpan Subtract (TimeSpan ts)
278 return new TimeSpan (_ticks - ts.Ticks);
282 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
286 public override string ToString ()
288 StringBuilder sb = new StringBuilder (14);
293 // We need to take absolute values of all components.
294 // Can't handle negative timespans by negating the TimeSpan
295 // as a whole. This would lead to an overflow for the
296 // degenerate case "TimeSpan.MinValue.ToString()".
298 sb.Append (Math.Abs (Days));
302 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Hours), 2, 4));
304 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Minutes), 2, 4));
306 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Seconds), 2, 4));
308 int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
309 if (fractional != 0) {
311 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (fractional), 7, 4));
314 return sb.ToString ();
317 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
322 public static bool operator == (TimeSpan t1, TimeSpan t2)
324 return t1._ticks == t2._ticks;
327 public static bool operator > (TimeSpan t1, TimeSpan t2)
329 return t1._ticks > t2._ticks;
332 public static bool operator >= (TimeSpan t1, TimeSpan t2)
334 return t1._ticks >= t2._ticks;
337 public static bool operator != (TimeSpan t1, TimeSpan t2)
339 return t1._ticks != t2._ticks;
342 public static bool operator < (TimeSpan t1, TimeSpan t2)
344 return t1._ticks < t2._ticks;
347 public static bool operator <= (TimeSpan t1, TimeSpan t2)
349 return t1._ticks <= t2._ticks;
352 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
354 return t1.Subtract (t2);
357 public static TimeSpan operator - (TimeSpan t)
362 public static TimeSpan operator + (TimeSpan t)
367 // Class Parser implements parser for TimeSpan.Parse
371 private int _cur = 0;
374 public Parser (string src)
377 _length = _src.Length;
382 return _cur >= _length;
386 private void ThrowFormatException ()
388 throw new FormatException (Locale.GetText ("Invalid format for TimeSpan.Parse."));
391 // All "Parse" functions throw a FormatException on syntax error.
392 // Their return value is semantic value of the item parsed.
394 // Range checking is spread over three different places:
395 // 1) When parsing "int" values, an exception is thrown immediately
396 // when the value parsed exceeds the maximum value for an int.
397 // 2) An explicit check is built in that checks for hours > 23 and
398 // for minutes and seconds > 59.
399 // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
400 // or < MinValue is left to the TimeSpan constructor called.
402 // Parse zero or more whitespace chars.
403 private void ParseWhiteSpace ()
405 while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
410 // Parse optional sign character.
411 private bool ParseSign ()
415 if (!AtEnd && _src[_cur] == '-') {
423 // Parse simple int value
424 private int ParseInt ()
429 while (!AtEnd && Char.IsDigit (_src, _cur)) {
431 res = res * 10 + _src[_cur] - '0';
438 ThrowFormatException ();
443 // Parse optional dot
444 private bool ParseOptDot ()
449 if (_src[_cur] == '.') {
456 // Parse NON-optional colon
457 private void ParseColon ()
459 if (!AtEnd && _src[_cur] == ':')
462 ThrowFormatException ();
465 // Parse [1..7] digits, representing fractional seconds (ticks)
466 private long ParseTicks ()
470 bool digitseen = false;
472 while (mag > 0 && !AtEnd && Char.IsDigit (_src, _cur)) {
473 res = res + (_src[_cur] - '0') * mag;
480 ThrowFormatException ();
485 public TimeSpan Execute ()
494 // Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
498 if (ParseOptDot ()) {
506 minutes = ParseInt ();
508 seconds = ParseInt ();
509 if ( ParseOptDot () ) {
510 ticks = ParseTicks ();
518 ThrowFormatException ();
520 if (hours > 23 || minutes > 59 || seconds > 59) {
521 throw new OverflowException (Locale.GetText (
522 "Invalid time data."));
525 TimeSpan ts = new TimeSpan (sign, days, hours, minutes, seconds, ticks);