Calculate the difference in minutes between startTime and endTime - sql

I have to calculate the difference from StartTime and EndTime. If the EndTime is less then 15 minutes that of StartTime I have to show an error.
CREATE PROCEDURE [Travel].[TravelRequirementValidate]
#Action char(1)
,#CallId int
,#PhaseId int
,#ShipId int
,#CallStartDate datetime
,#CallEndDate DATETIME
,#CallStartTime datetime
,#CallEndTime datetime
,#LanguageId int
,#SessionGroup nvarchar(100)
,#SessionPlace nvarchar(100)
,#ActiveFlg tinyint
,#WarningMessage nvarchar(300)=NULL output
,#Minutes int
as
if #Action in ('I','U')
begin
#Minutes=select DATEDIFF(#CallStartDate,#CallStartTime,#CallEndTime) from [Travel].[TravelRequirement]
if #Minutes<=15
begin
raiserror(3,11,1) --CallEndTime must be equals or higher than 15 minutes
return
end
end
This code doesn't work. I've got an error for the first parameter of DATEDIFF (invalid parameter 1 specified for datediff).
How can I fix my code?
EDIT
I changed #Minutes=select
DATEDIFF(#CallStartDate,#CallStartTime,#CallEndTime) from
[Travel].[TravelRequirement]
in
declare #Diff int
#Diff=select DATEDIFF(#Minutes,#CallStartTime,#CallEndTime) from [Travel].[TravelRequirement]
but I have the same error

Function DATEDIFF wants as first parameter the portion of time.
The correct use of it is:
DATEDIFF(minute, #CallStartTime, #CallEndTime)

The correct syntax for that expression would be:
select #Minutes = datediff(minute, #CallStartTime, #CallEndTime)
from [Travel].[TravelRequirement];
However, this does't make sense, because -- presumably -- the table has more than one row. Which row do you want?

I would suggest using seconds and devide by 60.0 - provides a more accurate result:
select #Minutes = datediff(second, #CallStartTime, #CallEndTime)/60.0
from [Travel].[TravelRequirement];

Related

T-SQL - Arithmetic overflow while working with milliseconds

I have a very simple script in TSQL that tries to convert a timestamp in milliseconds to a DATETIME data type. This also includes the local time offset.
DECLARE #Time AS BIGINT
SET #Time = 1413381394000
SELECT DATEADD(MILLISECOND,
#Time - DATEDIFF(MILLISECOND, GETDATE(), GETUTCDATE()), CAST('1970-01-01 00:00:00' AS DATETIME)) AS [Datetime]
The error it's giving me all the time is:
Arithmetic overflow error converting expression to data type int.
Now, I don't have any explicit int variables in this query, and any CAST() to BIGINT or DECIMAL(13,0) I've did, resulted in the same error.
Which is the wrong part in this query? Is int the default return data type of DATEDIFF()?
I know that I could just divide #Time by 1000 and work with SECONDS instead of MILLISECONDS, I just want to know if there is a way to work directly with milliseconds, since the idea is to use this script as an Inline Table-Valued Function (cannot use scalar ones for other reasons outside this query).
Your calculation for local offset has the potential to be wrong by an hour due to Daylight Savings Time. DATEDIFF(MILLISECOND, GETDATE(), GETUTCDATE()) will only get the current offset and not the offset for the given date. Conversions to and from UTC and local time are generally best handled in application or SQLCLR code due to SQL's lack of functionality for this purpose. See How can I get the correct offset between UTC and local times for a date that is before or after DST?.
In DATEADD (Transact-SQL) Microsoft states that:
number
Is an expression that can be resolved to an int that is added
to a datepart of date. User-defined variables are valid.
If you specify a value with a decimal fraction, the fraction is
truncated and not rounded.
Therefore, you cannot directly work with millisecond values larger than the maximum value for an int which supports a range of -2^31 (-2,147,483,648) to 2^31-1 (2,147,483,647) as stated in int, bigint, smallint, and tinyint (Transact-SQL). You'll have to do to separate date adds and some modulo division.
DECLARE #Time bigint
DECLARE #Seconds int
DECLARE #RemainingMilliseconds int
DECLARE #EpochDate datetime
SET #Time = 1413381394000
SET #EpochDate = '1970-01-01 00:00:00'
SET #Seconds = #Time / 1000
SET #RemainingMilliseconds = #Time % 1000
SELECT DATEADD(MILLISECOND, #RemainingMilliseconds, DATEADD(SECOND,#Seconds, #EpochDate))
DateDiff() does indeed return an int, but I suspect that it's DateAdd() that's giving you the error message.
You'll need to work in that precision, unfortunately, as you said you wanted to avoid, as you're wanting to work in milliseconds.
DATEADD (datepart , number , date )
number Is an expression that can be resolved to an int that is added to a datepart of date. User-defined variables are valid.
You could obviously code your away around it with loops or something, but obviously there's a cost/benefit there that you would need to go through.
The second parameter to DATEADD should be INT not BIGINT. Convert to seconds and then pass to date add.
DECLARE #Time AS BIGINT
SET #Time = 1413381394000
DECLARE #seconds AS INT = #Time / 1000
SELECT DATEADD(SECOND, #seconds, ...)
If you don't want to lose milliseconds, you can add a second step:
DECLARE #milliseconds INT = #Time % 1000
SELECT DATEADD(MILLISECOND, #milliseconds, <date from before>)

How to find total time difference bwteen date range

I have created a procedure which returns time difference in minutes between 2 time i.e. ShiftStartTime, ShiftEndtime.
Now problem is that if i pass 2 date ranges then how i will calculate total shiftTime between those 2 dates e.g. TimeDifference returns me 480 min for today but i want to find total ShiftDifference for 3 days or more, then how i will add TimeDifference for more than 1 day ?
CREATE PROCEDURE GetShiftTotalDuration
#ShiftID int = 9,
#FromDate DateTime,
#ToDate DateTime
AS
BEGIN
Declare #TimeDifference int
Declare #ShiftStartTime time
Declare #ShiftEndTime time
Set #ShiftStartTime = (Select DeparmentShiftsHistory.StartTime from DeparmentShiftsHistory
where DeparmentShiftsHistory.Shift_ID= 9)
Set #ShiftEndTime = (Select DeparmentShiftsHistory.EndTime from DeparmentShiftsHistory
where DeparmentShiftsHistory.Shift_ID= 9)
Set #TimeDifference = (Select DATEDIFF(mi,#ShiftStartTime,#ShiftEndTime)) --Returns time difference in seconds
END
GO
I will calculate total shiftTime between those 2 dates
You mention that you will calculate difference between two dates but in your code you declare the variables as time
Declare #ShiftStartTime time
Declare #ShiftEndTime time
Thus DATEDIFF(mi,#ShiftStartTime,#ShiftEndTime) only the difference between the times not the dates
If you want to get the difference between dates you need use datetime datatype
On a side note, you have declared input parameters which have not been used in the proocedure as mentioned by Pradeep

SQL: Using DATEADD with bigints

I have some SQL to convert javascript dates to SQL dates which works great. However, I've encoutered some data which is too large and is causing an exception:
Arithmetic overflow error converting expression to data type int
Here is the SQL in question:
DATEADD(MILLISECOND, cast(569337307200000 as bigint) % 1000, DATEADD(SECOND, cast(569337307200000 as bigint) / 1000, '19700101'))
I am running this on SQL Server 2008.
Just do the problematic DATEADD in two steps, starting with a coarser time unit (seconds, minutes, hours etc.), then dropping back to the fine grained one for the remainder.
Avoid going to the level of weeks and months though as that would require actual calendar calculations and we would prefer the system to handle that.
Example below needs to calculate a start time given a (possibly) large current duration in milliseconds.
-- large durations can overflow the integer argument needed for DATEADD
-- so do as two steps subtracting minutes (60000ms) and then remaining milliseconds.
DATEADD(ms, -large_duration_ms%60000, DATEADD(minute, -large_duration_ms/60000, GETDATE()))
One way I got around the Integer overflow issue was to subtract a more recent date from the microtime unix time stamp.
DATEADD(s, (CreationTimeStamp/1000-1384128000), '2013-11-11') AS CreateDate,
This will not fix the OP's problem because they will still overflow the max on the date column.
According to MSDN, in DATEADD (datepart , number , date )
number is an expression that can be resolved to an int that is added
to a datepart of date. User-defined variables are valid. If you
specify a value with a decimal fraction, the fraction is truncated and
not rounded.
Also notice that even if you give number as an integer, depending on your date & datepart, it could overflow the max range of the date which is 31-12-9999 for sql server 2008
Number has to be an integer. Here is a Test Demo
I had the same problem and I wanted to be meet the datetime range of mssql
Minimun datetime: 1753-01-01 00:00:00.000 (-6847804800)
Maximum datetime: 9999-12-31 23:59:59.997 (253402300799)
To achieve this the only solution I found was to loop to use DATEADD with int range values.
So based on this answer: https://stackoverflow.com/a/2904294/687490
CREATE FUNCTION dbo.fn_ConvertToBigDateTime (#Datetime BIGINT)
RETURNS DATETIME
AS
BEGIN
DECLARE #result datetime = Convert(datetime, '01/01/1970');
DECLARE #LocalTimeOffset BIGINT
,#AdjustedLocalDatetime BIGINT
,#MinIntValue INT
,#MaxIntValue INT
,#RemainingSeconds BIGINT;
-- define int limit
SET #MinIntValue = -2147483648;
SET #MaxIntValue = 2147483647;
-- compute the datetime with the offset
SET #LocalTimeOffset = DATEDIFF(second,GETDATE(),GETUTCDATE())
SET #AdjustedLocalDatetime = #Datetime - #LocalTimeOffset
-- going to the future
WHILE(#AdjustedLocalDatetime>#MaxIntValue)
BEGIN
SET #AdjustedLocalDatetime = #AdjustedLocalDatetime - #MaxIntValue;
SELECT #result = Convert(datetime, dateadd(ss, #MaxIntValue,#result));
END
-- going back in the past
WHILE(#AdjustedLocalDatetime<#MinIntValue)
BEGIN
SET #AdjustedLocalDatetime = #AdjustedLocalDatetime - #MinIntValue;
SELECT #result = Convert(datetime, dateadd(ss, #MinIntValue,#result));
END
RETURN (SELECT DATEADD(second,#AdjustedLocalDatetime, #result))
END;
You can then test the function with :
select dbo.fn_ConvertToBigDateTime(-6847804800) as 'min datetime',
dbo.fn_ConvertToBigDateTime(253402300799) as 'max datetime'
Hope it will help.
You can try converting the millis to days, add the days to beginning of EPOCH, and add the ms part to the date at the end. The problem is that you were trying to convert millis to seconds, which can still be too large number for INT for larger dates.
DATEADD(MILLISECOND,
CAST(myLongDateMs AS BIGINT) % 86400000,
DATEADD(day,
CAST(myLongDateMs AS BIGINT) / 86400000,
'19700101'
)
)
I faced this problem too. In my sql statement, the error occurred when the date time value is null.
My solution is to check whether the date time value is null using "CASE When". Only running the arithmetic when it is not null, and the problem solved.

Datetime comparison error in sql?

I have used date and time validation for scheduling a report...I have to schedule that reports for future date and time only and not previous date and time..I have used this
declare #Dt varchar(50)
declare #Hr varchar(50)
declare #trandate_time_tmp as TIME(0)
select #trandate_time_tmp = getdate()
set #Dt = DATEDIFF (D,#schedule_date ,#trandate_tmp )
set #Hr = DATEDIFF (S,#schedule_date ,#trandate_time_tmp )
if ( #Dt > 0)
begin
raiserror('Schedule Date should not be earlier than system date',16,1)
return
end
if ( #Hr > 0)
begin
raiserror('Schedule Time should not be earlier than system time',16,1)
return
end
For date part it is checking correctly but for time it is throwing error as
The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.
Not exactly answering your question, but perhaps a solution to your problem. You don't need to use DATEDIFF and check the results, you could just compare the two dates.
IF ( #schedule_date <= GETDATE() )
BEGIN
RAISERROR('Schedule date should not be earlier than system date', 16, 1)
RETURN
END
I just ran into this same problem when trying to make a Unix timestamp from a date,
Here's an example of what I was trying to do:
select DATEDIFF(second,'1970-01-01','2200-01-11');
It overflows since DATEDIFF is trying to return a signed integer - which can only hold just over 68 years worth of seconds.
In order to get the Unix timestamp (which I need so I can feed it into Sphinx Search), you can get the difference in minutes first, then cast the result as a big integer and then multiply by 60 seconds:
select CAST(DATEDIFF(minute,'1970-01-01','2200-01-11') AS BIGINT) * 60;
Now we should be able to handle dates that vary in difference of up to 4000 years or so. If you need even more room, simply change out minute with bigger and bigger intervals, and change the seconds multiplier accordingly.

Transforming nvarchar day duration setting into datetime

I have a SQL Server function which converts a nvarchar day duration setting into a datetime value.
The day duration format is >days<.>hours<:>minutes<, for instance 1.2:00 for one day and two hours.
The format of the day duration setting can not be changed, and we can be sure that all data is correctly formatted and present.
Giving the function a start time and the day duration setting it should return the end time.
For instance: 2010-01-02 13:30 ==> 2010-01-03 2:00
I'm using a combination of charindex, substring and convert methods to calculate the value,
which is kind of slow and akward. Is there any other way to directly convert this day duration setting into a datetime value?
Not from what I can see. I would end up with a similar bit of SQL like you, using charindex etc. Unfortunately it's down to the format the day duration is stored in. I know you can't change it, but if it was in a different format then it would be a lot easier - the way I'd usually do this for example, is to rationalise the duration down to a base unit like minutes.
Instead of storing 1.2:00 for 1 day and 2 hours, it would be (1 * 24 * 60) + (2 * 60) = 1560. This could then be used in a straightforward DATEADD on the original date (date part only).
With the format you have, all approaches I can think of involve using CHARINDEX etc.
One alternative would be to build a string with the calculation. Then you can run the generated SQL with sp_executesql, specifying #enddate as an output parameter:
declare #startdate datetime
declare #duration varchar(10)
declare #enddate datetime
set #startdate = '2010-01-02 13:30'
set #duration = '0.12:30'
declare #sql nvarchar(max)
set #sql = 'set #enddate = dateadd(mi,24*60*' +
replace(replace(#duration,'.','+60*'),':','+') + ', #startdate)'
exec sp_executesql #sql,
N'#startdate datetime, #enddate datetime out',
#startdate, #enddate out
This creates a string containing set #enddate = dateadd(mi,24*60*0+60*12+30, #startdate) and then runs it.
I doubt this is faster than the regular charindex way:
declare #pos_dot int
declare #day int
declare #hour int
declare #minute int
select
#pos_dot = charindex('.',#duration),
#day = cast(left(#duration, #pos_dot-1) as int),
#hour = cast(left(right(#duration, 5), 2) as int),
#minute = cast(right(#duration, 2) as int),
#enddate = dateadd(mi, 24*60*#day + 60*#hour + #minute, #startdate)