Converting "42978.6736458333" varchar to datetime - sql

I've a varchar column with a value of 42978.6736458333 that I want to convert back to a proper datetime. I've searched quite a bit and tried many of the suggestions but I cannot seem to find one that works with the data I have.
I got this value from excel when I did a CONCATENATE of all the fields in the sheet to do an insert to my DB; the output of the datetime column looks like "42978.6736458333" (the cell originally contained "2017-08-31 16:10:03"). I tried formatting and various things in excel to no avail.
Here are a few examples of what I've tried:
Select
convert(varchar(23), date, 112) DATE1,
convert(datetime, '20160805') DATE2,
convert(datetime, '2011-09-28 18:01:00', 120) DATE3,
dateadd(second, 42978.6736458333 * 24*24*60, '1899-12-31') DATE4
From
[dbo].[trainingLog]
Results:
DATE 1 = 42978.6736458333
DATE 2 = 2016-08-05 00:00:00.000
DATE 3 = 2011-09-28 18:01:00.000
DATE 4 = 1947-01-25 11:16:01.000
For every result. DATE 2/3/4 don't count up even though the original datetime varchar increments.
For example, here are more varchar values:
42981.5092361111
42982.7187615741
42983.8171527778
The above attempts return a value/date, but it's the same date even though my varchar value increments.
I expect any datetime format. I really only want the month/day/year in any format.

Try
SELECT CAST(42978.6736458333 AS DATETIME)
returns
2017-09-02 16:10:03.000
However SQL server uses 01/01/1900 as the epoch whereas your excel uses 30/12/1899 or 31/12/1899 as the epoch so it looks like you need to subtract 2 days off after you cast.
e.g.
SELECT dateadd(d, -2, CAST(42978.6736458333 AS DATETIME) )
returns
2017-08-31 16:10:03.000
From you comment I am not sure how you get your value as running
SELECT CAST(42981.5092361111 AS DATETIME)
returns for me
2017-09-05 12:13:18.000

This looks like an Excel time. So:
select dateadd(minute, 42978.6736458333 * 24*60, '1899-12-31')
If you try to use seconds, you will get an overflow error. If seconds are important, you can do:
select dateadd(day, floor(42978.6736458333), '1899-12-31') +
dateadd(second, (42978.6736458333 % 1)*24*60*60, 0)

Related

I need to convert this string to datetime in SQL Server

