TimeSpan.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  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. using System.Globalization;
  5. using System.Runtime.CompilerServices;
  6. namespace System
  7. {
  8. // TimeSpan represents a duration of time. A TimeSpan can be negative
  9. // or positive.
  10. //
  11. // TimeSpan is internally represented as a number of milliseconds. While
  12. // this maps well into units of time such as hours and days, any
  13. // periods longer than that aren't representable in a nice fashion.
  14. // For instance, a month can be between 28 and 31 days, while a year
  15. // can contain 365 or 364 days. A decade can have between 1 and 3 leapyears,
  16. // depending on when you map the TimeSpan into the calendar. This is why
  17. // we do not provide Years() or Months().
  18. //
  19. // Note: System.TimeSpan needs to interop with the WinRT structure
  20. // type Windows::Foundation:TimeSpan. These types are currently binary-compatible in
  21. // memory so no custom marshalling is required. If at any point the implementation
  22. // details of this type should change, or new fields added, we need to remember to add
  23. // an appropriate custom ILMarshaler to keep WInRT interop scenarios enabled.
  24. //
  25. [Serializable]
  26. public readonly struct TimeSpan : IComparable, IComparable<TimeSpan>, IEquatable<TimeSpan>, IFormattable, ISpanFormattable
  27. {
  28. public const long TicksPerMillisecond = 10000;
  29. public const long TicksPerSecond = TicksPerMillisecond * 1000; // 10,000,000
  30. public const long TicksPerMinute = TicksPerSecond * 60; // 600,000,000
  31. public const long TicksPerHour = TicksPerMinute * 60; // 36,000,000,000
  32. public const long TicksPerDay = TicksPerHour * 24; // 864,000,000,000
  33. internal const long MaxSeconds = long.MaxValue / TicksPerSecond;
  34. internal const long MinSeconds = long.MinValue / TicksPerSecond;
  35. internal const long MaxMilliSeconds = long.MaxValue / TicksPerMillisecond;
  36. internal const long MinMilliSeconds = long.MinValue / TicksPerMillisecond;
  37. internal const long TicksPerTenthSecond = TicksPerMillisecond * 100;
  38. public static readonly TimeSpan Zero = new TimeSpan(0);
  39. public static readonly TimeSpan MaxValue = new TimeSpan(long.MaxValue);
  40. public static readonly TimeSpan MinValue = new TimeSpan(long.MinValue);
  41. // internal so that DateTime doesn't have to call an extra get
  42. // method for some arithmetic operations.
  43. internal readonly long _ticks; // Do not rename (binary serialization)
  44. public TimeSpan(long ticks)
  45. {
  46. this._ticks = ticks;
  47. }
  48. public TimeSpan(int hours, int minutes, int seconds)
  49. {
  50. _ticks = TimeToTicks(hours, minutes, seconds);
  51. }
  52. public TimeSpan(int days, int hours, int minutes, int seconds)
  53. : this(days, hours, minutes, seconds, 0)
  54. {
  55. }
  56. public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds)
  57. {
  58. long totalMilliSeconds = ((long)days * 3600 * 24 + (long)hours * 3600 + (long)minutes * 60 + seconds) * 1000 + milliseconds;
  59. if (totalMilliSeconds > MaxMilliSeconds || totalMilliSeconds < MinMilliSeconds)
  60. throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong);
  61. _ticks = (long)totalMilliSeconds * TicksPerMillisecond;
  62. }
  63. public long Ticks => _ticks;
  64. public int Days => (int)(_ticks / TicksPerDay);
  65. public int Hours => (int)((_ticks / TicksPerHour) % 24);
  66. public int Milliseconds => (int)((_ticks / TicksPerMillisecond) % 1000);
  67. public int Minutes => (int)((_ticks / TicksPerMinute) % 60);
  68. public int Seconds => (int)((_ticks / TicksPerSecond) % 60);
  69. public double TotalDays => ((double)_ticks) / TicksPerDay;
  70. public double TotalHours => (double)_ticks / TicksPerHour;
  71. public double TotalMilliseconds
  72. {
  73. get
  74. {
  75. double temp = (double)_ticks / TicksPerMillisecond;
  76. if (temp > MaxMilliSeconds)
  77. return (double)MaxMilliSeconds;
  78. if (temp < MinMilliSeconds)
  79. return (double)MinMilliSeconds;
  80. return temp;
  81. }
  82. }
  83. public double TotalMinutes => (double)_ticks / TicksPerMinute;
  84. public double TotalSeconds => (double)_ticks / TicksPerSecond;
  85. public TimeSpan Add(TimeSpan ts)
  86. {
  87. long result = _ticks + ts._ticks;
  88. // Overflow if signs of operands was identical and result's
  89. // sign was opposite.
  90. // >> 63 gives the sign bit (either 64 1's or 64 0's).
  91. if ((_ticks >> 63 == ts._ticks >> 63) && (_ticks >> 63 != result >> 63))
  92. throw new OverflowException(SR.Overflow_TimeSpanTooLong);
  93. return new TimeSpan(result);
  94. }
  95. // Compares two TimeSpan values, returning an integer that indicates their
  96. // relationship.
  97. //
  98. public static int Compare(TimeSpan t1, TimeSpan t2)
  99. {
  100. if (t1._ticks > t2._ticks) return 1;
  101. if (t1._ticks < t2._ticks) return -1;
  102. return 0;
  103. }
  104. // Returns a value less than zero if this object
  105. public int CompareTo(object? value)
  106. {
  107. if (value == null) return 1;
  108. if (!(value is TimeSpan))
  109. throw new ArgumentException(SR.Arg_MustBeTimeSpan);
  110. long t = ((TimeSpan)value)._ticks;
  111. if (_ticks > t) return 1;
  112. if (_ticks < t) return -1;
  113. return 0;
  114. }
  115. public int CompareTo(TimeSpan value)
  116. {
  117. long t = value._ticks;
  118. if (_ticks > t) return 1;
  119. if (_ticks < t) return -1;
  120. return 0;
  121. }
  122. public static TimeSpan FromDays(double value)
  123. {
  124. return Interval(value, TicksPerDay);
  125. }
  126. public TimeSpan Duration()
  127. {
  128. if (Ticks == TimeSpan.MinValue.Ticks)
  129. throw new OverflowException(SR.Overflow_Duration);
  130. return new TimeSpan(_ticks >= 0 ? _ticks : -_ticks);
  131. }
  132. public override bool Equals(object? value)
  133. {
  134. if (value is TimeSpan)
  135. {
  136. return _ticks == ((TimeSpan)value)._ticks;
  137. }
  138. return false;
  139. }
  140. public bool Equals(TimeSpan obj)
  141. {
  142. return _ticks == obj._ticks;
  143. }
  144. public static bool Equals(TimeSpan t1, TimeSpan t2)
  145. {
  146. return t1._ticks == t2._ticks;
  147. }
  148. public override int GetHashCode()
  149. {
  150. return (int)_ticks ^ (int)(_ticks >> 32);
  151. }
  152. public static TimeSpan FromHours(double value)
  153. {
  154. return Interval(value, TicksPerHour);
  155. }
  156. private static TimeSpan Interval(double value, double scale)
  157. {
  158. if (double.IsNaN(value))
  159. throw new ArgumentException(SR.Arg_CannotBeNaN);
  160. double ticks = value * scale;
  161. if ((ticks > long.MaxValue) || (ticks < long.MinValue))
  162. throw new OverflowException(SR.Overflow_TimeSpanTooLong);
  163. return new TimeSpan((long)ticks);
  164. }
  165. public static TimeSpan FromMilliseconds(double value)
  166. {
  167. return Interval(value, TicksPerMillisecond);
  168. }
  169. public static TimeSpan FromMinutes(double value)
  170. {
  171. return Interval(value, TicksPerMinute);
  172. }
  173. public TimeSpan Negate()
  174. {
  175. if (Ticks == TimeSpan.MinValue.Ticks)
  176. throw new OverflowException(SR.Overflow_NegateTwosCompNum);
  177. return new TimeSpan(-_ticks);
  178. }
  179. public static TimeSpan FromSeconds(double value)
  180. {
  181. return Interval(value, TicksPerSecond);
  182. }
  183. public TimeSpan Subtract(TimeSpan ts)
  184. {
  185. long result = _ticks - ts._ticks;
  186. // Overflow if signs of operands was different and result's
  187. // sign was opposite from the first argument's sign.
  188. // >> 63 gives the sign bit (either 64 1's or 64 0's).
  189. if ((_ticks >> 63 != ts._ticks >> 63) && (_ticks >> 63 != result >> 63))
  190. throw new OverflowException(SR.Overflow_TimeSpanTooLong);
  191. return new TimeSpan(result);
  192. }
  193. public TimeSpan Multiply(double factor) => this * factor;
  194. public TimeSpan Divide(double divisor) => this / divisor;
  195. public double Divide(TimeSpan ts) => this / ts;
  196. public static TimeSpan FromTicks(long value)
  197. {
  198. return new TimeSpan(value);
  199. }
  200. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  201. internal static long TimeToTicks(int hour, int minute, int second)
  202. {
  203. // totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31,
  204. // which is less than 2^44, meaning we won't overflow totalSeconds.
  205. long totalSeconds = (long)hour * 3600 + (long)minute * 60 + (long)second;
  206. if (totalSeconds > MaxSeconds || totalSeconds < MinSeconds)
  207. ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
  208. return totalSeconds * TicksPerSecond;
  209. }
  210. // See System.Globalization.TimeSpanParse and System.Globalization.TimeSpanFormat
  211. #region ParseAndFormat
  212. private static void ValidateStyles(TimeSpanStyles style, string parameterName)
  213. {
  214. if (style != TimeSpanStyles.None && style != TimeSpanStyles.AssumeNegative)
  215. throw new ArgumentException(SR.Argument_InvalidTimeSpanStyles, parameterName);
  216. }
  217. public static TimeSpan Parse(string s)
  218. {
  219. if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
  220. /* Constructs a TimeSpan from a string. Leading and trailing white space characters are allowed. */
  221. return TimeSpanParse.Parse(s, null);
  222. }
  223. public static TimeSpan Parse(string input, IFormatProvider? formatProvider)
  224. {
  225. if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
  226. return TimeSpanParse.Parse(input, formatProvider);
  227. }
  228. public static TimeSpan Parse(ReadOnlySpan<char> input, IFormatProvider? formatProvider = null)
  229. {
  230. return TimeSpanParse.Parse(input, formatProvider);
  231. }
  232. public static TimeSpan ParseExact(string input, string format, IFormatProvider? formatProvider)
  233. {
  234. if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
  235. if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format);
  236. return TimeSpanParse.ParseExact(input, format, formatProvider, TimeSpanStyles.None);
  237. }
  238. public static TimeSpan ParseExact(string input, string[] formats, IFormatProvider? formatProvider)
  239. {
  240. if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
  241. return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None);
  242. }
  243. public static TimeSpan ParseExact(string input, string format, IFormatProvider? formatProvider, TimeSpanStyles styles)
  244. {
  245. ValidateStyles(styles, nameof(styles));
  246. if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
  247. if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format);
  248. return TimeSpanParse.ParseExact(input, format, formatProvider, styles);
  249. }
  250. public static TimeSpan ParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format, IFormatProvider? formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
  251. {
  252. ValidateStyles(styles, nameof(styles));
  253. return TimeSpanParse.ParseExact(input, format, formatProvider, styles);
  254. }
  255. public static TimeSpan ParseExact(string input, string[] formats, IFormatProvider? formatProvider, TimeSpanStyles styles)
  256. {
  257. ValidateStyles(styles, nameof(styles));
  258. if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
  259. return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles);
  260. }
  261. public static TimeSpan ParseExact(ReadOnlySpan<char> input, string[] formats, IFormatProvider? formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
  262. {
  263. ValidateStyles(styles, nameof(styles));
  264. return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles);
  265. }
  266. public static bool TryParse(string? s, out TimeSpan result)
  267. {
  268. if (s == null)
  269. {
  270. result = default;
  271. return false;
  272. }
  273. return TimeSpanParse.TryParse(s, null, out result);
  274. }
  275. public static bool TryParse(ReadOnlySpan<char> s, out TimeSpan result)
  276. {
  277. return TimeSpanParse.TryParse(s, null, out result);
  278. }
  279. public static bool TryParse(string? input, IFormatProvider? formatProvider, out TimeSpan result)
  280. {
  281. if (input == null)
  282. {
  283. result = default;
  284. return false;
  285. }
  286. return TimeSpanParse.TryParse(input, formatProvider, out result);
  287. }
  288. public static bool TryParse(ReadOnlySpan<char> input, IFormatProvider? formatProvider, out TimeSpan result)
  289. {
  290. return TimeSpanParse.TryParse(input, formatProvider, out result);
  291. }
  292. public static bool TryParseExact(string? input, string format, IFormatProvider? formatProvider, out TimeSpan result)
  293. {
  294. if (input == null || format == null)
  295. {
  296. result = default;
  297. return false;
  298. }
  299. return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result);
  300. }
  301. public static bool TryParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format, IFormatProvider? formatProvider, out TimeSpan result)
  302. {
  303. return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result);
  304. }
  305. public static bool TryParseExact(string? input, string[] formats, IFormatProvider? formatProvider, out TimeSpan result)
  306. {
  307. if (input == null)
  308. {
  309. result = default;
  310. return false;
  311. }
  312. return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result);
  313. }
  314. public static bool TryParseExact(ReadOnlySpan<char> input, string[] formats, IFormatProvider? formatProvider, out TimeSpan result)
  315. {
  316. return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result);
  317. }
  318. public static bool TryParseExact(string? input, string format, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
  319. {
  320. ValidateStyles(styles, nameof(styles));
  321. if (input == null || format == null)
  322. {
  323. result = default;
  324. return false;
  325. }
  326. return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result);
  327. }
  328. public static bool TryParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
  329. {
  330. ValidateStyles(styles, nameof(styles));
  331. return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result);
  332. }
  333. public static bool TryParseExact(string? input, string[] formats, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
  334. {
  335. ValidateStyles(styles, nameof(styles));
  336. if (input == null)
  337. {
  338. result = default;
  339. return false;
  340. }
  341. return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result);
  342. }
  343. public static bool TryParseExact(ReadOnlySpan<char> input, string[] formats, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
  344. {
  345. ValidateStyles(styles, nameof(styles));
  346. return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result);
  347. }
  348. public override string ToString()
  349. {
  350. return TimeSpanFormat.FormatC(this);
  351. }
  352. public string ToString(string? format)
  353. {
  354. return TimeSpanFormat.Format(this, format, null);
  355. }
  356. public string ToString(string? format, IFormatProvider? formatProvider)
  357. {
  358. return TimeSpanFormat.Format(this, format, formatProvider);
  359. }
  360. public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? formatProvider = null)
  361. {
  362. return TimeSpanFormat.TryFormat(this, destination, out charsWritten, format, formatProvider);
  363. }
  364. #endregion
  365. public static TimeSpan operator -(TimeSpan t)
  366. {
  367. if (t._ticks == TimeSpan.MinValue._ticks)
  368. throw new OverflowException(SR.Overflow_NegateTwosCompNum);
  369. return new TimeSpan(-t._ticks);
  370. }
  371. public static TimeSpan operator -(TimeSpan t1, TimeSpan t2) => t1.Subtract(t2);
  372. public static TimeSpan operator +(TimeSpan t) => t;
  373. public static TimeSpan operator +(TimeSpan t1, TimeSpan t2) => t1.Add(t2);
  374. public static TimeSpan operator *(TimeSpan timeSpan, double factor)
  375. {
  376. if (double.IsNaN(factor))
  377. {
  378. throw new ArgumentException(SR.Arg_CannotBeNaN, nameof(factor));
  379. }
  380. // Rounding to the nearest tick is as close to the result we would have with unlimited
  381. // precision as possible, and so likely to have the least potential to surprise.
  382. double ticks = Math.Round(timeSpan.Ticks * factor);
  383. if (ticks > long.MaxValue || ticks < long.MinValue)
  384. {
  385. throw new OverflowException(SR.Overflow_TimeSpanTooLong);
  386. }
  387. return FromTicks((long)ticks);
  388. }
  389. public static TimeSpan operator *(double factor, TimeSpan timeSpan) => timeSpan * factor;
  390. public static TimeSpan operator /(TimeSpan timeSpan, double divisor)
  391. {
  392. if (double.IsNaN(divisor))
  393. {
  394. throw new ArgumentException(SR.Arg_CannotBeNaN, nameof(divisor));
  395. }
  396. double ticks = Math.Round(timeSpan.Ticks / divisor);
  397. if (ticks > long.MaxValue || ticks < long.MinValue || double.IsNaN(ticks))
  398. {
  399. throw new OverflowException(SR.Overflow_TimeSpanTooLong);
  400. }
  401. return FromTicks((long)ticks);
  402. }
  403. // Using floating-point arithmetic directly means that infinities can be returned, which is reasonable
  404. // if we consider TimeSpan.FromHours(1) / TimeSpan.Zero asks how many zero-second intervals there are in
  405. // an hour for which infinity is the mathematic correct answer. Having TimeSpan.Zero / TimeSpan.Zero return NaN
  406. // is perhaps less useful, but no less useful than an exception.
  407. public static double operator /(TimeSpan t1, TimeSpan t2) => t1.Ticks / (double)t2.Ticks;
  408. public static bool operator ==(TimeSpan t1, TimeSpan t2) => t1._ticks == t2._ticks;
  409. public static bool operator !=(TimeSpan t1, TimeSpan t2) => t1._ticks != t2._ticks;
  410. public static bool operator <(TimeSpan t1, TimeSpan t2) => t1._ticks < t2._ticks;
  411. public static bool operator <=(TimeSpan t1, TimeSpan t2) => t1._ticks <= t2._ticks;
  412. public static bool operator >(TimeSpan t1, TimeSpan t2) => t1._ticks > t2._ticks;
  413. public static bool operator >=(TimeSpan t1, TimeSpan t2) => t1._ticks >= t2._ticks;
  414. }
  415. }