5 // Duco Fijma (duco@lorentz.xs4all.nl)
6 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 // Sebastien Pouliot <sebastien@ximian.com>
10 // (C) 2004 Andreas Nahr
11 // Copyright (C) 2004 Novell (http://www.novell.com)
19 public struct TimeSpan : IComparable
21 public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
22 public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
23 public static readonly TimeSpan Zero = new TimeSpan (0L);
25 public const long TicksPerDay = 864000000000L;
26 public const long TicksPerHour = 36000000000L;
27 public const long TicksPerMillisecond = 10000L;
28 public const long TicksPerMinute = 600000000L;
29 public const long TicksPerSecond = 10000000L;
33 public TimeSpan (long value)
38 public TimeSpan (int hours, int minutes, int seconds)
40 _ticks = CalculateTicks (0, hours, minutes, seconds, 0);
43 public TimeSpan (int days, int hours, int minutes, int seconds)
45 _ticks = CalculateTicks (days, hours, minutes, seconds, 0);
48 public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
50 _ticks = CalculateTicks (days, hours, minutes, seconds, milliseconds);
53 internal static long CalculateTicks (int days, int hours, int minutes, int seconds, int milliseconds)
55 bool overflow = false;
57 // this part cannot overflow Int64
58 long t = checked (TimeSpan.TicksPerHour * hours +
59 TimeSpan.TicksPerMinute * minutes +
60 TimeSpan.TicksPerSecond * seconds +
61 TimeSpan.TicksPerMillisecond * milliseconds);
63 // days is problematic because it can overflow but that overflow can be
64 // "legal" (i.e. temporary) (e.g. if other parameters are negative) or
65 // illegal (e.g. sign change).
67 long td = TicksPerDay * days;
71 // positive days -> total ticks should be lower
72 overflow = (ticks > t);
76 // positive + positive != negative result
81 long td = TicksPerDay * days;
84 // negative + negative != positive result
90 // negative days -> total ticks should be lower
91 overflow = (t > ticks);
96 throw new ArgumentOutOfRangeException (Locale.GetText ("The timespan is too big or too small."));
103 return (int) (_ticks / TicksPerDay);
109 return (int) (_ticks % TicksPerDay / TicksPerHour);
113 public int Milliseconds {
115 return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
121 return (int) (_ticks % TicksPerHour / TicksPerMinute);
127 return (int) (_ticks % TicksPerMinute / TicksPerSecond);
137 public double TotalDays {
139 return (double) _ticks / TicksPerDay;
143 public double TotalHours {
145 return (double) _ticks / TicksPerHour;
149 public double TotalMilliseconds {
151 return (double) _ticks / TicksPerMillisecond;
155 public double TotalMinutes {
157 return (double) _ticks / TicksPerMinute;
161 public double TotalSeconds {
163 return (double) _ticks / TicksPerSecond;
167 public TimeSpan Add (TimeSpan ts)
171 return new TimeSpan (_ticks + ts.Ticks);
174 catch (OverflowException) {
175 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
179 public static int Compare (TimeSpan t1, TimeSpan t2)
181 if (t1._ticks < t2._ticks)
183 if (t1._ticks > t2._ticks)
188 public int CompareTo (object value)
193 if (!(value is TimeSpan)) {
194 throw new ArgumentException (Locale.GetText ("Argument has to be a TimeSpan."), "value");
197 return Compare (this, (TimeSpan) value);
200 public TimeSpan Duration ()
204 return new TimeSpan (Math.Abs (_ticks));
207 catch (OverflowException) {
208 throw new OverflowException (Locale.GetText (
209 "This TimeSpan value is MinValue so you cannot get the duration."));
213 public override bool Equals (object value)
215 if (!(value is TimeSpan))
218 return _ticks == ((TimeSpan) value)._ticks;
221 public static bool Equals (TimeSpan t1, TimeSpan t2)
223 return t1._ticks == t2._ticks;
226 public static TimeSpan FromDays (double value)
228 return From (value, TicksPerDay);
231 public static TimeSpan FromHours (double value)
233 return From (value, TicksPerHour);
236 public static TimeSpan FromMinutes (double value)
238 return From (value, TicksPerMinute);
241 public static TimeSpan FromSeconds (double value)
243 return From (value, TicksPerSecond);
246 public static TimeSpan FromMilliseconds (double value)
248 return From (value, TicksPerMillisecond);
251 private static TimeSpan From (double value, long tickMultiplicator)
253 if (Double.IsNaN (value))
254 throw new ArgumentException (Locale.GetText ("Value cannot be NaN."), "value");
255 if (Double.IsNegativeInfinity (value) || Double.IsPositiveInfinity (value) ||
256 (value < MinValue.Ticks) || (value > MaxValue.Ticks))
257 throw new OverflowException (Locale.GetText ("Outside range [MinValue,MaxValue]"));
260 value = (value * (tickMultiplicator / TicksPerMillisecond));
263 long val = (long) Math.Round(value);
264 return new TimeSpan (val * TicksPerMillisecond);
267 catch (OverflowException) {
268 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
272 public static TimeSpan FromTicks (long value)
274 return new TimeSpan (value);
277 public override int GetHashCode ()
279 return _ticks.GetHashCode ();
282 public TimeSpan Negate ()
284 if (_ticks == MinValue._ticks)
285 throw new OverflowException (Locale.GetText (
286 "This TimeSpan value is MinValue and cannot be negated."));
287 return new TimeSpan (-_ticks);
290 public static TimeSpan Parse (string s)
293 throw new ArgumentNullException ("s");
296 Parser p = new Parser (s);
300 public TimeSpan Subtract (TimeSpan ts)
304 return new TimeSpan (_ticks - ts.Ticks);
307 catch (OverflowException) {
308 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
312 public override string ToString ()
314 StringBuilder sb = new StringBuilder (14);
319 // We need to take absolute values of all components.
320 // Can't handle negative timespans by negating the TimeSpan
321 // as a whole. This would lead to an overflow for the
322 // degenerate case "TimeSpan.MinValue.ToString()".
324 sb.Append (Math.Abs (Days));
328 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Hours), 2, 4));
330 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Minutes), 2, 4));
332 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Seconds), 2, 4));
334 int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
335 if (fractional != 0) {
337 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (fractional), 7, 4));
340 return sb.ToString ();
343 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
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 bool operator >= (TimeSpan t1, TimeSpan t2)
360 return t1._ticks >= t2._ticks;
363 public static bool operator != (TimeSpan t1, TimeSpan t2)
365 return t1._ticks != t2._ticks;
368 public static bool operator < (TimeSpan t1, TimeSpan t2)
370 return t1._ticks < t2._ticks;
373 public static bool operator <= (TimeSpan t1, TimeSpan t2)
375 return t1._ticks <= t2._ticks;
378 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
380 return t1.Subtract (t2);
383 public static TimeSpan operator - (TimeSpan t)
388 public static TimeSpan operator + (TimeSpan t)
393 // Class Parser implements parser for TimeSpan.Parse
397 private int _cur = 0;
399 private bool formatError;
401 public Parser (string src)
404 _length = _src.Length;
409 return _cur >= _length;
413 // All "Parse" functions throw a FormatException on syntax error.
414 // Their return value is semantic value of the item parsed.
416 // Range checking is spread over three different places:
417 // 1) When parsing "int" values, an exception is thrown immediately
418 // when the value parsed exceeds the maximum value for an int.
419 // 2) An explicit check is built in that checks for hours > 23 and
420 // for minutes and seconds > 59.
421 // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
422 // or < MinValue is left to the TimeSpan constructor called.
424 // Parse zero or more whitespace chars.
425 private void ParseWhiteSpace ()
427 while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
432 // Parse optional sign character.
433 private bool ParseSign ()
437 if (!AtEnd && _src[_cur] == '-') {
445 // Parse simple int value
446 private int ParseInt (bool optional)
448 if (optional && AtEnd)
454 while (!AtEnd && Char.IsDigit (_src, _cur)) {
456 res = res * 10 + _src[_cur] - '0';
468 // Parse optional dot
469 private bool ParseOptDot ()
474 if (_src[_cur] == '.') {
481 // Parse optional (LAMESPEC) colon
482 private void ParseOptColon ()
485 if (_src[_cur] == ':')
492 // Parse [1..7] digits, representing fractional seconds (ticks)
493 private long ParseTicks ()
497 bool digitseen = false;
499 while (mag > 0 && !AtEnd && Char.IsDigit (_src, _cur)) {
500 res = res + (_src[_cur] - '0') * mag;
512 public TimeSpan Execute ()
522 // Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
523 // ... but not entirely true as an lonely
524 // integer will be parsed as a number of days
527 days = ParseInt (false);
528 if (ParseOptDot ()) {
529 hours = ParseInt (true);
536 minutes = ParseInt (true);
538 seconds = ParseInt (true);
539 if ( ParseOptDot () ) {
540 ticks = ParseTicks ();
550 // Overflow has presceance over FormatException
551 if (hours > 23 || minutes > 59 || seconds > 59) {
552 throw new OverflowException (
553 Locale.GetText ("Invalid time data."));
555 else if (formatError) {
556 throw new FormatException (
557 Locale.GetText ("Invalid format for TimeSpan.Parse."));
560 long t = TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0);
564 return new TimeSpan (t);