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.
5 using System
.Globalization
;
6 using System
.Runtime
.CompilerServices
;
10 // TimeSpan represents a duration of time. A TimeSpan can be negative
13 // TimeSpan is internally represented as a number of milliseconds. While
14 // this maps well into units of time such as hours and days, any
15 // periods longer than that aren't representable in a nice fashion.
16 // For instance, a month can be between 28 and 31 days, while a year
17 // can contain 365 or 364 days. A decade can have between 1 and 3 leapyears,
18 // depending on when you map the TimeSpan into the calendar. This is why
19 // we do not provide Years() or Months().
21 // Note: System.TimeSpan needs to interop with the WinRT structure
22 // type Windows::Foundation:TimeSpan. These types are currently binary-compatible in
23 // memory so no custom marshalling is required. If at any point the implementation
24 // details of this type should change, or new fields added, we need to remember to add
25 // an appropriate custom ILMarshaler to keep WInRT interop scenarios enabled.
28 public readonly struct TimeSpan
: IComparable
, IComparable
<TimeSpan
>, IEquatable
<TimeSpan
>, IFormattable
, ISpanFormattable
30 public const long TicksPerMillisecond
= 10000;
31 private const double MillisecondsPerTick
= 1.0 / TicksPerMillisecond
;
33 public const long TicksPerSecond
= TicksPerMillisecond
* 1000; // 10,000,000
34 private const double SecondsPerTick
= 1.0 / TicksPerSecond
; // 0.0000001
36 public const long TicksPerMinute
= TicksPerSecond
* 60; // 600,000,000
37 private const double MinutesPerTick
= 1.0 / TicksPerMinute
; // 1.6666666666667e-9
39 public const long TicksPerHour
= TicksPerMinute
* 60; // 36,000,000,000
40 private const double HoursPerTick
= 1.0 / TicksPerHour
; // 2.77777777777777778e-11
42 public const long TicksPerDay
= TicksPerHour
* 24; // 864,000,000,000
43 private const double DaysPerTick
= 1.0 / TicksPerDay
; // 1.1574074074074074074e-12
45 internal const long MaxSeconds
= long.MaxValue
/ TicksPerSecond
;
46 internal const long MinSeconds
= long.MinValue
/ TicksPerSecond
;
48 internal const long MaxMilliSeconds
= long.MaxValue
/ TicksPerMillisecond
;
49 internal const long MinMilliSeconds
= long.MinValue
/ TicksPerMillisecond
;
51 internal const long TicksPerTenthSecond
= TicksPerMillisecond
* 100;
53 public static readonly TimeSpan Zero
= new TimeSpan(0);
55 public static readonly TimeSpan MaxValue
= new TimeSpan(long.MaxValue
);
56 public static readonly TimeSpan MinValue
= new TimeSpan(long.MinValue
);
58 // internal so that DateTime doesn't have to call an extra get
59 // method for some arithmetic operations.
60 internal readonly long _ticks
; // Do not rename (binary serialization)
62 public TimeSpan(long ticks
)
67 public TimeSpan(int hours
, int minutes
, int seconds
)
69 _ticks
= TimeToTicks(hours
, minutes
, seconds
);
72 public TimeSpan(int days
, int hours
, int minutes
, int seconds
)
73 : this(days
, hours
, minutes
, seconds
, 0)
77 public TimeSpan(int days
, int hours
, int minutes
, int seconds
, int milliseconds
)
79 long totalMilliSeconds
= ((long)days
* 3600 * 24 + (long)hours
* 3600 + (long)minutes
* 60 + seconds
) * 1000 + milliseconds
;
80 if (totalMilliSeconds
> MaxMilliSeconds
|| totalMilliSeconds
< MinMilliSeconds
)
81 throw new ArgumentOutOfRangeException(null, SR
.Overflow_TimeSpanTooLong
);
82 _ticks
= (long)totalMilliSeconds
* TicksPerMillisecond
;
85 public long Ticks
=> _ticks
;
87 public int Days
=> (int)(_ticks
/ TicksPerDay
);
89 public int Hours
=> (int)((_ticks
/ TicksPerHour
) % 24);
91 public int Milliseconds
=> (int)((_ticks
/ TicksPerMillisecond
) % 1000);
93 public int Minutes
=> (int)((_ticks
/ TicksPerMinute
) % 60);
95 public int Seconds
=> (int)((_ticks
/ TicksPerSecond
) % 60);
97 public double TotalDays
=> ((double)_ticks
) * DaysPerTick
;
99 public double TotalHours
=> (double)_ticks
* HoursPerTick
;
101 public double TotalMilliseconds
105 double temp
= (double)_ticks
* MillisecondsPerTick
;
106 if (temp
> MaxMilliSeconds
)
107 return (double)MaxMilliSeconds
;
109 if (temp
< MinMilliSeconds
)
110 return (double)MinMilliSeconds
;
116 public double TotalMinutes
=> (double)_ticks
* MinutesPerTick
;
118 public double TotalSeconds
=> (double)_ticks
* SecondsPerTick
;
120 public TimeSpan
Add(TimeSpan ts
)
122 long result
= _ticks
+ ts
._ticks
;
123 // Overflow if signs of operands was identical and result's
124 // sign was opposite.
125 // >> 63 gives the sign bit (either 64 1's or 64 0's).
126 if ((_ticks
>> 63 == ts
._ticks
>> 63) && (_ticks
>> 63 != result
>> 63))
127 throw new OverflowException(SR
.Overflow_TimeSpanTooLong
);
128 return new TimeSpan(result
);
132 // Compares two TimeSpan values, returning an integer that indicates their
135 public static int Compare(TimeSpan t1
, TimeSpan t2
)
137 if (t1
._ticks
> t2
._ticks
) return 1;
138 if (t1
._ticks
< t2
._ticks
) return -1;
142 // Returns a value less than zero if this object
143 public int CompareTo(object? value)
145 if (value == null) return 1;
146 if (!(value is TimeSpan
))
147 throw new ArgumentException(SR
.Arg_MustBeTimeSpan
);
148 long t
= ((TimeSpan
)value)._ticks
;
149 if (_ticks
> t
) return 1;
150 if (_ticks
< t
) return -1;
154 public int CompareTo(TimeSpan
value)
156 long t
= value._ticks
;
157 if (_ticks
> t
) return 1;
158 if (_ticks
< t
) return -1;
162 public static TimeSpan
FromDays(double value)
164 return Interval(value, TicksPerDay
);
167 public TimeSpan
Duration()
169 if (Ticks
== TimeSpan
.MinValue
.Ticks
)
170 throw new OverflowException(SR
.Overflow_Duration
);
171 return new TimeSpan(_ticks
>= 0 ? _ticks
: -_ticks
);
174 public override bool Equals(object? value)
176 if (value is TimeSpan
)
178 return _ticks
== ((TimeSpan
)value)._ticks
;
183 public bool Equals(TimeSpan obj
)
185 return _ticks
== obj
._ticks
;
188 public static bool Equals(TimeSpan t1
, TimeSpan t2
)
190 return t1
._ticks
== t2
._ticks
;
193 public override int GetHashCode()
195 return (int)_ticks ^
(int)(_ticks
>> 32);
198 public static TimeSpan
FromHours(double value)
200 return Interval(value, TicksPerHour
);
203 private static TimeSpan
Interval(double value, double scale
)
205 if (double.IsNaN(value))
206 throw new ArgumentException(SR
.Arg_CannotBeNaN
);
207 double millis
= value * scale
;
208 if ((millis
> long.MaxValue
) || (millis
< long.MinValue
))
209 throw new OverflowException(SR
.Overflow_TimeSpanTooLong
);
210 return new TimeSpan((long)millis
);
213 public static TimeSpan
FromMilliseconds(double value)
215 return Interval(value, TicksPerMillisecond
);
218 public static TimeSpan
FromMinutes(double value)
220 return Interval(value, TicksPerMinute
);
223 public TimeSpan
Negate()
225 if (Ticks
== TimeSpan
.MinValue
.Ticks
)
226 throw new OverflowException(SR
.Overflow_NegateTwosCompNum
);
227 return new TimeSpan(-_ticks
);
230 public static TimeSpan
FromSeconds(double value)
232 return Interval(value, TicksPerSecond
);
235 public TimeSpan
Subtract(TimeSpan ts
)
237 long result
= _ticks
- ts
._ticks
;
238 // Overflow if signs of operands was different and result's
239 // sign was opposite from the first argument's sign.
240 // >> 63 gives the sign bit (either 64 1's or 64 0's).
241 if ((_ticks
>> 63 != ts
._ticks
>> 63) && (_ticks
>> 63 != result
>> 63))
242 throw new OverflowException(SR
.Overflow_TimeSpanTooLong
);
243 return new TimeSpan(result
);
246 public TimeSpan
Multiply(double factor
) => this * factor
;
248 public TimeSpan
Divide(double divisor
) => this / divisor
;
250 public double Divide(TimeSpan ts
) => this / ts
;
252 public static TimeSpan
FromTicks(long value)
254 return new TimeSpan(value);
257 [MethodImpl(MethodImplOptions
.AggressiveInlining
)]
258 internal static long TimeToTicks(int hour
, int minute
, int second
)
260 // totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31,
261 // which is less than 2^44, meaning we won't overflow totalSeconds.
262 long totalSeconds
= (long)hour
* 3600 + (long)minute
* 60 + (long)second
;
263 if (totalSeconds
> MaxSeconds
|| totalSeconds
< MinSeconds
)
264 ThrowHelper
.ThrowArgumentOutOfRange_TimeSpanTooLong();
265 return totalSeconds
* TicksPerSecond
;
268 // See System.Globalization.TimeSpanParse and System.Globalization.TimeSpanFormat
269 #region ParseAndFormat
270 private static void ValidateStyles(TimeSpanStyles style
, string parameterName
)
272 if (style
!= TimeSpanStyles
.None
&& style
!= TimeSpanStyles
.AssumeNegative
)
273 throw new ArgumentException(SR
.Argument_InvalidTimeSpanStyles
, parameterName
);
275 public static TimeSpan
Parse(string s
)
277 if (s
== null) ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.input
);
278 /* Constructs a TimeSpan from a string. Leading and trailing white space characters are allowed. */
279 return TimeSpanParse
.Parse(s
, null);
281 public static TimeSpan
Parse(string input
, IFormatProvider
? formatProvider
)
283 if (input
== null) ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.input
);
284 return TimeSpanParse
.Parse(input
, formatProvider
);
286 public static TimeSpan
Parse(ReadOnlySpan
<char> input
, IFormatProvider
? formatProvider
= null)
288 return TimeSpanParse
.Parse(input
, formatProvider
);
290 public static TimeSpan
ParseExact(string input
, string format
, IFormatProvider
? formatProvider
)
292 if (input
== null) ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.input
);
293 if (format
== null) ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.format
);
294 return TimeSpanParse
.ParseExact(input
, format
, formatProvider
, TimeSpanStyles
.None
);
296 public static TimeSpan
ParseExact(string input
, string[] formats
, IFormatProvider
? formatProvider
)
298 if (input
== null) ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.input
);
299 return TimeSpanParse
.ParseExactMultiple(input
, formats
, formatProvider
, TimeSpanStyles
.None
);
301 public static TimeSpan
ParseExact(string input
, string format
, IFormatProvider
? formatProvider
, TimeSpanStyles styles
)
303 ValidateStyles(styles
, nameof(styles
));
304 if (input
== null) ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.input
);
305 if (format
== null) ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.format
);
306 return TimeSpanParse
.ParseExact(input
, format
, formatProvider
, styles
);
309 public static TimeSpan
ParseExact(ReadOnlySpan
<char> input
, ReadOnlySpan
<char> format
, IFormatProvider
? formatProvider
, TimeSpanStyles styles
= TimeSpanStyles
.None
)
311 ValidateStyles(styles
, nameof(styles
));
312 return TimeSpanParse
.ParseExact(input
, format
, formatProvider
, styles
);
314 public static TimeSpan
ParseExact(string input
, string[] formats
, IFormatProvider
? formatProvider
, TimeSpanStyles styles
)
316 ValidateStyles(styles
, nameof(styles
));
317 if (input
== null) ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.input
);
318 return TimeSpanParse
.ParseExactMultiple(input
, formats
, formatProvider
, styles
);
320 public static TimeSpan
ParseExact(ReadOnlySpan
<char> input
, string[] formats
, IFormatProvider
? formatProvider
, TimeSpanStyles styles
= TimeSpanStyles
.None
)
322 ValidateStyles(styles
, nameof(styles
));
323 return TimeSpanParse
.ParseExactMultiple(input
, formats
, formatProvider
, styles
);
325 public static bool TryParse(string? s
, out TimeSpan result
)
332 return TimeSpanParse
.TryParse(s
, null, out result
);
334 public static bool TryParse(ReadOnlySpan
<char> s
, out TimeSpan result
)
336 return TimeSpanParse
.TryParse(s
, null, out result
);
339 public static bool TryParse(string? input
, IFormatProvider
? formatProvider
, out TimeSpan result
)
346 return TimeSpanParse
.TryParse(input
, formatProvider
, out result
);
348 public static bool TryParse(ReadOnlySpan
<char> input
, IFormatProvider
? formatProvider
, out TimeSpan result
)
350 return TimeSpanParse
.TryParse(input
, formatProvider
, out result
);
352 public static bool TryParseExact(string? input
, string format
, IFormatProvider
? formatProvider
, out TimeSpan result
)
354 if (input
== null || format
== null)
359 return TimeSpanParse
.TryParseExact(input
, format
, formatProvider
, TimeSpanStyles
.None
, out result
);
362 public static bool TryParseExact(ReadOnlySpan
<char> input
, ReadOnlySpan
<char> format
, IFormatProvider
? formatProvider
, out TimeSpan result
)
364 return TimeSpanParse
.TryParseExact(input
, format
, formatProvider
, TimeSpanStyles
.None
, out result
);
366 public static bool TryParseExact(string? input
, string[] formats
, IFormatProvider
? formatProvider
, out TimeSpan result
)
373 return TimeSpanParse
.TryParseExactMultiple(input
, formats
, formatProvider
, TimeSpanStyles
.None
, out result
);
375 public static bool TryParseExact(ReadOnlySpan
<char> input
, string[] formats
, IFormatProvider
? formatProvider
, out TimeSpan result
)
377 return TimeSpanParse
.TryParseExactMultiple(input
, formats
, formatProvider
, TimeSpanStyles
.None
, out result
);
380 public static bool TryParseExact(string? input
, string format
, IFormatProvider
? formatProvider
, TimeSpanStyles styles
, out TimeSpan result
)
382 ValidateStyles(styles
, nameof(styles
));
383 if (input
== null || format
== null)
389 return TimeSpanParse
.TryParseExact(input
, format
, formatProvider
, styles
, out result
);
392 public static bool TryParseExact(ReadOnlySpan
<char> input
, ReadOnlySpan
<char> format
, IFormatProvider
? formatProvider
, TimeSpanStyles styles
, out TimeSpan result
)
394 ValidateStyles(styles
, nameof(styles
));
395 return TimeSpanParse
.TryParseExact(input
, format
, formatProvider
, styles
, out result
);
397 public static bool TryParseExact(string? input
, string[] formats
, IFormatProvider
? formatProvider
, TimeSpanStyles styles
, out TimeSpan result
)
399 ValidateStyles(styles
, nameof(styles
));
405 return TimeSpanParse
.TryParseExactMultiple(input
, formats
, formatProvider
, styles
, out result
);
408 public static bool TryParseExact(ReadOnlySpan
<char> input
, string[] formats
, IFormatProvider
? formatProvider
, TimeSpanStyles styles
, out TimeSpan result
)
410 ValidateStyles(styles
, nameof(styles
));
411 return TimeSpanParse
.TryParseExactMultiple(input
, formats
, formatProvider
, styles
, out result
);
413 public override string ToString()
415 return TimeSpanFormat
.FormatC(this);
417 public string ToString(string? format
)
419 return TimeSpanFormat
.Format(this, format
, null);
421 public string ToString(string? format
, IFormatProvider
? formatProvider
)
423 return TimeSpanFormat
.Format(this, format
, formatProvider
);
426 public bool TryFormat(Span
<char> destination
, out int charsWritten
, ReadOnlySpan
<char> format
= default, IFormatProvider
? formatProvider
= null)
428 return TimeSpanFormat
.TryFormat(this, destination
, out charsWritten
, format
, formatProvider
);
432 public static TimeSpan
operator -(TimeSpan t
)
434 if (t
._ticks
== TimeSpan
.MinValue
._ticks
)
435 throw new OverflowException(SR
.Overflow_NegateTwosCompNum
);
436 return new TimeSpan(-t
._ticks
);
439 public static TimeSpan
operator -(TimeSpan t1
, TimeSpan t2
)
441 return t1
.Subtract(t2
);
444 public static TimeSpan
operator +(TimeSpan t
)
449 public static TimeSpan
operator +(TimeSpan t1
, TimeSpan t2
)
454 public static TimeSpan
operator *(TimeSpan timeSpan
, double factor
)
456 if (double.IsNaN(factor
))
458 throw new ArgumentException(SR
.Arg_CannotBeNaN
, nameof(factor
));
461 // Rounding to the nearest tick is as close to the result we would have with unlimited
462 // precision as possible, and so likely to have the least potential to surprise.
463 double ticks
= Math
.Round(timeSpan
.Ticks
* factor
);
464 if (ticks
> long.MaxValue
|| ticks
< long.MinValue
)
466 throw new OverflowException(SR
.Overflow_TimeSpanTooLong
);
469 return FromTicks((long)ticks
);
472 public static TimeSpan
operator *(double factor
, TimeSpan timeSpan
) => timeSpan
* factor
;
474 public static TimeSpan
operator /(TimeSpan timeSpan
, double divisor
)
476 if (double.IsNaN(divisor
))
478 throw new ArgumentException(SR
.Arg_CannotBeNaN
, nameof(divisor
));
481 double ticks
= Math
.Round(timeSpan
.Ticks
/ divisor
);
482 if (ticks
> long.MaxValue
|| ticks
< long.MinValue
|| double.IsNaN(ticks
))
484 throw new OverflowException(SR
.Overflow_TimeSpanTooLong
);
487 return FromTicks((long)ticks
);
490 // Using floating-point arithmetic directly means that infinities can be returned, which is reasonable
491 // if we consider TimeSpan.FromHours(1) / TimeSpan.Zero asks how many zero-second intervals there are in
492 // an hour for which infinity is the mathematic correct answer. Having TimeSpan.Zero / TimeSpan.Zero return NaN
493 // is perhaps less useful, but no less useful than an exception.
494 public static double operator /(TimeSpan t1
, TimeSpan t2
) => t1
.Ticks
/ (double)t2
.Ticks
;
496 public static bool operator ==(TimeSpan t1
, TimeSpan t2
)
498 return t1
._ticks
== t2
._ticks
;
501 public static bool operator !=(TimeSpan t1
, TimeSpan t2
)
503 return t1
._ticks
!= t2
._ticks
;
506 public static bool operator <(TimeSpan t1
, TimeSpan t2
)
508 return t1
._ticks
< t2
._ticks
;
511 public static bool operator <=(TimeSpan t1
, TimeSpan t2
)
513 return t1
._ticks
<= t2
._ticks
;
516 public static bool operator >(TimeSpan t1
, TimeSpan t2
)
518 return t1
._ticks
> t2
._ticks
;
521 public static bool operator >=(TimeSpan t1
, TimeSpan t2
)
523 return t1
._ticks
>= t2
._ticks
;