Casting 0 as DATE and DATETIME - sql

I just happened to stumble upon this and couldn't find any technical explanation:
In SQL Server 2014:
SELECT CAST('' AS DATETIME);
1900-01-01 00:00:00.000
SELECT CAST(0 AS DATETIME);
1900-01-01 00:00:00.000
SELECT CAST('' AS DATE);
1900-01-01
SELECT CAST(CAST(0 AS DATETIME) AS DATE);
1900-01-01
SELECT CAST(0 AS DATE);
Msg 529, Level 16, State 2, Line 4
Explicit conversion from data type int to date is not allowed.
Why does the CAST from an empty string to DATE and DATETIME work, but from 0 it only works as DATETIME?
I'm interested in a technical explanation

I think it's a matter of what Microsoft chose to support. More or less it comes down to the fact that data conversions to datetime are allowed from numeric values, while date does not allow conversions from numeric values. Within SQL server, it must be converting the 0 first to a numeric value like 0.000000000 and then to the datetime equivalent of that number. My example shows that it's possible to convert the current date into a numeric value and then back to a datetime. However, converting to a date throws an error. I would guess to avoid rounding issues that you may have if you tried to convert the value 0.5 to a date. Would program the SQL engine to implicitly convert 0.5 to the date 1900-01-01 or 1900-01-02 in that case. You'd have to make arbitrary decisions on what date should be returned in that case.
This works:
--produces a numeric value like 42746.97660799
select cast(getdate() as numeric(18,8))
select cast(42746.97660799 as datetime)
While this throws the same error you received:
select cast(42746.97660799 as date)
Msg 529, Level 16, State 2, Line 5
Explicit conversion from data type numeric to date is not allowed.

Your first question, why you can't cast 0 to Date, it is an illegal cast from Int to Date (see the matrix in the linked doc)
I found your question after finding similar code and wondered why cast(0 as datetime) yielded 1900-01-01. I wanted to know whether this was a "standard" that I could trust or undefined "it works that way until we change it." I've even found other SO posts that suggest the Epoch is 1900, but unable to find any official documentation on this.
While reading the Cast/Convert documentation there is a reference to what Cast will do if the Year or the Time is missing (albeit in the varchar conversion section). When undefined, Date defaults to 1900-01-01 and Time to 00:00:00. (see subscript 6 in the linked doc). There's sample code in the article that casts a Time to Datetime and 1900-01-01 is generated for the date portion.
This (thankfully?!) yields 0 (whereas min-date of 1753 yields a large negative number)
select cast(cast('1900-01-01' as datetime) as Int)
Microsoft documentation titled "CAST and CONVERT (Transact-SQL)"
https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver15

It's maybe from timestamp behavioral.
You can cast 0 to datetime then cast to date.
Also this is a rule from Microsoft. An not a national rule.

I'm not sure there is much of clean technical explanation other than what SQL Server supports. SQL server ranks all of its data types Data Type Precendence. The implicit conversions have evolved over time. There is a Microsoft Blog by Craig Friedman that explains the logic and evolution pretty well More on Implicit Conversions. What #hamid_reza hobab indicates is essentially true. SQL server is supporting a conversion of an int to a datetime. Datetime is a higher precendence then date, so transitively datetime can be converted to the date. SQL server is not supporting an implicit or explicit conversion of int to date as indicated in the support matrix posted in the comment by #dfundako

Related

SQL Server nvarchar to date

