Consider the following program:
DateTime dateTime = new DateTime(634546165144647370);
SqlDateTime sqlDateTime = new SqlDateTime(dateTime);
Console.WriteLine("dateTime.TimeOfDay = " + dateTime.TimeOfDay);
Console.WriteLine("sqlDateTime.TimeOfDay = " + sqlDateTime.Value.TimeOfDay);
Console.ReadLine();
That will have the following output:
dateTime.TimeOfDay = 10:21:54.4647370
sqlDateTime.TimeOfDay = 10:21:54.4630000
What is odd to me is that .464737 was rounded to .463. Shouldn't that have rounded to .464?
I assume I have not found a bug in .NET code, so the question is:
Why did it round to what it did? and how can I get client side rounding that will do what SqlServer is going to do?
As a side note, I saved this date time to a SQL Server database (in a DateTime column) and pulled it out again and it came out as 10:21:54.4670000. So I am really confused. (I thought SqlDateTime would match up with what SQL Server was going to do.)
Note: Because I am using OData I cannot use DateTime2 in SQL Server.
SQL Server's
datetype
datatype is, internally, two 32-bit words (integers). The high-order word is the offset in days since the epoch (zero point) of the internal calendar used by SQL Server: that epoch is 1 January 1900 00:00:00.000. The low-order word is the offset in milliseconds since start-of-day (00:00:00.000/midnight).For historic reasons, while the precision of the low-order word is 1 millisecond; the accuracy is 1/300th second (!?). That means that any given point-in-time is rounded to an increment of of 0, 3 or 7 milliseconds.
To do the conversion in the manner in which SQL Server does it, do the following: Take the milliseconds portion of the actual time under consideration, a value from 0-999, modulo 100. That gives you the low order digit, a value from 0-9. So if the current time is 23:57:23.559, the milliseconds component is 559. Modulo 100 you get 9.
Brilliant! Or something.
See the "Remarks" section under SQL Server 2005 BOL here: http://msdn.microsoft.com/en-us/library/ms187819(v=SQL.90).aspx
The upshot of all of this is that you can't do the obvious check for a date range/time period...something like
as that potentially brings in a [small] chunk of data from the next period. And you can't say
as that potentially excludes data that belongs in the period. Instead, you must say something like
where myDateColumn >= '1 September 2011 00:00:00.000' and myDateColumn < '1 October 2011 00:00:00.000'
, orwhere myDateColumn >= '1 September 2011 00:00:00.000' and myDateColumn <= '30 September 2011 23:59:59.997'
It should be noted that
smalldatetime
, which has a precision of 1 minute, exhibits the same bogus behaviour: if the seconds component of the time under consideration is 29.998 seconds or less, it is rounded down to the nearest minute; if 29.999 or higher, it is rounded UP to the next minute, so the value31 Dec 2010 23:59:30.000' winds up as a
smalldatetimevalue of
1 Jan 2011 00:00:00`.This has all sorts of implications, especially WRT to billing systems and the like.
I would say that if precision is important to you, store your date/time values in SQL Server as strings in ISO 8601 format, something like
2011-10-30T23:59:55.1234
(or the equivalent 'compact' form (20111030T235955.1234
). ISO 8601 collates and compares properly; it converts easily and it is human-readable. Even better split it out into two columns — one for date (2011-10-30
) and one for time (23:59:55.1234
). Then add a third, computed column to put it all together:A pretty good summary of ISO 8601 is at http://www.cl.cam.ac.uk/~mgk25/iso-time.html. Wikipedia also has pretty good information: http://en.wikipedia.org/wiki/ISO_8601.
SQL Server
DATETIME
has an accuracy of 3.33ms - therefore, you cannot get all possible values, and there's a good chance.464
was just such a value.On SQL Server 2008, you could use
DATETIME2
orTIME(x)
datatypes which have an accuracy to 100ns - that should be plenty enough for "regular" useSqlDateTime Structure
I would argue that sqlDateTime is within that 3.33 ms accuracy.
As a side note, I saved this date time to a SQL Server database (in a DateTime column) and pulled it out again and it came out as 10:21:54.4670000. So I am really confused. (I thought SqlDateTime would match up with what SQL Server was going to do.)
and
It looks like DateTime is accurate within 100ns which is about the difference you have experienced.
This is also within the 3.33 ms accuracy...If you need something more accurate then 3.33ms then you will have to use SQL Server 2008