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;
57 catch (OverflowException) {
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 +
76 catch (OverflowException) {
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);
154 catch (OverflowException) {
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));
187 catch (OverflowException) {
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 From (value, TicksPerDay);
211 public static TimeSpan FromHours (double value)
213 return From (value, TicksPerHour);
216 public static TimeSpan FromMinutes (double value)
218 return From (value, TicksPerMinute);
221 public static TimeSpan FromSeconds (double value)
223 return From (value, TicksPerSecond);
226 public static TimeSpan FromMilliseconds (double value)
228 return From (value, TicksPerMillisecond);
231 private static TimeSpan From (double value, long tickMultiplicator)
233 if (Double.IsNaN (value))
234 throw new ArgumentException (Locale.GetText ("Value cannot be NaN."), "value");
235 if (Double.IsNegativeInfinity (value) || Double.IsPositiveInfinity (value) ||
236 (value < MinValue.Ticks) || (value > MaxValue.Ticks))
237 throw new OverflowException (Locale.GetText ("Outside range [MinValue,MaxValue]"));
240 value = (value * (tickMultiplicator / TicksPerMillisecond));
243 long val = (long) Math.Round(value);
244 return new TimeSpan (val * TicksPerMillisecond);
247 catch (OverflowException) {
248 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
252 public static TimeSpan FromTicks (long value)
254 return new TimeSpan (value);
257 public override int GetHashCode ()
259 return _ticks.GetHashCode ();
262 public TimeSpan Negate ()
264 if (_ticks == MinValue._ticks)
265 throw new OverflowException (Locale.GetText (
266 "This TimeSpan value is MinValue and cannot be negated."));
267 return new TimeSpan (-_ticks);
270 public static TimeSpan Parse (string s)
273 throw new ArgumentNullException ("s");
276 Parser p = new Parser (s);
280 public TimeSpan Subtract (TimeSpan ts)
284 return new TimeSpan (_ticks - ts.Ticks);
287 catch (OverflowException) {
288 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
292 public override string ToString ()
294 StringBuilder sb = new StringBuilder (14);
299 // We need to take absolute values of all components.
300 // Can't handle negative timespans by negating the TimeSpan
301 // as a whole. This would lead to an overflow for the
302 // degenerate case "TimeSpan.MinValue.ToString()".
304 sb.Append (Math.Abs (Days));
308 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Hours), 2, 4));
310 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Minutes), 2, 4));
312 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Seconds), 2, 4));
314 int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
315 if (fractional != 0) {
317 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (fractional), 7, 4));
320 return sb.ToString ();
323 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
328 public static bool operator == (TimeSpan t1, TimeSpan t2)
330 return t1._ticks == t2._ticks;
333 public static bool operator > (TimeSpan t1, TimeSpan t2)
335 return t1._ticks > t2._ticks;
338 public static bool operator >= (TimeSpan t1, TimeSpan t2)
340 return t1._ticks >= t2._ticks;
343 public static bool operator != (TimeSpan t1, TimeSpan t2)
345 return t1._ticks != t2._ticks;
348 public static bool operator < (TimeSpan t1, TimeSpan t2)
350 return t1._ticks < t2._ticks;
353 public static bool operator <= (TimeSpan t1, TimeSpan t2)
355 return t1._ticks <= t2._ticks;
358 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
360 return t1.Subtract (t2);
363 public static TimeSpan operator - (TimeSpan t)
368 public static TimeSpan operator + (TimeSpan t)
373 // Class Parser implements parser for TimeSpan.Parse
377 private int _cur = 0;
380 public Parser (string src)
383 _length = _src.Length;
388 return _cur >= _length;
392 private void ThrowFormatException ()
394 throw new FormatException (Locale.GetText ("Invalid format for TimeSpan.Parse."));
397 // All "Parse" functions throw a FormatException on syntax error.
398 // Their return value is semantic value of the item parsed.
400 // Range checking is spread over three different places:
401 // 1) When parsing "int" values, an exception is thrown immediately
402 // when the value parsed exceeds the maximum value for an int.
403 // 2) An explicit check is built in that checks for hours > 23 and
404 // for minutes and seconds > 59.
405 // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
406 // or < MinValue is left to the TimeSpan constructor called.
408 // Parse zero or more whitespace chars.
409 private void ParseWhiteSpace ()
411 while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
416 // Parse optional sign character.
417 private bool ParseSign ()
421 if (!AtEnd && _src[_cur] == '-') {
429 // Parse simple int value
430 private int ParseInt ()
435 while (!AtEnd && Char.IsDigit (_src, _cur)) {
437 res = res * 10 + _src[_cur] - '0';
444 ThrowFormatException ();
449 // Parse optional dot
450 private bool ParseOptDot ()
455 if (_src[_cur] == '.') {
462 // Parse NON-optional colon
463 private void ParseColon ()
465 if (!AtEnd && _src[_cur] == ':')
468 ThrowFormatException ();
471 // Parse [1..7] digits, representing fractional seconds (ticks)
472 private long ParseTicks ()
476 bool digitseen = false;
478 while (mag > 0 && !AtEnd && Char.IsDigit (_src, _cur)) {
479 res = res + (_src[_cur] - '0') * mag;
486 ThrowFormatException ();
491 public TimeSpan Execute ()
500 // Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
504 if (ParseOptDot ()) {
512 minutes = ParseInt ();
514 seconds = ParseInt ();
515 if ( ParseOptDot () ) {
516 ticks = ParseTicks ();
524 ThrowFormatException ();
526 if (hours > 23 || minutes > 59 || seconds > 59) {
527 throw new OverflowException (Locale.GetText (
528 "Invalid time data."));
531 TimeSpan ts = new TimeSpan (sign, days, hours, minutes, seconds, ticks);