1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Text; 6 using System; 7 using System.Runtime; 8 using System.Runtime.CompilerServices; 9 using System.Globalization; 10 11 namespace System 12 { 13 // TimeSpan represents a duration of time. A TimeSpan can be negative 14 // or positive. 15 // 16 // TimeSpan is internally represented as a number of milliseconds. While 17 // this maps well into units of time such as hours and days, any 18 // periods longer than that aren't representable in a nice fashion. 19 // For instance, a month can be between 28 and 31 days, while a year 20 // can contain 365 or 364 days. A decade can have between 1 and 3 leapyears, 21 // depending on when you map the TimeSpan into the calendar. This is why 22 // we do not provide Years() or Months(). 23 // 24 // Note: System.TimeSpan needs to interop with the WinRT structure 25 // type Windows::Foundation:TimeSpan. These types are currently binary-compatible in 26 // memory so no custom marshalling is required. If at any point the implementation 27 // details of this type should change, or new fields added, we need to remember to add 28 // an appropriate custom ILMarshaler to keep WInRT interop scenarios enabled. 29 // 30 [Serializable] 31 public struct TimeSpan : IComparable, IComparable<TimeSpan>, IEquatable<TimeSpan>, IFormattable, ISpanFormattable 32 { 33 public const long TicksPerMillisecond = 10000; 34 private const double MillisecondsPerTick = 1.0 / TicksPerMillisecond; 35 36 public const long TicksPerSecond = TicksPerMillisecond * 1000; // 10,000,000 37 private const double SecondsPerTick = 1.0 / TicksPerSecond; // 0.0001 38 39 public const long TicksPerMinute = TicksPerSecond * 60; // 600,000,000 40 private const double MinutesPerTick = 1.0 / TicksPerMinute; // 1.6666666666667e-9 41 42 public const long TicksPerHour = TicksPerMinute * 60; // 36,000,000,000 43 private const double HoursPerTick = 1.0 / TicksPerHour; // 2.77777777777777778e-11 44 45 public const long TicksPerDay = TicksPerHour * 24; // 864,000,000,000 46 private const double DaysPerTick = 1.0 / TicksPerDay; // 1.1574074074074074074e-12 47 48 private const int MillisPerSecond = 1000; 49 private const int MillisPerMinute = MillisPerSecond * 60; // 60,000 50 private const int MillisPerHour = MillisPerMinute * 60; // 3,600,000 51 private const int MillisPerDay = MillisPerHour * 24; // 86,400,000 52 53 internal const long MaxSeconds = Int64.MaxValue / TicksPerSecond; 54 internal const long MinSeconds = Int64.MinValue / TicksPerSecond; 55 56 internal const long MaxMilliSeconds = Int64.MaxValue / TicksPerMillisecond; 57 internal const long MinMilliSeconds = Int64.MinValue / TicksPerMillisecond; 58 59 internal const long TicksPerTenthSecond = TicksPerMillisecond * 100; 60 61 public static readonly TimeSpan Zero = new TimeSpan(0); 62 63 public static readonly TimeSpan MaxValue = new TimeSpan(Int64.MaxValue); 64 public static readonly TimeSpan MinValue = new TimeSpan(Int64.MinValue); 65 66 // internal so that DateTime doesn't have to call an extra get 67 // method for some arithmetic operations. 68 internal long _ticks; // Do not rename (binary serialization) 69 TimeSpanSystem.TimeSpan70 public TimeSpan(long ticks) 71 { 72 this._ticks = ticks; 73 } 74 TimeSpanSystem.TimeSpan75 public TimeSpan(int hours, int minutes, int seconds) 76 { 77 _ticks = TimeToTicks(hours, minutes, seconds); 78 } 79 TimeSpanSystem.TimeSpan80 public TimeSpan(int days, int hours, int minutes, int seconds) 81 : this(days, hours, minutes, seconds, 0) 82 { 83 } 84 TimeSpanSystem.TimeSpan85 public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds) 86 { 87 Int64 totalMilliSeconds = ((Int64)days * 3600 * 24 + (Int64)hours * 3600 + (Int64)minutes * 60 + seconds) * 1000 + milliseconds; 88 if (totalMilliSeconds > MaxMilliSeconds || totalMilliSeconds < MinMilliSeconds) 89 throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong); 90 _ticks = (long)totalMilliSeconds * TicksPerMillisecond; 91 } 92 93 public long Ticks 94 { 95 get { return _ticks; } 96 } 97 98 public int Days 99 { 100 get { return (int)(_ticks / TicksPerDay); } 101 } 102 103 public int Hours 104 { 105 get { return (int)((_ticks / TicksPerHour) % 24); } 106 } 107 108 public int Milliseconds 109 { 110 get { return (int)((_ticks / TicksPerMillisecond) % 1000); } 111 } 112 113 public int Minutes 114 { 115 get { return (int)((_ticks / TicksPerMinute) % 60); } 116 } 117 118 public int Seconds 119 { 120 get { return (int)((_ticks / TicksPerSecond) % 60); } 121 } 122 123 public double TotalDays 124 { 125 get { return ((double)_ticks) * DaysPerTick; } 126 } 127 128 public double TotalHours 129 { 130 get { return (double)_ticks * HoursPerTick; } 131 } 132 133 public double TotalMilliseconds 134 { 135 get 136 { 137 double temp = (double)_ticks * MillisecondsPerTick; 138 if (temp > MaxMilliSeconds) 139 return (double)MaxMilliSeconds; 140 141 if (temp < MinMilliSeconds) 142 return (double)MinMilliSeconds; 143 144 return temp; 145 } 146 } 147 148 public double TotalMinutes 149 { 150 get { return (double)_ticks * MinutesPerTick; } 151 } 152 153 public double TotalSeconds 154 { 155 get { return (double)_ticks * SecondsPerTick; } 156 } 157 AddSystem.TimeSpan158 public TimeSpan Add(TimeSpan ts) 159 { 160 long result = _ticks + ts._ticks; 161 // Overflow if signs of operands was identical and result's 162 // sign was opposite. 163 // >> 63 gives the sign bit (either 64 1's or 64 0's). 164 if ((_ticks >> 63 == ts._ticks >> 63) && (_ticks >> 63 != result >> 63)) 165 throw new OverflowException(SR.Overflow_TimeSpanTooLong); 166 return new TimeSpan(result); 167 } 168 169 170 // Compares two TimeSpan values, returning an integer that indicates their 171 // relationship. 172 // CompareSystem.TimeSpan173 public static int Compare(TimeSpan t1, TimeSpan t2) 174 { 175 if (t1._ticks > t2._ticks) return 1; 176 if (t1._ticks < t2._ticks) return -1; 177 return 0; 178 } 179 180 // Returns a value less than zero if this object CompareToSystem.TimeSpan181 public int CompareTo(Object value) 182 { 183 if (value == null) return 1; 184 if (!(value is TimeSpan)) 185 throw new ArgumentException(SR.Arg_MustBeTimeSpan); 186 long t = ((TimeSpan)value)._ticks; 187 if (_ticks > t) return 1; 188 if (_ticks < t) return -1; 189 return 0; 190 } 191 CompareToSystem.TimeSpan192 public int CompareTo(TimeSpan value) 193 { 194 long t = value._ticks; 195 if (_ticks > t) return 1; 196 if (_ticks < t) return -1; 197 return 0; 198 } 199 FromDaysSystem.TimeSpan200 public static TimeSpan FromDays(double value) 201 { 202 return Interval(value, MillisPerDay); 203 } 204 DurationSystem.TimeSpan205 public TimeSpan Duration() 206 { 207 if (Ticks == TimeSpan.MinValue.Ticks) 208 throw new OverflowException(SR.Overflow_Duration); 209 return new TimeSpan(_ticks >= 0 ? _ticks : -_ticks); 210 } 211 EqualsSystem.TimeSpan212 public override bool Equals(Object value) 213 { 214 if (value is TimeSpan) 215 { 216 return _ticks == ((TimeSpan)value)._ticks; 217 } 218 return false; 219 } 220 EqualsSystem.TimeSpan221 public bool Equals(TimeSpan obj) 222 { 223 return _ticks == obj._ticks; 224 } 225 EqualsSystem.TimeSpan226 public static bool Equals(TimeSpan t1, TimeSpan t2) 227 { 228 return t1._ticks == t2._ticks; 229 } 230 GetHashCodeSystem.TimeSpan231 public override int GetHashCode() 232 { 233 return (int)_ticks ^ (int)(_ticks >> 32); 234 } 235 FromHoursSystem.TimeSpan236 public static TimeSpan FromHours(double value) 237 { 238 return Interval(value, MillisPerHour); 239 } 240 IntervalSystem.TimeSpan241 private static TimeSpan Interval(double value, int scale) 242 { 243 if (Double.IsNaN(value)) 244 throw new ArgumentException(SR.Arg_CannotBeNaN); 245 double tmp = value * scale; 246 double millis = tmp + (value >= 0 ? 0.5 : -0.5); 247 if ((millis > Int64.MaxValue / TicksPerMillisecond) || (millis < Int64.MinValue / TicksPerMillisecond)) 248 throw new OverflowException(SR.Overflow_TimeSpanTooLong); 249 return new TimeSpan((long)millis * TicksPerMillisecond); 250 } 251 FromMillisecondsSystem.TimeSpan252 public static TimeSpan FromMilliseconds(double value) 253 { 254 return Interval(value, 1); 255 } 256 FromMinutesSystem.TimeSpan257 public static TimeSpan FromMinutes(double value) 258 { 259 return Interval(value, MillisPerMinute); 260 } 261 NegateSystem.TimeSpan262 public TimeSpan Negate() 263 { 264 if (Ticks == TimeSpan.MinValue.Ticks) 265 throw new OverflowException(SR.Overflow_NegateTwosCompNum); 266 return new TimeSpan(-_ticks); 267 } 268 FromSecondsSystem.TimeSpan269 public static TimeSpan FromSeconds(double value) 270 { 271 return Interval(value, MillisPerSecond); 272 } 273 SubtractSystem.TimeSpan274 public TimeSpan Subtract(TimeSpan ts) 275 { 276 long result = _ticks - ts._ticks; 277 // Overflow if signs of operands was different and result's 278 // sign was opposite from the first argument's sign. 279 // >> 63 gives the sign bit (either 64 1's or 64 0's). 280 if ((_ticks >> 63 != ts._ticks >> 63) && (_ticks >> 63 != result >> 63)) 281 throw new OverflowException(SR.Overflow_TimeSpanTooLong); 282 return new TimeSpan(result); 283 } 284 MultiplySystem.TimeSpan285 public TimeSpan Multiply(double factor) => this * factor; 286 DivideSystem.TimeSpan287 public TimeSpan Divide(double divisor) => this / divisor; 288 289 public double Divide(TimeSpan ts) => this / ts; 290 FromTicksSystem.TimeSpan291 public static TimeSpan FromTicks(long value) 292 { 293 return new TimeSpan(value); 294 } 295 TimeToTicksSystem.TimeSpan296 internal static long TimeToTicks(int hour, int minute, int second) 297 { 298 // totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31, 299 // which is less than 2^44, meaning we won't overflow totalSeconds. 300 long totalSeconds = (long)hour * 3600 + (long)minute * 60 + (long)second; 301 if (totalSeconds > MaxSeconds || totalSeconds < MinSeconds) 302 throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong); 303 return totalSeconds * TicksPerSecond; 304 } 305 306 // See System.Globalization.TimeSpanParse and System.Globalization.TimeSpanFormat 307 #region ParseAndFormat ValidateStylesSystem.TimeSpan308 private static void ValidateStyles(TimeSpanStyles style, String parameterName) 309 { 310 if (style != TimeSpanStyles.None && style != TimeSpanStyles.AssumeNegative) 311 throw new ArgumentException(SR.Argument_InvalidTimeSpanStyles, parameterName); 312 } ParseSystem.TimeSpan313 public static TimeSpan Parse(String s) 314 { 315 if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); 316 /* Constructs a TimeSpan from a string. Leading and trailing white space characters are allowed. */ 317 return TimeSpanParse.Parse(s, null); 318 } ParseSystem.TimeSpan319 public static TimeSpan Parse(String input, IFormatProvider formatProvider) 320 { 321 if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); 322 return TimeSpanParse.Parse(input, formatProvider); 323 } ParseSystem.TimeSpan324 public static TimeSpan Parse(ReadOnlySpan<char> input, IFormatProvider formatProvider = null) 325 { 326 return TimeSpanParse.Parse(input, formatProvider); 327 } ParseExactSystem.TimeSpan328 public static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider) 329 { 330 if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); 331 if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); 332 return TimeSpanParse.ParseExact(input, format, formatProvider, TimeSpanStyles.None); 333 } ParseExactSystem.TimeSpan334 public static TimeSpan ParseExact(String input, String[] formats, IFormatProvider formatProvider) 335 { 336 if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); 337 return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None); 338 } ParseExactSystem.TimeSpan339 public static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles) 340 { 341 ValidateStyles(styles, nameof(styles)); 342 if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); 343 if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); 344 return TimeSpanParse.ParseExact(input, format, formatProvider, styles); 345 } 346 ParseExactSystem.TimeSpan347 public static TimeSpan ParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None) 348 { 349 ValidateStyles(styles, nameof(styles)); 350 return TimeSpanParse.ParseExact(input, format, formatProvider, styles); 351 } ParseExactSystem.TimeSpan352 public static TimeSpan ParseExact(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles) 353 { 354 ValidateStyles(styles, nameof(styles)); 355 if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); 356 return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles); 357 } ParseExactSystem.TimeSpan358 public static TimeSpan ParseExact(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None) 359 { 360 ValidateStyles(styles, nameof(styles)); 361 return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles); 362 } TryParseSystem.TimeSpan363 public static Boolean TryParse(String s, out TimeSpan result) 364 { 365 if (s == null) 366 { 367 result = default(TimeSpan); 368 return false; 369 } 370 return TimeSpanParse.TryParse(s, null, out result); 371 } TryParseSystem.TimeSpan372 public static bool TryParse(ReadOnlySpan<char> s, out TimeSpan result) 373 { 374 return TimeSpanParse.TryParse(s, null, out result); 375 } 376 TryParseSystem.TimeSpan377 public static Boolean TryParse(String input, IFormatProvider formatProvider, out TimeSpan result) 378 { 379 if (input == null) 380 { 381 result = default(TimeSpan); 382 return false; 383 } 384 return TimeSpanParse.TryParse(input, formatProvider, out result); 385 } TryParseSystem.TimeSpan386 public static bool TryParse(ReadOnlySpan<char> input, IFormatProvider formatProvider, out TimeSpan result) 387 { 388 return TimeSpanParse.TryParse(input, formatProvider, out result); 389 } TryParseExactSystem.TimeSpan390 public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, out TimeSpan result) 391 { 392 if (input == null || format == null) 393 { 394 result = default; 395 return false; 396 } 397 return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result); 398 } 399 TryParseExactSystem.TimeSpan400 public static bool TryParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format, IFormatProvider formatProvider, out TimeSpan result) 401 { 402 return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result); 403 } TryParseExactSystem.TimeSpan404 public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, out TimeSpan result) 405 { 406 if (input == null) 407 { 408 result = default(TimeSpan); 409 return false; 410 } 411 return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result); 412 } TryParseExactSystem.TimeSpan413 public static bool TryParseExact(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, out TimeSpan result) 414 { 415 return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result); 416 } 417 TryParseExactSystem.TimeSpan418 public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) 419 { 420 ValidateStyles(styles, nameof(styles)); 421 if (input == null || format == null) 422 { 423 result = default; 424 return false; 425 } 426 427 return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result); 428 } 429 TryParseExactSystem.TimeSpan430 public static bool TryParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) 431 { 432 ValidateStyles(styles, nameof(styles)); 433 return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result); 434 } TryParseExactSystem.TimeSpan435 public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) 436 { 437 ValidateStyles(styles, nameof(styles)); 438 if (input == null) 439 { 440 result = default(TimeSpan); 441 return false; 442 } 443 return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result); 444 } 445 TryParseExactSystem.TimeSpan446 public static bool TryParseExact(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) 447 { 448 ValidateStyles(styles, nameof(styles)); 449 return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result); 450 } ToStringSystem.TimeSpan451 public override String ToString() 452 { 453 return TimeSpanFormat.Format(this, null, null); 454 } ToStringSystem.TimeSpan455 public String ToString(String format) 456 { 457 return TimeSpanFormat.Format(this, format, null); 458 } ToStringSystem.TimeSpan459 public String ToString(String format, IFormatProvider formatProvider) 460 { 461 return TimeSpanFormat.Format(this, format, formatProvider); 462 } 463 TryFormatSystem.TimeSpan464 public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider formatProvider = null) 465 { 466 return TimeSpanFormat.TryFormat(this, destination, out charsWritten, format, formatProvider); 467 } 468 #endregion 469 operator -System.TimeSpan470 public static TimeSpan operator -(TimeSpan t) 471 { 472 if (t._ticks == TimeSpan.MinValue._ticks) 473 throw new OverflowException(SR.Overflow_NegateTwosCompNum); 474 return new TimeSpan(-t._ticks); 475 } 476 operator -System.TimeSpan477 public static TimeSpan operator -(TimeSpan t1, TimeSpan t2) 478 { 479 return t1.Subtract(t2); 480 } 481 operator +System.TimeSpan482 public static TimeSpan operator +(TimeSpan t) 483 { 484 return t; 485 } 486 operator +System.TimeSpan487 public static TimeSpan operator +(TimeSpan t1, TimeSpan t2) 488 { 489 return t1.Add(t2); 490 } 491 operator *System.TimeSpan492 public static TimeSpan operator *(TimeSpan timeSpan, double factor) 493 { 494 if (double.IsNaN(factor)) 495 { 496 throw new ArgumentException(SR.Arg_CannotBeNaN, nameof(factor)); 497 } 498 499 // Rounding to the nearest tick is as close to the result we would have with unlimited 500 // precision as possible, and so likely to have the least potential to surprise. 501 double ticks = Math.Round(timeSpan.Ticks * factor); 502 if (ticks > long.MaxValue | ticks < long.MinValue) 503 { 504 throw new OverflowException(SR.Overflow_TimeSpanTooLong); 505 } 506 507 return FromTicks((long)ticks); 508 } 509 operator *System.TimeSpan510 public static TimeSpan operator *(double factor, TimeSpan timeSpan) => timeSpan * factor; 511 operator /System.TimeSpan512 public static TimeSpan operator /(TimeSpan timeSpan, double divisor) 513 { 514 if (double.IsNaN(divisor)) 515 { 516 throw new ArgumentException(SR.Arg_CannotBeNaN, nameof(divisor)); 517 } 518 519 double ticks = Math.Round(timeSpan.Ticks / divisor); 520 if (ticks > long.MaxValue | ticks < long.MinValue || double.IsNaN(ticks)) 521 { 522 throw new OverflowException(SR.Overflow_TimeSpanTooLong); 523 } 524 525 return FromTicks((long)ticks); 526 } 527 528 // Using floating-point arithmetic directly means that infinities can be returned, which is reasonable 529 // if we consider TimeSpan.FromHours(1) / TimeSpan.Zero asks how many zero-second intervals there are in 530 // an hour for which infinity is the mathematic correct answer. Having TimeSpan.Zero / TimeSpan.Zero return NaN 531 // is perhaps less useful, but no less useful than an exception. operator /System.TimeSpan532 public static double operator /(TimeSpan t1, TimeSpan t2) => t1.Ticks / (double)t2.Ticks; 533 operator ==System.TimeSpan534 public static bool operator ==(TimeSpan t1, TimeSpan t2) 535 { 536 return t1._ticks == t2._ticks; 537 } 538 operator !=System.TimeSpan539 public static bool operator !=(TimeSpan t1, TimeSpan t2) 540 { 541 return t1._ticks != t2._ticks; 542 } 543 operator <System.TimeSpan544 public static bool operator <(TimeSpan t1, TimeSpan t2) 545 { 546 return t1._ticks < t2._ticks; 547 } 548 operator <=System.TimeSpan549 public static bool operator <=(TimeSpan t1, TimeSpan t2) 550 { 551 return t1._ticks <= t2._ticks; 552 } 553 operator >System.TimeSpan554 public static bool operator >(TimeSpan t1, TimeSpan t2) 555 { 556 return t1._ticks > t2._ticks; 557 } 558 operator >=System.TimeSpan559 public static bool operator >=(TimeSpan t1, TimeSpan t2) 560 { 561 return t1._ticks >= t2._ticks; 562 } 563 } 564 } 565