I believe the issue is that you're dividing by d.Ticks
, which will truncate (throw away) the decimal place of the result, then you're multiplying by d.Ticks
, which means you're always flooring to the nearest 5 minute interval, rather than rounding.
You first need to cast d.Ticks
to a float
/double
so the division becomes a floating point division, and the decimal portion of the result is retained. You then have a multiple of "5 minutes" expressed as a fraction, which you can then round using Math.Round
to round it up or down (whichever's nearest), and then lastly multiply that by d.Ticks
to get the final result.
Hopefully that's what you're explaining in your question - I verified it using some of your expected outcomes:
class Program {
static void Main(string[] args) {
var tests = new (DateTime Input, string Output)[] {
(new DateTime(2021, 10, 14, 13, 51, 00), "14/10/2021 13:50:00"),
(new DateTime(2021, 10, 14, 13, 53, 00), "14/10/2021 13:55:00"),
(new DateTime(2021, 10, 14, 16, 31, 00), "14/10/2021 16:30:00"),
(new DateTime(2021, 10, 14, 19, 47, 00), "14/10/2021 19:45:00"),
(new DateTime(2021, 10, 14, 11, 29, 00), "14/10/2021 11:30:00"),
(new DateTime(2021, 10, 14, 10, 01, 00), "14/10/2021 10:00:00"),
(new DateTime(2021, 10, 14, 18, 35, 00), "14/10/2021 18:35:00")
};
DateTime Round(DateTime date, TimeSpan interval) {
return new DateTime(
(long)Math.Round(date.Ticks / (double)interval.Ticks) * interval.Ticks
);
}
var interval = TimeSpan.FromMinutes(5);
foreach (var (Input, Output) in tests) {
var actual = Round(Input, interval).ToString();
var expected = Output;
Console.WriteLine($"Actual: {actual}, Expected: {expected}, Pass: {actual == expected}");
}
}
}