Truncate Datetime to Second (Remove Milliseconds) in T-SQL - tsql

What is the best way to shorten a datetime that includes milliseconds to only have the second?
For example 2012-01-25 17:24:05.784 to 2012-01-25 17:24:05

This will truncate the milliseconds.
declare #X datetime
set #X = '2012-01-25 17:24:05.784'
select convert(datetime, convert(char(19), #X, 126))
or
select dateadd(millisecond, -datepart(millisecond, #X), #X)
CAST and CONVERT
DATEADD
DATEPART

The fastest, also language safe and deterministic
DATEADD(second, DATEDIFF(second, '20000101', getdate()), '20000101')

The easiest way now is:
select convert(datetime2(0) , getdate())

convert(datetime, convert(varchar, #datetime_var, 120), 120)

The following has very fast performance, but it not only removes millisecond but also rounds to minute. See (http://msdn.microsoft.com/en-us/library/bb677243.aspx)
select cast(yourdate as smalldatetime) from yourtable
Edit:
The following script is made to compare the scripts from Mikael and gbn I upvoted them both since both answers are great. The test will show that gbn' script is slightly faster than Mikaels:
declare #a datetime
declare #x int = 1
declare #mikaelend datetime
declare #mikael datetime = getdate()
while #x < 5000000
begin
select #a = dateadd(millisecond, -datepart(millisecond, getdate()), getdate()) , #x +=1
end
set #mikaelend = getdate()
set #x = 1
declare #gbnend datetime
declare #gbn datetime = getdate()
while #x < 5000000
begin
select #a = DATEADD(second, DATEDIFF(second, '20000101', getdate()), '20000101') , #x +=1
end
set #gbnend = getdate()
select datediff(ms, #mikael, #mikaelend) mikael, datediff(ms, #gbn, #gbnend) gbn
First run
mikael gbn
----------- -----------
5320 4686
Second run
mikael gbn
----------- -----------
5286 4883
Third run
mikael gbn
----------- -----------
5346 4620

declare #dt datetime2
set #dt = '2019-09-04 17:24:05.784'
select convert(datetime2(0), #dt)

Expanding on accepted answer by #Mikael Eriksson:
To truncate a datetime2(7) to 3 places (aka milliseconds):
-- Strip of fractional part then add desired part back in
select dateadd(nanosecond,
-datepart(nanosecond, TimeUtc) + datepart(millisecond, TimeUtc) * 1e6,
TimeUtc) as TimeUtc
The current max precision of datetime2(p) is (7) (from learn.microsoft.com)

--- DOES NOT Truncate milliseconds
--- 2018-07-19 12:00:00.000
SELECT CONVERT(DATETIME, '2018-07-19 11:59:59.999')
--- Truncate milliseconds
--- 2018-07-19 11:59:59.000
SELECT CONVERT(DATETIME, CONVERT(CHAR(19), '2018-07-19 11:59:59.999', 126))
--- Current Date Time with milliseconds truncated
SELECT CONVERT(DATETIME, CONVERT(CHAR(19), GETDATE(), 126))

SELECT CAST( LEFT( '2018-07-19 11:59:59.999' , 19 ) AS DATETIME2(0) )

Related

Convert Excel formula (using Date and subtraction) into T-SQL

I am trying to write this Excel formula into T-SQL (to write a function).
Expected output is 0.71944444, but currently my output (using T-SQL) is 24.0000.
I am not sure why we have to add a day to same date and subtract the same date.
Bottom is a screenshot from Excel:
This is what I have so far in T-SQL:
CREATE FUNCTION [dbo].[fn_0921] (
#Punch_Start nvarchar(max)
)
RETURNS decimal(36, 8) AS
BEGIN
DECLARE #return_value nvarchar(max);
SET #return_value =
DATEDIFF(
MINUTE, CAST(#Punch_Start AS datetime2),
(
dateadd(
day, 1, CAST(#Punch_Start AS datetime2)
)
)
)
/ (60.0)
RETURN #return_value
END;
Thanks for help.
The Excel formula is returning the difference between the datetime in cell K4 & the start of the next day (i.e. 7/26/2021 00:00) as a fraction of a whole day. The following is the equivalent in T-SQL:
DECLARE #Punch_Start datetime2 = '7/25/2021 06:44';
SELECT DATEDIFF(
MINUTE,
#Punch_Start,
CAST(
CAST(
DATEADD(DAY, 1, #Punch_Start)
AS date) -- Add 1 day to #Punch_Start & cast as date to remove the time component - this is the start of the next day
AS datetime2) -- Cast back to datetime2 to get the difference in minutes
) / 1440.; -- Divide the difference in minutes by the number of minutes in a day (60 minutes per hour, 24 hours per day) to get the difference as a fraction of a day
This can probably help you:
DECLARE #date DATETIME2 = '2021-07-25 06:44'
DECLARE #seconds INT = DATEDIFF(second, CAST(#date AS date), #date)
DECLARE #secondsFromEnd FLOAT = 86400 - #seconds
SELECT #secondsFromEnd / 86400

How do I use a calculated value within a DATEFROMPARTS in SQL

I need a calculated month value within DATEFROMPARTS function. The month has to be seven month prior to CURRENT_TIMESTAMP month.
This is what I tried:
DATEFROMPARTS(Year(CURRENT_TIMESTAMP), Month(CURRENT_TIMESTAMP)-7, 1) as SevenMoAgo;
I will eventually use this in the following expression where '12-01-2018' is:
where RECORDED_SERVICE_STARTTIME > ='12-01-2018'
I later used
declare #CurMo AS INT;
declare #MonPri7 AS INT;
set #CurMo = Month(CURRENT_TIMESTAMP);
set #MonPri7 = (#CurMo -7);
Datefromparts(Year(CURRENT_TIMESTAMP), #MonPri7, 1) as SevenMoAgo;
This also did not work.
I get the following error message:
"Cannot construct data type date, some of the arguments have values which are not valid."
For the second code I get:
Msg 102, Level 15, State 1, Line 8
Incorrect syntax near 'Datefromparts'.
Try this...
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) - 7, 0)
Let me explain. First off, we need to understand that SQL Server interprets 0 as 1900-01-01 as shown by the following DATEPART functions.
SELECT DATEPART(YEAR, 0) AS Year
, DATEPART(MONTH, 0) AS Month
, DATEPART(DAY, 0) AS Day;
Which returns...
Year Month Day
----------- ----------- -----------
1900 1 1
Therefore, my SQL could be rewritten as...
SELECT DATEADD(MONTH, DATEDIFF(MONTH, '1900-01-01', CURRENT_TIMESTAMP) - 7, '1900-01-01')
Now perhaps it is a little easier to see what is going on here. The DATEDIFF function returns the number number of months between 1900-01-01 and today (CURRENT_TIMESTAMP) which is 1434.
SELECT DATEADD(MONTH, 1434 - 7, '1900-01-01')
Then we subtract 7 from 1434 which is 1427 and add that many months back to 1900-01-01.
SELECT DATEADD(MONTH, 1427, '1900-01-01')
Which yields 2018-12-01.
The reason is #MonPri7 is equal to ZERO when you say (#CurMo -7)
There are many different ways to calculate it, but if you want to fix your logic, you should use this:
declare #CurMo AS INT;
declare #MonPri7 AS INT;
set #CurMo = Month(CURRENT_TIMESTAMP);
set #MonPri7 = (#CurMo -7);
declare #Y int = Year(CURRENT_TIMESTAMP) -- <-- This is new variable
-- if 7 months ago is ZERO then you should go back to prev year December
if #MonPri7 = 0
begin
set #MonPri7 = 12
set #Y = #Y - 1
end
Edit:
declare #SevenMonthsAgo datetime;
select #SevenMonthsAgo = Datefromparts(#Y, #MonPri7, 1);
SELECT yourfields
FROM yourtable
where RECORDED_SERVICE_STARTTIME > = '01-01-2019' and
RECORDED_SERVICE_STARTTIME > = #SevenMonthsAgo

Convert HH:MM:SS string to number of minutes

I have the below query.
select cast(dateadd(minute, datediff(minute, TimeIn, TimeOut), 0) as time(0) )
I get the results from two columns in the format of hrs-min-seconds.
I would like it in the format of min only. So 02:47:00 will read 167.
SQL Server Query:
SELECT cast(substring('02:47:00',1,2) AS int)*60+
cast(substring('02:47:00',4,2) AS int)+
cast(substring('02:47:00',7,2) AS int)/60.0 AS minutes
MYSQL Query:
SELECT TIME_TO_SEC('02:47:00') / 60
Result:
| MINUTES |
-----------
| 167 |
declare #Time DATETIME = '01:05:00'
select ((DATEPART(HOUR, #Time)*60) + (DATEPART(MINUTE, #Time)))
For SQL Server (works for 2005 too):
select Datediff(mi,convert(datetime,'00:00:00',108), convert(datetime,'02:47:00',108))
Try this:
datediff(minute, 0, '02:47')
Expanding on Justin's answer. This allows for situations where hours is larger than 2 digits.
declare #time varchar(50) = '102:47:05'
SELECT cast(right(#time,2) AS int)+
cast(left(right(#time,5),2) AS int)*60+
cast(left(#time,len(#time)-6) AS int)*3600 AS seconds,
(cast(right(#time,2) AS int)+
cast(left(right(#time,5),2) AS int)*60+
cast(left(#time,len(#time)-6) AS int)*3600)/60.0 AS minutes
Result:
seconds minutes
----------- ---------------------------------------
370025 6167.083333
SELECT DATEDIFF(minute,CAST('00:00' AS TIME), CAST('02:47' AS TIME)) AS difference
Gives you:
| DIFFERENCE |
--------------
| 167 |
Unfortunately, if you want to use DATEPART function for values with more than 24 hours, you will receive an error:
Conversion failed when converting date and/or time from character string."
You can test it with this code:
declare #Time DATETIME = '32:00:00'
select ((DATEPART(HOUR, #Time)*60) + (DATEPART(MINUTE, #Time)))
To solve this, I worked with this another approach:
declare #tbl table(WorkHrs VARCHAR(8))
insert into #tbl(WorkHrs) values ('02:47:00')
insert into #tbl(WorkHrs) values ('32:00:00')
-- Sum in minutes
SELECT TRY_CAST(([HOURS] * 60) + [MINUTES] + ([SECOND] / 60) AS INT) as TotalInMinutes
FROM (
SELECT
-- Use this aproach to get separated values
SUBSTRING(WorkHrs,1,CHARINDEX(':',WorkHrs)-1) AS [HOURS],
SUBSTRING(WorkHrs,4,CHARINDEX(':',WorkHrs)-1) AS [MINUTES],
SUBSTRING(WorkHrs,7,CHARINDEX(':',WorkHrs)-1) AS [SECOND] -- probably you can ignore this one
FROM #tbl
)
tbl
-- Sum in seconds
SELECT TRY_CAST(([HOURS] * 3600) + ([MINUTES] * 60) + [SECOND] AS INT) as TotalInSeconds
FROM (
SELECT
-- Use this aproach to get separated values
SUBSTRING(WorkHrs,1,CHARINDEX(':',WorkHrs)-1) AS [HOURS],
SUBSTRING(WorkHrs,4,CHARINDEX(':',WorkHrs)-1) AS [MINUTES],
SUBSTRING(WorkHrs,7,CHARINDEX(':',WorkHrs)-1) AS [SECOND]
FROM #tbl
)
tbl
This code will return like this:
$time = '02:47:00';
$time = explode(":",$time);
$total = ($a[0]*60)+$a[1];
echo 'Minutes : '.$total;<br>
echo 'Seconds : '.$a[2];

Most Performant Way to Convert DateTime to Int Format

I need to convert Datetime fields to a specifically formatted INT type. For example, I want
2000-01-01 00:00:00.000 to convert to 20010101.
What is the most performant way to make that conversion for comparison in a query?
Something like:
DATEPART(year, orderdate) * 10000 + DATEPART(month, orderdate) * 100 +
DATEPART(day, orderdate)
or
cast(convert(char(8), orderdate, 112) as int)
What's the most performant way to do this?
Your example of cast(convert(char(8), orderdate, 112) as int) seems fine to me. It quickly gets the date down to the format you need and converted to an int.
From an execution plan standpoint, there seems to be no difference between the two.
You can try with TSQL builtin functions.
It's not .NET tick compatible but it's still FAST sortable and you can pick your GRANULARITY on demand:
SELECT setup.DateToINT(GETDATE(), 4) -- will output 2019 for 2019-06-06 12:00.456
SELECT setup.DateToINT(GETDATE(), 6) -- will output 201906 for 2019-06-06 12:00.456
SELECT setup.DateToINT(GETDATE(), 20) -- will output 20190606120045660 for 2019-05-05 12:00.456
CREATE FUNCTION setup.DateToINT(#datetime DATETIME, #length int)
RETURNS
BIGINT WITH SCHEMABINDING AS
BEGIN
RETURN CONVERT(BIGINT,
SUBSTRING(
REPLACE(REPLACE(
REPLACE(REPLACE(
CONVERT(CHAR(25), GETDATE(), 121)
,'-','')
,':','')
,' ','')
,'.','')
,0
,#length+1)
)
END
GO
Is this what you need
SELECT REPLACE(CONVERT(VARCHAR(10),'2010-01-01 00:00:00.000',101),'-','')
When you pass '2010-01-01 00:00:00.000' directly in your code, the SELECT statement looks at it as a string and not a datetime data type. Its not the same as selecting a datetime field directly.
There is no need to do outer CAST because SQL Server will do implicit conversion, here is a proof.
DECLARE #t DATETIME = '2010-01-10 00:00:00.000',#u INT
SELECT #u = CONVERT(CHAR(8), #t, 112)
IF ISNUMERIC(#u) = 1
PRINT 'Integer'

TSQL need to return last day of month only, how do I drop year, month & time?

I am writing a function in T-SQL returning the last day of the month regardless of the date input.
Here is my code:
Alter Function dbo.FN_Get_Last_Day_in_Month2
(#FN_InputDt Datetime)
Returns smalldatetime
as
Begin
Declare #Result smalldatetime
Set #Result =
case when #FN_InputDt <> 01-01-1900 then
DATEADD(m, DATEDIFF(M, 0,#FN_InputDt)+1, -1)
Else 0 End
Return #Result
End
The code is not working correctly, here is a test that shows the bad behavior:
SELECT dbo.fn_get_last_day_in_month (07-05-2010)
Here is the (incorrect) result:
2010-07-31 00:00:00
What is 07-05-2010...May 7th or July 5th? You need to use a safe date format, take a look at Setting a standard DateFormat for SQL Server
example from How to find the first and last days in years, months etc
DECLARE #d DATETIME
SET #d = '20100705' -- notice ISO format
SELECT
DATEADD(yy, DATEDIFF(yy, 0, #d), 0) AS FirstDayOfYear,
DATEADD(yy, DATEDIFF(yy, 0, #d)+1, -1) AS LastDayOfYear,
DATEADD(qq, DATEDIFF(qq, 0, #d), 0) AS FirstDayOfQuarter,
DATEADD(qq, DATEDIFF(qq, 0, #d)+1, -1) AS LastDayOfQuarter,
DATEADD(mm, DATEDIFF(mm, 0, #d), 0) AS FirstDayOfMonth,
DATEADD(mm, DATEDIFF(mm, 0, #d)+1, -1) AS LastDayOfMonth,
#d - DATEDIFF(dd, ##DATEFIRST - 1, #d) % 7 AS FirstDayOfWeek,
#d - DATEDIFF(dd, ##DATEFIRST - 1, #d) % 7 + 6 AS LastDayOfWeek
for just the day use day or datepart
select DAY(getdate()),
DATEPART(dd,GETDATE())
Cast the return value to a SQL datetime type, and then call the "DAY" function to get the day in as an integer. See the function reference here:
http://msdn.microsoft.com/en-us/library/ms176052.aspx
Not sure which database you're using, but this should be a standard function across all databases.
I'd return a DATETIME, I've had trouble with SMALLDATETIME in the past.
DECLARE #Result DATETIME
SET #Result = DATEADD(m , 1, #FN_Input);
RETURN CAST(FLOOR(CAST(DATEADD(d, DATEPART(d, #Result) * -1, #Result) AS FLOAT)) AS DATETIME)
Also, I think you may be a victim of SQL's complete disregard of date formatting. Always, always, always, when typing a string into test a SQL function use the following format;
'05 Jul 2010'
Your function probably works but it interpreted your date as 5th July - not 7th May.
DECLARE #date DATETIME = '20130624';
SELECT Day(EOMONTH ( #date )) AS LastDay;
GO