TimeSpan.cs 22 KB

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