summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTommy Sørbråten <tommysor@gmail.com>2024-03-02 02:27:17 +0100
committerGitHub <noreply@github.com>2024-03-01 17:27:17 -0800
commit6fa046837910fcc7a7dbd64be4b1ad98f9ea5db3 (patch)
treed3d0512d5f5e6db145cacf12d80232a8debd1425
parenta604763dc82d382e47f5be9fad0b672dc3b3ad72 (diff)
New TimeSpan.From overloads which take integers (#98633)
* Add FromX overloads Implementation copied from ctor TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds, int microseconds) * Handle overflow and underflow * Remove duplicate test data * Improve naming and readability * Add tripple-slash documentation * Remove whitespace * Ensure unrelated test hits same overload as before * Remove extra whitespace * Clearify test data * Use BigMul for multiplication * Add "returns" tripple-slash documentation * Optimize the single argument methods * Optimize FromMilliseconds(long, long) * Optimize remaining multi argument methods
-rw-r--r--src/libraries/System.Linq.Expressions/tests/DebugViewTests.cs2
-rw-r--r--src/libraries/System.Private.CoreLib/src/System/Math.cs10
-rw-r--r--src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs189
-rw-r--r--src/libraries/System.Runtime/ref/System.Runtime.cs10
-rw-r--r--src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs406
5 files changed, 616 insertions, 1 deletions
diff --git a/src/libraries/System.Linq.Expressions/tests/DebugViewTests.cs b/src/libraries/System.Linq.Expressions/tests/DebugViewTests.cs
index 05f74015a269..4d9167f24940 100644
--- a/src/libraries/System.Linq.Expressions/tests/DebugViewTests.cs
+++ b/src/libraries/System.Linq.Expressions/tests/DebugViewTests.cs
@@ -316,7 +316,7 @@ namespace System.Linq.Expressions.Tests
Check(".Call $x.ToString()", Expression.Call(x, typeof(int).GetMethod("ToString", Type.EmptyTypes)));
Check(".Call $s.Substring($x)", Expression.Call(s, typeof(string).GetMethod("Substring", new[] { typeof(int) }), x));
Check(".Call $s.Substring(\\r\\n $x,\\r\\n $y)", Expression.Call(s, typeof(string).GetMethod("Substring", new[] { typeof(int), typeof(int) }), x, y));
- Check(".Call System.TimeSpan.FromSeconds($d)", Expression.Call(null, typeof(TimeSpan).GetMethod("FromSeconds", new[] { typeof(int) }), d));
+ Check(".Call System.TimeSpan.FromSeconds($d)", Expression.Call(null, typeof(TimeSpan).GetMethod("FromSeconds", new[] { typeof(double) }), d));
}
[Fact]
diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs
index 1d1c50a4e2b5..60a638198f8f 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Math.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs
@@ -235,6 +235,16 @@ namespace System
return (long)high - ((a >> 63) & b) - ((b >> 63) & a);
}
+ /// <summary>Produces the full product of two 64-bit numbers.</summary>
+ /// <param name="a">The first number to multiply.</param>
+ /// <param name="b">The second number to multiply.</param>
+ /// <returns>The full product of the specified numbers.</returns>
+ internal static Int128 BigMul(long a, long b)
+ {
+ long high = Math.BigMul(a, b, out long low);
+ return new Int128((ulong)high, (ulong)low);
+ }
+
public static double BitDecrement(double x)
{
ulong bits = BitConverter.DoubleToUInt64Bits(x);
diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs b/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs
index ed952a89499d..98d6b9523f22 100644
--- a/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs
@@ -308,6 +308,195 @@ namespace System
public override int GetHashCode() => _ticks.GetHashCode();
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static TimeSpan FromUnits(long units, long ticksPerUnit, long minUnits, long maxUnits)
+ {
+ System.Diagnostics.Debug.Assert(minUnits < 0);
+ System.Diagnostics.Debug.Assert(maxUnits > 0);
+
+ if (units > maxUnits || units < minUnits)
+ {
+ ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
+ }
+ return TimeSpan.FromTicks(units * ticksPerUnit);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpan"/> structure to a specified number of
+ /// days.
+ /// </summary>
+ /// <param name="days">Number of days.</param>
+ /// <returns>Returns a <see cref="TimeSpan"/> that represents a specified number of days.</returns>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The parameters specify a <see cref="TimeSpan"/> value less than <see cref="MinValue"/> or greater than <see cref="MaxValue"/>
+ /// </exception>
+ public static TimeSpan FromDays(int days) => FromUnits(days, TicksPerDay, MinDays, MaxDays);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpan"/> structure to a specified number of
+ /// days, hours, minutes, seconds, milliseconds, and microseconds.
+ /// </summary>
+ /// <param name="days">Number of days.</param>
+ /// <param name="hours">Number of hours.</param>
+ /// <param name="minutes">Number of minutes.</param>
+ /// <param name="seconds">Number of seconds.</param>
+ /// <param name="milliseconds">Number of milliseconds.</param>
+ /// <param name="microseconds">Number of microseconds.</param>
+ /// <returns>Returns a <see cref="TimeSpan"/> that represents a specified number of days, hours, minutes, seconds, milliseconds, and microseconds.</returns>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The parameters specify a <see cref="TimeSpan"/> value less than <see cref="MinValue"/> or greater than <see cref="MaxValue"/>
+ /// </exception>
+ public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0)
+ {
+ Int128 totalMicroseconds = Math.BigMul(days, MicrosecondsPerDay)
+ + Math.BigMul(hours, MicrosecondsPerHour)
+ + Math.BigMul(minutes, MicrosecondsPerMinute)
+ + Math.BigMul(seconds, MicrosecondsPerSecond)
+ + Math.BigMul(milliseconds, MicrosecondsPerMillisecond)
+ + microseconds;
+
+ return FromMicroseconds(totalMicroseconds);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpan"/> structure to a specified number of
+ /// hours.
+ /// </summary>
+ /// <param name="hours">Number of hours.</param>
+ /// <returns>Returns a <see cref="TimeSpan"/> that represents a specified number of hours.</returns>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The parameters specify a <see cref="TimeSpan"/> value less than <see cref="MinValue"/> or greater than <see cref="MaxValue"/>
+ /// </exception>
+ public static TimeSpan FromHours(int hours) => FromUnits(hours, TicksPerHour, MinHours, MaxHours);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpan"/> structure to a specified number of
+ /// hours, minutes, seconds, milliseconds, and microseconds.
+ /// </summary>
+ /// <param name="hours">Number of hours.</param>
+ /// <param name="minutes">Number of minutes.</param>
+ /// <param name="seconds">Number of seconds.</param>
+ /// <param name="milliseconds">Number of milliseconds.</param>
+ /// <param name="microseconds">Number of microseconds.</param>
+ /// <returns>Returns a <see cref="TimeSpan"/> that represents a specified number of hours, minutes, seconds, milliseconds, and microseconds.</returns>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The parameters specify a <see cref="TimeSpan"/> value less than <see cref="MinValue"/> or greater than <see cref="MaxValue"/>
+ /// </exception>
+ public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0)
+ {
+ Int128 totalMicroseconds = Math.BigMul(hours, MicrosecondsPerHour)
+ + Math.BigMul(minutes, MicrosecondsPerMinute)
+ + Math.BigMul(seconds, MicrosecondsPerSecond)
+ + Math.BigMul(milliseconds, MicrosecondsPerMillisecond)
+ + microseconds;
+
+ return FromMicroseconds(totalMicroseconds);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpan"/> structure to a specified number of
+ /// minutes.
+ /// </summary>
+ /// <param name="minutes">Number of minutes.</param>
+ /// <returns>Returns a <see cref="TimeSpan"/> that represents a specified number of minutes.</returns>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The parameters specify a <see cref="TimeSpan"/> value less than <see cref="MinValue"/> or greater than <see cref="MaxValue"/>
+ /// </exception>
+ public static TimeSpan FromMinutes(long minutes) => FromUnits(minutes, TicksPerMinute, MinMinutes, MaxMinutes);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpan"/> structure to a specified number of
+ /// minutes, seconds, milliseconds, and microseconds.
+ /// </summary>
+ /// <param name="minutes">Number of minutes.</param>
+ /// <param name="seconds">Number of seconds.</param>
+ /// <param name="milliseconds">Number of milliseconds.</param>
+ /// <param name="microseconds">Number of microseconds.</param>
+ /// <returns>Returns a <see cref="TimeSpan"/> that represents a specified number of minutes, seconds, milliseconds, and microseconds.</returns>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The parameters specify a <see cref="TimeSpan"/> value less than <see cref="MinValue"/> or greater than <see cref="MaxValue"/>
+ /// </exception>
+ public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0)
+ {
+ Int128 totalMicroseconds = Math.BigMul(minutes, MicrosecondsPerMinute)
+ + Math.BigMul(seconds, MicrosecondsPerSecond)
+ + Math.BigMul(milliseconds, MicrosecondsPerMillisecond)
+ + microseconds;
+
+ return FromMicroseconds(totalMicroseconds);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpan"/> structure to a specified number of
+ /// seconds.
+ /// </summary>
+ /// <param name="seconds">Number of seconds.</param>
+ /// <returns>Returns a <see cref="TimeSpan"/> that represents a specified number of seconds.</returns>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The parameters specify a <see cref="TimeSpan"/> value less than <see cref="MinValue"/> or greater than <see cref="MaxValue"/>
+ /// </exception>
+ public static TimeSpan FromSeconds(long seconds) => FromUnits(seconds, TicksPerSecond, MinSeconds, MaxSeconds);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpan"/> structure to a specified number of
+ /// seconds, milliseconds, and microseconds.
+ /// </summary>
+ /// <param name="seconds">Number of seconds.</param>
+ /// <param name="milliseconds">Number of milliseconds.</param>
+ /// <param name="microseconds">Number of microseconds.</param>
+ /// <returns>Returns a <see cref="TimeSpan"/> that represents a specified number of seconds, milliseconds, and microseconds.</returns>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The parameters specify a <see cref="TimeSpan"/> value less than <see cref="MinValue"/> or greater than <see cref="MaxValue"/>
+ /// </exception>
+ public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0)
+ {
+ Int128 totalMicroseconds = Math.BigMul(seconds, MicrosecondsPerSecond)
+ + Math.BigMul(milliseconds, MicrosecondsPerMillisecond)
+ + microseconds;
+
+ return FromMicroseconds(totalMicroseconds);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpan"/> structure to a specified number of
+ /// milliseconds, and microseconds.
+ /// </summary>
+ /// <param name="milliseconds">Number of milliseconds.</param>
+ /// <param name="microseconds">Number of microseconds.</param>
+ /// <returns>Returns a <see cref="TimeSpan"/> that represents a specified number of milliseconds, and microseconds.</returns>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The parameters specify a <see cref="TimeSpan"/> value less than <see cref="MinValue"/> or greater than <see cref="MaxValue"/>
+ /// </exception>
+ public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0)
+ {
+ Int128 totalMicroseconds = Math.BigMul(milliseconds, MicrosecondsPerMillisecond)
+ + microseconds;
+
+ return FromMicroseconds(totalMicroseconds);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static TimeSpan FromMicroseconds(Int128 microseconds)
+ {
+ if ((microseconds > MaxMicroseconds) || (microseconds < MinMicroseconds))
+ {
+ ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
+ }
+ long ticks = (long)microseconds * TicksPerMicrosecond;
+ return TimeSpan.FromTicks(ticks);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpan"/> structure to a specified number of
+ /// microseconds.
+ /// </summary>
+ /// <param name="microseconds">Number of microseconds.</param>
+ /// <returns>Returns a <see cref="TimeSpan"/> that represents a specified number of microseconds.</returns>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The parameters specify a <see cref="TimeSpan"/> value less than <see cref="MinValue"/> or greater than <see cref="MaxValue"/>
+ /// </exception>
+ public static TimeSpan FromMicroseconds(long microseconds) => FromUnits(microseconds, TicksPerMicrosecond, MinMicroseconds, MaxMicroseconds);
+
public static TimeSpan FromHours(double value) => Interval(value, TicksPerHour);
private static TimeSpan Interval(double value, double scale)
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index a721035ec309..b787a2af8c68 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -5614,6 +5614,16 @@ namespace System
public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? value) { throw null; }
public bool Equals(System.TimeSpan obj) { throw null; }
public static bool Equals(System.TimeSpan t1, System.TimeSpan t2) { throw null; }
+ public static System.TimeSpan FromDays(int days) { throw null; }
+ public static System.TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) { throw null; }
+ public static System.TimeSpan FromHours(int hours) { throw null; }
+ public static System.TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) { throw null; }
+ public static System.TimeSpan FromMinutes(long minutes) { throw null; }
+ public static System.TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) { throw null; }
+ public static System.TimeSpan FromSeconds(long seconds) { throw null; }
+ public static System.TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) { throw null; }
+ public static System.TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) { throw null; }
+ public static System.TimeSpan FromMicroseconds(long microseconds) { throw null; }
public static System.TimeSpan FromDays(double value) { throw null; }
public static System.TimeSpan FromHours(double value) { throw null; }
public static System.TimeSpan FromMicroseconds(double value) { throw null; }
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs
index de434b059fad..9085d957fa7e 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeSpanTests.cs
@@ -347,6 +347,412 @@ namespace System.Tests
Assert.Equal(expected, timeSpan1.Equals(obj));
}
+#region FromX_int_overloads
+ [Fact]
+ public static void FromDays_Int_Positive()
+ {
+ var expectedaaa = new TimeSpan(1, 2, 3, 4, 5, 6);
+ var actual = TimeSpan.FromDays(1, 2, 3, 4, 5, 6);
+ Assert.Equal(expectedaaa, actual);
+ }
+ [Fact]
+ public static void FromDays_Int_Negative()
+ {
+ var expectedaaa = new TimeSpan(-1, -2, -3, -4, -5, -6);
+ var actual = TimeSpan.FromDays(-1, -2, -3, -4, -5, -6);
+ Assert.Equal(expectedaaa, actual);
+ }
+ [Fact]
+ public static void FromDays_Int_Zero()
+ {
+ var expectedaaa = new TimeSpan(0, 0, 0, 0, 0, 0);
+ var actual = TimeSpan.FromDays(0, 0, 0, 0, 0, 0);
+ Assert.Equal(expectedaaa, actual);
+ }
+
+ [Fact]
+ public static void FromSeconds_Int_ShouldGiveResultWithPrecision()
+ {
+ // Given example of problem with double in: https://github.com/dotnet/runtime/issues/93890#issue-1957706751
+ Assert.Equal(new TimeSpan(0, 0, 0, 101, 832), TimeSpan.FromSeconds(101, 832));
+ }
+
+ [Fact]
+ public static void FromDays_Int_ShouldOverflow_WhenIntermediateCalculationCouldOverflowBackIntoValidRange()
+ {
+ // Given example of problematic day count in comment in abandoned pr https://github.com/dotnet/runtime/pull/95779/files#r1439772903
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromDays(1067519900));
+ }
+
+ [Fact]
+ public static void FromDays_Int_ShouldConstructMaxValueAproximation()
+ {
+ var expected = TimeSpan.MaxValue;
+ var actual = TimeSpan.FromDays(expected.Days, expected.Hours, expected.Minutes, expected.Seconds, expected.Milliseconds, expected.Microseconds);
+ // Should be within TicksPerMicrosecond (10) ticks of expected
+ var diffTicks = (expected - actual).Ticks;
+ Assert.True(Math.Abs(diffTicks) < 10, $"Diff ticks was {diffTicks}");
+ }
+ [Fact]
+ public static void FromDays_Int_ShouldConstructMinValueAproximation()
+ {
+ var expected = TimeSpan.MinValue;
+ var actual = TimeSpan.FromDays(expected.Days, expected.Hours, expected.Minutes, expected.Seconds, expected.Milliseconds, expected.Microseconds);
+ // Should be within TicksPerMicrosecond (10) ticks of expected
+ var diffTicks = (actual - expected).Ticks;
+ Assert.True(Math.Abs(diffTicks) < 10, $"Diff ticks was {diffTicks}");
+ }
+
+ // Consts copied from internal const in TimeSpan
+ // Max and Min are symmetrical
+ private const long maxMicroseconds = 922_337_203_685_477_580;
+ private const long maxMilliseconds = 922_337_203_685_477;
+ private const long maxSeconds = 922_337_203_685;
+ private const long maxMinutes = 15_372_286_728;
+ private const int maxHours = 256_204_778;
+ private const int maxDays = 10_675_199;
+ public static IEnumerable<object[]> FromDays_Int_ShouldOverflowOrUnderflow_Data()
+ {
+ long[] individualMaxValues = [ maxDays, maxHours, maxMinutes, maxSeconds, maxMilliseconds, maxMicroseconds ];
+ // Each possibility for individual property to overflow or underflow
+ for (var i = 0; i < individualMaxValues.Length; i++)
+ {
+ var iVal = individualMaxValues[i] + 1;
+ object[] resultPos = [ 0, 0, 0, 0, 0, 0 ];
+ resultPos[i] = iVal;
+ yield return resultPos;
+ object[] resultNeg = [ 0, 0, 0, 0, 0, 0 ];
+ resultNeg[i] = -iVal;
+ yield return resultNeg;
+ }
+ // Each possibility for 2 properties to overflow or underflow
+ // while neither of them individually overflow or underflow
+ for (var i = 0; i < individualMaxValues.Length; i++)
+ {
+ for (var j = i + 1; j < individualMaxValues.Length; j++)
+ {
+ var iVal = individualMaxValues[i];
+ var jVal = individualMaxValues[j];
+ object[] resultPos = [ 0, 0, 0, 0, 0, 0 ];
+ resultPos[i] = iVal;
+ resultPos[j] = jVal;
+ yield return resultPos;
+ object[] resultNeg = [ 0, 0, 0, 0, 0, 0 ];
+ resultNeg[i] = -iVal;
+ resultNeg[j] = -jVal;
+ yield return resultNeg;
+ }
+ }
+ }
+ [Theory]
+ [MemberData(nameof(FromDays_Int_ShouldOverflowOrUnderflow_Data))]
+ public static void FromDays_Int_ShouldOverflowOrUnderflow(int days, int hours, long minutes, long seconds, long milliseconds, long microseconds)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromDays(days, hours, minutes, seconds, milliseconds, microseconds));
+ }
+
+ public static IEnumerable<object[]> FromDays_Int_ShouldNotOverflow_WhenOverflowingParamIsCounteredByOppositeSignParam_Data()
+ {
+ long[] individualMaxValues = [ maxDays, maxHours, maxMinutes, maxSeconds, maxMilliseconds, maxMicroseconds ];
+ for (var i = 0; i < individualMaxValues.Length; i++)
+ {
+ for (var j = 0; j < individualMaxValues.Length; j++)
+ {
+ if (i == j)
+ {
+ continue;
+ }
+ var iVal = individualMaxValues[i] + 1;
+ var jVal = individualMaxValues[j] + 1;
+ object[] result = [ 0, 0, 0, 0, 0, 0 ];
+ result[i] = iVal;
+ result[j] = -jVal;
+ yield return result;
+ }
+ }
+ }
+ [Theory]
+ [MemberData(nameof(FromDays_Int_ShouldNotOverflow_WhenOverflowingParamIsCounteredByOppositeSignParam_Data))]
+ public static void FromDays_Int_ShouldNotOverflow_WhenOverflowingParamIsCounteredByOppositeSignParam(int days, int hours, long minutes, long seconds, long milliseconds, long microseconds)
+ {
+ var actual = TimeSpan.FromDays(days, hours, minutes, seconds, milliseconds, microseconds);
+ // 2 individually overflowing or underflowing params with opposite sign should end up close to TimeSpan.FromDays(0)
+ // This is an implementation detail of the chosen test data, but a nice sanity check of expected result
+ Assert.True(actual > TimeSpan.FromDays(-1));
+ Assert.True(actual < TimeSpan.FromDays(1));
+ }
+ [Theory]
+ [InlineData(maxDays, maxHours, maxMinutes, maxSeconds, maxMilliseconds, maxMicroseconds)]
+ [InlineData(-maxDays, -maxHours, -maxMinutes, -maxSeconds, -maxMilliseconds, -maxMicroseconds)]
+ [InlineData(int.MaxValue, int.MaxValue, long.MaxValue, long.MaxValue, long.MaxValue, long.MaxValue)]
+ [InlineData(int.MinValue, int.MinValue, long.MinValue, long.MinValue, long.MinValue, long.MinValue)]
+ [InlineData(int.MaxValue, 0, 0, 0, 0, 0)]
+ [InlineData(int.MinValue, 0, 0, 0, 0, 0)]
+ [InlineData(0, int.MaxValue, 0, 0, 0, 0)]
+ [InlineData(0, int.MinValue, 0, 0, 0, 0)]
+ [InlineData(0, 0, long.MaxValue, 0, 0, 0)]
+ [InlineData(0, 0, long.MinValue, 0, 0, 0)]
+ [InlineData(0, 0, 0, long.MaxValue, 0, 0)]
+ [InlineData(0, 0, 0, long.MinValue, 0, 0)]
+ [InlineData(0, 0, 0, 0, long.MaxValue, 0)]
+ [InlineData(0, 0, 0, 0, long.MinValue, 0)]
+ [InlineData(0, 0, 0, 0, 0, long.MaxValue)]
+ [InlineData(0, 0, 0, 0, 0, long.MinValue)]
+ public static void FromDays_Int_ShouldOverflow(int days, int hours, long minutes, long seconds, long milliseconds, long microseconds)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromDays(days, hours, minutes, seconds, milliseconds, microseconds));
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(-1)]
+ [InlineData(maxDays)]
+ [InlineData(-maxDays)]
+ public static void FromDays_Int_Single_ShouldCreate(int days)
+ {
+ Assert.Equal(new TimeSpan(days, 0, 0, 0), TimeSpan.FromDays(days));
+ }
+ [Theory]
+ [InlineData(maxDays + 1)]
+ [InlineData(-(maxDays + 1))]
+ [InlineData(int.MaxValue)]
+ [InlineData(int.MinValue)]
+ public static void FromDays_Int_Single_ShouldOverflow(int days)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromDays(days));
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(-1)]
+ [InlineData(maxHours)]
+ [InlineData(-maxHours)]
+ public static void FromHours_Int_Single_ShouldCreate(int hours)
+ {
+ Assert.Equal(new TimeSpan(0, hours, 0, 0), TimeSpan.FromHours(hours));
+ }
+ [Theory]
+ [InlineData(maxHours + 1)]
+ [InlineData(-(maxHours + 1))]
+ [InlineData(int.MaxValue)]
+ [InlineData(int.MinValue)]
+ public static void FromHours_Int_Single_ShouldOverflow(int hours)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromHours(hours));
+ }
+
+ [Theory]
+ [InlineData(0, 0, 0, 0, 0)]
+ [InlineData(1, 1, 1, 1, 1)]
+ [InlineData(-1, -1, -1, -1, -1)]
+ [InlineData(maxHours, 0, 0, 0, 0)]
+ [InlineData(-maxHours, 0, 0, 0, 0)]
+ [InlineData(0, maxMinutes, 0, 0, 0)]
+ [InlineData(0, -maxMinutes, 0, 0, 0)]
+ [InlineData(0, 0, maxSeconds, 0, 0)]
+ [InlineData(0, 0, -maxSeconds, 0, 0)]
+ [InlineData(0, 0, 0, maxMilliseconds, 0)]
+ [InlineData(0, 0, 0, -maxMilliseconds, 0)]
+ [InlineData(0, 0, 0, 0, maxMicroseconds)]
+ [InlineData(0, 0, 0, 0, -maxMicroseconds)]
+ public static void FromHours_Int_ShouldCreate(int hours, long minutes, long seconds, long milliseconds, long microseconds)
+ {
+ var ticksFromHours = hours * TimeSpan.TicksPerHour;
+ var ticksFromMinutes = minutes * TimeSpan.TicksPerMinute;
+ var ticksFromSeconds = seconds * TimeSpan.TicksPerSecond;
+ var ticksFromMilliseconds = milliseconds * TimeSpan.TicksPerMillisecond;
+ var ticksFromMicroseconds = microseconds * TimeSpan.TicksPerMicrosecond;
+ var expected = TimeSpan.FromTicks(ticksFromHours + ticksFromMinutes + ticksFromSeconds + ticksFromMilliseconds + ticksFromMicroseconds);
+ Assert.Equal(expected, TimeSpan.FromHours(hours, minutes, seconds, milliseconds, microseconds));
+ }
+ [Theory]
+ [InlineData(maxHours + 1, 0, 0, 0, 0)]
+ [InlineData(-(maxHours + 1), 0, 0, 0, 0)]
+ [InlineData(0, maxMinutes + 1, 0, 0, 0)]
+ [InlineData(0, -(maxMinutes + 1), 0, 0, 0)]
+ [InlineData(0, 0, maxSeconds + 1, 0, 0)]
+ [InlineData(0, 0, -(maxSeconds + 1), 0, 0)]
+ [InlineData(0, 0, 0, maxMilliseconds + 1, 0)]
+ [InlineData(0, 0, 0, -(maxMilliseconds + 1), 0)]
+ [InlineData(0, 0, 0, 0, maxMicroseconds + 1)]
+ [InlineData(0, 0, 0, 0, -(maxMicroseconds + 1))]
+ public static void FromHours_Int_ShouldOverflow(int hours, long minutes, long seconds, long milliseconds, long microseconds)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromHours(hours, minutes, seconds, milliseconds, microseconds));
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(-1)]
+ [InlineData(maxMinutes)]
+ [InlineData(-maxMinutes)]
+ public static void FromMinutes_Int_Single_ShouldCreate(long minutes)
+ {
+ Assert.Equal(TimeSpan.FromDays(0, minutes: minutes), TimeSpan.FromMinutes(minutes));
+ }
+ [Theory]
+ [InlineData(maxMinutes + 1)]
+ [InlineData(-(maxMinutes + 1))]
+ [InlineData(long.MaxValue)]
+ [InlineData(long.MinValue)]
+ public static void FromMinutes_Int_Single_ShouldOverflow(long minutes)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromMinutes(minutes));
+ }
+
+ [Theory]
+ [InlineData(0, 0, 0, 0)]
+ [InlineData(1, 1, 1, 1)]
+ [InlineData(-1, -1, -1, -1)]
+ [InlineData(maxMinutes, 0, 0, 0)]
+ [InlineData(-maxMinutes, 0, 0, 0)]
+ [InlineData(0, maxSeconds, 0, 0)]
+ [InlineData(0, -maxSeconds, 0, 0)]
+ [InlineData(0, 0, maxMilliseconds, 0)]
+ [InlineData(0, 0, -maxMilliseconds, 0)]
+ [InlineData(0, 0, 0, maxMicroseconds)]
+ [InlineData(0, 0, 0, -maxMicroseconds)]
+ public static void FromMinutes_Int_ShouldCreate(long minutes, long seconds, long milliseconds, long microseconds)
+ {
+ var ticksFromMinutes = minutes * TimeSpan.TicksPerMinute;
+ var ticksFromSeconds = seconds * TimeSpan.TicksPerSecond;
+ var ticksFromMilliseconds = milliseconds * TimeSpan.TicksPerMillisecond;
+ var ticksFromMicroseconds = microseconds * TimeSpan.TicksPerMicrosecond;
+ var expected = TimeSpan.FromTicks(ticksFromMinutes + ticksFromSeconds + ticksFromMilliseconds + ticksFromMicroseconds);
+ Assert.Equal(expected, TimeSpan.FromMinutes(minutes, seconds, milliseconds, microseconds));
+ }
+ [Theory]
+ [InlineData(maxMinutes + 1, 0, 0, 0)]
+ [InlineData(-(maxMinutes + 1), 0, 0, 0)]
+ [InlineData(0, maxSeconds + 1, 0, 0)]
+ [InlineData(0, -(maxSeconds + 1), 0, 0)]
+ [InlineData(0, 0, maxMilliseconds + 1, 0)]
+ [InlineData(0, 0, -(maxMilliseconds + 1), 0)]
+ [InlineData(0, 0, 0, maxMicroseconds + 1)]
+ [InlineData(0, 0, 0, -(maxMicroseconds + 1))]
+ public static void FromMinutes_Int_ShouldOverflow(long minutes, long seconds, long milliseconds, long microseconds)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromMinutes(minutes, seconds, milliseconds, microseconds));
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(-1)]
+ [InlineData(maxSeconds)]
+ [InlineData(-maxSeconds)]
+ public static void FromSeconds_Int_Single_ShouldCreate(long seconds)
+ {
+ Assert.Equal(TimeSpan.FromDays(0, seconds: seconds), TimeSpan.FromSeconds(seconds));
+ }
+ [Theory]
+ [InlineData(maxSeconds + 1)]
+ [InlineData(-(maxSeconds + 1))]
+ [InlineData(long.MaxValue)]
+ [InlineData(long.MinValue)]
+ public static void FromSeconds_Int_Single_ShouldOverflow(long seconds)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromSeconds(seconds));
+ }
+
+ [Theory]
+ [InlineData(0, 0, 0)]
+ [InlineData(1, 1, 1)]
+ [InlineData(-1, -1, -1)]
+ [InlineData(maxSeconds, 0, 0)]
+ [InlineData(-maxSeconds, 0, 0)]
+ [InlineData(0, maxMilliseconds, 0)]
+ [InlineData(0, -maxMilliseconds, 0)]
+ [InlineData(0, 0, maxMicroseconds)]
+ [InlineData(0, 0, -maxMicroseconds)]
+ public static void FromSeconds_Int_ShouldCreate(long seconds, long milliseconds, long microseconds)
+ {
+ var ticksFromSeconds = seconds * TimeSpan.TicksPerSecond;
+ var ticksFromMilliseconds = milliseconds * TimeSpan.TicksPerMillisecond;
+ var ticksFromMicroseconds = microseconds * TimeSpan.TicksPerMicrosecond;
+ var expected = TimeSpan.FromTicks(ticksFromSeconds + ticksFromMilliseconds + ticksFromMicroseconds);
+ Assert.Equal(expected, TimeSpan.FromSeconds(seconds, milliseconds, microseconds));
+ }
+ [Theory]
+ [InlineData(maxSeconds + 1, 0, 0)]
+ [InlineData(-(maxSeconds + 1), 0, 0)]
+ [InlineData(0, maxMilliseconds + 1, 0)]
+ [InlineData(0, -(maxMilliseconds + 1), 0)]
+ [InlineData(0, 0, maxMicroseconds + 1)]
+ [InlineData(0, 0, -(maxMicroseconds + 1))]
+ [InlineData(long.MaxValue, 0, 0)]
+ [InlineData(long.MinValue, 0, 0)]
+ [InlineData(0, long.MaxValue, 0)]
+ [InlineData(0, long.MinValue, 0)]
+ [InlineData(0, 0, long.MaxValue)]
+ [InlineData(0, 0, long.MinValue)]
+ [InlineData(maxSeconds, maxMilliseconds, 0)]
+ [InlineData(0, maxMilliseconds, maxMicroseconds)]
+ [InlineData(maxSeconds, 0, maxMicroseconds)]
+ public static void FromSeconds_Int_ShouldOverflow(long seconds, long milliseconds, long microseconds)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromSeconds(seconds, milliseconds, microseconds));
+ }
+
+ [Theory]
+ [InlineData(0, 0)]
+ [InlineData(1, 0)]
+ [InlineData(0, 1)]
+ [InlineData(-1, 0)]
+ [InlineData(0, -1)]
+ [InlineData(maxMilliseconds, 0)]
+ [InlineData(-maxMilliseconds, 0)]
+ [InlineData(0, maxMicroseconds)]
+ [InlineData(0, -maxMicroseconds)]
+ public static void FromMilliseconds_Int_ShouldCreate(long milliseconds, long microseconds)
+ {
+ long ticksFromMilliseconds = milliseconds * TimeSpan.TicksPerMillisecond;
+ long ticksFromMicroseconds = microseconds * TimeSpan.TicksPerMicrosecond;
+ var expected = TimeSpan.FromTicks(ticksFromMilliseconds + ticksFromMicroseconds);
+ Assert.Equal(expected, TimeSpan.FromMilliseconds(milliseconds, microseconds));
+ }
+ [Theory]
+ [InlineData(maxMilliseconds + 1, 0)]
+ [InlineData(-(maxMilliseconds + 1), 0)]
+ [InlineData(long.MaxValue, 0)]
+ [InlineData(long.MinValue, 0)]
+ [InlineData(0, maxMicroseconds + 1)]
+ [InlineData(0, -(maxMicroseconds + 1))]
+ [InlineData(0, long.MaxValue)]
+ [InlineData(0, long.MinValue)]
+ [InlineData(maxMilliseconds, 1000)]
+ [InlineData(-maxMilliseconds, -1000)]
+ [InlineData(1, maxMicroseconds)]
+ [InlineData(-1, -maxMicroseconds)]
+ public static void FromMilliseconds_Int_ShouldOverflow(long milliseconds, long microseconds)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromMilliseconds(milliseconds, microseconds));
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(-1)]
+ [InlineData(maxMicroseconds)]
+ [InlineData(-maxMicroseconds)]
+ public static void FromMicroseconds_Int_Single_ShouldCreate(long microseconds)
+ {
+ Assert.Equal(TimeSpan.FromDays(0, microseconds: microseconds), TimeSpan.FromMicroseconds(microseconds));
+ }
+ [Theory]
+ [InlineData(maxMicroseconds + 1)]
+ [InlineData(-(maxMicroseconds + 1))]
+ [InlineData(long.MaxValue)]
+ [InlineData(long.MinValue)]
+ public static void FromMicroseconds_Int_Single_ShouldOverflow(long microseconds)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => TimeSpan.FromMicroseconds(microseconds));
+ }
+#endregion
+
public static IEnumerable<object[]> FromDays_TestData()
{
yield return new object[] { 100.5, new TimeSpan(100, 12, 0, 0) };