Rounding datetime in tsql - tsql

These are the values I have:
DECLARE #temp2 DATETIME = '9/1/2016 09:30:29.499 AM'; --rounded to '2016-09-01 09:31:00.000'
DECLARE #temp3 DATETIME = '9/1/2016 09:30:29.496 AM'; --rounded to '2016-09-01 09:30:00.000'
I want to round the miliseconds to seconds and the seconds to minutes, so that if there are 30 or more seconds, add one minute, and if the miliseconds are 500 or greater than 500, add one second. And this is how I've been trying to do it:
SELECT DATEADD(MINUTE, 0, DATEADD(MI, DATEDIFF(MI, 0, DATEADD(S, 30, CAST(#temp AS DATETIME2(0)))), 0))
As you can see, '9/1/2016 09:30:29.499 AM' gets rounded to 09:31, when it should get rounded to 09:30. I know that this happens because of the way SQL server handles miliseconds. How do I handle this?

I'd recommend using DATETIME2 by default - it has more accuracy, for no more storage space. DATETIME is only accurate to ~ 3.33ms but DATETIME2 allows for much more accuracy (100 nanoseconds) and avoids the rounding issue you are seeing.
You can also control the precision of a DATETIME2 (see above MSDN link)

You can try convert
DECLARE #temp2 DATETIME = '9/1/2016 09:30:29.499 AM'; --rounded to '2016-09-01 09:31:00.000'
DECLARE #temp3 DATETIME = '9/1/2016 09:30:29.496 AM';
SELECT convert(datetime,CONVERT(VARCHAR(16), #temp2, 120))
--SELECT CONVERT(VARCHAR(13), #temp3, 120)

This is primarily because you are specifying datetime as your initial variable declaration.
But assuming that your source data was datetime2, you would also face issues because you are using datetime(0) in your select, which means 0 fractional seconds.
So at present, your calculation steps are as follows:
declare 09:30:29.499 as datetime results in 09:30:29.500 as datetime can only handle times to the nearest 3 milliseconds.
cast 09:30:29.500 as datetime(0) rounds off the fractional seconds for 09:30:30.
you add 30 seconds to it so 09:30:30 becomes 09:31:00.
If your source data is held in datetime you will never receive a value with 498 or 499 milliseconds as datetime does not support that level of precision, so you don't need to worry about this.
If you are receiving dates with 498 or 499 level precision, you can change your cast(#temp as datetime(0)) to cast(#temp as datetime(6)) or whatever precision you require and your script will return the correct roundings.

Related

I got one problem in SQL,i need to get start of next minute value

I need to get the start of next Minute value, that means suppose I got the output for GETDATE() is 19.11.2019 12:52:51 but I need to get 19.11.2019 12:53:00
This is my code:
DECLARE #date DATETIME
SET #date = GETDATE()
DECLARE #increase int = 1;
SELECT DATEADD(mi, #increase,#date) as nextminutedate;
You want to round the date down. One method in SQL Server is:
select dateadd(minute, 1+datediff(minute, 0, getdate()), 0)
This is a little inscrutable. The datediff() calculates the number of minutes from a time of 0 for the current date/time. The dateadd() adds this back in.
Note: This works for minutes. You might have overflow problems with seconds or milliseconds.
For this reason, I rather prefer:
select dateadd(minute, 1+datediff(minute, '2000-01-01', getdate()), '2000-01-01')
I find this is clearer in the intention.

TSQL update Datetime with Random Value between 2 Dates

What's the easiest way to update a table that contains a DATETIME column on TSQL with RANDOM value between 2 dates?
I see various post related to that but their Random values are really sequential when you ORDER BY DATE after the update.
Assumptions
First assume that you have a database containing a table with a start datetime column and a end datetime column, which together define a datetime range:
CREATE DATABASE StackOverflow11387226;
GO
USE StackOverflow11387226;
GO
CREATE TABLE DateTimeRanges (
StartDateTime DATETIME NOT NULL,
EndDateTime DATETIME NOT NULL
);
GO
ALTER TABLE DateTimeRanges
ADD CONSTRAINT CK_PositiveRange CHECK (EndDateTime > StartDateTime);
And assume that the table contains some data:
INSERT INTO DateTimeRanges (
StartDateTime,
EndDateTime
)
VALUES
('2012-07-09 00:30', '2012-07-09 01:30'),
('2012-01-01 00:00', '2013-01-01 00:00'),
('1988-07-25 22:30', '2012-07-09 00:30');
GO
Method
The following SELECT statement returns the start datetime, the end datetime, and a pseudorandom datetime with minute precision greater than or equal to the start datetime and less than the second datetime:
SELECT
StartDateTime,
EndDateTime,
DATEADD(
MINUTE,
ABS(CHECKSUM(NEWID())) % DATEDIFF(MINUTE, StartDateTime, EndDateTime) + DATEDIFF(MINUTE, 0, StartDateTime),
0
) AS RandomDateTime
FROM DateTimeRanges;
Result
Because the NEWID() function is nondeterministic, this will return a different result set for every execution. Here is the result set I generated just now:
StartDateTime EndDateTime RandomDateTime
----------------------- ----------------------- -----------------------
2012-07-09 00:30:00.000 2012-07-09 01:30:00.000 2012-07-09 00:44:00.000
2012-01-01 00:00:00.000 2013-01-01 00:00:00.000 2012-09-08 20:41:00.000
1988-07-25 22:30:00.000 2012-07-09 00:30:00.000 1996-01-05 23:48:00.000
All the values in the column RandomDateTime lie between the values in columns StartDateTime and EndDateTime.
Explanation
This technique for generating random values is due to Jeff Moden. He wrote a great article on SQL Server Central about data generation. Read it for a more thorough explanation. Registration is required, but it's well worth it.
The idea is to generate a random offset from the start datetime, and add the offset to the start datetime to get a new datetime in between the start datetime and the end datetime.
The expression DATEDIFF(MINUTE, StartDateTime, EndDateTime) represents the total number of minutes between the start datetime and the end datetime. The offset must be less than or equal to this value.
The expression ABS(CHECKSUM(NEWID())) generates an independent random positive integer for every row. The expression can have any value from 0 to 2,147,483,647. This expression mod the first expression gives a valid offset in minutes.
The epxression DATEDIFF(MINUTE, 0, StartDateTime) represents the total number of minutes between the start datetime and a reference datetime of 0, which is shorthand for '1900-01-01 00:00:00.000'. The value of the reference datetime does not matter, but it matters that the same reference date is used in the whole expression. Add this to the offset to get the total number of minutes between the reference datetime.
The ecapsulating DATEADD function converts this to a datetime value by adding the number of minutes produced by the previous expression to the reference datetime.
You can use RAND for this:
select cast(cast(RAND()*100000 as int) as datetime)
from here
Sql-Fiddle looks quite good: http://sqlfiddle.com/#!3/b9e44/2/0

T-SQL How to update hour in a SmallDateTime column?

let's say that I have a SmallDateTime column in my table. How to update hours in each row in T-SQL ?
Get the target value's hour part.
Find the difference between the hour you want and the found hour.
Add the difference of hours to the target value.
The script:
UPDATE atable
SET datetimevalue = DATEADD(hour, #hour - DATEPART(hour, datetimevalue),
datetimevalue)
WHERE ...
UPDATE YourTable SET YourDateColumn = DateAdd(hh, 1, YourDateColumn)
will add 1 hour to the time value in YourDateColumn in every single row.
See this page for more info.
UPDATE YourTableName
SET YourSmallDateTimeColumn = DATEADD(HH, 1, YourSmallDateTimeColumn)
Will add any number of hours to your column. So if you get rid of the time component first:
SELECT CAST(CONVERT(char(8), YourSmallDateTimeColumn, 112) AS smalldatetime)
and add hour component to it later it should work.
From here
To add or subtract hours to a datetime
or smalldatetime value, you will use
the DATEADD date function. The
DATEADD date function returns a new
datetime value based on adding an
interval to the specified date. The
syntax of the DATEADD date function is
as follows:
DATEADD ( datepart , number, date )
datepart is the parameter that
specifies on which part of the date to
return a new value. For hours, you
can use either HOUR or HH. number is
the value used to increment datepart.
date is an expression that returns a
datetime or smalldatetime value, or a
character string in a date format.
Here's an example on how to use the
DATEADD date function to increase or
decrease a datetime value by a certain
number of hours:
SELECT DATEADD(HOUR, -12, GETDATE())AS [TwelveHoursAgo]
SELECT DATEADD(HH, 6, GETDATE()) AS [SixHoursLater]

Quartz .Net - Meaning of BigInt DateTime

we've used sql server as our persisted data store for Quartz.net. I'd like to write some queries looking # the Time values. Specifically - Qrtz_Fired_Triggers.Fired_Time, Qrtz_Triggers.Next_fire_time, Prev_fire_time.
For the life of me, I can't find anything that says what this data is - ticks, milliseconds, microseconds, nanoseconds. I've guessed at a couple of things, but they've all proven wrong.
The best answer would include the math to convert the big int into a datetime and perhaps even a link(s) to the pages/documentation that I should have found - explaining the meaning of the data in those fields.
If you have specific instructions on using Quartz .Net libraries to view this information, that would be appreciated, but, I really have 2 goals - to understand the meaning of the date/time data being stored and to keep this in T-SQL. If I get the one, I can figure out T-SQL or out.
On the SQL side, you can convert from Quartz.NET BIGINT times to a DateTime in UTC time with:
SELECT CAST(NEXT_FIRE_TIME/864000000000.0 - 693595.0 AS DATETIME) FROM QRTZ_TRIGGERS
Numbers Explanation
Values stored in the column are the number of ticks from .NET DateTime.MinValue in UTC time. There are 10000 ticks per millisecond.
The 864000000000.0 represents the number of ticks in a single day. You can verify this with
SELECT DATEDIFF(ms,'19000101','19000102')*10000.0
Now, if we take March 13, 2013 at midnight, .NET returns 634987296000000000 as the number of ticks.
var ticks = new DateTime(2013, 3, 13).Ticks;
To get a floating point number where whole numbers represent days and decimal numbers represent time, we take the ticks and divide by the number of ticks per day (giving us 734939.0 in our example)
SELECT 634987296000000000/(DATEDIFF(ms,'19000101','19000102')*10000.0)
If we get put the date in SQL and convert to a float, we get a different number: 41344.0
SELECT CAST(CAST('March 13, 2013 0:00' AS DATETIME) AS FLOAT)
So, we need to generate a conversion factor for the .NET-to-SQL days. SQL minimum date is January 1, 1900 0:00, so the correction factor can be calculated by taking the number of ticks for that time (599266080000000000) and dividing by the ticks per day, giving us 693595.0
SELECT 599266080000000000/(DATEDIFF(ms,'19000101','19000102')*10000.0)
So, to calculate the DateTime of a Quartz.NET date:
take the value in the column
divide by the number of ticks per day
subtract out the correction factor
convert to a DATETIME
SELECT CAST([Column]/864000000000.0 - 693595.0 AS DATETIME)
The value stored in database is the DateTime.Ticks value. From MSDN:
A single tick represents one hundred
nanoseconds or one ten-millionth of a
second. There are 10,000 ticks in a
millisecond.
The value of this property represents
the number of 100-nanosecond intervals
that have elapsed since 12:00:00
midnight, January 1, 0001, which
represents DateTime.MinValue. It does
not include the number of ticks that
are attributable to leap seconds.
So, unless I missed something and am making this too complicated, I couldn't get the dateadd functions in Ms Sql Server 2008 to handle such large values and I kept getting overflow errors. The approach I took in Ms Sql Server was this:
a) find a date closer to now than 0001.01.01 & its ticks value
b) use a function to give me a DateTime value.
Notes:
* for my application - seconds was good enough.
* I've not tested this extensively, but so far, it has acted pretty well for me.
The function:
CREATE FUNCTION [dbo].[net_ticks_to_date_time]
(
#net_ticks BIGINT
)
RETURNS DATETIME
AS
BEGIN
DECLARE
#dt_2010_11_01 AS DATETIME = '2010-11-01'
, #bi_ticks_for_2010_11_01 AS BIGINT = 634241664000000000
, #bi_ticks_in_a_second AS BIGINT = 10000000
RETURN
(
DATEADD(SECOND , ( ( #net_ticks - #bi_ticks_for_2010_11_01 ) / #bi_ticks_in_a_second ) , #dt_2010_11_01)
);
END
GO
Here is how I came up with the # of ticks to some recent date:
DECLARE
#dt2_dot_net_min AS DATETIME2 = '01/01/0001'
, #dt2_first_date AS DATETIME2
, #dt2_next_date AS DATETIME2
, #bi_seconds_since_0101001 BIGINT = 0
SET #dt2_first_date = #dt2_dot_net_min;
SET #dt2_next_date = DATEADD ( DAY, 1, #dt2_first_date )
WHILE ( #dt2_first_date < '11/01/2010' )
BEGIN
SELECT #bi_seconds_since_0101001 = DATEDIFF(SECOND, #dt2_first_date, #dt2_next_date ) + #bi_seconds_since_0101001
PRINT 'seconds 01/01/0001 to ' + CONVERT ( VARCHAR, #dt2_next_date, 101) + ' = ' + CONVERT ( VARCHAR, CAST ( #bi_seconds_since_0101001 AS MONEY ), 1)
SET #dt2_first_date = DATEADD ( DAY, 1, #dt2_first_date );
SET #dt2_next_date = DATEADD ( DAY, 1, #dt2_first_date )
END

How to Round a Time in T-SQL

I'me looking for a function that would receive a time and would round it to the next/previous hour / half-hour / quarter / minute.
Try this function
CREATE FUNCTION [dbo].[RoundTime] (#Time datetime, #RoundTo float)
RETURNS datetime
AS
BEGIN
DECLARE #RoundedTime smalldatetime
DECLARE #Multiplier float
SET #Multiplier= 24.0/#RoundTo
SET #RoundedTime= ROUND(CAST(CAST(CONVERT(varchar,#Time,121) AS datetime) AS float) * #Multiplier,0)/#Multiplier
RETURN #RoundedTime
END
select dbo.roundtime('13:15',0.5)
The 1st param is the time to be rounded and the 2nd will be base on your list (0.5-half hour, 1-one hour, ...)
Very nice thanks. I used it in-line to round to 15 minutes
convert(smalldatetime,ROUND(cast(TDatalog.Time as float) * (24/.25),0)/(24/.25)) AS RoundedTime