Rounding DateTime objects
Floor
long ticks = date.Ticks / span.Ticks;
return new DateTime( ticks * span.Ticks, date.Kind );
Round (up on midpoint)
long ticks = (date.Ticks + (span.Ticks / 2) + 1)/ span.Ticks;
return new DateTime( ticks * span.Ticks, date.Kind );
Ceiling
long ticks = (date.Ticks + span.Ticks - 1)/ span.Ticks;
return new DateTime( ticks * span.Ticks, date.Kind );
This will let you round to any interval given. It's also slightly faster than dividing and then multiplying the ticks.
public static class DateTimeExtensions
{
public static DateTime Floor(this DateTime dateTime, TimeSpan interval)
{
return dateTime.AddTicks(-(dateTime.Ticks % interval.Ticks));
}
public static DateTime Ceiling(this DateTime dateTime, TimeSpan interval)
{
var overflow = dateTime.Ticks % interval.Ticks;
return overflow == 0 ? dateTime : dateTime.AddTicks(interval.Ticks - overflow);
}
public static DateTime Round(this DateTime dateTime, TimeSpan interval)
{
var halfIntervalTicks = (interval.Ticks + 1) >> 1;
return dateTime.AddTicks(halfIntervalTicks - ((dateTime.Ticks + halfIntervalTicks) % interval.Ticks));
}
}
You should also be clear if you want your rounding to:
- be to the start, end or middle of the interval
- start is the easiest and often the expected but you should be clear in your initial spec.
- How you want boundary cases to round.
- normally only an issue if you are rounding to the middle rather than the end.
- Since rounding to the middle is an attempt at a bias free answer you need to use something like Bankers Rounding technically round half even to be truly free from bias.
It is quite likely that you really only care about the first point but in these 'simple' questions the resulting behaviour can have far reaching consequences as you use it in the real world (often at the intervals adjacent to zero)
tvanfosson's solution's cover all the cases listed in 1. The midpoint example is biased upwards. It is doubtful that this would be a problem in time related rounding.