tags:

views:

1079

answers:

3

I want to round dates/times to the nearest interval for a charting application. I'd like an extension method signature like follows so that the rounding can be acheived for any level of accuracy:

static DateTime Round(this DateTime date, TimeSpan span);

The idea is that if I pass in a timespan of ten minutes, it will round to the nearest ten minute interval. I can't get my head around the implementation and am hoping one of you will have written or used something similar before.

I think either a floor, ceiling or nearest implementation is fine.

Any ideas?

Edit: Thanks to @tvanfosson & @ShuggyCoUk, the implementation looks like this:

public static class DateExtensions {
    public static DateTime Round(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks / span.Ticks) + (span.Ticks / 2) + 1;
        return new DateTime(ticks * span.Ticks);
    }
    public static DateTime Floor(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks / span.Ticks);
        return new DateTime(ticks * span.Ticks);
    }
    public static DateTime Ceil(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks + span.Ticks - 1) / span.Ticks;
        return new DateTime(ticks * span.Ticks);
    }
}

And is called like so:

DateTime nearestHour = DateTime.Now.Round(new TimeSpan(1,0,0));
DateTime minuteCeiling = DateTime.Now.Ceil(new TimeSpan(0,1,0));
DateTime weekFloor = DateTime.Now.Floor(new TimeSpan(7,0,0,0));
...

Cheers!

+1  A: 

Just use the Ticks, using that to divide, floor/ceil/round the value, and multiply it back.

Lucero
+17  A: 

Floor

long ticks = date.Ticks / span.Ticks;

return new DateTime( ticks * span.Ticks );

Round (up on midpoint)

long ticks = (date.Ticks + (span.Ticks / 2) + 1)/ span.Ticks;

return new DateTime( ticks * span.Ticks );

Ceiling

long ticks = (date.Ticks + span.Ticks - 1)/ span.Ticks;

return new DateTime( ticks * span.Ticks );
tvanfosson
your excellent edits will make my task of referencing you harder ;)
ShuggyCoUk
Round (up on midpoint) gives me strange values.E.g. if date is '01/01/2010 00:00:00' span is 5 secs I get '25/03/2014 06:13:25'.Think the code should be:long ticks = (date.Ticks + (span.Ticks / 2)) / span.Ticks;
openshac
@openshac -- thanks for the correction. Cut/paste error. Note that I think you still need to add one to make sure that it rounds up.
tvanfosson
+3  A: 

You should also be clear if you want your rounding to:

  1. 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.
  2. 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.

ShuggyCoUk