10

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.

Luke Belbina
  • 5,708
  • 12
  • 52
  • 75
  • Can you not pass the javascript UTC date as a string? It'll be much easier to parse, and read for that matter... – twoleggedhorse Feb 22 '13 at 18:24
  • Is that data even valid? If I enter `new Date(569337307200000)` into the JS console it says `Mon Aug 01 20011 05:00:00 GMT+0100`. Highly unlikely that you are dealing with dates 18,000 years into the future. – Martin Smith Feb 22 '13 at 19:04
  • Yeah the client should've not accepted this input but I'm dealing w/ a legacy migration project :-) – Luke Belbina Feb 22 '13 at 20:06

6 Answers6

12

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()))
bdukes
  • 152,002
  • 23
  • 148
  • 175
Andrew
  • 121
  • 1
  • 2
6

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.

Steve Putala
  • 181
  • 2
  • 5
3

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

Kaf
  • 33,101
  • 7
  • 58
  • 78
  • Yeah I know this, so what's a solution for converting that to a sql date. – Luke Belbina Feb 22 '13 at 18:44
  • 1
    In your case, you simply CANNOT DO IT. Because even if you get away from int overflow issue by converting your milliseconds to (say) hours, since it is bigger than 70389504 (hour diff of '19700101' and '99991231' ), it will overflow the max possible date of sql server. [Check this demo to understand](http://sqlfiddle.com/#!3/d41d8/9379) – Kaf Feb 22 '13 at 18:57
  • Agree, The method in the OP will work fine for numbers up to `2147483647999` which is `2038-01-19 03:14:08`. Anything beyond that they will need to look at breaking down into (say) hours and miliseconds instead. That number in the question is clearly a mistyped date that should have been rejected by client validation. – Martin Smith Feb 22 '13 at 19:38
  • Yeah unfortunately I am converting a legacy database. I'll have to add an SQL if and just default it to the max. – Luke Belbina Feb 22 '13 at 20:06
1

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.

Vespucci75fr
  • 338
  • 2
  • 7
  • That original answer has problems with Daylight Savings Time / Summer Time, I guess this function has the same problem? – Rory Jan 06 '22 at 11:15
  • results in: Adding a value to a 'datetime' column caused an overflow. – Asher Aug 20 '23 at 07:14
1

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'
  )
) 
Aqeel Ashiq
  • 1,988
  • 5
  • 24
  • 57
-2

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.

TT.
  • 15,774
  • 6
  • 47
  • 88