T-SQL Convert String to Time for DATEDIFF() Calculation - sql-server

I am having a problem converting a string to time in order to perform a DATEDIFF() calculation with another legitimate time field. My end goal is to create a report in SSRS.
The only way I have been able to perform this task in SSMS is by using:
,CAST(msdb.dbo.agent_datetime('19000101', P.[3]+'00') AS TIME(0)) AS [Reached Time]
P.[3] being a string field (Example value of 0744). I added the +'00' for milliseconds due to the format of "msdb.dbo.agent_datetime".
The above works perfectly in SSMS and gives a value of 07:44:00. However, this "msdb.dbo.agent_datetime" function will NOT work in SSRS. My report will only run if I remove any mention of "msdb.dbo.agent_datetime".
I need a different method to change this string value, such as 0744, to a legitimate time value of 07:44 or 07:44:00.
I have tried:
FORMAT(CONVERT(INT,P.[3]), 'HH:mm', 'en-us') --Returns value of HH:mm
CONVERT(VARCHAR, P.[3], 24) --Returns value of 0744
CONVERT(TIME, P.[3]) --Returns value of 00:00:00.0000000
CAST(P.[3] AS TIME) --Returns value of 00:00:00.0000000
CAST(CONVERT(INT,P.[3]) AS TIME) --Explicit conversion from data type int to time is not allowed.
Other combinations also error out. I have even tried to format the time in SSRS with no success.
I can make it look like a time by modifying the string, but then can't perform a calculation with DATEDIFF()
Please Help!

