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 [System.Runtime.InteropServices.ComVisible (true)]
39 public struct TimeSpan : IComparable, IComparable<TimeSpan>, IEquatable <TimeSpan>
41 public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
42 public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
43 public static readonly TimeSpan Zero = new TimeSpan (0L);
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;
53 public TimeSpan (long ticks)
58 public TimeSpan (int hours, int minutes, int seconds)
60 _ticks = CalculateTicks (0, hours, minutes, seconds, 0);
63 public TimeSpan (int days, int hours, int minutes, int seconds)
65 _ticks = CalculateTicks (days, hours, minutes, seconds, 0);
68 public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
70 _ticks = CalculateTicks (days, hours, minutes, seconds, milliseconds);
73 internal static long CalculateTicks (int days, int hours, int minutes, int seconds, int milliseconds)
75 // there's no overflow checks for hours, minutes, ...
76 // so big hours/minutes values can overflow at some point and change expected values
77 int hrssec = (hours * 3600); // break point at (Int32.MaxValue - 596523)
78 int minsec = (minutes * 60);
79 long t = ((long)(hrssec + minsec + seconds) * 1000L + (long)milliseconds);
82 bool overflow = false;
83 // days is problematic because it can overflow but that overflow can be
84 // "legal" (i.e. temporary) (e.g. if other parameters are negative) or
85 // illegal (e.g. sign change).
87 long td = TicksPerDay * days;
91 // positive days -> total ticks should be lower
92 overflow = (ticks > t);
96 // positive + positive != negative result
101 long td = TicksPerDay * days;
104 // negative + negative != positive result
110 // negative days -> total ticks should be lower
111 overflow = (t > ticks);
116 throw new ArgumentOutOfRangeException (Locale.GetText ("The timespan is too big or too small."));
123 return (int) (_ticks / TicksPerDay);
129 return (int) (_ticks % TicksPerDay / TicksPerHour);
133 public int Milliseconds {
135 return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
141 return (int) (_ticks % TicksPerHour / TicksPerMinute);
147 return (int) (_ticks % TicksPerMinute / TicksPerSecond);
157 public double TotalDays {
159 return (double) _ticks / TicksPerDay;
163 public double TotalHours {
165 return (double) _ticks / TicksPerHour;
169 public double TotalMilliseconds {
171 return (double) _ticks / TicksPerMillisecond;
175 public double TotalMinutes {
177 return (double) _ticks / TicksPerMinute;
181 public double TotalSeconds {
183 return (double) _ticks / TicksPerSecond;
187 public TimeSpan Add (TimeSpan ts)
191 return new TimeSpan (_ticks + ts.Ticks);
194 catch (OverflowException) {
195 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
199 public static int Compare (TimeSpan t1, TimeSpan t2)
201 if (t1._ticks < t2._ticks)
203 if (t1._ticks > t2._ticks)
208 public int CompareTo (object value)
213 if (!(value is TimeSpan)) {
214 throw new ArgumentException (Locale.GetText ("Argument has to be a TimeSpan."), "value");
217 return Compare (this, (TimeSpan) value);
220 public int CompareTo (TimeSpan value)
222 return Compare (this, value);
225 public bool Equals (TimeSpan obj)
227 return obj._ticks == _ticks;
230 public TimeSpan Duration ()
234 return new TimeSpan (Math.Abs (_ticks));
237 catch (OverflowException) {
238 throw new OverflowException (Locale.GetText (
239 "This TimeSpan value is MinValue so you cannot get the duration."));
243 public override bool Equals (object value)
245 if (!(value is TimeSpan))
248 return _ticks == ((TimeSpan) value)._ticks;
251 public static bool Equals (TimeSpan t1, TimeSpan t2)
253 return t1._ticks == t2._ticks;
256 public static TimeSpan FromDays (double value)
258 return From (value, TicksPerDay);
261 public static TimeSpan FromHours (double value)
263 return From (value, TicksPerHour);
266 public static TimeSpan FromMinutes (double value)
268 return From (value, TicksPerMinute);
271 public static TimeSpan FromSeconds (double value)
273 return From (value, TicksPerSecond);
276 public static TimeSpan FromMilliseconds (double value)
278 return From (value, TicksPerMillisecond);
281 private static TimeSpan From (double value, long tickMultiplicator)
283 if (Double.IsNaN (value))
284 throw new ArgumentException (Locale.GetText ("Value cannot be NaN."), "value");
285 if (Double.IsNegativeInfinity (value) || Double.IsPositiveInfinity (value) ||
286 (value < MinValue.Ticks) || (value > MaxValue.Ticks))
287 throw new OverflowException (Locale.GetText ("Outside range [MinValue,MaxValue]"));
290 value = (value * (tickMultiplicator / TicksPerMillisecond));
293 long val = (long) Math.Round(value);
294 return new TimeSpan (val * TicksPerMillisecond);
297 catch (OverflowException) {
298 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
302 public static TimeSpan FromTicks (long value)
304 return new TimeSpan (value);
307 public override int GetHashCode ()
309 return _ticks.GetHashCode ();
312 public TimeSpan Negate ()
314 if (_ticks == MinValue._ticks)
315 throw new OverflowException (Locale.GetText (
316 "This TimeSpan value is MinValue and cannot be negated."));
317 return new TimeSpan (-_ticks);
320 public static TimeSpan Parse (string s)
323 throw new ArgumentNullException ("s");
326 Parser p = new Parser (s);
330 public static bool TryParse (string s, out TimeSpan result)
336 result = TimeSpan.Zero;
341 public TimeSpan Subtract (TimeSpan ts)
345 return new TimeSpan (_ticks - ts.Ticks);
348 catch (OverflowException) {
349 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
353 public override string ToString ()
355 StringBuilder sb = new StringBuilder (14);
360 // We need to take absolute values of all components.
361 // Can't handle negative timespans by negating the TimeSpan
362 // as a whole. This would lead to an overflow for the
363 // degenerate case "TimeSpan.MinValue.ToString()".
365 sb.Append (Math.Abs (Days));
369 sb.Append (Math.Abs (Hours).ToString ("D2"));
371 sb.Append (Math.Abs (Minutes).ToString ("D2"));
373 sb.Append (Math.Abs (Seconds).ToString ("D2"));
375 int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
376 if (fractional != 0) {
378 sb.Append (fractional.ToString ("D7"));
381 return sb.ToString ();
384 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
389 public static bool operator == (TimeSpan t1, TimeSpan t2)
391 return t1._ticks == t2._ticks;
394 public static bool operator > (TimeSpan t1, TimeSpan t2)
396 return t1._ticks > t2._ticks;
399 public static bool operator >= (TimeSpan t1, TimeSpan t2)
401 return t1._ticks >= t2._ticks;
404 public static bool operator != (TimeSpan t1, TimeSpan t2)
406 return t1._ticks != t2._ticks;
409 public static bool operator < (TimeSpan t1, TimeSpan t2)
411 return t1._ticks < t2._ticks;
414 public static bool operator <= (TimeSpan t1, TimeSpan t2)
416 return t1._ticks <= t2._ticks;
419 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
421 return t1.Subtract (t2);
424 public static TimeSpan operator - (TimeSpan t)
429 public static TimeSpan operator + (TimeSpan t)
434 // Class Parser implements parser for TimeSpan.Parse
438 private int _cur = 0;
440 private bool formatError;
442 public Parser (string src)
445 _length = _src.Length;
450 return _cur >= _length;
454 // All "Parse" functions throw a FormatException on syntax error.
455 // Their return value is semantic value of the item parsed.
457 // Range checking is spread over three different places:
458 // 1) When parsing "int" values, an exception is thrown immediately
459 // when the value parsed exceeds the maximum value for an int.
460 // 2) An explicit check is built in that checks for hours > 23 and
461 // for minutes and seconds > 59.
462 // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
463 // or < MinValue is left to the TimeSpan constructor called.
465 // Parse zero or more whitespace chars.
466 private void ParseWhiteSpace ()
468 while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
473 // Parse optional sign character.
474 private bool ParseSign ()
478 if (!AtEnd && _src[_cur] == '-') {
486 // Parse simple int value
487 private int ParseInt (bool optional)
489 if (optional && AtEnd)
495 while (!AtEnd && Char.IsDigit (_src, _cur)) {
497 res = res * 10 + _src[_cur] - '0';
503 if (!optional && (count == 0))
509 // Parse optional dot
510 private bool ParseOptDot ()
515 if (_src[_cur] == '.') {
522 // Parse optional (LAMESPEC) colon
523 private void ParseOptColon ()
526 if (_src[_cur] == ':')
533 // Parse [1..7] digits, representing fractional seconds (ticks)
534 private long ParseTicks ()
538 bool digitseen = false;
540 while (mag > 0 && !AtEnd && Char.IsDigit (_src, _cur)) {
541 res = res + (_src[_cur] - '0') * mag;
553 public TimeSpan Execute ()
563 // Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
564 // ... but not entirely true as an lonely
565 // integer will be parsed as a number of days
568 days = ParseInt (false);
569 if (ParseOptDot ()) {
570 hours = ParseInt (true);
577 minutes = ParseInt (true);
579 seconds = ParseInt (true);
580 if ( ParseOptDot () ) {
581 ticks = ParseTicks ();
591 // Overflow has presceance over FormatException
592 if (hours > 23 || minutes > 59 || seconds > 59) {
593 throw new OverflowException (
594 Locale.GetText ("Invalid time data."));
596 else if (formatError) {
597 throw new FormatException (
598 Locale.GetText ("Invalid format for TimeSpan.Parse."));
601 long t = TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0);
602 t = checked ((sign) ? (-t - ticks) : (t + ticks));
603 return new TimeSpan (t);