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)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 public struct TimeSpan : IComparable
40 public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
41 public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
42 public static readonly TimeSpan Zero = new TimeSpan (0L);
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;
52 public TimeSpan (long value)
57 public TimeSpan (int hours, int minutes, int seconds)
59 _ticks = CalculateTicks (0, hours, minutes, seconds, 0);
62 public TimeSpan (int days, int hours, int minutes, int seconds)
64 _ticks = CalculateTicks (days, hours, minutes, seconds, 0);
67 public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
69 _ticks = CalculateTicks (days, hours, minutes, seconds, milliseconds);
72 internal static long CalculateTicks (int days, int hours, int minutes, int seconds, int milliseconds)
74 bool overflow = false;
76 // this part cannot overflow Int64
77 long t = checked (TimeSpan.TicksPerHour * hours +
78 TimeSpan.TicksPerMinute * minutes +
79 TimeSpan.TicksPerSecond * seconds +
80 TimeSpan.TicksPerMillisecond * milliseconds);
82 // days is problematic because it can overflow but that overflow can be
83 // "legal" (i.e. temporary) (e.g. if other parameters are negative) or
84 // illegal (e.g. sign change).
86 long td = TicksPerDay * days;
90 // positive days -> total ticks should be lower
91 overflow = (ticks > t);
95 // positive + positive != negative result
100 long td = TicksPerDay * days;
103 // negative + negative != positive result
109 // negative days -> total ticks should be lower
110 overflow = (t > ticks);
115 throw new ArgumentOutOfRangeException (Locale.GetText ("The timespan is too big or too small."));
122 return (int) (_ticks / TicksPerDay);
128 return (int) (_ticks % TicksPerDay / TicksPerHour);
132 public int Milliseconds {
134 return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
140 return (int) (_ticks % TicksPerHour / TicksPerMinute);
146 return (int) (_ticks % TicksPerMinute / TicksPerSecond);
156 public double TotalDays {
158 return (double) _ticks / TicksPerDay;
162 public double TotalHours {
164 return (double) _ticks / TicksPerHour;
168 public double TotalMilliseconds {
170 return (double) _ticks / TicksPerMillisecond;
174 public double TotalMinutes {
176 return (double) _ticks / TicksPerMinute;
180 public double TotalSeconds {
182 return (double) _ticks / TicksPerSecond;
186 public TimeSpan Add (TimeSpan ts)
190 return new TimeSpan (_ticks + ts.Ticks);
193 catch (OverflowException) {
194 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
198 public static int Compare (TimeSpan t1, TimeSpan t2)
200 if (t1._ticks < t2._ticks)
202 if (t1._ticks > t2._ticks)
207 public int CompareTo (object value)
212 if (!(value is TimeSpan)) {
213 throw new ArgumentException (Locale.GetText ("Argument has to be a TimeSpan."), "value");
216 return Compare (this, (TimeSpan) value);
219 public TimeSpan Duration ()
223 return new TimeSpan (Math.Abs (_ticks));
226 catch (OverflowException) {
227 throw new OverflowException (Locale.GetText (
228 "This TimeSpan value is MinValue so you cannot get the duration."));
232 public override bool Equals (object value)
234 if (!(value is TimeSpan))
237 return _ticks == ((TimeSpan) value)._ticks;
240 public static bool Equals (TimeSpan t1, TimeSpan t2)
242 return t1._ticks == t2._ticks;
245 public static TimeSpan FromDays (double value)
247 return From (value, TicksPerDay);
250 public static TimeSpan FromHours (double value)
252 return From (value, TicksPerHour);
255 public static TimeSpan FromMinutes (double value)
257 return From (value, TicksPerMinute);
260 public static TimeSpan FromSeconds (double value)
262 return From (value, TicksPerSecond);
265 public static TimeSpan FromMilliseconds (double value)
267 return From (value, TicksPerMillisecond);
270 private static TimeSpan From (double value, long tickMultiplicator)
272 if (Double.IsNaN (value))
273 throw new ArgumentException (Locale.GetText ("Value cannot be NaN."), "value");
274 if (Double.IsNegativeInfinity (value) || Double.IsPositiveInfinity (value) ||
275 (value < MinValue.Ticks) || (value > MaxValue.Ticks))
276 throw new OverflowException (Locale.GetText ("Outside range [MinValue,MaxValue]"));
279 value = (value * (tickMultiplicator / TicksPerMillisecond));
282 long val = (long) Math.Round(value);
283 return new TimeSpan (val * TicksPerMillisecond);
286 catch (OverflowException) {
287 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
291 public static TimeSpan FromTicks (long value)
293 return new TimeSpan (value);
296 public override int GetHashCode ()
298 return _ticks.GetHashCode ();
301 public TimeSpan Negate ()
303 if (_ticks == MinValue._ticks)
304 throw new OverflowException (Locale.GetText (
305 "This TimeSpan value is MinValue and cannot be negated."));
306 return new TimeSpan (-_ticks);
309 public static TimeSpan Parse (string s)
312 throw new ArgumentNullException ("s");
315 Parser p = new Parser (s);
319 public TimeSpan Subtract (TimeSpan ts)
323 return new TimeSpan (_ticks - ts.Ticks);
326 catch (OverflowException) {
327 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
331 public override string ToString ()
333 StringBuilder sb = new StringBuilder (14);
338 // We need to take absolute values of all components.
339 // Can't handle negative timespans by negating the TimeSpan
340 // as a whole. This would lead to an overflow for the
341 // degenerate case "TimeSpan.MinValue.ToString()".
343 sb.Append (Math.Abs (Days));
347 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Hours), 2, 4));
349 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Minutes), 2, 4));
351 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (Seconds), 2, 4));
353 int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
354 if (fractional != 0) {
356 sb.Append (IntegerFormatter.FormatDecimal (Math.Abs (fractional), 7, 4));
359 return sb.ToString ();
362 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
367 public static bool operator == (TimeSpan t1, TimeSpan t2)
369 return t1._ticks == t2._ticks;
372 public static bool operator > (TimeSpan t1, TimeSpan t2)
374 return t1._ticks > t2._ticks;
377 public static bool operator >= (TimeSpan t1, TimeSpan t2)
379 return t1._ticks >= t2._ticks;
382 public static bool operator != (TimeSpan t1, TimeSpan t2)
384 return t1._ticks != t2._ticks;
387 public static bool operator < (TimeSpan t1, TimeSpan t2)
389 return t1._ticks < t2._ticks;
392 public static bool operator <= (TimeSpan t1, TimeSpan t2)
394 return t1._ticks <= t2._ticks;
397 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
399 return t1.Subtract (t2);
402 public static TimeSpan operator - (TimeSpan t)
407 public static TimeSpan operator + (TimeSpan t)
412 // Class Parser implements parser for TimeSpan.Parse
416 private int _cur = 0;
418 private bool formatError;
420 public Parser (string src)
423 _length = _src.Length;
428 return _cur >= _length;
432 // All "Parse" functions throw a FormatException on syntax error.
433 // Their return value is semantic value of the item parsed.
435 // Range checking is spread over three different places:
436 // 1) When parsing "int" values, an exception is thrown immediately
437 // when the value parsed exceeds the maximum value for an int.
438 // 2) An explicit check is built in that checks for hours > 23 and
439 // for minutes and seconds > 59.
440 // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
441 // or < MinValue is left to the TimeSpan constructor called.
443 // Parse zero or more whitespace chars.
444 private void ParseWhiteSpace ()
446 while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
451 // Parse optional sign character.
452 private bool ParseSign ()
456 if (!AtEnd && _src[_cur] == '-') {
464 // Parse simple int value
465 private int ParseInt (bool optional)
467 if (optional && AtEnd)
473 while (!AtEnd && Char.IsDigit (_src, _cur)) {
475 res = res * 10 + _src[_cur] - '0';
487 // Parse optional dot
488 private bool ParseOptDot ()
493 if (_src[_cur] == '.') {
500 // Parse optional (LAMESPEC) colon
501 private void ParseOptColon ()
504 if (_src[_cur] == ':')
511 // Parse [1..7] digits, representing fractional seconds (ticks)
512 private long ParseTicks ()
516 bool digitseen = false;
518 while (mag > 0 && !AtEnd && Char.IsDigit (_src, _cur)) {
519 res = res + (_src[_cur] - '0') * mag;
531 public TimeSpan Execute ()
541 // Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
542 // ... but not entirely true as an lonely
543 // integer will be parsed as a number of days
546 days = ParseInt (false);
547 if (ParseOptDot ()) {
548 hours = ParseInt (true);
555 minutes = ParseInt (true);
557 seconds = ParseInt (true);
558 if ( ParseOptDot () ) {
559 ticks = ParseTicks ();
569 // Overflow has presceance over FormatException
570 if (hours > 23 || minutes > 59 || seconds > 59) {
571 throw new OverflowException (
572 Locale.GetText ("Invalid time data."));
574 else if (formatError) {
575 throw new FormatException (
576 Locale.GetText ("Invalid format for TimeSpan.Parse."));
579 long t = TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0);
580 t = checked ((sign) ? (-t - ticks) : (t + ticks));
581 return new TimeSpan (t);