How to Round a Time in T-SQL - tsql

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

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.

Rounding datetime in 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.

How to convert a column in seconds to dateformat in Teradata?

I have a column which is of bigint datatype(in seconds) which should be added to a date, so i need to convert this column into dateformat.
The arithmetic must be done against a timestamp data type in Teradata. The date data type does not have a time element associated with it. The following SQL should help point you in the right direction:
SELECT CAST(CAST(1234 AS BIGINT) AS INTERVAL SECOND(4)) AS Seconds_
, CURRENT_TIMESTAMP(0) AS CurrentTimestamp_
, CURRENT_TIMESTAMP + Seconds_ AS NewTimeStamp
If the number of seconds is less than 864000000 you can simply use interval arithmetic:
CAST(col AS TIMESTAMP) + (bigintcol * INTERVAL '0000 00:00:01' DAY TO SECOND)
Based on your other question your input is a Unixtime, those are two functions for converting them from/to Teradata timestamps:
/**********
Converting Unix/POSIX time to a Timestamp
Unix time: Number of seconds since 1970-01-01 00:00:00 UTC not counting leap seconds (currently 24 in 2011)
Also working for negative numbers.
The maximum range of Timestamps is based on the range of INTEGERs:
1901-12-13 20:45:52 (-2147483648) to 2038-01-19 03:14:07 (2147483647)
Can be changed to use BIGINT instead of INTEGER
20101211 initial version - Dieter Noeth
**********/
REPLACE FUNCTION UnixTime_to_TimeStamp (UnixTime INT)
RETURNS TimeStamp(0)
LANGUAGE SQL
CONTAINS SQL
DETERMINISTIC
SQL SECURITY DEFINER
COLLATION INVOKER
INLINE TYPE 1
RETURN
CAST(DATE '1970-01-01' + (UnixTime / 86400) AS TIMESTAMP(0))
+ ((UnixTime MOD 86400) * INTERVAL '00:00:01' HOUR TO SECOND)
;
SELECT
UnixTime_to_TimeStamp(-2147483648)
,UnixTime_to_TimeStamp(0)
,UnixTime_to_TimeStamp(2147483647)
;
/**********
Converting a Timestamp to Unix/POSIX time
Unix time: Number of seconds since 1970-01-01 00:00:00 UTC not counting leap seconds (currently 24 in 2011)
The maximum range of Timestamps is based on the range of INTEGERs:
1901-12-13 20:45:52 (-2147483648) to 2038-01-19 03:14:07 (2147483647)
Can be changed to use BIGINT instead of INTEGER
20101211 initial version - Dieter Noeth
**********/
REPLACE FUNCTION TimeStamp_to_UnixTime (ts TimeStamp(6))
RETURNS INTEGER
LANGUAGE SQL
CONTAINS SQL
DETERMINISTIC
SQL SECURITY DEFINER
COLLATION INVOKER
INLINE TYPE 1
RETURN
(CAST(ts AS DATE) - DATE '1970-01-01') * 86400
+ (EXTRACT(HOUR FROM ts) * 3600)
+ (EXTRACT(MINUTE FROM ts) * 60)
+ (EXTRACT(SECOND FROM ts))
;
SELECT
TimeStamp_to_UnixTime(TIMESTAMP '1901-12-13 20:45:52')
,TimeStamp_to_UnixTime(CURRENT_TIMESTAMP)
,TimeStamp_to_UnixTime(TIMESTAMP '2038-01-19 03:14:07')
;

Count differnce of two Date's with Respect that every month has EXACT 30 days

I thought it was clear, but doesn't seem so.
This question is about T-SQL (since it's tagged with tsql :) )
So I couldn't find any out-of-the-box solution to calculate my problem.
Let's assume you have these two dateTimes:
DECLARE #start DATETIME = '2011-01-01',
#end DATETIME = '2011-04-15'
The difference of these two datetimes in Days should be quivalent to 105.
The calculation works as follows: For every full month add 30 days, for the rest add the days till the date is achieved.
I could program this, but it would be an enormous SQL-statement, which I find find kinda ugly.
Is there any simple solution for this, like a built-in function or something short?
Thanks in advance.
Does this do the trick?
;with dates as
(
SELECT
CAST ('2011-01-01' AS DATETIME) as start_date
,CAST('2011-04-15' AS DATETIME) as end_date
)
SELECT
start_date
,end_date
,CASE WHEN DATEDIFF(MM,start_date,end_date) = 0 THEN DAY(end_date) - DAY(start_date)
WHEN DAY(start_date) = 1 THEN (30 * (DATEDIFF(MM,start_date,end_date))) + DAY(end_date)
WHEN DAY(start_date) <> 1 THEN 30 * DATEDIFF(MM,start_date,end_date) + (DAY(end_date) - DAY(start_date))
END AS gap_in_days
FROM dates
Short Answer
There's no built in function, but you could pretty easily create your own to handle converting a datetime to an int. From there, the SQL you would have to write would be trivial.
Long Answer
There's no built in function that will do this, probably because every month doesn't have 30 days. :)
You can start with this:
DECLARE #start DATETIME = '2011-01-01',
#end DATETIME = '2011-04-15'
DECLARE #endConverted INT
SELECT #endConverted = DATEPART(month, #end) * 30
+ CASE
WHEN DATEPART(DAY, #end) <= 30
THEN datepart(DAY, #end)
ELSE 30
END
DECLARE #startConverted INT
SELECT #startConverted = DATEPART(MONTH, #start) * 30
+ CASE
WHEN DATEPART(DAY, #start) <= 30
THEN DATEPART(DAY, #start)
ELSE 30
END
SELECT #endConverted - #startConverted
This isn't beautiful SQL, but it works. Note that it returns 104 (because 15 days - 1 day = 14 days), but simple enough to tack on a + 1 to the end of the final select if you want to handle the boundry days differently.
Note that the math here could pretty easily be moved into a function, which would allow you to clean your SQL up. Let's assume you created a function called GetDateTimeAsInt which holds the math; your SQL could be as simple as
DECLARE #start INT = GetDateTimeAsInt('2011-01-01'),
#end INT = GetDateTimeAsInt('2011-04-15')
SELECT #end - #start -- may need to add 1 here
In my testing, this seems to work. It will return the same result as the DATEDIFF function for the date range you specify in your post, but this is because there are 2 days with 31 days and 1 day with 28, so effectively, Jan - April have 30 days each. If you use it with a wider date range, you'll begin to get different results with my code vs. the DATEDIFF function.
Hope this helps.
I use PERIODDIFF. To get the year and the month of the date, I use the function EXTRACT:
SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM time)) AS months FROM your_table;
T-sql
SELECT DATEDIFF(dd, "2011-01-01","2011-04-15")

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