casting odd smallint time to to datetime format - sql

I'm working with a db (SQL server 2008), and have an interesting issue with times stored in the db.
The DBA who originally set it up was crafty and stored scheduled times as smallints in 12-hour form-- 6:00AM would be represented as 600. I've figured out how to split them into hours and minutes like thus:
select floor(time/100) as hr, right(time, 2) as min from table;
What I want to do is compare these scheduled times to actual times, which are stored in the proper datetime format. Ideally, I would do this with two datetime fields and use datediff() between them, but this would require converting the smallint time into a datetime, which I can't figure out.
Does anyone have suggestions on how to do this?
Thanks in advance.

Can think of two ways to do that. The first is to build a string in the HH:MM format, and cast that to datetime. The second is to convert the smallint format to float with the number of days as unit. The number of days is the internal time representation, so you can cast that to datetime too.
Example code:
declare #i smallint
set #i = 621
-- Cast to a string '6:21', then to a datetime
select cast(CAST(#i / 100 as varchar) + ':' + CAST(#i % 100 as varchar)
as datetime)
-- Convert to number of days, which is the interal datetime format
select cast((#i/100)/24.0 + (#i%100)/(24*60.0) as datetime)
P.S. If you divide an integer by another integer. the result is a third integer: 100 / 24 = 4. If you divide an integer by a float, the result is a float: 100 / 24.0 = 4.16666.

Since you are using SQL Server 2008, you can take advantage of the new Time data type. In order to convert the integer to a time value, we need to assume that the last two digits are minutes. To get the minute portion, divide by 100, take the integer portion and subtract it from the initial value. So in the case of 621 we get:
621 - Floor(621/100)* 100
621 - Floor(6.21)*100
621 - 6*100
621 - 600 = 21 minutes
For the hour portion, we can simply take the integer value after dividing by 100.
Create Table #Test( IntVal smallint not null )
Insert #Test Values( 621 )
Insert #Test Values( 2359 )
Insert #Test Values( 1200 )
Insert #Test Values( 1201 )
Insert #Test Values( 1159 )
Select Z.TimeVal, GetDate(), DateDiff(hh, Z.TimeVal, Cast(GetDate() As Time(0)))
From (
Select Cast(DateAdd(mi
, IntVal - Floor(IntVal/100)*100
, DateAdd(hh, Floor(IntVal/100), 0)
) As Time(0)) As TimeVal
From #Test
) As Z
Part of the trick here is to use DateAdd(hh, Floor(IntVal/100), 0) which does a DateAdd against the zero value for datetime.

Related

I have column x in hh:mm format of datatype varchar in SQL Server and I want to perform sum on that 'x' column

I have column x in hh:mm format of datatype varchar in SQL Server and I want to perform sum on that x column.
I created a user-defined function to convert total min into hh:mm format.
Then I tried to perform sum to calculate total duration:
sum(cast(new_totalmin AS Int))
also i want total of HH:mm exactly as example
4:20
+1:10
5:30
5 hour: 30 minute
or i can do one thing here insted hh:mm i keep column as it is which is totalmin as int once sum cal insted of hh:mm (hh.mm which is in decimal also ok for me PSB it will be ok for me ':' or '.' format )
(60 min --> 1:00 --> 1.00
90 min --> 1:30 -->1.30
---------------------------------
sum --> 150 min -->2:30 --> 2.30)
but it did not work.
I got an error like
Conversion failed when converting the varchar value '01:00' to data type int
DECLARE #SampleData AS TABLE (HourMinutes VARCHAR(10));
INSERT INTO #SampleData VALUES ('4:32');
INSERT INTO #SampleData VALUES ('5:28');
INSERT INTO #SampleData VALUES ('6:00');
INSERT INTO #SampleData VALUES ('7:10');
SELECT * FROM #SampleData
SELECT SUM(datediff(minute, 0, HourMinutes)) TotalMinute
FROM #SampleData
You will get following output
hh:mm is a varchar data and applying SUM will not work on it.
As you are telling that you are already having a function, I would suggest you to perform sum of the minutes and then later convert them to hh:mm
SELECT ... , YourUserDefinedFunction(sum(minuteData)) as minutesInHHMM_Format
FROM ...
WHERE ...
GROUP BY ...
I would recommend that you store numeric values -- such as the number of minutes -- as a number rather than a string.
The challenge is converting the value back to an HH:MM format. SQL Server does not support time values of 24 hours or greater, so you need to use string manipulations.
Assuming that your values are all less than 24 hours, you can use:
select sum(datediff(minute, 0, hhmm)) as num_minutes,
concat(sum(datediff(minute, 0, hhmm)) / 60, ':',
format(sum(datediff(minute, 0, hhmm)) % 60, '00')
)
from t;
The result here is a string, so this can exceed 24 hours.
A more general solution eschews date/times altogether:
select sum(v.minutes) as num_minutes,
concat(sum(v.minutes) / 60, ':',
format(sum(v.minutes) % 60, '00')
)
from t cross apply
(values (left(t.hhmm, charindex(':', t.hhmm) - 1) * 60 + right(t.hhmm, 2))
) v(minutes);
Here is a db<>fiddle.

Convert varchar of HH:MM:SS to minutes value

Using SQL Server, I have a column with varchar data coming from another source that is formatted in pseudo HH:MM:SS string representing a duration time like:
Duration
HH:MM:SS
00:43:46
01:30:06
43:56:38
89:24:00
5890:01:00
I wanted to convert it to a simple minutes (int) value for each
Duration MinuteDuration
HH:MM:SS mm
00:43:46 43
01:30:06 90
43:56:38 2636
89:24:00 5364
5890:01:00 353401
I looked around stackoverflow and found several people suggesting CONVERT with the TIME param
USE [MyDB]
GO
SELECT [User],
[Duration],
(SELECT CONVERT(TIME, Duration, 8)) as DurationMinutes,
FROM [dbo].[MyTable]
GO
but I cannot use that since my hours values may be larger than 24/12 (my hours value could be in the thousands). Since the original data is in a varchar, I need to interpret the string first, then multiple the hours by 60* and add it to the minutes value (and just drop the seconds value).
Although parsing the components of the time is definitely a possibility, the string manipulation is pretty simple too:
select duration,
(convert(int, left(duration, charindex(':', duration) - 1)) * 60 +
convert(int, left(right(duration, 5), 2))
)
from t;
Here is a db<>fiddle.
Another option is ParseName() in concert with a CROSS APPLY
Example
Declare #YourTable Table ([Duration] varchar(50))
Insert Into #YourTable Values
('00:43:46')
,('01:30:06')
,('43:56:38')
,('89:24:00')
,('5890:01:00')
Select A.Duration
,Minutes = parsename(NewValue,2) + (parsename(NewValue,3)*60)
from #YourTable A
Cross Apply ( values (replace(Duration,':','.') ) ) B(NewValue)
Returns
Duration Minutes
00:43:46 43
01:30:06 90
43:56:38 2636
89:24:00 5364
5890:01:00 353401

Conversion failed date and/or time from character string

the output is a combination from two column and need to convert into date. Data source is numeric. The code works well for the first 100,000 and and error occur after 409,560. Error as "Conversion failed when converting date and/or time from character string."
SELECT
CASE WHEN LEN(OR6) = 6 THEN
CAST(CONCAT(SUBSTRING(CONVERT(VARCHAR(10),ORT),1,4),SUBSTRING(CONVERT(varchar(10),OR6),3,2),SUBSTRING(CONVERT(varchar(10),OR6),1,2))AS DATE)
WHEN LEN(OR6) = 5 THEN
CAST(CONCAT(SUBSTRING(CONVERT(VARCHAR(10),ORT),1,4),SUBSTRING(CONVERT(varchar(10),OR6),2,2),'0',SUBSTRING(CONVERT(varchar(10),OR6),1,1))AS DATE)
ELSE NULL
END AS ORI_MAT_DT ,DATE1 = OR6 ,DATE2 = ORT
FROM DATETABLE
This is the original source database:
ROW OR6 ORT
409,559 10611 2011152
409,560 50618 2018156
409,561 10615 2015152
409,562 50618 2018156
EXPECTED RESULT:
ORI_MAT_DT DATE1 DATE2
2001-08-15 150801 2001227
You can use a little math to get the values you want from the numeric values in OR6 and ORT, and then use DateFromParts to construct the date:
Source data:
DECLARE #Date1 numeric(10,2) = 150801,
#Date2 numeric(10,2) = 2001227
Query:
SELECT DATEFROMPARTS(
#Date2 / 1000, -- Get the year
(#Date1 % 10000 - #Date1 % 100) / 100, -- Get the month
#Date1 / 10000 -- Get the day
) As Date
Result:
Date
2001-08-15
The DateFromParts functions takes int values representing the year, month, and day and construct a Date object. Since it takes int, SQL Server will implicitly convert the numeric values you get from the calculations to int by truncating the decimal part.

How do I convert a 5 or 6 digit decimal to a date in sql

I've got a column that shows the date as a decimal such as 101118 for 10-11-18 and 90118 for 09-01-18. I am trying to create a simple report that would give me all reservations yesterday.
So for example
Select playerid, datereservationmade
from dbo.lms
normally there is very simple and I would just do
Select playerid, datereservationmade
from dbo.lms
where datereservationmade >= dateadd(day,datediff(day,1,GETDATE()),0)
AND datereservationmade < dateadd(day,datediff(day,0,GETDATE()),0)
That does not work in this case because the datereservationmade field is a decimal and if its a month 1-9 it leaves off the 0 and makes it a 5 digit decimal then if its 10-12 it is a 6 digit decimal.
Someone please help me figure out how to convert this!
If at all possible, you really should fix your schema so that dates are actually being stored as dates.
If you need to work with the decimal data type, you can use something like the following...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
BEGIN DROP TABLE #TestData; END;
CREATE TABLE #TestData (
decimal_date DECIMAL(6, 0) NOT NULL
);
INSERT #TestData (decimal_date) VALUES (101118), (90118), (101718);
--==============================================
SELECT
td.decimal_date,
dd.date_date
FROM
#TestData td
CROSS APPLY ( VALUES (RIGHT('0' + CONVERT(VARCHAR(6), td.decimal_date), 6)) ) cd (char_date)
CROSS APPLY ( VALUES (CONVERT(DATE, STUFF(STUFF(cd.char_date, 5, 0, '/'), 3, 0, '/'), 1)) ) dd (date_date)
WHERE
dd.date_date = CONVERT(DATE, DATEADD(DAY, -1, GETDATE()));
Convert the decimal to varchar(6) by adding a zero in front and getting the RIGHT 6 characters.
Then convert the string to a date from its parts, which are substrings in your varchar(6). This is made easier in SQL Server 2012 with the DATEFROMPARTS function.
Using the DATEFROMPARTS, as Tab Alleman suggested, you might get something like this:
-- Example of the arithmetic
SELECT 101118 / 10000 AS Month, (101118 % 10000) / 100 AS Day, (101118 % 100) AS Year
-- Using the math in DATEFROMPARTS
SELECT DATEFROMPARTS((101118 % 100) + 2000, 101118 / 10000, (101118 % 10000) / 100 )
However, I'm skeptical that you've provided all the correct information. What happens on January first? Your decimal value won't start with zero (as you stated). Will your day always pad with zero? If not, then 1119 won't produce the same result as 10119. If, however, your day does start with zero, then the equation above should work fine.

Finding difference between time

In below code I am trying to find time difference in minutes
declare #a time
declare #b time
select #a = "Apr 1, 2014 22:36.000"
select #b = "Apr 2, 2014 02:25.000"
select datediff(minutes,#a,#b)
Expected output is 229 minutes.
24 min (60- 36) + 3 hour (180 min) + 25 min = 229 minutes
But I am getting -1211.This function is doing direct subration.
Please help.
You have declared the variables as time which cannot exceed 24 hours. The date portion is ignored.
In effect you are sending this to the datediff functoon
datediff(minute,'22:36.000', '02:25.000')
and that is why you are getting a negative result.
I suggest you declare #a and #b as datetime instead.
declare #a datetime
declare #b datetime
select #a = "Apr 1, 2014 22:36.000"
select #b = "Apr 2, 2014 02:25.000"
select datediff(minutes,#a,#b)
You can use this query as reference to calculate time difference .
select DATEDIFF(day,2007-11-30,2007-11-20) AS NumberOfDays, DATEDIFF(hour,2007-11-30,2007-11-20) AS NumberOfHours, DATEDIFF(minute,2007-11-30,2007-11-20) AS NumberOfMinutes FROM test_table
you are using time data types rather than datetime data types in your code ( they will ignore the date part and just store the time part.
so its calculating 2:25 - 22:36, ie 20 hours ( 1200 ) plus 11 minutes (all minus because the first time is after the second)
sql server date / time date types are documented here : http://msdn.microsoft.com/en-gb/library/ms186724.aspx