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>
43 if (MonoTouchAOTHelper.FalseFlag) {
44 var comparer = new System.Collections.Generic.GenericComparer <TimeSpan> ();
45 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <TimeSpan> ();
49 public static readonly TimeSpan MaxValue = new TimeSpan (long.MaxValue);
50 public static readonly TimeSpan MinValue = new TimeSpan (long.MinValue);
51 public static readonly TimeSpan Zero = new TimeSpan (0L);
53 public const long TicksPerDay = 864000000000L;
54 public const long TicksPerHour = 36000000000L;
55 public const long TicksPerMillisecond = 10000L;
56 public const long TicksPerMinute = 600000000L;
57 public const long TicksPerSecond = 10000000L;
61 public TimeSpan (long ticks)
66 public TimeSpan (int hours, int minutes, int seconds)
68 _ticks = CalculateTicks (0, hours, minutes, seconds, 0);
71 public TimeSpan (int days, int hours, int minutes, int seconds)
73 _ticks = CalculateTicks (days, hours, minutes, seconds, 0);
76 public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds)
78 _ticks = CalculateTicks (days, hours, minutes, seconds, milliseconds);
81 internal static long CalculateTicks (int days, int hours, int minutes, int seconds, int milliseconds)
83 // there's no overflow checks for hours, minutes, ...
84 // so big hours/minutes values can overflow at some point and change expected values
85 int hrssec = (hours * 3600); // break point at (Int32.MaxValue - 596523)
86 int minsec = (minutes * 60);
87 long t = ((long)(hrssec + minsec + seconds) * 1000L + (long)milliseconds);
90 bool overflow = false;
91 // days is problematic because it can overflow but that overflow can be
92 // "legal" (i.e. temporary) (e.g. if other parameters are negative) or
93 // illegal (e.g. sign change).
95 long td = TicksPerDay * days;
99 // positive days -> total ticks should be lower
100 overflow = (ticks > t);
104 // positive + positive != negative result
109 long td = TicksPerDay * days;
112 // negative + negative != positive result
118 // negative days -> total ticks should be lower
119 overflow = (t > ticks);
124 throw new ArgumentOutOfRangeException (Locale.GetText ("The timespan is too big or too small."));
131 return (int) (_ticks / TicksPerDay);
137 return (int) (_ticks % TicksPerDay / TicksPerHour);
141 public int Milliseconds {
143 return (int) (_ticks % TicksPerSecond / TicksPerMillisecond);
149 return (int) (_ticks % TicksPerHour / TicksPerMinute);
155 return (int) (_ticks % TicksPerMinute / TicksPerSecond);
165 public double TotalDays {
167 return (double) _ticks / TicksPerDay;
171 public double TotalHours {
173 return (double) _ticks / TicksPerHour;
177 public double TotalMilliseconds {
179 return (double) _ticks / TicksPerMillisecond;
183 public double TotalMinutes {
185 return (double) _ticks / TicksPerMinute;
189 public double TotalSeconds {
191 return (double) _ticks / TicksPerSecond;
195 public TimeSpan Add (TimeSpan ts)
199 return new TimeSpan (_ticks + ts.Ticks);
202 catch (OverflowException) {
203 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
207 public static int Compare (TimeSpan t1, TimeSpan t2)
209 if (t1._ticks < t2._ticks)
211 if (t1._ticks > t2._ticks)
216 public int CompareTo (object value)
221 if (!(value is TimeSpan)) {
222 throw new ArgumentException (Locale.GetText ("Argument has to be a TimeSpan."), "value");
225 return Compare (this, (TimeSpan) value);
228 public int CompareTo (TimeSpan value)
230 return Compare (this, value);
233 public bool Equals (TimeSpan obj)
235 return obj._ticks == _ticks;
238 public TimeSpan Duration ()
242 return new TimeSpan (Math.Abs (_ticks));
245 catch (OverflowException) {
246 throw new OverflowException (Locale.GetText (
247 "This TimeSpan value is MinValue so you cannot get the duration."));
251 public override bool Equals (object value)
253 if (!(value is TimeSpan))
256 return _ticks == ((TimeSpan) value)._ticks;
259 public static bool Equals (TimeSpan t1, TimeSpan t2)
261 return t1._ticks == t2._ticks;
264 public static TimeSpan FromDays (double value)
266 return From (value, TicksPerDay);
269 public static TimeSpan FromHours (double value)
271 return From (value, TicksPerHour);
274 public static TimeSpan FromMinutes (double value)
276 return From (value, TicksPerMinute);
279 public static TimeSpan FromSeconds (double value)
281 return From (value, TicksPerSecond);
284 public static TimeSpan FromMilliseconds (double value)
286 return From (value, TicksPerMillisecond);
289 private static TimeSpan From (double value, long tickMultiplicator)
291 if (Double.IsNaN (value))
292 throw new ArgumentException (Locale.GetText ("Value cannot be NaN."), "value");
293 if (Double.IsNegativeInfinity (value) || Double.IsPositiveInfinity (value) ||
294 (value < MinValue.Ticks) || (value > MaxValue.Ticks))
295 throw new OverflowException (Locale.GetText ("Outside range [MinValue,MaxValue]"));
298 value = (value * (tickMultiplicator / TicksPerMillisecond));
301 long val = (long) Math.Round(value);
302 return new TimeSpan (val * TicksPerMillisecond);
305 catch (OverflowException) {
306 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
310 public static TimeSpan FromTicks (long value)
312 return new TimeSpan (value);
315 public override int GetHashCode ()
317 return _ticks.GetHashCode ();
320 public TimeSpan Negate ()
322 if (_ticks == MinValue._ticks)
323 throw new OverflowException (Locale.GetText (
324 "This TimeSpan value is MinValue and cannot be negated."));
325 return new TimeSpan (-_ticks);
328 public static TimeSpan Parse (string s)
331 throw new ArgumentNullException ("s");
334 Parser p = new Parser (s);
338 public static bool TryParse (string s, out TimeSpan result)
341 result = TimeSpan.Zero;
348 result = TimeSpan.Zero;
353 public TimeSpan Subtract (TimeSpan ts)
357 return new TimeSpan (_ticks - ts.Ticks);
360 catch (OverflowException) {
361 throw new OverflowException (Locale.GetText ("Resulting timespan is too big."));
365 public override string ToString ()
367 StringBuilder sb = new StringBuilder (14);
372 // We need to take absolute values of all components.
373 // Can't handle negative timespans by negating the TimeSpan
374 // as a whole. This would lead to an overflow for the
375 // degenerate case "TimeSpan.MinValue.ToString()".
377 sb.Append (Math.Abs (Days));
381 sb.Append (Math.Abs (Hours).ToString ("D2"));
383 sb.Append (Math.Abs (Minutes).ToString ("D2"));
385 sb.Append (Math.Abs (Seconds).ToString ("D2"));
387 int fractional = (int) Math.Abs (_ticks % TicksPerSecond);
388 if (fractional != 0) {
390 sb.Append (fractional.ToString ("D7"));
393 return sb.ToString ();
396 public static TimeSpan operator + (TimeSpan t1, TimeSpan t2)
401 public static bool operator == (TimeSpan t1, TimeSpan t2)
403 return t1._ticks == t2._ticks;
406 public static bool operator > (TimeSpan t1, TimeSpan t2)
408 return t1._ticks > t2._ticks;
411 public static bool operator >= (TimeSpan t1, TimeSpan t2)
413 return t1._ticks >= t2._ticks;
416 public static bool operator != (TimeSpan t1, TimeSpan t2)
418 return t1._ticks != t2._ticks;
421 public static bool operator < (TimeSpan t1, TimeSpan t2)
423 return t1._ticks < t2._ticks;
426 public static bool operator <= (TimeSpan t1, TimeSpan t2)
428 return t1._ticks <= t2._ticks;
431 public static TimeSpan operator - (TimeSpan t1, TimeSpan t2)
433 return t1.Subtract (t2);
436 public static TimeSpan operator - (TimeSpan t)
441 public static TimeSpan operator + (TimeSpan t)
446 // Class Parser implements parser for TimeSpan.Parse
450 private int _cur = 0;
452 private bool formatError;
454 public Parser (string src)
457 _length = _src.Length;
462 return _cur >= _length;
466 // All "Parse" functions throw a FormatException on syntax error.
467 // Their return value is semantic value of the item parsed.
469 // Range checking is spread over three different places:
470 // 1) When parsing "int" values, an exception is thrown immediately
471 // when the value parsed exceeds the maximum value for an int.
472 // 2) An explicit check is built in that checks for hours > 23 and
473 // for minutes and seconds > 59.
474 // 3) Throwing an exceptions for a final TimeSpan value > MaxValue
475 // or < MinValue is left to the TimeSpan constructor called.
477 // Parse zero or more whitespace chars.
478 private void ParseWhiteSpace ()
480 while (!AtEnd && Char.IsWhiteSpace (_src, _cur)) {
485 // Parse optional sign character.
486 private bool ParseSign ()
490 if (!AtEnd && _src[_cur] == '-') {
498 // Parse simple int value
499 private int ParseInt (bool optional)
501 if (optional && AtEnd)
507 while (!AtEnd && Char.IsDigit (_src, _cur)) {
509 res = res * 10 + _src[_cur] - '0';
515 if (!optional && (count == 0))
521 // Parse optional dot
522 private bool ParseOptDot ()
527 if (_src[_cur] == '.') {
534 // Parse optional (LAMESPEC) colon
535 private void ParseOptColon ()
538 if (_src[_cur] == ':')
545 // Parse [1..7] digits, representing fractional seconds (ticks)
546 private long ParseTicks ()
550 bool digitseen = false;
552 while (mag > 0 && !AtEnd && Char.IsDigit (_src, _cur)) {
553 res = res + (_src[_cur] - '0') * mag;
565 public TimeSpan Execute ()
575 // Parse [ws][-][dd.]hh:mm:ss[.ff][ws]
576 // ... but not entirely true as an lonely
577 // integer will be parsed as a number of days
580 days = ParseInt (false);
581 if (ParseOptDot ()) {
582 hours = ParseInt (true);
589 minutes = ParseInt (true);
591 seconds = ParseInt (true);
592 if ( ParseOptDot () ) {
593 ticks = ParseTicks ();
603 // Overflow has presceance over FormatException
604 if (hours > 23 || minutes > 59 || seconds > 59) {
605 throw new OverflowException (
606 Locale.GetText ("Invalid time data."));
608 else if (formatError) {
609 throw new FormatException (
610 Locale.GetText ("Invalid format for TimeSpan.Parse."));
613 long t = TimeSpan.CalculateTicks (days, hours, minutes, seconds, 0);
614 t = checked ((sign) ? (-t - ticks) : (t + ticks));
615 return new TimeSpan (t);