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
Related
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).
I have a Firebird database that saves the datetime field as a DOUBLE. I have created a ColdFusion datasource connection, so I can query the data remotely. While the rest of the data is being returned correctly, the datetime field is unreadable. I have tried using CAST and CONVERT to no avail. How can I convert this to a timestamp?
An example of the data stored is: 43016.988360
You can't just convert a DOUBLE PRECISION to a TIMESTAMP, not without explicitly defining how you want it mapped and writing that conversion yourself (or hoping there is an existing third-party UDF that does this for you).
A TIMESTAMP in Firebird is a date + time represented as an 8 byte value, where the date range is from January 1, 1 a.d. to December 31, 9999 a.d. and the time range is 00:00 to 23:59.9999 (so, 100 microsecond precision).
A DOUBLE PRECISION is - usually - the wrong type for storing date and time information, and as you haven't provided how that double value should be interpreted, we can't help you other than saying: there is no default method in Firebird to do this.
Based on the comments below, it looks like the value is a ColdFusion date value stored as double precision with the number of days since December 30th 1899, see also why is ColdFusion's Epoch Time Dec 30, 1899?. If this is really the case, then you can use the following for conversion to a TIMESTAMP:
select timestamp'1899-12-30 00:00' + 43016.988360 from rdb$database
Which will yield the value 2017-10-08 23:43:14.304. Using the value 43182.4931754 from the comments will yield 2018-03-23 11:50:10.354. That is a millisecond off from your expectation, but that might be a rounding/presentation issue, eg I get the exact expected date if I use 43182.49317539 instead.
I would strongly suggest you carefully test this with known values.
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
I added named query to my data source view, converting datetime to date. When I run the query it displays datetime anyway. Do you know how to fix this? I need date format to match with the Time dimension.
I'm using SQL Server Data Tools 2012.
The SQL DATE datatype accepts time values, so that is how it is shown in the Query preview. If the datatype of your Time dimension key is also DATE, then you should be OK to continue. Have you tried?
FWIW I would use a BIGINT key for a Date Dimension, containing a date represented as YYYYMMDD. This allows for extra rows e.g. with a -1 Key for unspecified dates (e.g. null in Fact data).
Please see the variable declaration below:
dim DateTest As Date = DateValue("0:0:0")
What is DateTest actually initialised with. If I step through the code, then it says: #12:00:00 AM#. If I try to enter this into the database (datetime field) then an SQLTypeException is thrown, which says: "SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM."
Does DateValue("0:0:0") initialise the date variable with a date value?
Obviously it returns Date.MinValue which is the smallest possible value of Date. The value of this constant is equivalent to 00:00:00.0000000, January 1, 0001. Since the year 0001 is smaller than 1753, the database throws the exception.
I didn't know DateValue, but instead of old VB functions i would use .NET methods like Date.Parse. For example:
Dim DateTest As Date = Date.Parse("2008-05-01")
Ah, the min of a datetime field in .net is much smaller than the min date of a datetime field in Sql Server. If you would like to store a value that matches, I suggest using the DateTime2(7) datatype in sql server.
Please see the following articles about min date times:
tsql min datetime
http://technet.microsoft.com/en-us/library/ms187819.aspx
.net min datetime
http://msdn.microsoft.com/en-us/library/system.datetime.minvalue.aspx
tsql datetime2 description:
http://technet.microsoft.com/en-us/library/bb677335.aspx