TimeSpan.cs 21 KB

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