diff --git a/src/Polyfill/TimeSpanPolyfill.cs b/src/Polyfill/TimeSpanPolyfill.cs index a9f3909b..74ecebd9 100644 --- a/src/Polyfill/TimeSpanPolyfill.cs +++ b/src/Polyfill/TimeSpanPolyfill.cs @@ -54,6 +54,64 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) return new((long)ticks); } + // The decimal accumulation above costs hundreds of nanoseconds. When every component is small + // enough that its own tick count is at most an eighth of long.MaxValue, the (at most six) + // components cannot overflow an Int64 between them, so the ticks can be summed directly and + // the result is always in range. Anything larger takes the decimal path, which decides + // whether the total overflows. + const long MaxFastPathTicks = long.MaxValue / 8; + + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + + // Components an overload does not have arrive as zero; leave them out of the decimal sum + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + + return TicksFromMicroseconds(total); + } + extension(TimeSpan) { /// @@ -166,62 +224,42 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// //Link: https://learn.microsoft.com/en-us/dotnet/api/system.timespan.fromdays?view=net-11.0#system-timespan-fromdays(system-int32-system-int32-system-int64-system-int64-system-int64-system-int64) public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// //Link: https://learn.microsoft.com/en-us/dotnet/api/system.timespan.fromhours?view=net-11.0#system-timespan-fromhours(system-int32-system-int64-system-int64-system-int64-system-int64) public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// //Link: https://learn.microsoft.com/en-us/dotnet/api/system.timespan.fromminutes?view=net-11.0#system-timespan-fromminutes(system-int64-system-int64-system-int64-system-int64) public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// //Link: https://learn.microsoft.com/en-us/dotnet/api/system.timespan.fromseconds?view=net-11.0#system-timespan-fromseconds(system-int64-system-int64-system-int64) public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// //Link: https://learn.microsoft.com/en-us/dotnet/api/system.timespan.frommilliseconds?view=net-11.0#system-timespan-frommilliseconds(system-int64-system-int64) public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// //Link: https://learn.microsoft.com/en-us/dotnet/api/system.timespan.frommicroseconds?view=net-11.0#system-timespan-frommicroseconds(system-int64) public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } #endif } diff --git a/src/Split/net461/TimeSpanPolyfill.cs b/src/Split/net461/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/net461/TimeSpanPolyfill.cs +++ b/src/Split/net461/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/net462/TimeSpanPolyfill.cs b/src/Split/net462/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/net462/TimeSpanPolyfill.cs +++ b/src/Split/net462/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/net47/TimeSpanPolyfill.cs b/src/Split/net47/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/net47/TimeSpanPolyfill.cs +++ b/src/Split/net47/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/net471/TimeSpanPolyfill.cs b/src/Split/net471/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/net471/TimeSpanPolyfill.cs +++ b/src/Split/net471/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/net472/TimeSpanPolyfill.cs b/src/Split/net472/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/net472/TimeSpanPolyfill.cs +++ b/src/Split/net472/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/net48/TimeSpanPolyfill.cs b/src/Split/net48/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/net48/TimeSpanPolyfill.cs +++ b/src/Split/net48/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/net481/TimeSpanPolyfill.cs b/src/Split/net481/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/net481/TimeSpanPolyfill.cs +++ b/src/Split/net481/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/net5.0/TimeSpanPolyfill.cs b/src/Split/net5.0/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/net5.0/TimeSpanPolyfill.cs +++ b/src/Split/net5.0/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/net6.0/TimeSpanPolyfill.cs b/src/Split/net6.0/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/net6.0/TimeSpanPolyfill.cs +++ b/src/Split/net6.0/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/net7.0/TimeSpanPolyfill.cs b/src/Split/net7.0/TimeSpanPolyfill.cs index c9e91081..7306e9ae 100644 --- a/src/Split/net7.0/TimeSpanPolyfill.cs +++ b/src/Split/net7.0/TimeSpanPolyfill.cs @@ -19,6 +19,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -85,51 +128,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/net8.0/TimeSpanPolyfill.cs b/src/Split/net8.0/TimeSpanPolyfill.cs index c9e91081..7306e9ae 100644 --- a/src/Split/net8.0/TimeSpanPolyfill.cs +++ b/src/Split/net8.0/TimeSpanPolyfill.cs @@ -19,6 +19,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -85,51 +128,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/netcoreapp2.0/TimeSpanPolyfill.cs b/src/Split/netcoreapp2.0/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/netcoreapp2.0/TimeSpanPolyfill.cs +++ b/src/Split/netcoreapp2.0/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/netcoreapp2.1/TimeSpanPolyfill.cs b/src/Split/netcoreapp2.1/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/netcoreapp2.1/TimeSpanPolyfill.cs +++ b/src/Split/netcoreapp2.1/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/netcoreapp2.2/TimeSpanPolyfill.cs b/src/Split/netcoreapp2.2/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/netcoreapp2.2/TimeSpanPolyfill.cs +++ b/src/Split/netcoreapp2.2/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/netcoreapp3.0/TimeSpanPolyfill.cs b/src/Split/netcoreapp3.0/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/netcoreapp3.0/TimeSpanPolyfill.cs +++ b/src/Split/netcoreapp3.0/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/netcoreapp3.1/TimeSpanPolyfill.cs b/src/Split/netcoreapp3.1/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/netcoreapp3.1/TimeSpanPolyfill.cs +++ b/src/Split/netcoreapp3.1/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/netstandard2.0/TimeSpanPolyfill.cs b/src/Split/netstandard2.0/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/netstandard2.0/TimeSpanPolyfill.cs +++ b/src/Split/netstandard2.0/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/netstandard2.1/TimeSpanPolyfill.cs b/src/Split/netstandard2.1/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/netstandard2.1/TimeSpanPolyfill.cs +++ b/src/Split/netstandard2.1/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Split/uap10.0/TimeSpanPolyfill.cs b/src/Split/uap10.0/TimeSpanPolyfill.cs index 255ad1d2..462545bf 100644 --- a/src/Split/uap10.0/TimeSpanPolyfill.cs +++ b/src/Split/uap10.0/TimeSpanPolyfill.cs @@ -38,6 +38,49 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) } return new((long)ticks); } + const long MaxFastPathTicks = long.MaxValue / 8; + static bool IsWithin(long value, long bound) => + value >= -bound && value <= bound; + static TimeSpan FromComponents(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + if (IsWithin(days, MaxFastPathTicks / TimeSpan.TicksPerDay) && + IsWithin(hours, MaxFastPathTicks / TimeSpan.TicksPerHour) && + IsWithin(minutes, MaxFastPathTicks / TimeSpan.TicksPerMinute) && + IsWithin(seconds, MaxFastPathTicks / TimeSpan.TicksPerSecond) && + IsWithin(milliseconds, MaxFastPathTicks / TimeSpan.TicksPerMillisecond) && + IsWithin(microseconds, MaxFastPathTicks / MicrosecondsToTicks)) + { + return new( + days * TimeSpan.TicksPerDay + + hours * TimeSpan.TicksPerHour + + minutes * TimeSpan.TicksPerMinute + + seconds * TimeSpan.TicksPerSecond + + milliseconds * TimeSpan.TicksPerMillisecond + + microseconds * MicrosecondsToTicks); + } + decimal total = microseconds; + if (days != 0) + { + total += (decimal)days * MicrosecondsPerDay; + } + if (hours != 0) + { + total += (decimal)hours * MicrosecondsPerHour; + } + if (minutes != 0) + { + total += (decimal)minutes * MicrosecondsPerMinute; + } + if (seconds != 0) + { + total += (decimal)seconds * MicrosecondsPerSecond; + } + if (milliseconds != 0) + { + total += (decimal)milliseconds * MicrosecondsPerMillisecond; + } + return TicksFromMicroseconds(total); + } extension(TimeSpan) { /// @@ -104,51 +147,31 @@ static TimeSpan TicksFromMicroseconds(decimal totalMicroseconds) /// Initializes a new instance of the structure to a specified number of days, hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)days * MicrosecondsPerDay + - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(days, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of hours, minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)hours * MicrosecondsPerHour + - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, hours, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of minutes, seconds, milliseconds, and microseconds. /// public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)minutes * MicrosecondsPerMinute + - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, minutes, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of seconds, milliseconds, and microseconds. /// public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)seconds * MicrosecondsPerSecond + - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, seconds, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of milliseconds and microseconds. /// public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0) => - TicksFromMicroseconds( - (decimal)milliseconds * MicrosecondsPerMillisecond + - microseconds); + FromComponents(0, 0, 0, 0, milliseconds, microseconds); /// /// Initializes a new instance of the structure to a specified number of microseconds. /// public static TimeSpan FromMicroseconds(long microseconds) => - TicksFromMicroseconds(microseconds); + FromComponents(0, 0, 0, 0, 0, microseconds); } } diff --git a/src/Tests/TimeSpanPolyfillTests.cs b/src/Tests/TimeSpanPolyfillTests.cs index 3e698d27..6bedb20d 100644 --- a/src/Tests/TimeSpanPolyfillTests.cs +++ b/src/Tests/TimeSpanPolyfillTests.cs @@ -176,4 +176,281 @@ public async Task UnitConstants_AgreeWithTicksPer() await Assert.That(TimeSpan.MillisecondsPerDay).IsEqualTo(TimeSpan.MillisecondsPerHour * TimeSpan.HoursPerDay); await Assert.That(TimeSpan.MicrosecondsPerDay).IsEqualTo(TimeSpan.MicrosecondsPerHour * TimeSpan.HoursPerDay); } + + // What the From* factories are specified to do: sum every component exactly, then throw + // ArgumentOutOfRangeException when the total does not fit in the tick range. + static long? ExpectedTicks(long days, long hours, long minutes, long seconds, long milliseconds, long microseconds) + { + var ticks = + (days * 86_400_000_000m + + hours * 3_600_000_000m + + minutes * 60_000_000m + + seconds * 1_000_000m + + milliseconds * 1_000m + + microseconds) * 10m; + if (ticks < long.MinValue || ticks > long.MaxValue) + { + return null; + } + + return (long) ticks; + } + + // The ticks a factory call produced, or null when it threw ArgumentOutOfRangeException. + // Any other exception propagates and fails the test. + static long? ActualTicks(Func factory) + { + try + { + return factory().Ticks; + } + catch (ArgumentOutOfRangeException) + { + return null; + } + } + + // Runs every multi-component factory that can take these components and returns a description + // of the first one that disagrees with the specification. + static string? CompareFactories(int days, int hours, long minutes, long seconds, long milliseconds, long microseconds) + { + var expected = ExpectedTicks(days, hours, minutes, seconds, milliseconds, microseconds); + var actual = ActualTicks(() => TimeSpan.FromDays(days, hours, minutes, seconds, milliseconds, microseconds)); + if (actual != expected) + { + return $"FromDays({days}, {hours}, {minutes}, {seconds}, {milliseconds}, {microseconds}): {Describe(actual)} rather than {Describe(expected)}"; + } + + expected = ExpectedTicks(0, hours, minutes, seconds, milliseconds, microseconds); + actual = ActualTicks(() => TimeSpan.FromHours(hours, minutes, seconds, milliseconds, microseconds)); + if (actual != expected) + { + return $"FromHours({hours}, {minutes}, {seconds}, {milliseconds}, {microseconds}): {Describe(actual)} rather than {Describe(expected)}"; + } + + expected = ExpectedTicks(0, 0, minutes, seconds, milliseconds, microseconds); + actual = ActualTicks(() => TimeSpan.FromMinutes(minutes, seconds, milliseconds, microseconds)); + if (actual != expected) + { + return $"FromMinutes({minutes}, {seconds}, {milliseconds}, {microseconds}): {Describe(actual)} rather than {Describe(expected)}"; + } + + expected = ExpectedTicks(0, 0, 0, seconds, milliseconds, microseconds); + actual = ActualTicks(() => TimeSpan.FromSeconds(seconds, milliseconds, microseconds)); + if (actual != expected) + { + return $"FromSeconds({seconds}, {milliseconds}, {microseconds}): {Describe(actual)} rather than {Describe(expected)}"; + } + + expected = ExpectedTicks(0, 0, 0, 0, milliseconds, microseconds); + actual = ActualTicks(() => TimeSpan.FromMilliseconds(milliseconds, microseconds)); + if (actual != expected) + { + return $"FromMilliseconds({milliseconds}, {microseconds}): {Describe(actual)} rather than {Describe(expected)}"; + } + + return null; + + static string Describe(long? ticks) + { + if (ticks == null) + { + return "ArgumentOutOfRangeException"; + } + + return ticks + " ticks"; + } + } + + [Test] + [Arguments(0, 0, 0L, 0L, 0L, 0L)] + [Arguments(1, 2, 3L, 4L, 5L, 6L)] + [Arguments(-1, -2, -3L, -4L, -5L, -6L)] + [Arguments(1, -23, 59L, -59L, 999L, -999L)] + [Arguments(0, 0, 0L, 0L, 0L, 1L)] + [Arguments(0, 0, 0L, 0L, 0L, -1L)] + [Arguments(10_675_199, 2, 48L, 5L, 477L, 580L)] + [Arguments(10_675_199, 2, 48L, 5L, 477L, 581L)] + [Arguments(-10_675_199, -2, -48L, -5L, -477L, -580L)] + [Arguments(-10_675_199, -2, -48L, -5L, -477L, -581L)] + [Arguments(10_675_200, 0, 0L, 0L, 0L, 0L)] + [Arguments(-10_675_200, 0, 0L, 0L, 0L, 0L)] + [Arguments(int.MaxValue, int.MaxValue, long.MaxValue, long.MaxValue, long.MaxValue, long.MaxValue)] + [Arguments(int.MinValue, int.MinValue, long.MinValue, long.MinValue, long.MinValue, long.MinValue)] + [Arguments(int.MaxValue, int.MinValue, long.MaxValue, long.MinValue, long.MaxValue, long.MinValue)] + [Arguments(0, 0, 0L, 0L, 0L, long.MaxValue)] + [Arguments(0, 0, 0L, 0L, 0L, long.MinValue)] + [Arguments(0, 0, 0L, 0L, long.MaxValue, long.MinValue)] + [Arguments(0, 0, long.MaxValue, long.MinValue, 0L, 0L)] + public async Task From_Components_MatchSpecification(int days, int hours, long minutes, long seconds, long milliseconds, long microseconds) => + await Assert.That(CompareFactories(days, hours, minutes, seconds, milliseconds, microseconds)).IsNull(); + + // The largest and smallest totals that fit: +/-922337203685477580 microseconds + [Test] + public async Task From_Components_TickRangeBoundaries() + { + const long maxMicroseconds = long.MaxValue / 10; + + await Assert.That(TimeSpan.FromMilliseconds(0, maxMicroseconds).Ticks).IsEqualTo(9223372036854775800L); + await Assert.That(TimeSpan.FromMilliseconds(0, -maxMicroseconds).Ticks).IsEqualTo(-9223372036854775800L); + await Assert.That(() => TimeSpan.FromMilliseconds(0, maxMicroseconds + 1)).Throws(); + await Assert.That(() => TimeSpan.FromMilliseconds(0, -maxMicroseconds - 1)).Throws(); + + // reached through every unit at once + await Assert.That(TimeSpan.FromDays(10_675_199, 2, 48, 5, 477, 580).Ticks).IsEqualTo(9223372036854775800L); + await Assert.That(() => TimeSpan.FromDays(10_675_199, 2, 48, 5, 477, 581)).Throws(); + await Assert.That(TimeSpan.FromDays(-10_675_199, -2, -48, -5, -477, -580).Ticks).IsEqualTo(-9223372036854775800L); + await Assert.That(() => TimeSpan.FromDays(-10_675_199, -2, -48, -5, -477, -581)).Throws(); + + // components far out of range that cancel back into range + await Assert.That(TimeSpan.FromMinutes(150_000_000_000_000, -9_000_000_000_000_000 + 5, 0, 0).Ticks).IsEqualTo(5 * TimeSpan.TicksPerSecond); + await Assert.That(TimeSpan.FromSeconds(long.MaxValue / 1000, -(long.MaxValue / 1000) * 1000, 7).Ticks).IsEqualTo(70L); + await Assert.That(TimeSpan.FromMilliseconds(long.MaxValue / 1000, -(long.MaxValue / 1000) * 1000 - 3).Ticks).IsEqualTo(-30L); + + // components that are each in range but overflow together + await Assert.That(() => TimeSpan.FromDays(6_000_000, 0, 0, 6_000_000L * 86_400)).Throws(); + await Assert.That(() => TimeSpan.FromSeconds(long.MaxValue / 10_000_000, long.MaxValue / 10_000, 0)).Throws(); + } + + // Every component on its own, around each power of two and each power of ten, so that whatever + // magnitude an implementation switches arithmetic at is covered from both sides. + [Test] + public async Task From_Components_SingleComponentMagnitudes() + { + var magnitudes = new List {0, 1, long.MaxValue}; + for (var shift = 1; shift < 63; shift++) + { + magnitudes.Add((1L << shift) - 1); + magnitudes.Add(1L << shift); + magnitudes.Add((1L << shift) + 1); + } + + var power = 1L; + for (var exponent = 1; exponent < 19; exponent++) + { + power *= 10; + magnitudes.Add(power - 1); + magnitudes.Add(power); + magnitudes.Add(power + 1); + } + + // the limits of each unit: the last value that fits in the tick range, and its neighbours + foreach (var ticksPerUnit in new[] {10L, TimeSpan.TicksPerMillisecond, TimeSpan.TicksPerSecond, TimeSpan.TicksPerMinute, TimeSpan.TicksPerHour, TimeSpan.TicksPerDay}) + { + for (var divisor = 1; divisor <= 16; divisor++) + { + var limit = long.MaxValue / divisor / ticksPerUnit; + magnitudes.Add(limit - 1); + magnitudes.Add(limit); + magnitudes.Add(limit + 1); + } + } + + var values = new HashSet {long.MinValue}; + foreach (var magnitude in magnitudes) + { + values.Add(magnitude); + values.Add(-magnitude); + } + + var failures = new List(); + foreach (var value in values) + { + Check(CompareFactories(0, 0, 0, 0, 0, value)); + Check(CompareFactories(0, 0, 0, 0, value, 0)); + Check(CompareFactories(0, 0, 0, value, 0, 0)); + Check(CompareFactories(0, 0, value, 0, 0, 0)); + if (value is >= int.MinValue and <= int.MaxValue) + { + Check(CompareFactories(0, (int) value, 0, 0, 0, 0)); + Check(CompareFactories((int) value, 0, 0, 0, 0, 0)); + } + } + + await Assert.That(string.Join(Environment.NewLine, failures)).IsEqualTo(string.Empty); + + void Check(string? failure) + { + if (failure != null && failures.Count < 20) + { + failures.Add(failure); + } + } + } + + [Test] + public async Task From_Components_Random_MatchSpecification() + { + var random = new Random(20240917); + var failures = new List(); + for (var index = 0; index < 5_000; index++) + { + var failure = CompareFactories( + (int) Next(31), + (int) Next(31), + Next(63), + Next(63), + Next(63), + Next(63)); + if (failure != null && failures.Count < 20) + { + failures.Add(failure); + } + } + + await Assert.That(string.Join(Environment.NewLine, failures)).IsEqualTo(string.Empty); + + // A signed value with a random number of significant bits, so that small, medium and + // overflowing magnitudes are all common. One call in four returns zero. + long Next(int maxBits) + { + if (random.Next(4) == 0) + { + return 0; + } + + var bits = random.Next(1, maxBits + 1); + var buffer = new byte[8]; + random.NextBytes(buffer); + var value = (long) (BitConverter.ToUInt64(buffer, 0) >> (64 - bits)); + if (random.Next(2) == 0) + { + return -value; + } + + return value; + } + } + + [Test] + public async Task FromMicroseconds_Long_MatchesSpecification() + { + // Below net9 a long argument binds to the BCL's FromMicroseconds(double) where that exists + // (net7 and net8), which rounds. Values a double holds exactly behave the same everywhere. + foreach (var value in new[] {0L, 1L, -1L, 999L, 1_000_000L, -86_400_000_000L, 1L << 40, -(1L << 52)}) + { + await Assert.That(TimeSpan.FromMicroseconds(value).Ticks).IsEqualTo(value * 10); + } + + // 2^53 + 1 is not representable as a double, so this tells the exact overload apart + var exact = TimeSpan.FromMicroseconds(9_007_199_254_740_993L).Ticks == 90_071_992_547_409_930L; + if (!exact) + { + return; + } + + const long maxMicroseconds = long.MaxValue / 10; + foreach (var divisor in new long[] {1, 2, 4, 7, 8, 9, 16}) + { + foreach (var delta in new long[] {-1, 0, 1}) + { + var value = maxMicroseconds / divisor + delta; + await Assert.That(ActualTicks(() => TimeSpan.FromMicroseconds(value))).IsEqualTo(ExpectedTicks(0, 0, 0, 0, 0, value)); + await Assert.That(ActualTicks(() => TimeSpan.FromMicroseconds(-value))).IsEqualTo(ExpectedTicks(0, 0, 0, 0, 0, -value)); + } + } + + await Assert.That(() => TimeSpan.FromMicroseconds(long.MaxValue)).Throws(); + await Assert.That(() => TimeSpan.FromMicroseconds(long.MinValue)).Throws(); + } }