Reputation: 15923
I have written a scalar function to try to return a datetime that is rounded down to a specified multiple.
The function is based from this formula
select dateadd(minute, (datediff(minute, 0, '2014-01-05 12:23:05') / 30) * 30, 0)
that correctly returns
2014-01-05 12:00:00.000
I tried to write it as a function:
CREATE FUNCTION [dbo].[RoundTime] (@Time datetime, @RoundTo float) RETURNS datetime
AS
BEGIN
DECLARE @RoundedTime datetime
SET @RoundedTime = dateadd(minute, (datediff(minute, 0, @Time) / @RoundTo) * @RoundTo, 0)
RETURN @RoundedTime
END
but when I run a select statement
select dbo.roundtime('2014-01-05 12:23:05',30)
or even when I force the time into a datetime format to match the definition
select businessusers.dbo.roundtime(cast('2014-01-05 12:23:05' as datetime),30)
it returns this:
2014-01-05 12:23:00.000
What am i doing wrong?
Upvotes: 0
Views: 834
Reputation: 77717
This method of rounding relies on the division's being integral, as Lamak has correctly noted.
In SQL Server, division is integral when both operands are integers.
In your first example, the result of DATEDIFF
is int
by definition and the 30
is recognised by the parser as an int
too. Therefore, the query works as expected.
In your function, however, one of the operands of the division, @RoundTo
, is declared to be a float
. That prescribes SQL Server to use normal division, preventing any rounding from happening.
There are basically two ways of resolving this. One is, obviously, to change the type of @RoundTo
to an integer type. (Usually int
works fine, even though its capacity may appear too large for this use case.)
The other method, if for some reason you want to stick with the float
(e.g. you want to be able to round down to the nearest half-minute or some such interval), would be to apply the FLOOR()
function to the division's result:
floor(datediff(minute, 0, @Time) / @RoundTo) * @RoundTo
However, if @RoundTo
was not a whole number, the result of multiplication by @RoundTo
might not be whole either. To make the resulting intervals consistent, you would probably need to convert the minutes to seconds at the final stage:
dateadd(second, floor(datediff(minute, 0, @Time) / @RoundTo) * @RoundTo * 60, 0)
Upvotes: 2