We need to floor a time to the nearest arbitrary interval (represented by e.g. a Timespan or a Duration).
Assume for an example that we need to floor it to the nearest ten minutes. e.g. 13:02 becomes 13:00 and 14:12 becomes 14:10
Without using Nodatime you could do something like this:
// Floor
long ticks = date.Ticks / span.Ticks;
return new DateTime( ticks * span.Ticks );
Which will use the ticks of a timespan to floor a datetime to a specific time.
It seems NodaTime exposes some complexity we hadn't considered before. You can write a function like this:
public static Instant FloorBy(this Instant time, Duration duration)
=> time.Minus(Duration.FromTicks(time.ToUnixTimeTicks() % duration.BclCompatibleTicks));
But that implementation doesn't seem correct. "Floor to nearest ten minutes" seems to be dependent on timezone/offset of the time. While might be 13:02 in UTC, in Nepal which has an offset of +05:45, the time would be 18:47.
This means that in UTC, flooring to the nearest ten minutes, would mean subtracting two minutes, while in Nepal, it would mean subtracting seven minutes.
I feel like I should be able to round a ZonedDateTime or an OffsetDateTime by an arbitrary timespan somehow. I can get close by writing a function like this
public static OffsetDateTime FloorToNearestTenMinutes(this OffsetDateTime time)
{
return time
.Minus(Duration.FromMinutes(time.Minute % 10))
.Minus(Duration.FromSeconds(time.Second));
}
but that doesn't allow me to specify an arbitrary duration, as the OffsetDateTime has no concept of ticks.
How do I round an Instant/ZonedDateTime/OffsetDateTime correctly, with an arbitrary interval, taking into account time zones?