SQL convert string(date or text) to date - sql

I have a database which has a column called stringNextDue that contains data like dates (UK format) and text (e.g "overdue", "completed")
I am trying to create a view that shows courses that are due within a month from now:
WHERE
CONVERT(DATETIME, mt.stringNextDue , 103) < DATEADD(MONTH, 1, GETDATE())
This throws an error:
Conversion failed when converting date and/or time from character string.
Which is probably due to the fact that stringNextDue may contain actual strings of text.
I tried using
WHERE
ISDATE(mt.NextDateString) = 1
AND CONVERT(DATETIME, mt.stringNextDue , 103) < DATEADD(MONTH, 1, GETDATE())
But ISDATE only accepts US date formats therefore ignoring a lot of actual dates as strings
Tried set dateformat 'dmy', which fixed the IsDate issue, but it cannot be used in views.
Any suggestions?
Server update is not an option

If you cannot use the new TRY_CONVERT you might use a function like this:
Attention: This will not catch a wrong date like 31.06.2016, you'd have to modify the BETWEEN 1 AND 31 if you need this...
Attention2: If your text might include characters forbidden in xml you should replace < with <, > with > and & with & ...
CREATE FUNCTION dbo.TestDate(#TestString VARCHAR(100))
RETURNS DATE
AS
BEGIN
DECLARE #x XML=CAST('<x>' + REPLACE(#TestString,'.','</x><x>') + '</x>' AS XML)
DECLARE #p1 VARCHAR(10) = #x.value('x[1]','varchar(10)');
DECLARE #p2 VARCHAR(10) = #x.value('x[2]','varchar(10)');
DECLARE #p3 VARCHAR(10) = #x.value('x[3]','varchar(10)');
IF LEN(#p1)=2 AND ISNUMERIC(#p1)=1 AND CAST(#p1 AS INT) BETWEEN 1 AND 31
AND LEN(#p2)=2 AND ISNUMERIC(#p2)=1 AND CAST(#p2 AS INT) BETWEEN 1 AND 12
AND LEN(#p3)=4 AND ISNUMERIC(#p3)=1 AND CAST(#p3 AS INT) BETWEEN 1900 AND 2100
RETURN CONVERT(DATETIME, #TestString , 103);
RETURN NULL;
END
GO
SELECT
dbo.TestDate('overdue') AS SureNoDate
,dbo.TestDate('01.04.2016') AS EuropeanDate
,dbo.TestDate('2016.04.01') AS WrongFormat
,dbo.TestDate('01.13.2016') AS BadDate;
GO
DROP FUNCTION dbo.TestDate;
The result
SureNoDate EuropeanDate WrongFormat BadDate
NULL 2016-04-01 NULL NULL
You might pass back a valid date (RETURN GETDATE() ?) instead of RETURN NULL for your comparisson outside. This depends on your needs...

It should be possible to replace WHERE clause using this:
SELECT *
FROM
-- sample data
(values('2015-01-01'),('01-01-2015'), ('x-x-x-x')) mt(NextDateString)
-- Replace WHERE statement with the following
CROSS APPLY
(
SELECT
RIGHT('0000'+PARSENAME(REPLACE(mt.NextDateString, '-', '.'), 1),4) yyy,
RIGHT('0000'+PARSENAME(REPLACE(mt.NextDateString, '-', '.'), 2),4) mmm,
RIGHT('0000'+PARSENAME(REPLACE(mt.NextDateString, '-', '.'), 3),4) ddd
) x
WHERE
x.yyy BETWEEN '1950' AND '2050'
AND x.mmm BETWEEN '0001' AND '0012'
AND x.ddd BETWEEN '0001' AND '0031'
AND ISDATE(mt.NextDateString) = 1
AND x.yyy+x.mmm+x.ddd < CONVERT(char(8), DATEADD(MONTH, 1, GETDATE()), 112)
Result:
NextDateString yyy mmm ddd
01-01-2015 2015 0001 0001

Thank you for suggestions,
I fixed it by setting language to British on user settings
EXEC sp_defaultlanguage 'username', 'british'

Related

Need to format date in SQL from string using charindex

I am trying to format a date in mm/dd/yyyy from string data using charindex, the date in the string is yyyymmdd. I have tried using convert, cast, castconvert.
If anyone could help that would be great! I have sample code
CONVERT (
VARCHAR (10)
, CASE
WHEN CHARINDEX ('^3507=', reg.BetaRequestString) = 0 THEN
''
ELSE
RIGHT(LEFT(reg.BetaRequestString, CHARINDEX ('^3507=', reg.BetaRequestString) + 13), 8)
END
, 101
declare #a nvarchar(10)
set #a='20180203'
select
convert(nvarchar(10),substring(#a,5,2)+'/'+substring(#a,7,2)+'/'
+substring(#a,1,4),101)
It appears you want this:
CASE WHEN CHARINDEX ('^3507=', reg.BetaRequestString) = 0
THEN ''
ELSE CONVERT(VARCHAR(10),RIGHT(LEFT(reg.BetaRequestString, CHARINDEX ('^3507=', reg.BetaRequestString) + 13), 8), 101)
END
When you know your input date format , you can split the parts and reorganise the date.
declare #date varchar(100)='20021230' ,
#Year int,#month int ,#day int
select #year=Substring(#date,1,4) ,#month=SUBSTRING(#date,5,2),#day=SUBSTRING(#date,7,2)
select convert(VARCHAR(10),(DATEFROMPARTS(#year,#month,#day)),101)
output-12/30/2002
You can also use datetime style in convert function:
https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql#date-and-time-styles
DECLARE #yyyymmdd sysname = '20180101'
SELECT CONVERT(sysname, CONVERT(datetime, #yyyymmdd, 112), 101)

Add dd.hh:mm:ss.nnnnn time format to Datetime in SQL Server

I have a value in dd.hh:mm:ss.nnnnn format. Some of the examples are 32.11:45:40.93877, 11:45:30.32012 which are in string format.
I want to add a perform addition of datetime value and above time span which is in string format. The value 30.12:43:10.98222 can be represented as 30 days 12 hours 43 minutes 10 seconds and 98222 milliseconds.
I wrote the following code (function) to get the result:
Pseudo code:
split the string split(5.11:45:40.90399, ':') and add into #temptable(id, value).
while(0 < #counter)
select #val = value from #tempTable where id = #counter
if (#counter = 0)
split '5.11' in 5 and 11 by using charindex, substring, left, Convert to Int function. Also handle availability of day value
datetimevalue = dateadd(day,5,datetimevalue)
datetimevalue = dateadd(hour, 11, datetimevalue)
if (#counter = 1)
datetimevalue = dateadd(minute, 45, datetimevalue)
if (##counter = 2)
split 40.90399 into 40 and 90399
datetimevalue = dateadd(second, 40, datetimevalue)
datetimevalue = dateadd(millisecond, 90399, datetimevalue)
End of while loop
return datetimevalue
Can we have an alternative solution or some change simplify the above process?
For now I was thinking of converting hh:mm:ss part to seconds and adding it as seconds thus reducing call of dateadd function.
First, to get that precision you can't use DateTime, you must use DateTime2.
Second, As Mayur Patil rightfully commented - the value you want to add is a time span, not a datetime value. There is no time span data type in Sql server, However, you can translate the string value into a time span using some string manipulations.
I came up with a suggested solution,
However, there is a problem with this solution that I couldn't solve: Your milliseconds part might be bigger then 1000 (and in fact it is), so the results seems to be a little off. I've tried treating it as nanoseconds, but that didn't give the results I was expecting. Perhaps someone else might refine it or come up with a better way to do it:
First, separate the days from the time,
Next, separate the time into it's components,
Then, use DateAdd to add it all to the original date:
DECLARE #Date datetime2 = GETDATE()
DECLARE #S as varchar(20) = '32.11:45:40.93877'
;WITH CTE1 As
(
SELECT LEFT(#S, CHARINDEX('.', #S)-1) As TheDay,
REPLACE(SUBSTRING(#S, CHARINDEX('.', #S) + 1, LEN(#S) - CHARINDEX('.', #S)), ':', '.') As TheTime
), CTE2 AS
(
SELECT CAST(TheDay As int) As TheDays,
CAST(PARSENAME(TheTime, 4) As Int) As TheHours,
CAST(PARSENAME(TheTime, 3) As Int) As TheMinutes,
CAST(PARSENAME(TheTime, 2) As Int) As TheSeconds,
CAST(PARSENAME(TheTime, 1) As Int) As TheNanoSeconds
FROM CTE1
)
SELECT #Date As OriginalDate,
#S As TimeSpan,
DATEADD(DAY, TheDays,
DATEADD(HOUR, TheHours,
DATEADD(MINUTE, TheMinutes,
DATEADD(SECOND, TheSeconds,
DATEADD(NANOSECOND, TheNanoSeconds, #Date)
)
)
)
) As Result
FROM CTE2
Results:
OriginalDate TimeSpan Result
--------------------------- -------------------- ---------------------------
2016-06-20 16:23:30.7470000 32.11:45:40.93877 2016-07-23 04:09:10.7470939

SQL Date/Time Format

How to convert date/time from 20150323153528 to 2015-03-23 15:35:28.000. I need this to filter based on the getdate(). Thanks in advance.
Select * from table
Where 20150323153528 > GETDATE() - 7
Statement to convert date to your requirement
DECLARE #Date varchar(20) = '20150323153528'
Select * from table Where
CONVERT(DATETIME, CONVERT(CHAR(8), #Date), 121) + ' ' + stuff(stuff(right('000000' + cast(#Date as varchar),6),5,0,':'),3,0,':') as DATETIME > GETDATE() - 7
In MS SQL you could use
DECLARE #Date varchar(20) = '20150323153528'
Select * from table Where CAST(convert(varchar,#Date) as datetime) > GETDATE() - 7
Please read this page.
SELECT convert(varchar, getdate(), 120) — yyyy-mm-dd hh:mm:ss(24h)
Note: I assume this is a Microsoft SQL Server environment using T-SQL:
The formatting of date / datetime values is not a concern of T-SQL. You should do that in your presentation-layer (i.e. your frontend code).
If you have date/time values represented as integers of the form 20150323153528 then you cannot use them in T-SQL. You need to convert them to strings (preferably in ISO-8601 format) for SQL Server to successfully internally convert them to datetime (or datetimeoffset) values which can then be compared with other datetime values.
I suggest performing the conversion in your application code before you send it to SQL, as a datetime-typed parameter value, like so:
Int32 weirdDateValue = 20150323153528;
String s = weirdDateValue.ToString( CultureInfo.InvariantCulture );
String dtValueAsIso8601 = String.Format("{0}-{1}-{2} {3}:{4}:{5}.{6}",
s.Substring(0, 4), s.Substring(4, 2), s.Substring(6, 2),
s.Substring(8, 2), s.Substring(10, 2), s.Substring(12, 2), s.Substring(14)
);
DateTime dtValue = DateTime.ParseExact( dtValueAsIso8601, "yyyy-MM-dd HH:mm:ss.fff" );
cmd.Parameters.Add("#dtValue", SqlDbType.DateTime).Value = dtValue;
In T-SQL the process is pretty much the same, except using MID - note that MID uses 1-based character indexes instead of 0-based:
DECLARE #input int = 20150323153528
DECLARE #s varchar( 14 ) = CONVERT( #input, nvarchar(14) )
DECLARE #dtStr varchar( 24 ) = MID( #s, 1, 2 ) + '-' + MID( #s, 3, 2 ) + '-' + MID( #s, 5, 2 ) + ' ' + -- etc...
DECLARE #dt datetime = CONVERT( #dtStr, datetime )
SELECT
*
FROM
[table]
WHERE
#dt > GETDATE() - 7
If the integer values are stored in an actual column instead of a parameter you'll need to convert the logic into a scalar UDF which performs the conversion. I strongly suggest you change the table's design to add a strongly-typed datetime column and permanently store the value there, and then drop the datetime-as-int column:
CREATE FUNCTION ConvertIntDateIntoDateTime(#dateAsInt int) RETURNS datetime AS
BEGIN
-- same code as above minus the SELECT statement
RETURN #dt
END
Used in an inner subquery to allow the data to be accessed in WHERE statements, like so:
SELECT
*
FROM
(
SELECT
*,
dbo.ConvertIntDateIntoDateTime( someDateColumn ) AS someDateColumn2
FROM
[table]
) AS FixedTable
WHERE
FixedTable.someDateColumn2 > GETDATE() - 7

Creating User Define Function to Convert Dates in SQL Server

I know how to convert to date in SQL Server (T-SQL) but how do I create a UDF so that I can call it each time in my code?
Example 1: The below code will format this 20120428 to 04/28/2012
SELECT CONVERT(CHAR(10), CONVERT(DATE, TEST_DATE), 101) AS MY_DATE
FROM
MEMBER
WHERE ISDATE(TEST_DATE) <> 0
Example 2: The below code will format this 20120428 to 2012-04-28
SELECT CONVERT(DATE, TEST_DATE) AS MY_DATE
FROM
MEMBER
WHERE ISDATE(TEST_DATE) <> 0
Thank for the input!
Guy
For the second example you can do this:
CREATE FUNCTION dbo.ConvertDate(#d CHAR(10))
RETURNS DATE
AS
BEGIN
RETURN (SELECT CONVERT(DATE, #d));
END
GO
But a more flexible approach might be:
CREATE FUNCTION dbo.ConvertRegional
(
#d CHAR(10),
#style TINYINT
)
RETURNS CHAR(10)
AS
BEGIN
RETURN (SELECT CONVERT(CHAR(10), CONVERT(DATE, #d), #style));
END
GO
DECLARE #d CHAR(10);
SELECT #d = '20120428';
SELECT
dbo.ConvertDate(#d),
dbo.ConvertRegional(#d, 101),
dbo.ConvertRegional(#d, 103),
dbo.ConvertRegional(#d, 120);
Results:
---------- ---------- ---------- ----------
2012-04-28 04/28/2012 28/04/2012 2012-04-28
If you don't want to continue filtering out bad non-dates from your source table (keeping the ISDATE() in the WHERE clause should prevent the function from having to deal with those rows), you can change the function this way to avoid errors, if NULL is okay as a substitute:
CREATE FUNCTION dbo.ConvertRegional
(
#d CHAR(10),
#style TINYINT
)
RETURNS CHAR(10)
AS
BEGIN
RETURN (SELECT CASE WHEN ISDATE(#d) = 1 THEN
CONVERT(CHAR(10), CONVERT(DATE, #d), #style)
END);
END
GO
In SQL Server 2012, you can do this instead, which does the same thing without having to write your own CASE:
CREATE FUNCTION dbo.ConvertRegional
(
#d CHAR(10),
#style TINYINT
)
RETURNS CHAR(10)
AS
BEGIN
RETURN (SELECT CONVERT(CHAR(10), TRY_CONVERT(DATE, #d), #style));
END
GO
(In fact in SQL Server 2012 you can also use FORMAT() so that you don't have to memorize the style numbers, but since I don't know what version you're using I'll leave that for another day.)
All that said, other than saving a few keystrokes in your queries, this encapsulation is actually going to make your queries perform worse (depending on where they are used). For simple conversions like this it is much better to just perform them inline in most cases.

How do I create a datetime from a custom format string?

I have datetime values stored in a field as strings. They are stored as strings because that's how they come across the wire and the raw values are used in other places.
For reporting, I want to convert the custom format string (yyyymmddhhmm) to a datetime field in a view. My reports will use the view and work with real datetime values. This will make queries involving date ranges much easier.
How do I perform this conversion? I created the view but can't find a way to convert the string to a datetime.
Thanks!
Update 1 -
Here's the SQL I have so far. When I try to execute, I get a conversion error "Conversion failed when converting datetime from character string."
How do I handle nulls and datetime strings that are missing the time portion (just yyyymmdd)?
SELECT
dbo.PV1_B.PV1_F44_C1 AS ArrivalDT,
cast(substring(dbo.PV1_B.PV1_F44_C1, 1, 8)+' '+substring(dbo.PV1_B.PV1_F44_C1, 9, 2)+':'+substring(dbo.PV1_B.PV1_F44_C1, 11, 2) as datetime) AS ArrDT,
dbo.MSH_A.MSH_F9_C2 AS MessageType,
dbo.PID_A.PID_F3_C1 AS PRC,
dbo.PID_A.PID_F5_C1 AS LastName,
dbo.PID_A.PID_F5_C2 AS FirstName,
dbo.PID_A.PID_F5_C3 AS MiddleInitial,
dbo.PV1_A.PV1_F2_C1 AS Score,
dbo.MSH_A.MessageID AS MessageId
FROM dbo.MSH_A
INNER JOIN dbo.PID_A ON dbo.MSH_A.MessageID = dbo.PID_A.MessageID
INNER JOIN dbo.PV1_A ON dbo.MSH_A.MessageID = dbo.PV1_A.MessageID
INNER JOIN dbo.PV1_B ON dbo.MSH_A.MessageID = dbo.PV1_B.MessageID
According to here, there's no out-of-the-box CONVERT to get from your yyyymmddhhmm format to datetime.
Your strategy will be parsing the string to one of the formats provided on the documentation, then convert it.
declare #S varchar(12)
set #S = '201107062114'
select cast(substring(#S, 1, 8)+' '+substring(#S, 9, 2)+':'+substring(#S, 11, 2) as datetime)
Result:
2011-07-06 21:14:00.000'
This first changes your date string to 20110706 21:14. Date format yyyymmdd as a string is safe to convert to datetime in SQL Server regardless of SET DATEFORMAT setting.
Edit:
declare #T table(S varchar(12))
insert into #T values('201107062114')
insert into #T values('20110706')
insert into #T values(null)
select
case len(S)
when 12 then cast(substring(S, 1, 8)+' '+substring(S, 9, 2)+':'+substring(S, 11, 2) as datetime)
when 8 then cast(S as datetime)
end
from #T
Result:
2011-07-06 21:14:00.000
2011-07-06 00:00:00.000
NULL
You can use CAST or CONVERT.
Example from the site:
G. Using CAST and CONVERT with
datetime data
The following example displays the
current date and time, uses CAST to
change the current date and time to a
character data type, and then uses
CONVERT display the date and time in
the ISO 8901 format.
SELECT
GETDATE() AS UnconvertedDateTime,
CAST(GETDATE() AS nvarchar(30)) AS UsingCast,
CONVERT(nvarchar(30), GETDATE(), 126) AS UsingConvertTo_ISO8601;
GO
Here is the result set.
UnconvertedDateTime UsingCast UsingConvertTo_ISO8601
----------------------- ------------------------------ ------------------------------
2006-04-18 09:58:04.570 Apr 18 2006 9:58AM 2006-04-18T09:58:04.570
(1 row(s) affected)
Generally, you can use this code:
SELECT convert(datetime,'20110706',112)
If you need to force SQL Server to use a custom format string, use the following code:
SET DATEFORMAT ymd
SELECT convert(datetime,'20110706')
A one liner:
declare #datestring varchar(255)
set #datestring = '201102281723'
select convert(datetime, stuff(stuff(#datestring,9,0,' '),12,0,':') , 112 )
Result:
2011-02-28 17:23:00.000
DECLARE #d VARCHAR(12);
SET #d = '201101011235';
SELECT CONVERT(SMALLDATETIME, STUFF(STUFF(#d,9,0,' '),12,0,':'));
Note that by storing date/time data using an inappropriate data type, you cannot prevent bad data from ending up in here. So it might be safer to do this:
WITH x(d) AS
(
SELECT d = '201101011235'
UNION SELECT '201101011267' -- not valid
UNION SELECT NULL -- NULL
UNION SELECT '20110101' -- yyyymmdd only
),
y(d, dt) AS
(
SELECT d,
dt = STUFF(STUFF(LEFT(d+'000000',12),9,0,' '),12,0,':')
FROM x
)
SELECT CONVERT(SMALLDATETIME, dt), ''
FROM y
WHERE ISDATE(dt) = 1 OR d IS NULL
UNION
SELECT NULL, d
FROM y
WHERE ISDATE(dt) = 0 AND d IS NOT NULL;
DECLARE #test varchar(100) = '201104050800'
DECLARE #dt smalldatetime
SELECT #dt = SUBSTRING(#test, 5, 2)
+ '/' + SUBSTRING(#test, 7, 2) + '/'
+ SUBSTRING(#test, 1, 4) + ' ' + SUBSTRING(#test, 9, 2)
+ ':' + SUBSTRING(#test, 11, 2)
SELECT #dt
Output:
2011-04-05 08:00:00