T-Sql 2005 Adding hours to a datetime field with the result within working hours
Question
I have two Datetime fields that I wish to add together. They are in the following format: '01/01/1900 00:00:00'.
The main issue with this is that I want the calculation to only include working hours. The working day is between 08:30 and 17:30 and does not include weekends:
Also if the first field starts out of the working day or is on a weekend then the second field should be added from the start of the next working day.
For example:
`'26/06/2009 15:45:00' + '01/01/1900 09:00:00' = '29/06/1900 15:45:00'
'12/07/2009 14:22:36' + '01/01/1900 18:00:00' = '13/07/1900 08:30:00'
'15/07/2009 08:50:00' + '01/01/1900 04:00:00' = '15/07/2009 12:50:00'`
Im pretty sure that this is going to involve creating a user defined function to work this out but I have no idea how to even start this(I am quite out of my depth here) Could anyone offer me some advice on how to achieve this?
Solution
try this, you might have to put it in a function
DECLARE @Date DATETIME,
@StartOfDay FLOAT,
@EndOfDay FLOAT,
@DateAdd DATETIME
SELECT @Date ='2009-06-26 15:45:00.000',
@StartOfDay = 8.5,
@EndOfDay = 17.5,
@DateAdd = '1900-01-01 09:00:00.000'
--fix up start date
--before start of day, move to start of day
IF ((CAST(@Date - DATEADD(dd,0, DATEDIFF(dd,0,@Date)) AS FLOAT) * 24) < @StartOfDay)
BEGIN
SET @Date = DATEADD(mi, @StartOfDay * 60, DATEDIFF(dd,0,@Date))
END
--after close of day, move to start of next day
IF ((CAST(@Date - DATEADD(dd,0, DATEDIFF(dd,0,@Date)) AS FLOAT) * 24) > @EndOfDay)
BEGIN
SET @Date = DATEADD(mi, @StartOfDay * 60, DATEDIFF(dd,0,@Date)) + 1
END
--move to monday if on weekend
WHILE DATENAME(dw, @Date) IN ('Saturday','Sunday')
BEGIN
SET @Date = @Date + 1
END
--get the number of hours to add and the total hours per day
DECLARE @HoursPerDay FLOAT
DECLARE @HoursAdd FLOAT
SET @HoursAdd = DATEDIFF(hh, '1900-01-01 00:00:00.000', @DateAdd)
SET @HoursPerDay = @EndOfDay - @StartOfDay
--date the time of geiven day
DECLARE @CurrentHours FLOAT
SET @CurrentHours = CAST(@Date - DATEADD(dd,0, DATEDIFF(dd,0,@Date)) AS FLOAT) * 24
--if we stay in the same day, all is fine
IF (@CurrentHours + @HoursAdd <= @EndOfDay)
BEGIN
SET @Date = @Date + @DateAdd
END
ELSE
BEGIN
--remove part of day
SET @HoursAdd = @HoursAdd - (@EndOfDay - @CurrentHours)
--,ove to next day
SET @Date = DATEADD(dd,0, DATEDIFF(dd,0,@Date)) + 1
--loop day
WHILE @HoursAdd > 0
BEGIN
--add day but keep hours to add same
IF (DATENAME(dw,@Date) IN ('Saturday','Sunday'))
BEGIN
SET @Date = @Date + 1
END
ELSE
BEGIN
--add a day, and reduce hours to add
IF (@HoursAdd > @HoursPerDay)
BEGIN
SET @Date = @Date + 1
SET @HoursAdd = @HoursAdd - @HoursPerDay
END
ELSE
BEGIN
--add the remainder of the day
SET @Date = DATEADD(mi, (@HoursAdd + @StartOfDay) * 60, DATEDIFF(dd,0,@Date))
SET @HoursAdd = 0
END
END
END
END
SELECT @Date
Hope that helps
OTHER TIPS
you could use the dayofweek function and some in-line case statements;
http://www.smallsql.de/doc/sql-functions/date-time/dayofweek.html
http://www.tizag.com/sqlTutorial/sqlcase.php
so, you'd perform the calculation if the dayofweek function didn't return sat. or sun.; else return a null.
I think you could get away without writing a user-defined function, but the sql statement would look a bit messy. but then again most non-basic sql statements all look a bit messy!
/*
This sample down below updating previous material an shown what's happened if you want to ADD more than 23 hours to a date:
*/
USE [REP]
GO
/****** Object: UserDefinedFunction [dbo].[fct_add_hours_depend_workshift_L-V_0830_1830] Script Date: 11/26/2018 3:22:37 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*
UTILIZATION
SELECT
GETDATE () as crt_date
,[rep].[dbo].[fct_add_hours_depend_workshift_L-V_0830_1830]
(
GETDATE()
,number_of_hours_as_integer -- ex: 96
) as_expiration_date
ORIGINAL SOURCE:
https://stackoverflow.com/questions/1130721/t-sql-2005-adding-hours-to-a-datetime-field-with-the-result-within-working-hours
*/
ALTER FUNCTION [dbo].[fct_add_hours_depend_workshift_L-V_0830_1830]
(
@Date DATETIME,
@HrsAdd INT
)
RETURNS DATETIME
as
BEGIN
DECLARE @StartOfDay FLOAT,
@EndOfDay FLOAT
-- workshift declaration de la 8:30 la 18:30
SELECT @StartOfDay = 8.5,
@EndOfDay = 18.5
--fix up start date
--before start of day, move to start of day
IF ((CAST(@Date - DATEADD(dd,0, DATEDIFF(dd,0,@Date)) AS FLOAT) * 24) < @StartOfDay)
BEGIN
SET @Date = DATEADD(mi, @StartOfDay * 60, DATEDIFF(dd,0,@Date))
END
--after close of day, move to start of next day
IF ((CAST(@Date - DATEADD(dd,0, DATEDIFF(dd,0,@Date)) AS FLOAT) * 24) > @EndOfDay)
BEGIN
SET @Date = DATEADD(mi, @StartOfDay * 60, DATEDIFF(dd,0,@Date)) + 1
END
--move to monday if on weekend
WHILE DATENAME(dw, @Date) IN ('Saturday','Sunday')
BEGIN
SET @Date = @Date + 1
END
--get the number of hours to add and the total hours per day
DECLARE @HoursPerDay FLOAT
DECLARE @HoursAdd FLOAT
DECLARE @DateAdd DATETIME
/*
Pentru ca scriptul initial nu-ti permite sa adaugi mai mult de 23 ore am facut o modificare:
- daca trebuie sa adaug mai putin de 23 de ore merg pe clasicul definit de dezvoltator
- daca trebuie sa adaug mai mult de 23 ore fac impartirea la 23 cu rest : ex 96 impartit cu rest la 23 = 4 zile (de workshift L-V 8:30-18:30) si 4 ore (de workshift L-V 8:30-18:30) si voi folosi numai intregul sau daca vrei 96/23 si iau modulo
*/
-- Do I have to add more than 23 hours?
IF (@HrsAdd > 23)
BEGIN
SET @DateAdd = DATEADD
(dd,
@HrsAdd/23,
CAST('1900-01-01 00:00:00.000' as DATETIME)
)
END
ELSE
SET @DateAdd = CAST('1900-01-01 ' + CAST(@HrsAdd as NVARCHAR(2)) + ':00:00.000' as DATETIME)
SET @HoursAdd = DATEDIFF(hh, '1900-01-01 00:00:00.000', @DateAdd)
SET @HoursPerDay = @EndOfDay - @StartOfDay
--date the time of geiven day
DECLARE @CurrentHours FLOAT
SET @CurrentHours = CAST(@Date - DATEADD(dd,0, DATEDIFF(dd,0,@Date)) AS FLOAT) * 24
--if we stay in the same day, all is fine
IF (@CurrentHours + @HoursAdd <= @EndOfDay)
BEGIN
SET @Date = @Date + @DateAdd
END
ELSE
BEGIN
--remove part of day
SET @HoursAdd = @HoursAdd - (@EndOfDay - @CurrentHours)
--,ove to next day
SET @Date = DATEADD(dd,0, DATEDIFF(dd,0,@Date)) + 1
--loop day
WHILE @HoursAdd > 0
BEGIN
--add day but keep hours to add same
IF (DATENAME(dw,@Date) IN ('Saturday','Sunday'))
BEGIN
SET @Date = @Date + 1
END
ELSE
BEGIN
--add a day, and reduce hours to add
IF (@HoursAdd > @HoursPerDay)
BEGIN
SET @Date = @Date + 1
SET @HoursAdd = @HoursAdd - @HoursPerDay
END
ELSE
BEGIN
--add the remainder of the day
SET @Date = DATEADD(mi, (@HoursAdd + @StartOfDay) * 60, DATEDIFF(dd,0,@Date))
SET @HoursAdd = 0
END
END
END
END
RETURN @Date
END