I'm trying to write a query and convert a nvarchar to a date. I've tried the below but keep receiving an error:
Conversion failed when converting date and/or time from character string.
cast(columnName as DATE) as castDate
convert(Date, columnName, 23) as convertDate
(nvarchar(max),null)
2021-12-30 02:22:24 UTC
Desired output:
2021-12-30
Date and time data types don't support 3 letter abbreviations for timezones (such as 'UTC' here). As you only need to date, then I would suggest you simply take the 10 left most characters and then CONVERT/CAST the value. I use TRY_CONVERT here, as due to the decision to use nvarchar for the data type (not even touching on the fact that it's MAX in length) you could have bad dates:
TRY_CONVERT(date,LEFT(YourColumn,10))
Fortunately, yyyy-MM-dd is an unambiguous format for the date data type.
I do, however, strongly suggest you fix your design. nvarchar is not an appropriate data type for a date and time value, and certainly a MAX length value (suggesting that the value of the date is likely to be more than 4,000 characters in length) is completely wrong. Most likely you should be using a datetimeoffset here (or maybe just as datetime2 if all your values are UTC).

Convert string to datetime in SQL, when Z and T literals are involved [duplicate]

This question already has answers here:
SQL Server : DATEADD() not working beyond milliseconds when using a string value
(2 answers)
Closed 1 year ago.
A date is stored as string in the database:
2021-12-15T14:18:22.6496978Z
I try to convert it to datetime:
CONVERT(DATETIME, '2021-12-15T14:18:22.6496978Z', 127)
using 127 which refers to yyyy-mm-ddThh:mi:ss.mmmZ, which I believe is the right style for the input date. But I am getting
Conversion failed when converting date and/or time from character string.
Any ideas why?
DATETIME and SMALLDATE are legacy types(as in replaced-15-years-ago, don't-use legacy) that have a lot of quirks and limited precision. For example, datetime is only accurate to 0, 3 or 7 milliseconds. The value you tried to parse can't be converted to a datetime without losing precision.
The docs warn strongly against using this type, with a big pink warning at the top of the DATETIME page:
Use the time, date, datetime2 and datetimeoffset data types for new work. These types align with the SQL Standard. They are more portable. time, datetime2 and datetimeoffset provide more seconds precision. datetimeoffset provides time zone support for globally deployed applications.
In this case you need the datetime2 or datetimeoffset types introduced in 2005. Both types allow specifying a precision.
To preserve the timezone offset, use datetimeoffset.
select CONVERT(datetimeoffset, '2021-12-15T14:18:22.6496978Z', 127)
----
2021-12-15 14:18:22.6496978 +00:00
To remove the offset, use datetime2. The result will have no assumed offset so you should take care to always treat it as UTC:
select CONVERT(datetimeoffset, '2021-12-15T14:18:22.6496978Z', 127)
----
2021-12-15 14:18:22.6496978
In both cases you can specify the desired precision. For example, datetime2(0) will truncate fractional seconds:
select CONVERT(datetime2(0), '2021-12-15T14:18:22.6496978Z', 127)
---
2021-12-15 14:18:23

Concatenate Date and Time as DATETIME in SQL Server

I'm having a trouble in combining date and time to DATETIME. I have an existing table with data so I can't change it to Datetime2.
DECLARE #t1 TABLE(StartDate DATE, StartTime Time)
INSERT INTO #t1
VALUES('2018-02-28','08:00:00')
SELECT
CAST(CONCAT(StartDate, ' ', StartTime) AS DATETIME)
FROM #t1
The error shown is:
Conversion failed when converting date and/or time from character string.
Don't concat(). Just add but they both need to be datetime:
select cast(startdate as datetime) + cast(starttime as datetime)
The cast fails because the time component of the datetime data type has less precision than the time data type, as described on this page, which is a useful reference for the various date and time types offered by SQL Server. As you can also see on that page, SQL Server has a datetime2 data type that offers greater precision; if you had used that in your cast, I don't think you would have had a problem.
Gordon's solution is a good one in that it avoids the need to deal with conversions to and from strings in the first place. Just be aware that combining a date and time into a datetime may involve a loss of precision. See this question on the Stack Exchange DBA site for a solution with datetime2 that retains the precision of the original values.

CAST vs ssis data flow implicit conversion difference

I have a SSIS package which transfers some data from Oracle to SQL Server.
In Oracle dates are stored as float, e.g. 42824 == '2017-04-01' - application which uses the database is written in Delphi.
While select CAST(42824 as datetime)
in Management Studio results in '2017-04-01 00:00:00.000', the same value (42824) inserted by package into datetime column in SQL Server table shows 2017-03-30 00:00:00.000.
Note: Source data type for this number is DT_R8, changing the type to DT_UI4 in Data Conversion component changes nothing
Can anyone explain this?
About date serials
The value stored in Oracle (42824) is known as date serial , it is also used in Microsoft Excel.
Date Serial represents the number of Days between the date value and the initial value that is 1899-12-30
You can Read more about Date Serials at:
Why is 1899-12-30 the zero date in Access / SQL Server instead of 12/31?
convert Excel Date Serial Number to Regular Date
CAST method
From Microsoft Docs - CAST and CONVERT (Transact-SQL):
Only supported when casting from character data to datetime or smalldatetime. When character data that represents only date or only time components is cast to the datetime or smalldatetime data types, the unspecified time component is set to 00:00:00.000, and the unspecified date component is set to 1900-01-01
So CAST function consider the value 1900-01-01 as an initial value when casting dates. So we need to subtract 2 days when using it to convert Date Serials
There are 2 ways to convert it to date using SQL Server:
select DATEADD(d,42824,'1899-12-30')
select CAST(36464 - 2 as SmallDateTime)
SSIS Implicit conversion
Also according to this Microsoft docs article
DBTYPE_DATE (This is an automation DATE type. It is internally represented as a double.. The whole part is the number of days since December 30, 1899 and the fractional part is the fraction of a day. This type has an accuracy of 1 second, so has an effective scale of 0.)
So implicit conversion in SSIS consider the value 1899-12-30 as an initial value when casting dates. So there is no need to subtract 2 days when using it to convert Date Serials

Why does SQL Server convert VARCHAR to DATETIME using an invalid style?

I can't make out from the documentation why SQL Server parses a text in a format other than the specified style.
Regardless of whether I provide text in the expected format:
SELECT CONVERT(DATETIME, N'20150601', 112)
or incorrect format (for style 113):
SELECT CONVERT(DATETIME, N'20150601', 113)
The results are the same: 2015-06-01 00:00:00.000 I would expect the latter to fail to convert the date (correctly).
What rules does it employ when trying to convert a VARCHAR to DATETIME? I.e. why does the latter (incorrect format style) still correctly parse the date?
EDIT: It seems I've not been clear enough. Style 113 should expect dd mon yyyy hh:mi:ss:mmm(24h) but it happily converts values in the format yyyymmdd for some reason.
Because the date is in a canonical format ie(20150101). The database engine falls over it implicitly. This is a compatibility feature.
If you swapped these around to UK or US date formats, you would receive conversion errors, because they cannot be implicitly converted.
EDIT: You could actually tell it to convert it to a pig, and it would still implicitly convert it to date time:
select convert(datetime,'20150425',99999999)
select convert(datetime,'20150425',100)
select convert(datetime,'20150425',113)
select convert(datetime,'20150425',010)
select convert(datetime,'20150425',8008135)
select convert(datetime,'20150425',000)
And proof of concept that this is a compatibility feature:
select convert(datetime2,'20150425',99999999)
Although you can still implicitly convert datetime2 objects, but the style must be in the scope of the conversion chart.
Reason why is the date N'20150601' converted to valid datetime is because of fact that literal N'20150601' is universal notation of datetime in SQL Server. That means, if you state datetime value in format N'yyyymmdd', SQL Server know that it is universal datetime format and know how to read it, in which order.
You should convert to varchar type in order to apply those formats:
SELECT CONVERT(varchar(100), CAST('20150601' as date), 113)
OK, you are converting datetime to datetime. What did you expect? In order to apply formats you should convert to varchar and you have to have date or time type as second parameter.