Rounding Time with NodaTime

I have some LocalDateTimes where I need to round the time of day up/down to the nearest minute number according to a list of defined minutes. For instance, if I have the datetime 2018-03-20T12:13:47 and I have to round it to the next half hour (so minutes are either 00 or 30), I would get 2018-03-20T12:30:00. Rounding down would give 2018-03-20T12:00:00.

The rounding could be to any arbitrary minute, e.g. every 10 minutes (0, 10, 20, 30, 40, 50), every quarter hour (0, 15, 30, 45), or say to a quarter to or after (15, 45). This means the rounded date could end up on another date.

Is there any functionality in NodaTime that could help with this rounding, or do I have to write it myself? I have a working version now, but it includes double loops and adding hours and minutes to a midnight.

Jon Skeet

There's a sort of functionality that will help: the idea of "adjusters". If you write your own method that returns a Func<LocalTime, LocalTime> you can apply that to LocalTime, LocalDateTime, OffsetDateTime and OffsetTime (in 2.3+) using the With method.

That will help with the truncate case pretty easily:

using System;
using NodaTime;

class Test
    static void Main()
        var truncator = CreateTruncator(10);
        var start = new LocalDateTime(2018, 3, 21, 7, 32, 15);
        var truncated = start.With(truncator);
        Console.WriteLine(truncated); // 2018-03-21T07:30:00

    static Func<LocalTime, LocalTime> CreateTruncator(int minuteGranularity) =>
        input => new LocalTime(
            input.Minute / minuteGranularity * minuteGranularity);

(Note that if you give that a minuteGranularity of 7 or something else that's not a factor of 60, it will still truncate to periods that always start on the hour.)

However, there's no way of that advancing the date. The adjuster mechanism works for a Func<LocalDateTime, LocalDateTime> as well - you can apply that to LocalDateTime, OffsetDateTime. But yes, you'd need to write the code to do that adjustment yourself. As you say, there are lots of different options you might want for rounding, and it's hard for Noda Time to cover all the possible use cases.

I'd definitely recommend writing a method to create an adjuster rather than just writing a method accepting a LocalDateTime and a number of minutes - it provides a way of you passing the adjuster around very easily, and as I've shown, it fits in with Noda Time's own adjustments. (Look in the TimeAdjusters and DateAdjusters classes for the ones we provide out of the box.)

If you need help writing a specific adjuster, that's probably worth asking in a separate question. I wouldn't expect it to need double loops etc.


See more on this question at Stackoverflow