Reputation: 3
I am calculating the number of hours from two smalldatetimes. The clients want the hours rounded to the nearest half hour. My calculation is:
hours = datediff(mi,ActualStart,ActualEnd)/60.0
This gives me a float, which I cannot round the way time is rounded. I do know how to round the start and end times, but would rather apply the rounding to the total hours.
I'm using SQL Server 2012
Upvotes: 0
Views: 1158
Reputation: 1038
DATEADD( minute, ( DATEDIFF( minute, 0, dateTimeX ) / 30 ) * 30, 0 ) AS dateTimeRoundDown,
DATEADD( minute, ( DATEDIFF( minute, 0, DATEADD( minute, 30 / 2, dateTimeX ) ) / 30 ) * 30, 0 ) AS dateTimeRoundNearest,
DATEADD( minute, ( DATEDIFF( minute, 0, DATEADD( minute, 30 , dateTimeX ) ) / 30 ) * 30, 0 ) AS dateTimeRoundUp
For more details, see this SO question & answer
Unit Test
DECLARE @start DATETIME = '2017-04-20 21:00:00'
DECLARE @end DATETIME = '2017-04-20 23:00:00'
;WITH CTE_dateTimes AS
(
SELECT @start AS dateTimeX
UNION ALL
SELECT DATEADD( minute, 1, dateTimeX )
FROM CTE_dateTimes
WHERE DATEADD( minute, 1, dateTimeX ) <= @end
)
SELECT dateTimeX,
DATEADD( minute, ( DATEDIFF( minute, 0, dateTimeX ) / 30 ) * 30, 0 ) AS dateTimeRoundDown,
DATEADD( minute, ( DATEDIFF( minute, 0, DATEADD( minute, 30 / 2, dateTimeX ) ) / 30 ) * 30, 0 ) AS dateTimeRoundNearest,
DATEADD( minute, ( DATEDIFF( minute, 0, DATEADD( minute, 30 , dateTimeX ) ) / 30 ) * 30, 0 ) AS dateTimeRoundUp
FROM CTE_dateTimes
OPTION ( MAXRECURSION 200 )
Upvotes: 3
Reputation: 3932
Try this:
round( 48 * ( cast(ActualEnd as float)-cast(ActualStart as float) ),0) /2.0 as HoursRoundedToHalfHour,
Casting a datetime value to float gives you the number of days since a particular date.
Subtracting one from the other gives the number of days between the two datetimes.
Multiply this by 48 to give the number of half-hour intervals.
Calling Round(x,0) rounds this to the nearest integer.
Dividing by 2.0 converts it to Hours.
Example:
create table Times ( ActualStart smalldatetime, ActualEnd smalldatetime )
insert into Times values
('20170125 01:00:00','20170125 01:10:00'),('20170125 01:00:00','20170125 01:15:00'),('20170125 01:00:00','20170125 01:25:00'),('20170125 01:00:00','20170125 01:40:00'),('20170125 01:00:00','20170125 01:55:00'),('20170125 01:00:00','20170125 02:05:00'),('20170125 01:00:00','20170125 02:55:00'),('20170125 01:00:00','20170125 04:14:00'),('20170125 01:00:00','20170125 22:05:00');
select
ActualStart,ActualEnd,
datediff(mi,ActualStart,ActualEnd) as [Minutes],
datediff(mi,ActualStart,ActualEnd)/60 as [Hr],
datediff(mi,ActualStart,ActualEnd)%60 as [Min],
round( 48 * ( cast(ActualEnd as float)-cast(ActualStart as float) ),0) /2.0 as [HoursRoundedToHalfHour]
from Times
ActualStart ActualEnd Minutes Hr Min HoursRoundedToHalfHour
----------------------- ----------------------- ----------- ----------- ----------- ----------------------
2017-01-25 01:00:00 2017-01-25 01:10:00 10 0 10 0
2017-01-25 01:00:00 2017-01-25 01:15:00 15 0 15 0.5
2017-01-25 01:00:00 2017-01-25 01:25:00 25 0 25 0.5
2017-01-25 01:00:00 2017-01-25 01:40:00 40 0 40 0.5
2017-01-25 01:00:00 2017-01-25 01:55:00 55 0 55 1
2017-01-25 01:00:00 2017-01-25 02:05:00 65 1 5 1
2017-01-25 01:00:00 2017-01-25 02:55:00 115 1 55 2
2017-01-25 01:00:00 2017-01-25 04:14:00 194 3 14 3
2017-01-25 01:00:00 2017-01-25 22:05:00 1265 21 5 21
Upvotes: 0
Reputation: 12243
Here is one way you could achieve what you are after, which should give you enough to make a start on your problem:
declare @t table(s datetime, e datetime);
insert into @t values
('20170125 01:00:00','20170125 01:10:00'),('20170125 01:00:00','20170125 01:15:00'),('20170125 01:00:00','20170125 01:25:00'),('20170125 01:00:00','20170125 01:40:00'),('20170125 01:00:00','20170125 01:55:00'),('20170125 01:00:00','20170125 02:05:00'),('20170125 01:00:00','20170125 02:55:00'),('20170125 01:00:00','20170125 04:14:00'),('20170125 01:00:00','20170125 22:05:00');
select datediff(mi,s,e) as MinutesDiff
,datediff(mi,s,e) / 60 as HoursDiff
,datediff(mi,s,e) % 60 as MinutesRemain
-- Take the Minutes Remaining after all the full hours are removed,
-- and integer divide by 15 to get the number of full quarter hours
-- and then round to the nearest number of minutes to add on.
,case round(datediff(mi,s,e) % 60 / 15,0)
when 0 then 0
when 1 then 30
when 2 then 30
when 3 then 60
end as MinutesRounded
from @t;
Output:
+-------------+-----------+---------------+----------------+
| MinutesDiff | HoursDiff | MinutesRemain | MinutesRounded |
+-------------+-----------+---------------+----------------+
| 10 | 0 | 10 | 0 |
| 15 | 0 | 15 | 30 |
| 25 | 0 | 25 | 30 |
| 40 | 0 | 40 | 30 |
| 55 | 0 | 55 | 60 |
| 65 | 1 | 5 | 0 |
| 115 | 1 | 55 | 60 |
| 194 | 3 | 14 | 0 |
| 1265 | 21 | 5 | 0 |
+-------------+-----------+---------------+----------------+
Upvotes: 0