Question

I'm trying to subtract 2 datetimes values and get a datetime back in SQL. I currently just do this:

DECLARE @difference DATETIME = @endtime - @starttime

However, what this does is: when

@endTime = '2014-2-22 00:12:00' and @startTime = '2014-2-22 00:00:00'

I expected @difference to be 0000-00-00 00:12:00 But instead it is: 1900-01-01 00:12:00

How can I get this fixed? I tried to use DATEDIFF but that returns a specific integer that can be the year, month etc but not a DATETIME.

Was it helpful?

Solution

The epoch (zero point) of the SQL Server datetime calendar is 1900-01-01T00:00:00.000. If you do something like

select convert(datetime,'')

That is the value you will get.

SQL Server's datetime data type is composed of 2 signed 32-bit integers. The first is the offset in days from the epoch; the second is the time-of-day expressed as an offset in milliseconds from start-of-day.

When you subtract two datetime values, say @end and @start it does what you'd expect it to.

  • if @end.time-of-day is < @start.time-of-day, carry a day's worth of milliseconds from @end.date and decrement @end.date.
  • compute the new time-of-day value by subtracting @start.time-of-day from @end.time-of-day.
  • compute the new date by subtracting @start.date from @end.date`

If the resulting value is outside the domain of datetime (1753-01-01T00:00:00.000 through 9999-12-31T23:59:59.997) an error is raised.

You are getting the expected result...for SQL Server.

Edited to show the what's going on under the covers:

declare @x datetime = '2014-02-22 00:12:00'
declare @y datetime = '2014-02-22 00:00:00'
declare @z datetime = @x - @y

      select 'end' ,
             date        = @x ,
             description = 'days_since_epoch' ,
             value       = convert(int,substring( convert(varbinary(8),@x) , 1 , 4 ) ) ,
             description = 'time_as_ms_offset' ,
             value       = convert(int,substring( convert(varbinary(8),@x) , 5 , 4 ) )
union select 'start' ,
             date        = @y ,
             description = 'days_since_epoch'  ,
             value       = convert(int,substring( convert(varbinary(8),@y) , 1 , 4 ) ) ,
             description = 'time_as_ms_offset' ,
             value       = convert(int,substring( convert(varbinary(8),@y) , 5 , 4 ) )
union select 'delta' ,
             date        = @z ,
             description = 'days_since_epoch'  ,
             value       = convert(int,substring( convert(varbinary(8),@z) , 1 , 4 ) ) ,
             description = 'time_as_ms_offset' ,
             value       = convert(int,substring( convert(varbinary(8),@z) , 5 , 4 ) )

produces this result, showing the math:

      date                    description      value description       value
----- ----------------------- ---------------- ----- ----------------- ------
end   2014-02-22 00:12:00.000 days_since_epoch 41690 time_as_ms_offset 216000
start 2014-02-22 00:00:00.000 days_since_epoch 41690 time_as_ms_offset      0
delta 1900-01-01 00:12:00.000 days_since_epoch     0 time_as_ms_offset 216000
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top