5 // Duco Fijma (duco@lorentz.xs4all.nl)
13 public struct TimeSpan : IComparable {
18 public TimeSpan (long value) { _ticks = value; }
19 public TimeSpan (int hours, int minutes, int seconds)
20 : this(false, 0, hours, minutes, seconds, 0, 0) {}
21 public TimeSpan (int days, int hours, int minutes, int seconds)
22 : this(false, days, hours, minutes, seconds, 0, 0) {}
23 public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
24 : this(false, days, hours, minutes, seconds, milliseconds, 0) {}
26 internal TimeSpan (bool sign, int days, int hours, int minutes, int seconds, int milliseconds, long ticks)
29 _ticks = TicksPerDay * days +
30 TicksPerHour * hours +
31 TicksPerMinute * minutes +
32 TicksPerSecond * seconds +
33 TicksPerMillisecond * milliseconds +
43 public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
44 public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
45 public const long TicksPerDay = 864000000000L;
46 public const long TicksPerHour = 36000000000L;
47 public const long TicksPerMillisecond = 10000L;
48 public const long TicksPerMinute = 600000000L;
49 public const long TicksPerSecond = 10000000L;
50 public static readonly TimeSpan Zero = new TimeSpan (0L);
57 return (int) TotalDays;
64 return (int) (_ticks % TicksPerDay / TicksPerHour);
68 public int Milliseconds
72 return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
80 return (int) (_ticks % TicksPerHour / TicksPerMinute);
88 return (int) (_ticks % TicksPerMinute / TicksPerSecond);
100 public double TotalDays
104 return (double) _ticks / TicksPerDay;
108 public double TotalHours
112 return (double) _ticks / TicksPerHour;
116 public double TotalMilliseconds
120 return (double) _ticks / TicksPerMillisecond;
124 public double TotalMinutes
127 return (double) _ticks / TicksPerMinute;
131 public double TotalSeconds
134 return (double) _ticks / TicksPerSecond;
140 public TimeSpan Add (TimeSpan ts)
143 return new TimeSpan (_ticks + ts.Ticks);
147 public static int Compare (TimeSpan t1, TimeSpan t2)
149 if (t1._ticks < t2._ticks) {
152 else if (t1._ticks > t2._ticks) {
160 public int CompareTo (object value)
163 if (value == null ) {
167 if (!(value is TimeSpan)) {
168 throw new ArgumentException ("Argument of System.TimeSpan.CompareTo should be a TimeSpan");
171 return Compare(this, (TimeSpan) value);
174 public TimeSpan Duration ()
177 return new TimeSpan (Math.Abs (_ticks));
181 public override bool Equals (object value)
183 if (!(value is TimeSpan)) {
186 return Equals (this, (TimeSpan) value);
189 public static bool Equals (TimeSpan t1, TimeSpan t2)
191 return t1._ticks == t2._ticks;
194 // Implementing FromDays -> FromHours -> FromMinutes -> FromSeconds ->
195 // FromMilliseconds as done here is probably not the most efficient
197 public static TimeSpan FromDays (double value)
199 if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
203 if (Double.IsPositiveInfinity (value)) {
207 return new TimeSpan ((int) value,0,0,0,0) + FromHours ((value - ((int) value)) * 24);
210 public static TimeSpan FromHours (double value)
212 if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
216 if (Double.IsPositiveInfinity (value)) {
220 return new TimeSpan ((int) value,0,0) + FromMinutes ((value - ((int) value)) * 60);
223 public static TimeSpan FromMinutes (double value)
225 if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
229 if (Double.IsPositiveInfinity (value)) {
233 return new TimeSpan (0, (int) value, 0) + FromSeconds((value - ((int) value)) * 60);
236 public static TimeSpan FromSeconds (double value)
238 if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
242 if (Double.IsPositiveInfinity (value)) {
246 return new TimeSpan (0, 0, 0, (int) value, ((int) ((value - ((int) value)) * 1000)));
250 public static TimeSpan FromTicks (long value)
252 return new TimeSpan (value);
255 public override int GetHashCode ()
257 return _ticks.GetHashCode ();
260 public TimeSpan Negate ()
263 return new TimeSpan (-_ticks);
267 public static TimeSpan Parse (string s)
270 throw new ArgumentNullException ("null reference passed to TimeSpan.Parse");
273 Parser p = new Parser (s);
277 public TimeSpan Subtract (TimeSpan ts)
280 return new TimeSpan (_ticks - ts.Ticks);
284 public override string ToString ()
292 // We need to take absolute values of all components.
293 // Can't handle negative timespans by negating the TimeSpan
294 // as a whole. This would lead to an overflow for the
295 // degenerate case "TimeSpan.MinValue.ToString()".
297 res += Math.Abs (Days) + "." ;
300 res += string.Format ("{0:00}:{1:00}:{2:00}", Math.Abs(Hours), Math.Abs(Minutes), Math.Abs(Seconds));
302 int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
303 if (fractional != 0) {
304 res += string.Format (".{0:0000000}", fractional);
310 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
315 public static bool operator == (TimeSpan t1, TimeSpan t2)
317 return Compare (t1, t2) == 0;
320 public static bool operator > (TimeSpan t1, TimeSpan t2)
322 return Compare (t1, t2) == 1;
325 public static bool operator >= (TimeSpan t1, TimeSpan t2)
327 return Compare (t1, t2) != -1;
330 public static bool operator != (TimeSpan t1, TimeSpan t2)
332 return Compare (t1, t2) != 0;
335 public static bool operator < (TimeSpan t1, TimeSpan t2)
337 return Compare (t1, t2) == -1;
340 public static bool operator <= (TimeSpan t1, TimeSpan t2)
342 return Compare (t1, t2) != 1;
345 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
347 return t1.Subtract (t2);
350 public static TimeSpan operator - (TimeSpan t)
355 public static TimeSpan operator + (TimeSpan t)
361 // Class Parser implements simple parser for TimeSpan::Parse
362 internal class Parser {
368 public Parser (string src)
377 _length = _src.Length;
383 return _cur >= _length;
387 private void ThrowFormatException()
389 throw new FormatException ("Invalid format for TimeSpan.Parse");
392 // All "Parse" functions throw a FormatException on syntax error.
393 // Their return value is semantic value of the item parsed.
395 // Range checking is spread over three different places:
396 // 1) When parsing "int" values, an exception is thrown immediately
397 // when the value parsed exceeds the maximum value for an int.
398 // 2) An explicit check is built in that checks for hours > 23 and
399 // for minutes and seconds > 59.
400 // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
401 // or < MinValue is left to the TimeSpan constructor called.
403 // Parse zero or more whitespace chars.
404 private void ParseWhiteSpace ()
406 while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
411 // Parse optional sign character.
412 private bool ParseSign ()
416 if (!AtEnd && _src[_cur] == '-') {
424 // Parse simple int value
425 private int ParseInt ()
430 while (!AtEnd && Char.IsDigit (_src, _cur)) {
432 res = res*10 + _src[_cur] - '0';
439 ThrowFormatException ();
445 // Parse optional dot
446 private bool ParseOptDot ()
452 if (_src[_cur] == '.') {
461 // Parse NON-optional colon
462 private void ParseColon ()
464 if (!AtEnd && _src[_cur] == ':') {
468 ThrowFormatException ();
472 // Parse [1..7] digits, representing fractional seconds (ticks)
473 private long ParseTicks ()
477 bool digitseen = false;
479 while ( mag > 0 && !AtEnd && Char.IsDigit (_src, _cur) ) {
480 res = res + (_src[_cur] - '0') * mag;
487 ThrowFormatException ();
493 public TimeSpan Execute ()
502 // Parse [ws][dd.]hh:mm:ss[.ff][ws]
506 if (ParseOptDot ()) {
514 minutes = ParseInt ();
516 seconds = ParseInt ();
517 if ( ParseOptDot () ) {
518 ticks = ParseTicks ();
526 ThrowFormatException ();
529 if ( hours > 23 || minutes > 59 || seconds > 59 ) {
530 throw new OverflowException ( "Value outside range in TimeSpan.Parse" );
533 TimeSpan ts = new TimeSpan (sign, days, hours, minutes, seconds, 0, ticks);