5 // Duco Fijma (duco@lorentz.xs4all.nl)
10 using System.Globalization;
15 public struct TimeSpan : IComparable {
19 public TimeSpan (long value) { _ticks = value; }
20 public TimeSpan (int hours, int minutes, int seconds)
21 : this(false, 0, hours, minutes, seconds, 0, 0) {}
22 public TimeSpan (int days, int hours, int minutes, int seconds)
23 : this(false, days, hours, minutes, seconds, 0, 0) {}
24 public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
25 : this(false, days, hours, minutes, seconds, milliseconds, 0) {}
27 internal TimeSpan (bool sign, int days, int hours, int minutes, int seconds, int milliseconds, long ticks)
30 _ticks = TicksPerDay * days +
31 TicksPerHour * hours +
32 TicksPerMinute * minutes +
33 TicksPerSecond * seconds +
34 TicksPerMillisecond * milliseconds +
42 public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
43 public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
44 public const long TicksPerDay = 864000000000L;
45 public const long TicksPerHour = 36000000000L;
46 public const long TicksPerMillisecond = 10000L;
47 public const long TicksPerMinute = 600000000L;
48 public const long TicksPerSecond = 10000000L;
49 public static readonly TimeSpan Zero = new TimeSpan (0L);
54 return (int) (_ticks / TicksPerDay);
61 return (int) (_ticks % TicksPerDay / TicksPerHour);
65 public int Milliseconds
69 return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
77 return (int) (_ticks % TicksPerHour / TicksPerMinute);
85 return (int) (_ticks % TicksPerMinute / TicksPerSecond);
97 public double TotalDays
101 return (double) _ticks / TicksPerDay;
105 public double TotalHours
109 return (double) _ticks / TicksPerHour;
113 public double TotalMilliseconds
117 return (double) _ticks / TicksPerMillisecond;
121 public double TotalMinutes
124 return (double) _ticks / TicksPerMinute;
128 public double TotalSeconds
131 return (double) _ticks / TicksPerSecond;
135 public TimeSpan Add (TimeSpan ts)
138 return new TimeSpan (_ticks + ts.Ticks);
142 public static int Compare (TimeSpan t1, TimeSpan t2)
144 if (t1._ticks < t2._ticks) {
147 else if (t1._ticks > t2._ticks) {
155 public int CompareTo (object value)
160 if (!(value is TimeSpan)) {
161 throw new ArgumentException (Locale.GetText (
162 "Argument of System.TimeSpan.CompareTo should be a TimeSpan"));
165 return Compare(this, (TimeSpan) value);
168 public TimeSpan Duration ()
171 return new TimeSpan (Math.Abs (_ticks));
175 public override bool Equals (object value)
177 if (!(value is TimeSpan)) {
180 return Equals (this, (TimeSpan) value);
183 public static bool Equals (TimeSpan t1, TimeSpan t2)
185 return t1._ticks == t2._ticks;
188 // Implementing FromDays -> FromHours -> FromMinutes -> FromSeconds ->
189 // FromMilliseconds as done here is probably not the most efficient
191 public static TimeSpan FromDays (double value)
193 if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
197 if (Double.IsPositiveInfinity (value)) {
201 return new TimeSpan ((int) value,0,0,0,0) + FromHours ((value - ((int) value)) * 24);
204 public static TimeSpan FromHours (double value)
206 if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
210 if (Double.IsPositiveInfinity (value)) {
214 return new TimeSpan ((int) value,0,0) + FromMinutes ((value - ((int) value)) * 60);
217 public static TimeSpan FromMinutes (double value)
219 if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
223 if (Double.IsPositiveInfinity (value)) {
227 return new TimeSpan (0, (int) value, 0) + FromSeconds((value - ((int) value)) * 60);
230 public static TimeSpan FromSeconds (double value)
232 if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
236 if (Double.IsPositiveInfinity (value)) {
240 return new TimeSpan (0, 0, 0, (int) value) + FromMilliseconds((value - ((int) value)) * 1000);
244 public static TimeSpan FromMilliseconds (double value)
246 if (Double.IsNaN (value) || Double.IsNegativeInfinity (value)) {
250 if (Double.IsPositiveInfinity (value)) {
254 return new TimeSpan (0, 0, 0, 0, (int) value);
257 public static TimeSpan FromTicks (long value)
259 return new TimeSpan (value);
262 public override int GetHashCode ()
264 return _ticks.GetHashCode ();
267 public TimeSpan Negate ()
269 if (_ticks == long.MinValue)
270 throw new OverflowException ("This TimeSpan value is MinValue and cannot be negated.");
271 return new TimeSpan (-_ticks);
274 public static TimeSpan Parse (string s)
277 throw new ArgumentNullException (
278 Locale.GetText ("null reference passed to TimeSpan.Parse"));
281 Parser p = new Parser (s);
285 public TimeSpan Subtract (TimeSpan ts)
288 return new TimeSpan (_ticks - ts.Ticks);
292 public override string ToString ()
300 // We need to take absolute values of all components.
301 // Can't handle negative timespans by negating the TimeSpan
302 // as a whole. This would lead to an overflow for the
303 // degenerate case "TimeSpan.MinValue.ToString()".
305 res += Math.Abs (Days) + "." ;
308 res += string.Format ("{0:D2}:{1:D2}:{2:D2}", Math.Abs(Hours), Math.Abs(Minutes), Math.Abs(Seconds));
310 int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
311 if (fractional != 0) {
312 res += string.Format (".{0:D7}", fractional);
318 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
323 public static bool operator == (TimeSpan t1, TimeSpan t2)
325 return Compare (t1, t2) == 0;
328 public static bool operator > (TimeSpan t1, TimeSpan t2)
330 return Compare (t1, t2) == 1;
333 public static bool operator >= (TimeSpan t1, TimeSpan t2)
335 return Compare (t1, t2) != -1;
338 public static bool operator != (TimeSpan t1, TimeSpan t2)
340 return Compare (t1, t2) != 0;
343 public static bool operator < (TimeSpan t1, TimeSpan t2)
345 return Compare (t1, t2) == -1;
348 public static bool operator <= (TimeSpan t1, TimeSpan t2)
350 return Compare (t1, t2) != 1;
353 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
355 return t1.Subtract (t2);
358 public static TimeSpan operator - (TimeSpan t)
363 public static TimeSpan operator + (TimeSpan t)
368 // Class Parser implements simple parser for TimeSpan::Parse
369 internal class Parser {
375 public Parser (string src)
384 _length = _src.Length;
390 return _cur >= _length;
394 private void ThrowFormatException()
396 throw new FormatException (Locale.GetText ("Invalid format for TimeSpan.Parse"));
399 // All "Parse" functions throw a FormatException on syntax error.
400 // Their return value is semantic value of the item parsed.
402 // Range checking is spread over three different places:
403 // 1) When parsing "int" values, an exception is thrown immediately
404 // when the value parsed exceeds the maximum value for an int.
405 // 2) An explicit check is built in that checks for hours > 23 and
406 // for minutes and seconds > 59.
407 // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
408 // or < MinValue is left to the TimeSpan constructor called.
410 // Parse zero or more whitespace chars.
411 private void ParseWhiteSpace ()
413 while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
418 // Parse optional sign character.
419 private bool ParseSign ()
423 if (!AtEnd && _src[_cur] == '-') {
431 // Parse simple int value
432 private int ParseInt ()
437 while (!AtEnd && Char.IsDigit (_src, _cur)) {
439 res = res*10 + _src[_cur] - '0';
446 ThrowFormatException ();
452 // Parse optional dot
453 private bool ParseOptDot ()
459 if (_src[_cur] == '.') {
468 // Parse NON-optional colon
469 private void ParseColon ()
471 if (!AtEnd && _src[_cur] == ':') {
475 ThrowFormatException ();
479 // Parse [1..7] digits, representing fractional seconds (ticks)
480 private long ParseTicks ()
484 bool digitseen = false;
486 while ( mag > 0 && !AtEnd && Char.IsDigit (_src, _cur) ) {
487 res = res + (_src[_cur] - '0') * mag;
494 ThrowFormatException ();
500 public TimeSpan Execute ()
509 // Parse [ws][dd.]hh:mm:ss[.ff][ws]
513 if (ParseOptDot ()) {
521 minutes = ParseInt ();
523 seconds = ParseInt ();
524 if ( ParseOptDot () ) {
525 ticks = ParseTicks ();
533 ThrowFormatException ();
536 if ( hours > 23 || minutes > 59 || seconds > 59 ) {
537 throw new OverflowException (Locale.GetText (
538 "Value outside range in TimeSpan.Parse" ));
541 TimeSpan ts = new TimeSpan (sign, days, hours, minutes, seconds, 0, ticks);