Skip to content

Instantly share code, notes, and snippets.

@tcortega
Last active October 31, 2023 01:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tcortega/f518f899b3b80cb62bdc3b5e8514f3bc to your computer and use it in GitHub Desktop.
Save tcortega/f518f899b3b80cb62bdc3b5e8514f3bc to your computer and use it in GitHub Desktop.
TimeSpan overflow checks
public sealed class CheckedOverflowChecks : TimeSpanOverflowChecksContext
{
public static TimeSpan Interval(int days = 0, int hours = 0, long minutes = 0, long seconds = 0,
long milliseconds = 0, long microseconds = 0)
{
try
{
long ticks = checked((long)days * TicksPerDay
+ (long)hours * TicksPerDay
+ minutes * TicksPerMinute
+ seconds * TicksPerSecond
+ milliseconds * TicksPerMillisecond
+ microseconds * TicksPerMicrosecond);
return new TimeSpan(ticks);
}
catch (OverflowException)
{
const string message = "The timespan is too long and resulted in an overflow.";
throw new ArgumentOutOfRangeException(null, message);
}
}
}
public sealed class Int128OverflowChecks : TimeSpanOverflowChecksContext
{
public static TimeSpan Interval(int days = 0, int hours = 0, long minutes = 0, long seconds = 0,
long milliseconds = 0, long microseconds = 0)
{
Int128 ticks = (Int128)days * TicksPerDay
+ (Int128)hours * TicksPerHour
+ (Int128)minutes * TicksPerMinute
+ (Int128)seconds * TicksPerSecond
+ (Int128)milliseconds * TicksPerMillisecond
+ (Int128)microseconds * TicksPerMicrosecond;
if (ticks < TimeSpan.MinValue.Ticks || ticks > TimeSpan.MaxValue.Ticks)
ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
return new TimeSpan((long)ticks);
}
public static TimeSpan SmartInterval(int days = 0, int hours = 0, long minutes = 0, long seconds = 0,
long milliseconds = 0, long microseconds = 0)
{
Int128 ticks = default;
if (days != 0)
{
ticks += (Int128)days * TicksPerDay;
ThrowOnOverflow(ticks);
}
if (hours != 0)
{
ticks += (Int128)hours * TicksPerHour;
ThrowOnOverflow(ticks);
}
if (minutes != 0)
{
ticks += (Int128)minutes * TicksPerMinute;
ThrowOnOverflow(ticks);
}
if (seconds != 0)
{
ticks += (Int128)seconds * TicksPerSecond;
ThrowOnOverflow(ticks);
}
if (milliseconds != 0)
{
ticks += (Int128)milliseconds * TicksPerMillisecond;
ThrowOnOverflow(ticks);
}
if (microseconds != 0)
{
ticks += (Int128)microseconds * TicksPerMicrosecond;
ThrowOnOverflow(ticks);
}
return new TimeSpan((long)ticks);
}
private static void ThrowOnOverflow(Int128 ticks)
{
if (ticks < TimeSpan.MinValue.Ticks || ticks > TimeSpan.MaxValue.Ticks)
ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
}
}
public sealed class ManualOverflowChecks : TimeSpanOverflowChecksContext
{
public static TimeSpan Interval(int days = 0, int hours = 0, long minutes = 0, long seconds = 0,
long milliseconds = 0, long microseconds = 0)
{
ValidateRange(days, MinDays, MaxDays);
ValidateRange(hours, MinHours, MaxHours);
ValidateRange(minutes, MinMinutes, MaxMinutes);
ValidateRange(seconds, MinSeconds, MaxSeconds);
ValidateRange(milliseconds, MinMilliSeconds, MaxMilliSeconds);
ValidateRange(microseconds, MinMicroSeconds, MaxMicroSeconds);
long totalTicks = (long)days * TicksPerDay;
AddTicks((long)hours * TicksPerHour);
AddTicks(minutes * TicksPerMinute);
AddTicks(seconds * TicksPerSecond);
AddTicks(milliseconds * TicksPerMillisecond);
AddTicks(microseconds * TicksPerMicrosecond);
return new TimeSpan(totalTicks);
void AddTicks(long ticksToAdd)
{
long previousTotalTicks = totalTicks;
totalTicks += ticksToAdd;
if (previousTotalTicks >> 63 == ticksToAdd >> 63 && previousTotalTicks >> 63 != totalTicks >> 63)
ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
}
}
private static void ValidateRange(long value, long min, long max)
{
if (value < min || value > max)
ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
}
}
public abstract class TimeSpanOverflowChecksContext
{
protected const long TicksPerMicrosecond = 10;
protected const long TicksPerMillisecond = TicksPerMicrosecond * 1000;
protected const long TicksPerSecond = TicksPerMillisecond * 1000;
protected const long TicksPerMinute = TicksPerSecond * 60;
protected const long TicksPerHour = TicksPerMinute * 60;
protected const long TicksPerDay = TicksPerHour * 24;
internal const long MaxDays = long.MaxValue / TicksPerDay;
internal const long MinDays = long.MinValue / TicksPerDay;
internal const long MaxHours = long.MaxValue / TicksPerHour;
internal const long MinHours = long.MinValue / TicksPerHour;
internal const long MaxMinutes = long.MaxValue / TicksPerMinute;
internal const long MinMinutes = long.MinValue / TicksPerMinute;
internal const long MaxSeconds = long.MaxValue / TicksPerSecond;
internal const long MinSeconds = long.MinValue / TicksPerSecond;
internal const long MaxMilliSeconds = long.MaxValue / TicksPerMillisecond;
internal const long MinMilliSeconds = long.MinValue / TicksPerMillisecond;
internal const long MaxMicroSeconds = long.MaxValue / TicksPerMicrosecond;
internal const long MinMicroSeconds = long.MinValue / TicksPerMicrosecond;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment