Converting Milliseconds to Days, hours, minutes and seconds - sql

i have a bigint field in Microsoft SQL Server 2008R2 filled with ticks (A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond.)
http://msdn.microsoft.com/en-us/library/system.datetime.ticks.aspx
and i need to convert the sum of all records to Days:Hours:Minutes:Seconds:Milliseconds.
it works for a single record:
SELECT CONVERT(TIME, DATEADD(ms, duration/10000, 0)) FROM tblMediaFileProperties WHERE FileId = '6C0A849D-95B4-4755-A923-B9DD8F1AF23E'
but if a sum it up to all records using:
SELECT CONVERT(TIME, DATEADD(ms, SUM(duration/10000), 0)) FROM tblMediaFileProperties
i get a:
Arithmetic overflow error converting expression to data type int.
i know the overflow comes from the CONVERT to Data Type TIME Function...
help's appreciated, thanks!

It's too big for DATEADD which only accepts an int.
Break it into two parts: seconds, then milliseconds.
SELECT CONVERT(TIME,
DATEADD(ms, SUM(duration/10000 % 1000),
DATEADD(ss, SUM(duration/10000000), 0)))
FROM tblMediaFileProperties
And if your total duration goes above 1 day, you can use this to get the days and hr:min:sec:ms separately. It's a matter of cast and string concat if you actually want the result in textual form.
declare #duration bigint
set #duration = 1230000000
SELECT #duration/10000/1000/60/60/24 DAYS,
CONVERT(TIME,
DATEADD(ms, SUM(#duration/10000 % 1000),
DATEADD(ss, SUM(#duration/10000000), 0))) HR_MIN_SEC

Related

Arithmetic overflow casting LDAP lastLogon

I am trying to convert an LDAP lastLogon value to an SQL DateTime value utilizing this code:
DATEADD(MS, CAST(lastLogon AS BIGINT), '19700101')
where lastLogin is the AD value for the user's latest login.
I receive this error:
Arithmetic overflow error converting expression to data type int.
I am checking for null and using ISNUMERIC.
We are running 2008 R2. Any ideas?
Your lastLogon value is too large, DATEADD() accepts an INT value not a BIGINT.
You can workaround this with multiple steps, dividing your time into smaller chunks of bigger units, i.e. Seconds first, then milliseconds:
DATEADD(MS,CAST(lastLogon AS BIGINT)%1000,DATEADD(SECOND, CAST(lastLogon AS BIGINT)/1000, '19700101'))
I think Week is as large as you can go in your first step, as a month's worth of milliseconds won't fit into an INT, but I'd probably stick with DAY for simplicity:
DATEADD(MS,CAST(lastLogon AS BIGINT)%(1000*60*60*24),DATEADD(DAY, CAST(lastLogon AS BIGINT)/(1000*60*60*24), '19700101'))
too late. but found solution
sample:
select dateadd(day,130787731801583080 / (60 * 10000000) / 1440 - (datediff(dd, '1/1/1900', dateadd(yy, 299, '1/1/1900'))), '1/1/1900')

Rounding a datetime value down to the nearest half hour

I have a requirement to round a datetime2 value down to the nearest half hour. For example '10/17/2013 12:10:00.123' would round down to '10/17/2013 12:00:00.0' And '10/17/2013 12:34:17.123' would round down to 10/17/2013 12:30:00.0'. My first thought was to create a UDF which would break the date and time apart and do it that way. However, I'm wondering if something like this can be done in a single T-SQL statement?
I'm using SQL Server 2012 and the data type of the column is a dateTime2 (which cannot be converted to a float!!)
The answer by Ian is good, but it contains an unnecessary conversion. I suggest
SELECT CONVERT(smalldatetime, ROUND(CAST([columnname] AS float) * 48.0,0,1)/48.0) FROM [tableName]
If you want to round to the nearest half-hour instead of always rounding down, use
SELECT CONVERT(smalldatetime, ROUND(CAST([columnname] AS float) * 48.0,0)/48.0) FROM [tableName]
How about this
declare #d datetime = '2013-05-06 12:29.123'
select
case
when datepart(minute, #d) < 30 then cast(dateadd(minute, -datepart(minute,#d)-datepart(second,#d), #d) as smalldatetime)
when datepart(minute, #d) >= 30 then cast(dateadd(minute, -datepart(minute,#d)-datepart(second,#d)+30, #d) as smalldatetime)
end
Here is one way to do it:
update t set
d = dateadd(minute,datediff(minute,'19000101',d)/30*30,'19000101');
select cast(floor(cast(
cast('10/17/2013 12:34:00' as datetime)
as float(53)) * 48) / 48 as datetime)
EDIT
Works better if you use smalldatetime to avoid the extra precision
select cast(floor(cast(
cast('2012-01-02 11:33:14.097' as smalldatetime)
as float(53)) * 48) / 48 as smalldatetime)
Here is a slightly different approach that I used when I needed to round down to the nearest 5 minute interval. There is probably a way to simplify this further, but at least this got me what I needed.
DECLARE #now datetime = GETDATE()
SELECT #now as cur_datetime, DATEADD(MINUTE, -(DATEDIFF(MINUTE,DATEADD(HOUR,DATEDIFF(HOUR,0,#now), 0),DATEADD(MINUTE,DATEDIFF(MINUTE,0,#now), 0)) % 5), DATEADD(MINUTE,DATEDIFF(MINUTE,0,#now), 0)) as round_down_to_nearest_5_minute_mark
#Twinkles's answer works well in SQL server to round to closest half an hour.
However, in development, strongly recommend use FLOOR to round to last half an hour.
SELECT CONVERT(datetime, FLOOR(CAST([columnname] AS float) * 48.0)/48.0) FROM [tableName]
You can use DATETIME2FROMPARTS to reconstruct the date. To round the minutes down to 30 minute intervals use the formula minutes intdiv 30 * 30
SELECT
dt2,
DATETIME2FROMPARTS(
DATEPART(year, dt2),
DATEPART(month, dt2),
DATEPART(day, dt2),
DATEPART(hour, dt2),
DATEPART(minute, dt2) / 30 * 30,
0,
0,
0
)
FROM (VALUES
-- generic datetime2
(SYSDATETIME()),
-- 30 minute boundary
('2001-01-01 00:29:59.9999999'),
('2001-01-01 00:30:00.0000000'),
('2001-01-01 00:30:00.0000001'),
-- min and max date
('0001-01-01 00:00:00.0000000'),
('9999-12-31 23:59:59.9999999')
) AS v(dt2)

SQL: Using DATEADD with bigints

I have some SQL to convert javascript dates to SQL dates which works great. However, I've encoutered some data which is too large and is causing an exception:
Arithmetic overflow error converting expression to data type int
Here is the SQL in question:
DATEADD(MILLISECOND, cast(569337307200000 as bigint) % 1000, DATEADD(SECOND, cast(569337307200000 as bigint) / 1000, '19700101'))
I am running this on SQL Server 2008.
Just do the problematic DATEADD in two steps, starting with a coarser time unit (seconds, minutes, hours etc.), then dropping back to the fine grained one for the remainder.
Avoid going to the level of weeks and months though as that would require actual calendar calculations and we would prefer the system to handle that.
Example below needs to calculate a start time given a (possibly) large current duration in milliseconds.
-- large durations can overflow the integer argument needed for DATEADD
-- so do as two steps subtracting minutes (60000ms) and then remaining milliseconds.
DATEADD(ms, -large_duration_ms%60000, DATEADD(minute, -large_duration_ms/60000, GETDATE()))
One way I got around the Integer overflow issue was to subtract a more recent date from the microtime unix time stamp.
DATEADD(s, (CreationTimeStamp/1000-1384128000), '2013-11-11') AS CreateDate,
This will not fix the OP's problem because they will still overflow the max on the date column.
According to MSDN, in DATEADD (datepart , number , date )
number is an expression that can be resolved to an int that is added
to a datepart of date. User-defined variables are valid. If you
specify a value with a decimal fraction, the fraction is truncated and
not rounded.
Also notice that even if you give number as an integer, depending on your date & datepart, it could overflow the max range of the date which is 31-12-9999 for sql server 2008
Number has to be an integer. Here is a Test Demo
I had the same problem and I wanted to be meet the datetime range of mssql
Minimun datetime: 1753-01-01 00:00:00.000 (-6847804800)
Maximum datetime: 9999-12-31 23:59:59.997 (253402300799)
To achieve this the only solution I found was to loop to use DATEADD with int range values.
So based on this answer: https://stackoverflow.com/a/2904294/687490
CREATE FUNCTION dbo.fn_ConvertToBigDateTime (#Datetime BIGINT)
RETURNS DATETIME
AS
BEGIN
DECLARE #result datetime = Convert(datetime, '01/01/1970');
DECLARE #LocalTimeOffset BIGINT
,#AdjustedLocalDatetime BIGINT
,#MinIntValue INT
,#MaxIntValue INT
,#RemainingSeconds BIGINT;
-- define int limit
SET #MinIntValue = -2147483648;
SET #MaxIntValue = 2147483647;
-- compute the datetime with the offset
SET #LocalTimeOffset = DATEDIFF(second,GETDATE(),GETUTCDATE())
SET #AdjustedLocalDatetime = #Datetime - #LocalTimeOffset
-- going to the future
WHILE(#AdjustedLocalDatetime>#MaxIntValue)
BEGIN
SET #AdjustedLocalDatetime = #AdjustedLocalDatetime - #MaxIntValue;
SELECT #result = Convert(datetime, dateadd(ss, #MaxIntValue,#result));
END
-- going back in the past
WHILE(#AdjustedLocalDatetime<#MinIntValue)
BEGIN
SET #AdjustedLocalDatetime = #AdjustedLocalDatetime - #MinIntValue;
SELECT #result = Convert(datetime, dateadd(ss, #MinIntValue,#result));
END
RETURN (SELECT DATEADD(second,#AdjustedLocalDatetime, #result))
END;
You can then test the function with :
select dbo.fn_ConvertToBigDateTime(-6847804800) as 'min datetime',
dbo.fn_ConvertToBigDateTime(253402300799) as 'max datetime'
Hope it will help.
You can try converting the millis to days, add the days to beginning of EPOCH, and add the ms part to the date at the end. The problem is that you were trying to convert millis to seconds, which can still be too large number for INT for larger dates.
DATEADD(MILLISECOND,
CAST(myLongDateMs AS BIGINT) % 86400000,
DATEADD(day,
CAST(myLongDateMs AS BIGINT) / 86400000,
'19700101'
)
)
I faced this problem too. In my sql statement, the error occurred when the date time value is null.
My solution is to check whether the date time value is null using "CASE When". Only running the arithmetic when it is not null, and the problem solved.

How to only check the time on datetime fields but ignore the date?

I have a column that stores data in datetime format. I want to check for all instances where the time part of this column is not equal to 00:00:00:000 - the date does not matter.
Basically, if time() was a function, something like this:
SELECT *
FROM progen.DY
WHERE TIME(DY_DATE) <> '00:00:00:000'
How do I go about doing this?
You only need a minor tweak on what you already have.
SELECT *
FROM progen.DY
WHERE TIME(DY_DATE) <> '00:00:00:000'
Use CONVERT to change your DATETIME to a TIME.
SELECT *
FROM progen.DY
WHERE CONVERT(TIME, DY_DATE) <> '00:00:00:000'
Another way is to convert it to different datatype, eg
SELECT *
FROM progen.DY
WHERE CAST(DY_DATE as float) - CAST(DY_DATE as int) > 0
SQLFiddle Demo
I do this all the time when trying to see if a table's column should be turned into a date instead of a datetime, which is really the answer.
select *
from progen.dy
where cast(dy_date as Date) <> dy_date
the cast removes the time and datetime has higher precedence, so when compared, if the are unequal then it has a time value. Same thing could be done with a cast to time, with a bit of different syntax.
Use DATEDIFF and DATEADD to instead get the date part of the datetime. Compare the column against the date only, and it will return those rows that have a non-zero time.
The way this works is that we first calculate the difference (in days) between the epoch and the value. We add that number to the epoch to create a new datetime. Since the result of DATEDIFF is an integer, any time component gets rounded off.
SELECT *
FROM Table
WHERE DateColumn <> DATEADD(d, DATEDIFF(d, 0, DateColumn), 0)
The time function could then be implemented by the following, not that I recommend it for this specific scenario:
SELECT DATEDIFF(minute, DATEADD(d, DATEDIFF(d, 0, DateColumn), 0), DateColumn) as MinutesIntoDay,
-- or, if you require higher precision
DATEDIFF(second, DATEADD(d, DATEDIFF(d, 0, DateColumn), 0), DateColumn) as MinutesIntoDay
FROM Table
Edit: As mentioned in other answers, you can cast to DATE to achieve the same effect as DATEADD(d, DATEDIFF(d, 0, DateColumn), 0), which cleans up nicely. However, DATE was only added in SQL Server 2008, whereas the formula has compatibility back to at least SQL 2000. So if you need the backwards compatibility or are dealing with SQL CE, casting to DATE is unavailable.
SELECT *
FROM progen.DY
WHERE CONVERT(TIME, DY_DATE - CONVERT(DATE, DY_DATE)) > '00:00'

Extract time from datetime efficiently (as decimal or datetime)

I have been able to find a lot of information for getting a string representation of just the time from a datetime column like this one.
I need to get the time part out of a datetime in a way that I can do some math on it like adding it to another datetime. So a string representation of the time wont help me.
However I've only found one example that will extract the time as a numeric type value. I.e:
SELECT CAST(GETDATE() AS FLOAT) - FLOOR(CAST(GETDATE() AS FLOAT))
This method requires two casts though and I have to run this on over 10,000 rows. Is there anything similar to the dateadd method for extracting the date part from a datetime column i.e.:
select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)
that I can use to get just the time out of a datetime column and return it as a decimal or datetime? Perhaps a solution that uses less casting?
I am using SQL Server 2000.
To get a datetime:
SELECT GetDate() - DateDiff(day, 0, GetDate());
-- returns the time with zero as the datetime part (1900-01-01).
And to get a number representing the time:
SELECT DateDiff(millisecond, DateDiff(day, 0, GetDate()), GetDate());
-- time since midnight in milliseconds, use as you wish
If you really want a string, then:
SELECT Convert(varchar(8), GetDate(), 108); -- 'hh:mm:ss'
SELECT Convert(varchar(12), GetDate(), 114); -- 'hh:mm:ss.nnn' where nnn is milliseconds
One way You can get the time in seconds is with:
select cast(datediff(second, DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0), getdate())/(60*60*24.0) as datetime)
This calculates the time in seconds and then converts back to a datetime.
To get it as a decimal:
select datediff(second, DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0), getdate())/(60*60*24.0)
Or use "ms" if you prefer millisecond precision.
Or, you can use the more readable:
select datepart(hh, getdate())/24.0+datepart(mm, getdate())/(24*60.0)+
datepart(ss, getdate())/(24*60*60.0)