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