I can't see any reason why you cannot use the function in your dataset query assuming permissions are correct for the credentials set against the datasource.
Anyway, to cheat you could recreate the function in you own database and use that version.
If you run the following in SSMS
USE msdb
exec sp_helptext 'dbo.agent_datetime'
You will get the script as follows,
CREATE FUNCTION agent_datetime(#date int, #time int)
RETURNS DATETIME
AS
BEGIN
RETURN
(
CONVERT(DATETIME,
CONVERT(NVARCHAR(4),#date / 10000) + N'-' +
CONVERT(NVARCHAR(2),(#date % 10000)/100) + N'-' +
CONVERT(NVARCHAR(2),#date % 100) + N' ' +
CONVERT(NVARCHAR(2),#time / 10000) + N':' +
CONVERT(NVARCHAR(2),(#time % 10000)/100) + N':' +
CONVERT(NVARCHAR(2),#time % 100),
120)
)
END
Use this to create the same function in your database.
Of course now, you see how simple the function is, you could create one that does the entire conversion in a single step.

This works for me on SSMS:
declare #sourcetime char(4) = '1714'
select cast(left(#sourcetime, 2) + ':' + right(#sourcetime, 2) as datetime)

Related

Adding two time fields together

I have two columns within my table they are set as nvarchar fields but contain time values.
one column is a time field one is the duration field
eg.
Time 1 = 15:05:22 (time field)
Time 2 = 00:02:00 (duration field)
I want to output Time 1 + Time 2 = 15:07:22
I have tried CAST(time1 as datetime)+CAST(time2 as datetime)
but I get 1900-01-01 15:07:22.000, and I don't want the date part. I can't use cast as time as I get an error I presume this is because the fields are set as nvarchar and not date/time?
Just cast the result to time to get rid of the date portion:
DECLARE #time_txt varchar(8);
DECLARE #duration_txt varchar(8);
SET #time_txt = '15:05:22';
SET #duration_txt = '00:02:00';
SELECT CAST(CAST(#time_txt as datetime) + CAST(#duration_txt as datetime) as time);
-- yields the time value 15:07:22.0000000
If you need this as a string (for example, in hh:mm:ss format), you can use CONVERT with the appropriate format option:
...
SELECT CONVERT(varchar(8), CAST(#time_txt as datetime) + CAST(#duration_txt as datetime), 108);
-- yields the string 15:07:22
PS: In general, you should use time columns for time values instead of varchar columns. Unfortunately, SQL Server does not have a really good data type for durations (time spans).
select dateadd(second,datediff(second,0,time1),time2) as Time3
from your_table

SQL Server: Transfer YYYYMMDD-HHMMSS to mm/dd/yyyy hh:mm:ss

My SQL Server system is 2016.
As topic, I want to convert YYYYMMDD-HHMMSS to mm/dd/yyyy hh:mm:ss, and use dynamic SQL to fulfill this.
My data looks like this:
ID
20161119-075950
20161117-110952
20161118-153406
The datatype is nvarchar.
While I used the syntax below:
SELECT convert(date,convert(varchar(max),id,130), 130) from abc
An error Conversion failed when converting date and/or time from character string. shows up. I am thinking whether it is because SQL Server cannot identify this YYYYMMDD-HHMMSS as date type, and I need to convert this to YYYYMMDD hh:mm:ss first and then mm/dd/yyyy hh:mm:ss? Feel free to shed some lights. Thanks!
Select CONVERT(VARCHAR(25) , CAST(LEFT(ID , 8) AS DATETIME), 101)
+ ' ' + LEFT(RIGHT(ID , 6) ,2) + ':'
+ SUBSTRING(RIGHT(ID , 6) , 3,2) + ':'
+ RIGHT(ID , 2)
FROM TableName
Try it like this
DECLARE #tbl TABLE(ID NVARCHAR(100));
INSERT INTO #tbl VALUES
('20161119-075950')
,('20161117-110952')
,('20161118-153406');
--This is the actual select you need:
SELECT CAST(LEFT(ID,8) AS DATETIME) + STUFF(STUFF(RIGHT(ID,6),5,0,':'),3,0,':')
FROM #tbl
Your first part is strictly 8 chars long and implicitly casteable (unseperated datetime yyyymmdd). The time part is strictly 6 chars long. I use STUFF to insert the colons. This time can be added to a DATETIME. It will be - again implicitly - casted to DATETIME.
EDIT
To reach the given format you stated in the title just convert the first part first with code 101:
SELECT CONVERT(VARCHAR(10),CAST(LEFT(ID,8) AS DATETIME),101) + ' ' + STUFF(STUFF(RIGHT(ID,6),5,0,':'),3,0,':')
FROM #tbl
This should get the format you want... but there are probably better ways.
select
convert(varchar(16),convert(date,left(ID,8)),101) +
' ' +
substring(substring(ID,10,6),1,2) +
':' +
substring(substring(ID,10,6),3,2) +
':' + substring(substring(ID,10,6),5,2)

How to convert date to 104 format without zero in month using T-SQL

For example have query :
declare #date datetime = '2014-06-18'
SELECT CONVERT(varchar,#date,104)
Response is : 18.06.2014 , but I want month without zero, it should be like : 18.6.2014 is it possible to do something like this ?
declare #date datetime = '2014-06-18'
SELECT CAST(DAY(#date) AS VARCHAR(2)) + '.' +
CAST(MONTH(#date) AS VARCHAR(2)) + '.' +
CAST(YEAR(#date) AS VARCHAR(4))
RESULT: 18.6.2014
It feels a little "kludgy", but the only place the sequence .0 can appear in the output date (assuming you're working with dates after 1000 A.D., which you are for datetime which cannot represent dates before 1753) is when there's a leading 0 on the month. So:
declare #date datetime = '2014-06-18'
SELECT REPLACE(CONVERT(varchar,#date,104),'.0','.')
Produces:
18.6.2014
If you are using SQL Server 2012 or above you can use the CONCAT function:
DECLARE #date DATETIME = '2014-06-18'
SELECT
CONCAT(DAY(#date),'.',MONTH(#date),'.',YEAR(#date))
This will return 18.6.2014
Also, if you want to strip the leading zero of the day (say for 2014-06-06), this will take care of that too (which damien_the_unbeliever's solution will not. Not that it is wrong, the behavior is just different so I wanted to make the distinction since it wasn't explicitly stated)
Reference: http://msdn.microsoft.com/en-us/library/hh231515(v=sql.110).aspx

T-SQL date conversion

I have the following date format 92845 which represent hhmmss.
I would like to convert that to a datetime in SQL.
I could use something like this:
SELECT STUFF(STUFF('84936',3,0,'-'),6,0,'-')
but it appears T-SQL is not liking the hour part.
thank you!
Seems like you wrote the code in the afternoon and now it doesn't work in the morning. If you're going to stuff based on absolute positioning, you need to make sure the string is always the same length. One way to do this is by padding the string with a 0 and then taking the right-most 6 characters. Also, you need : not - for H/M separators.
SELECT STUFF(STUFF(RIGHT('0' + '84936', 6),3,0,':'),6,0,':');
Result:
08:49:36
Now it can be converted to a datetime:
SELECT CONVERT(DATETIME, STUFF(STUFF(RIGHT('0' + '84936', 6),3,0,':'),6,0,':'));
Result:
1900-01-01 08:49:36.000
I don't know where 92845 is currently stored. If it's in a variable, then something like this will work:
declare #t varchar(6) = '92845';
declare #fullt char(6) = RIGHT('000000' + #t,6)
select DATEADD(second,
SUBSTRING(#fullt,1,2) * 3600 +
SUBSTRING(#fullt,3,2) * 60 +
SUBSTRING(#fullt,5,2),0)
Result:
1900-01-01 09:28:45.000
If it's in a column of a result set, then you can do similar manipulations using e.g. subqueries.
try this:
declare #t varchar(6) = '134524'
select CASE WHEN len(#t)=5 then convert(datetime,convert(varchar(10),CAST(getdate() as date))+' '+'0'+LEFT(#t,1)+':'+SUBSTRING(#t,2,2)+':'+SUBSTRING(#t,4,2))
else convert(datetime,convert(varchar(10),CAST(getdate() as date))+' '+LEFT(#t,2)+':'+SUBSTRING(#t,3,2)+':'+SUBSTRING(#t,5,2)) end

what is the best way to store time interval in a SQL server database

i have a table where i want to track time so a valid entry might be:
1 hour 3 mins
47 mins
10 hours
3 mins 14 seconds
what field type is best used for this. i obviously could use varchar . .but i thought there might be something better as i would like to run queries to total the amount of time over a number of records.
Do not use character types to store date/time information.
In SQL Server 2008, you have the time type for this, which is what you should use if your time intervals are less than 1 day. In previous versions, or if you need to store larger intervals, you will have to use datetime or smalldatetime (depending on the precision you need).
Another option would be to choose a time unit - say, minutes - and just use an int value to represent the number of units. Just make sure that (a) the unit you choose is actually precise enough to record the smallest intervals you need to track, and (b) the actual numeric type is large enough to hold the largest intervals you need to track. A smallint might be sufficient for tracking the number of minutes within a day; on the other hand, tracking the number of milliseconds within a 10-year timeframe would have to be stored as a bigint.
Just use integer to store interval in seconds. DATEDIFF returns integer. Write a function that turns it into text. This one needs some adjustmens (so it shows "1 min", not "1 mins"), but should work ok:
CREATE FUNCTION dbo.SecondsToText(#seconds int)
RETURNS VARCHAR(100)
AS
BEGIN
declare #days int;
set #days = #seconds/(3600 * 24);
declare #hours int;
set #hours = (#seconds - #days * 3600 * 24) / 3600;
declare #minutes int;
set #minutes = (#seconds - #days * 3600 * 24 - #hours * 3600) / 60;
set #seconds = (#seconds - #days * 3600 * 24 - #hours * 3600 - #minutes * 60);
RETURN
RTRIM(CASE WHEN #days > 0 THEN CAST(#days as varchar) + ' days ' ELSE '' END +
CASE WHEN #hours > 0 THEN CAST(#hours as varchar) + ' hours ' ELSE '' END +
CASE WHEN #minutes > 0 THEN CAST(#minutes as varchar) + ' minutes ' ELSE '' END +
CASE WHEN #seconds > 0 THEN CAST(#seconds as varchar) + ' seconds ' ELSE '' END)
END
GO
As #Aaronaught said use a date/time or datetime (as necessary) data type to store your values; but these types only store an instance in time and not a time span or duration. You will need to use two fields to store an interval e.g. [time_span_start] and [time_span_end]. The difference between the two will give you the interval.
The longer answer to your question can be answered by downloading a copy of "Developing Time-Oriented Database Applications in SQL" by Richard T. Snodgrass. It's freely available as a PDF, have a look here:
http://www.cs.arizona.edu/~rts/publications.html
Depends on your range of time - either convert everything to seconds and just store that value as an INT, or if your span of times is larger, you might want to use fields for hours, minutes, seconds separately.
Also, SQL Server 2008 introduces a new TIME data type which allows you to store time-only values.
Related to Tony's answer, you can also use a single datetime column relative to a standard start time which is implicit for all intervals - for instance: 1/1/1900 12:00 AM.
In this case it is easy enough for storage:
INSERT INTO tbl (interval) VALUES (DATEADD(s, '1/1/1900', DATEDIFF(s, #starttime, #endtime))
Now this is not obviously easy for doing SUMs of rows, so you could think about adding persisted computed column(s) of DATEDIFF(s, '1/1/1900', interval) to provide seconds to perform SUMs.
Now, here's where it gets interesting for SQL Server:
Because of SQL Server's implementation for converting numbers to and from dates, 0 -> 1/1/1900 12:00 AM, 0.5 -> 1/1/1900 12:00 PM, 1 -> 1/2/1900 12:00 AM etc. i.e. the whole number is treated as the number of days since 1/1/1900 and the fractional part is the fraction within the day. So you CAN actually naively add these to get an interval.
And in fact:
SELECT CONVERT(DATETIME, 1) + CONVERT(DATETIME, 0) + CONVERT(DATETIME, 2) + CONVERT(DATETIME, 0.5)
gives '1900-01-04 12:00:00.000' as expected
So you can do this (going around SUM by converting):
DECLARE #datetest TABLE ( dt DATETIME NOT NULL )
INSERT INTO #datetest ( dt )
VALUES ( 0 )
INSERT INTO #datetest ( dt )
VALUES ( 1 )
INSERT INTO #datetest ( dt )
VALUES ( 2 )
INSERT INTO #datetest ( dt )
VALUES ( 0.5 )
SELECT *
FROM #datetest
SELECT CONVERT(DATETIME, SUM(CONVERT(FLOAT, dt)))
FROM #datetest
I'm not advocating doing this in general, YMMV, and any design solution you choose should be verified against all your requirements.

Resources