Implicit Conversion
I supposed posted_date data type is Datetime. However it does not matter whether the type on the other side is Datetime, Datetime2 or just Time because the string (Varchar) will be implicitly converted to Datetime.
With posted_date declared as a Datetime2 (or Time), the posted_date <= '2015-07-27 23:59:59.99999'
where clause fails because altough 23:59:59.99999
is a valid Datetime2 value, this is not a valid Datetime value:
Conversion failed when converting date and/or time from character string.
Time Range for Datetime
The time range of Datetime is 00:00:00 through 23:59:59.997.
Therefore 23:59:59.999 is out of range and has to be round up or down to the closest value.
Accuracy
Besides Datetime values are rounded by increments of .000, .003, or .007 seconds. (ie. 000, 003, 007, 010, 013, 017, 020, ..., 997)
This is not the case with the value 2015-07-27 23:59:59.999
which is within this range: 2015-07-27 23:59:59.997
and 2015-07-28 0:00:00.000
.
This range correspond to the closest preceding and following options, both ending with either .000, .003 or .007.
Rounding up or down?
Because it is closer to 2015-07-28 0:00:00.000
(+1 versus -2) than 2015-07-27 23:59:59.997
, the string is rounded up and becomes this Datetime value: 2015-07-28 0:00:00.000
.
With an upper limit like 2015-07-27 23:59:59.998
(or .995, .996, .997, .998), it would have been round down to 2015-07-27 23:59:59.997
and you query would have worked as expected.
However it would not have been a solution but just a lucky value.
Datetime2 or Time types
Datetime2 and Time time ranges are 00:00:00.0000000
through 23:59:59.9999999
with an accuracy of 100ns (the last digit when used with a 7 digit precision).
However a Datetime(3) range is not similar to Datetime range:
- Datetime
0:0:00.000
to 23:59:59.997
- Datetime2
0:0:00.000000000
to 23:59:59.999
Solution
In the end it is safer to look for dates below the next day than dates below or equal to what you think it the last fragment of time of the day.
This is mainly because you know that the next day always starts at 0:00:00.000 but different data types may not have the same time at the end of the day:
Datetime `0:0:00.000` to `23:59:59.997`
Datetime2 `0:0:00.000000000` to `23:59:59.999-999-900`
Time2 `0:0:00.000000000` to `23:59:59.999-999-900`
< 2015-07-28 0:00:00.000
will give you an accurate results and is the best option
<= 2015-07-27 23:59:59.xxx
may return unexpected values when it is not rounded up to what you think it should be.
- Conversion to Date and usage of function should be avoided because it limits the usage of indexes
We could think that changing [posted_date] to Datetime2 and its higher precision could fix this problem but it won't help because the string is still converted to Datetime.
However, if a cast is added cast(2015-07-27 23:59:59.999' as datetime2)
, this works fine
Cast and Convert
Cast can convert a value with up to 3 digits to Datetime or with up to 9 digits to Datetime2 or Time and round it to the correct precision.
It is to be noted that Cast of Datetime2 and Time2 may give different results:
select cast('20150101 23:59:59.999999999' as datetime2(7))
is round up 2015-05-03 00:00:00.0000000 (for value greater than 999999949)
select cast('23:59:59.999999999' as time(7))
=> 23:59:59.9999999
It sort of fix the problem datetime is having with the 0, 3 and 7 increments although it is still always better to look for dates before the 1st nano second of the next day (always 0:00:00.000).
Source MSDN: datetime (Transact-SQL)