String:
'01/04/2019 01:50:31.230000000'
Expected result:
01/04/2019 01:50:31.230
as a DATETIME.
Query used:
SELECT CONVERT(DATETIME, '01/04/2019 01:50:31.230000000', 113)
Converting that string, which is in the 103 format, would work if it didn't have the last 6 zero's.
So a SUBSTRING or LEFT could be used to keep only 23 characters.
And then convert it to a DATETIME.
But that string, can be converted just fine to a DATETIME2.
Since a DATETIME2 is more accurate.
And a DATETIME2 can be simply casted or converted to a DATETIME.
Note that DATETIME isn't stored with a format in the table.
The way it's displayed is a setting.
However, you can FORMAT a DATETIME back to a string in the specific format you need. (starting with SQL Server 2012)
Example snippet:
select
col as col_input_string,
CAST(CONVERT(datetime2, col, 103) AS datetime) as col_as_datetime,
FORMAT(CONVERT(datetime2, col, 103), 'dd/MM/yyyy HH:mm:ss.fff') as col_as_formatted_string
from (values
('01/04/2019 01:50:31.230000000')
,('31/12/2018 13:33:44.123456789')
) q(col);
Result:
col_input_string col_as_datetime col_as_formatted_string
01/04/2019 01:50:31.230000000 2019-04-01 01:50:31.230 01/04/2019 01:50:31.230
31/12/2018 13:33:44.123456789 2018-12-31 13:33:44.123 31/12/2018 13:33:44.123
The code you want is:
SELECT CONVERT(datetime,LEFT('01/04/2019 01:50:31.230000000',23),103);
You need to use LEFT as datetime is only accurate to 1/300 of a second; thus you need to trim off the accuracy that can't be used.
Try to use
declare #vardate varchar(50) = '01/04/2019 01:50:31.230000000'
declare #date datetime =convert(date, left(#vardate,23), 103)
declare #time time = convert(time, substring(#vardate,12,12), 14)
select DATEADD(DAY, DATEDIFF(DAY, #time, #date), CAST(#time AS DATETIME)) AS Result
if that does not work check different convert formats.

Converting date and/or time from character string is failing

I have this query and I tried converting it to every format, I mean the date time etc but it doesn't work and throws error:
Conversion failed when converting date and/or time from character string.
SELECT W.Organization_ID,
W.NIT_No,
W.SchemeID,
OpeningDate,
OpeningTime,
GETDATE(),
WorkNo,
CONVERT(decimal(10, 2), W.Cost) AS Cost,
WorkName,
W.ExpiryDate as ExpiryDate,
CONVERT(VARCHAR,OpeningDate,106),
CASE WHEN
CONVERT(DATETIME, CONVERT(VARCHAR(20),OpeningDate,106) + ' '
+ CONVERT(VARCHAR(20),OpeningTime,108))< GETDATE()
THEN 1
ELSE 0 END AS OpeningVaild
FROM Works W
the CASE part throws error.
OpeningDate is of type Varchar and OpeningTime is of type Time.
Why?
You are converting just a TIME datatype not a DATETIME so you don't need to specify the style:
DECLARE #T TIME = '08:05:06';
SELECT CONVERT(VARCHAR(8), #T) AS [Time];
SELECT CAST(#T AS VARCHAR(8)) AS [Time];
Or since you are using CONVERT()pick the right style for TIME datatype which is 108 or 114, instead of 106
SELECT CONVERT(VARCHAR(8), #T, 108) AS [Time];
Update:
According to the error Msg, your problem is in the CASE part.
That because you are trying to concat a DATETIME with a VARCHAR datatype, look at here to what you'r converting:
CASE WHEN
CONVERT(DATETIME, CONVERT(VARCHAR(20),OpeningDate,106) + ' '
+ CONVERT(VARCHAR(20),OpeningTime,108))< GETDATE()
THEN 1
ELSE 0 END AS OpeningVaild
Also the column OpeningDate -according to the error Msg- is VARCHAR so your are convert a VARCHAR to VARCHAR then convert it again to DATETIME then you try to concatinate the DATETIME with the VARCHAR returned from converting OpeningTime column from TIMEto VARCHAR, then try to compare them with GETDATE() which is DATETIME datatype.
So you CASE should look like:
CASE WHEN
(
CAST(OpeningDate AS DATETIME) + -- VARCHAR to DATETIME
CAST(OpeningTime AS DATETIME) -- TIME to DATETIME
) < GETDATE()
THEN 1
ELSE 0 END AS OpeningVaild
A beside note, here in this line
CONVERT(VARCHAR,OpeningDate,106),
You are trying to convert a VARCHAR to VARCHAR and without specify the lenght too, so this line should be:
CONVERT(VARCHAR(10),CAST(OpeningDate AS DATE),106),
Finally, don't ever ever store DATE as VARCHAR, the DATE/ TIME/ DATEIME are there for a reason, so use them and all other datatypes wisely.
Here is a demo represent your issue, and how to fix it.
You can simplify this big time by changing the expression a little.
This way you don't have to convert and concatenate.
SELECT
CASE WHEN OpeningDate < GETDATE() - OpeningTime
THEN 1
ELSE 0 END AS OpeningVaild
Note I am assuming that Openingdate has the format dd-mon-yyyy. Otherwise you still need to convert it, but still shorter:
SELECT
CASE WHEN Convert(date, OpeningDate, 106) < GETDATE() - OpeningTime
THEN 1
ELSE 0 END AS OpeningVaild
So I understand the problem is with this part:
CASE WHEN CONVERT(DATETIME, CONVERT(VARCHAR(20),OpeningDate,106) + ' ' + CONVERT(VARCHAR(20),OpeningTime,108))< GETDATE() THEN 1 ELSE 0 END AS OpeningVaild
Update
Since I've first posted my answer it turns out that you store the opening date as varchar instead of date.
First, you should stop doing that. Never store dates in anything other than a Date column (unless you need them with time as well, and then use DateTime2).
For more information, read Aaron Bertrand's Bad habits to kick : choosing the wrong data type.
Assuming the data type of the column can't change, you wrote in the comments to the question:
#ZoharPeled: this is the format of openingdate 2017-04-10
Illustrating one of the problems caused by storing dates as strings - How can I, or anyone else for that matter, know if that's the 10th of April or the 4th of October? The answer is we can't.
So, assuming it's the 10th of April, you can convert it to DateTime using convert with 126 as the style parameter:
CASE
WHEN CONVERT(DateTime, OpeningDate, 126) + CAST(OpeningTime As DateTime) < GETDATE() THEN
1
ELSE
0
END As OpeningVaild
First version:
Assuming that the data type of OpeningDate is Date and the data type of OpeningTime is Time, Seems like you are attempting to figure out if these columns combination into a DateTime is before the current DateTime.
Instead of converting them into strings and back to DateTime, you can cast both to DateTime and simply add them together:
CASE
WHEN CAST(OpeningDate As DateTime) + CAST(OpeningTime As DateTime) < GETDATE() THEN
1
ELSE
0
END As OpeningVaild
Another option would be to use GETDATE() twice. I don't think it should matter in the select clause, but in the where clause it's important to use this option since the first one will make these columns non-seargable, meaning the database engine will not be able to use any indexes that might help the execution plan of the statement:
CASE
WHEN OpeningDate < CAST(GETDATE() AS DATE)
OR
(
OpeningDate = CAST(GETDATE() AS DATE)
AND OpeningTime <= CAST(GETDATE() AS TIME)
) THEN
1
ELSE
0
END AS OpeningVaild
That being said, your query also have CONVERT(VARCHAR,OpeningDate,106) - The 106 style returns a string representation of the date as dd mon yyyy - meaning 11 chars - so change that to CONVERT(CHAR(11),OpeningDate,106) Note that using varchar without specifying the length defaults to 30, which is not a problem in this case since it's more than he 11 chars you need, but it's a bad habit to not specify length and you should kick it.

Convert and Operation Not working as expected. Ignoring Year value

I have the following code:
CONVERT(VARCHAR(20), TimeCard_Date, 101) <
CONVERT(VARCHAR(20), dateadd(dd,-3,getdate()), 101)
The Original TimeCard_Date value = 2018-06-01
The GetDate() return = 11/14/2017
Can anyone assist as to why it thinks the Timecard_Date value set for June 2018 is less than the GetDate() minus 3 days value?
When you convert, it converts to a varchar datatype. 06/01/2018 is less than 11/14/2017 as a varchar since it is an alphabetical (or by number?) comparison. If you compare by date, the comparison is by the date datatype, which is as you expect.
You can change your code to:
TimeCard_Date < dateadd(dd,-3,getdate())
You don't need to convert DATETIME to VARCHAR in order to compare dates. Just use:
TimeCard_Date < DATEADD(dd,-3,GETDATE())
On the other hand, if you ever have to convert them to do it, you have to standarize the format (yyyyMMdd). You can check the FORMAT function https://learn.microsoft.com/en-us/sql/t-sql/functions/format-transact-sql

Why does my query return records which doesn't meet the where clause conditions?

I have written a query which returns records with dates that are actually older than the mentioned date.
Declare #DateFrom date
Set #DateFrom= '02/Oct/2019'
SELECT 1, Convert(varchar(11), AppliedDateTime, 106)
FROM [MC_Tenders].[dbo].[AppliedWorks]
Where
Convert(varchar, AppliedDateTime,106) >= Convert(varchar, #DateFrom,106)
Applied dates are saved in table as datetime e.g. 2017-04-25 15:51:25.257
You are doing the comparison as strings rather than dates. Remove the conversion:
SELECT 1, Convert(varchar(11), AppliedDateTime, 106)
FROM [MC_Tenders].[dbo].[AppliedWorks]
WHERE AppliedDateTime >= #DateFrom;
Type 106 is dd mm yyyy. When you compare as strings, the strings are compared, not the dates. With format 106, the days are compared first, so: '18-10-2017' < '25-12-1900' because "1" < "2".
Just to finish Gordon Linoff's thought, your code should look something like this:
SELECT
1
, CAST(AppliedDateTime AS DATE) AS AppliedDate
FROM
[MC_Tenders].[dbo].[AppliedWorks]
WHERE
CAST(AppliedDateTime AS DATE) >= #DateFrom;
Edit: I'm assuming AppliedDateTime is actually stored as a datetime, or some data type other than DATE. The explicit CAST to the DATE type will strip out the time component and allow SQL to just compare the date component to your variable.

convert Excel Date Serial Number to Regular Date

I got a column called DateOfBirth in my csv file with Excel Date Serial Number Date
Example:
36464
37104
35412
When i formatted cells in excel these are converted as
36464 => 1/11/1999
37104 => 1/08/2001
35412 => 13/12/1996
I need to do this transformation in SSIS or in SQL. How can this be achieved?
In SQL:
select dateadd(d,36464,'1899-12-30')
-- or thanks to rcdmk
select CAST(36464 - 2 as SmallDateTime)
In SSIS, see here
http://msdn.microsoft.com/en-us/library/ms141719.aspx
The marked answer is not working fine, please change the date to "1899-12-30" instead of "1899-12-31".
select dateadd(d,36464,'1899-12-30')
You can cast it to a SQL SMALLDATETIME:
CAST(36464 - 2 as SMALLDATETIME)
MS SQL Server counts its dates from 01/01/1900 and Excel from 12/30/1899 = 2 days less.
tldr:
select cast(#Input - 2e as datetime)
Explanation:
Excel stores datetimes as a floating point number that represents elapsed time since the beginning of the 20th century, and SQL Server can readily cast between floats and datetimes in the same manner. The difference between Excel and SQL server's conversion of this number to datetimes is 2 days (as of 1900-03-01, that is). Using a literal of 2e for this difference informs SQL Server to implicitly convert other datatypes to floats for very input-friendly and simple queries:
select
cast('43861.875433912' - 2e as datetime) as ExcelToSql, -- even varchar works!
cast(cast('2020-01-31 21:00:37.490' as datetime) + 2e as float) as SqlToExcel
-- Results:
-- ExcelToSql SqlToExcel
-- 2020-01-31 21:00:37.490 43861.875433912
this actually worked for me
dateadd(mi,CONVERT(numeric(17,5),41869.166666666664)*1440,'1899-12-30')
(minus 1 more day in the date)
referring to the negative commented post
SSIS Solution
The DT_DATE data type is implemented using an 8-byte floating-point number. Days are represented by whole number increments, starting with 30 December 1899, and midnight as time zero. Hour values are expressed as the absolute value of the fractional part of the number. However, a floating point value cannot represent all real values; therefore, there are limits on the range of dates that can be presented in DT_DATE. Read more
From the description above you can see that you can convert these values implicitly when mapping them to a DT_DATE Column after converting it to a 8-byte floating-point number DT_R8.
Use a derived column transformation to convert this column to 8-byte floating-point number:
(DT_R8)[dateColumn]
Then map it to a DT_DATE column
Or cast it twice:
(DT_DATE)(DT_R8)[dateColumn]
You can check my full answer here:
Is there a better way to parse [Integer].[Integer] style dates in SSIS?
Found this topic helpful so much so created a quick SQL UDF for it.
CREATE FUNCTION dbo.ConvertExcelSerialDateToSQL
(
#serial INT
)
RETURNS DATETIME
AS
BEGIN
DECLARE #dt AS DATETIME
SELECT #dt =
CASE
WHEN #serial is not null THEN CAST(#serial - 2 AS DATETIME)
ELSE NULL
END
RETURN #dt
END
GO
I had to take this to the next level because my Excel dates also had times, so I had values like this:
42039.46406 --> 02/04/2015 11:08 AM
42002.37709 --> 12/29/2014 09:03 AM
42032.61869 --> 01/28/2015 02:50 PM
(also, to complicate it a little more, my numeric value with decimal was saved as an NVARCHAR)
The SQL I used to make this conversion is:
SELECT DATEADD(SECOND, (
CONVERT(FLOAT, t.ColumnName) -
FLOOR(CONVERT(FLOAT, t.ColumnName))
) * 86400,
DATEADD(DAY, CONVERT(FLOAT, t.ColumnName), '1899-12-30')
)
In postgresql, you can use the following syntax:
SELECT ((DATE('1899-12-30') + INTERVAL '1 day' * FLOOR(38242.7711805556)) + (INTERVAL '1 sec' * (38242.7711805556 - FLOOR(38242.7711805556)) * 3600 * 24)) as date
In this case, 38242.7711805556 represents 2004-09-12 18:30:30 in excel format
In addition of #Nick.McDermaid answer I would like to post this solution, which convert not only the day but also the hours, minutes and seconds:
SELECT DATEADD(s, (42948.123 - FLOOR(42948.123))*3600*24, dateadd(d, FLOOR(42948.123),'1899-12-30'))
For example
42948.123 to 2017-08-01 02:57:07.000
42818.7166666667 to 2017-03-24 17:12:00.000
You can do this if you just need to display the date in a view:
CAST will be faster than CONVERT if you have a large amount of data, also remember to subtract (2) from the excel date:
CAST(CAST(CAST([Column_With_Date]-2 AS INT)AS smalldatetime) AS DATE)
If you need to update the column to show a date you can either update through a join (self join if necessary) or simply try the following:
You may not need to cast the excel date as INT but since the table I was working with was a varchar I had to do that manipulation first. I also did not want the "time" element so I needed to remove that element with the final cast as "date."
UPDATE [Table_with_Date]
SET [Column_With_Excel_Date] = CAST(CAST(CAST([Column_With_Excel_Date]-2 AS INT)AS smalldatetime) AS DATE)
If you are unsure of what you would like to do with this test and re-test! Make a copy of your table if you need. You can always create a view!
Google BigQuery solution
Standard SQL
Select Date, DATETIME_ADD(DATETIME(xy, xm, xd, 0, 0, 0), INTERVAL xonlyseconds SECOND) xaxsa
from (
Select Date, EXTRACT(YEAR FROM xonlydate) xy, EXTRACT(MONTH FROM xonlydate) xm, EXTRACT(DAY FROM xonlydate) xd, xonlyseconds
From (
Select Date
, DATE_ADD(DATE '1899-12-30', INTERVAL cast(FLOOR(cast(Date as FLOAT64)) as INT64) DAY ) xonlydate
, cast(FLOOR( ( cast(Date as FLOAT64) - cast(FLOOR( cast(Date as FLOAT64)) as INT64) ) * 86400 ) as INT64) xonlyseconds
FROM (Select '43168.682974537034' Date) -- 09.03.2018 16:23:28
) xx1
)
For those looking how to do this in excel (outside of formatting to a date field) you can do this by using the Text function https://exceljet.net/excel-functions/excel-text-function
i.e.
A1 = 132134
=Text(A1,"MM-DD-YYYY") will result in a date
This worked for me because sometimes the field was a numeric to get the time portion.
Command:
dateadd(mi,CONVERT(numeric(17,5),41869.166666666664)*1440,'1899-12-31')