sql server convert function behave differently on different machine - sql-server

When I run this query on one machine it works perfectly:
select convert(datetime, '2021-01-18 00:00:00.000')
But in another machine, I get an out of range error:
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.

That format is invalid when using certain language and dateformat settings. Example db<>fiddle and another example.
SET DATEFORMAT YDM;
SELECT CONVERT(datetime, '2021-01-18 00:00:00.000');
..or...
SET LANGUAGE BRITISH;
SELECT CONVERT(datetime, '2021-01-18 00:00:00.000');
Both yield:
Msg 242 Level 16 State 3
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
You do not need to reinstall SQL Server to fix this, as it's not the installation that is the problem.
The real solution is to always use a safe, unambiguous format - see the links in the "Regional formats" section of Dating Responsibly.
These all succeed, for example:
SET DATEFORMAT YDM;
SELECT CONVERT(datetime, '2021-01-18T00:00:00.000');
SELECT CONVERT(datetime, '20210118 00:00:00.000');
SELECT CONVERT(datetime, '20210118');
...and...
SET LANGUAGE BRITISH;
SELECT CONVERT(datetime, '2021-01-18T00:00:00.000');
SELECT CONVERT(datetime, '20210118 00:00:00.000');
SELECT CONVERT(datetime, '20210118');
If you can't control the format of the string, then another option is to manually apply these settings first (this should work on whatever machine it's currently failing for you):
SET DATEFORMAT YMD;
SET LANGUAGE us_english;
SELECT CONVERT(datetime, '2021-01-18 00:00:00.000');
This is obviously not the optimal solution.
The issue may also be that the login connecting to the server with wrong results has their default language set to something other than the default US English (more than half of the languages SQL Server supports interpret yyyy-mm-dd as yyyy-dd-mm, for reasons I will never understand).
If this is the case, you may be able to fix this login...
ALTER LOGIN [login name] WITH DEFAULT_LANGUAGE = [us_english];
...but unless you are switching from british, this will have other consequences for them, such as language of warnings and errors, interpretation of other data like numeric, and so on. It also won't last if the login changes their setting back, and it won't affect any future logins that are created with a non-default language (or any logins that later change in the "wrong" direction).
So I still feel like fixing the way the ambiguous string is interpreted is not the answer.
In the comment you say you have code that does this:
WHERE datetime_col >= '2018-01-12'
In which case you can either:
(a) convert to datetime with the 120 style, overriding regional/language settings:
WHERE datetime_col >= CONVERT(datetime, '2018-01-12', 120)
(b) convert to date, which doesn't care about regional/language settings:
WHERE datetime_col >= CONVERT(date, '2018-01-12')
(c) I still think the best (and easiest!) answer is to simply not use an ambiguous format that requires workarounds and/or every login to be created a specific way and never override session settings:
WHERE datetime_col >= '20180112'

Related

Check data type format of every value of a column in SQL Server

I have a table_1 that has column dob which has datatype nvarchar(max). Now I want to check every date whether it is in datetime format or not.
The dates which are in datetime format should be copied to table_2 that has column with a datatype of datetime.
Any help how can I do this?
If you are using SQL Server 2012 or later, then TRY_CONVERT can help here:
SELECT *
FROM yourTable
WHERE TRY_CONVERT(datetime, dob) IS NULL;
This would return every record from your table where the dob text field is in some format which SQL Server cannot natively convert to a datetime.
This will insert all the strings can be converted into Table_2
INSERT INTO Table_2 (DateTimeColumn)
SELECT dob
FROM Table_1
WHERE TRY_CAST(dob AS DATETIME) IS NOT NULL;
It is a very bad habit to store datetime values in a string. The reason why: Date-Time formats differ around the world and depend on your system's culture settings. Even worse, some format depend on a given language. Try this out:
SET LANGUAGE ENGLISH; --try with GERMAN to see the effect on "Decemeber"
SET DATEFORMAT ymd; --try with "ydm" or "dmy"
DECLARE #tbl TABLE(dob NVARCHAR(MAX));
INSERT INTO #tbl VALUES('blah') --just a wrong value
,('20201231') --ISO, "unseparated YMD-format (see CONVERT with 112)
,('2020-12-31') --ISO8601
,('2020-31-12') --European with leading year
,('12-31-2020') --USA (see CONVERT with 110)
,('31-12-2020') --European (see CONVERT with 113)
,('31 December 2020') --language dependant (see CONVERT with 113), try with German "Dezember"
,('2020-02-30'); --Invalid, there's no 30th of February
SELECT t.dob --Your value
,[cast] = TRY_CAST(t.dob AS DATETIME) --CAST relies on the system's settings (might work on your machine but can break on a customer's machine
,[convert] = TRY_CONVERT(DATETIME, t.dob, 112) --CONVERT allows to use the style paramter, better than CAST, but more strict
,[parse] = TRY_PARSE(t.dob AS DATETIME USING 'en-US') --Parsing allows to mention the culture. You do not need to specify the language as a general setting
,[xmlCast] = CAST(t.dob AS XML).value('. cast as xs:date?','datetime') --Works below v2012, but can deal with ISO8601 only.
FROM #tbl t;
Play with the settings
You can use GERMAN instead of ENGLISH. The entry with December will not work any more. You can change the general date format to any combination of ymd and find, that some formats stop to work while others start to work.
TRY_CAST, TRY_CONVERT and TRY_PARSE will need a version of v2012 or higher.
If you are running an older system you should upgrade ( :-) ). The only chance with an older system is a XML hack, but this is very tightly bound to ISO8601.
Finally: For your next question, please try to add more information. State some of your input values, mention your RDBMS with version and try to set up a mockup with DDL and INSERT (look what I've done above).
Hint: Very dangerous...
Try to insert a value like 2020-05-06 and you will find, that some styles read this as the 5th of June, while others return the 6th of May. Returning wrong values is worse than returning NULL or throwing an error...
StayAtHome
StayHealthy

Date format issue in SQL Server 2008

I have this query in SQL Server 2008:
SELECT DATEADD(mm, -2, '2017-05-10');
I have run my code today and remarked that the result looks like '2017-08-05' instead of '2017-03-10' expected. It seems like date format turned into yyyy-dd-mm before conversion and returned back to yyyy-mm-dd to display the result.
Is there any explanation? How to fix this definitely?
What are you talking about is not a date format issue, but datetime issue, in fact, if you try this code you'll ALWAYS get the same result:
set dateformat ymd
SELECT DATEADD(mm, -2, cast('2017-05-10' as date));
set dateformat ydm
SELECT DATEADD(mm, -2, cast('2017-05-10' as date));
While using datetime you'll get different results depending on your language/dateformat setting:
set language us_english
SELECT DATEADD(mm, -2, '2017-05-10');
set language russian
SELECT DATEADD(mm, -2, '2017-05-10');
The format yyyy-mm-dd is language neutral when using with date type but it's language dependent when using with datetime.
Explanation:
It is important to note that some character string formats of date and
time literals are language dependent, meaning that when you convert
them to a date and time data type, SQL Server might interpret the
value differently based on the language setting in effect in the
session. Each logon defined by the database administrator has a
default language associated with it, and unless it is changed
explicitly, that language becomes the effective language in the
session. You can overwrite the default language in your session by
using the SET LANGUAGE command, but this is generally not recom-
mended because some aspects of the code might rely on the user’s
default language. The effective language in the session sets several
language-related settings behind the scenes, among them one called
DATEFORMAT, which determines how SQL Server interprets the liter- als
you enter when they are converted from a character string type to a
date and time type. The DATEFORMAT setting is expressed as a
combination of the characters d, m, and y. For example, the
us_english language setting sets the DATEFORMAT to mdy, whereas the
British language setting sets the DATEFORMAT to dmy. You can override
the DATEFORMAT setting in your session by using the SET DATEFORMAT
command, but as mentioned earlier, changing language-related settings
is generally not recommended. Consider, for example, the literal
‘02/12/2007’. SQL Server can interpret the date as either February
12, 2007 or December 2, 2007 when you convert this literal to one of
the following types: DATETIME, DATE, DATETIME2, or DATETIMEOFFSET.
The effective LANGUAGE/DATEFORMAT setting is the determining factor.
To demonstrate different interpretations of the same character string
literal, run the following code. SET LANGUAGE British; SELECT
CAST('02/12/2007' AS DATETIME); SET LANGUAGE us_english; SELECT
CAST('02/12/2007' AS DATETIME);
Related article: Working with Date and Time Data by Itzik Ben-Gan
Following the link you'll find the table with language-neutral formats for different date/time types
Try this : SELECT DATEADD(mm, -2, '2017-10-05') =>2017-03-10

Error on converting date time to specific format in SQL Server

I am newbie to SQL Server and facing an issue. I am trying to convert date time which is in yyyy-mm-dd format to dd/mm/yyyy.
Used below code to change the format, which works well if dd <=12 and throws error The conversion of a varchar data type to a datetime data type resulted in an out-of-range value if dd>12
Declare #LastTime datetime
set #LastTime = '2015-06-12 12:08:18.690'
select convert(varchar, #LastTime, 101)
Tried with other output formats (from here) as well but no luck. Is there a way to fix the issue. I am using SQL Server 2008 version. Referred to many posts here but none of them solved the issue.
Thanks in advance.
If you only want to change the Date format try using this:
SET DATE FORMAT DMY
just write it on the screen, select it and execute it,that will allow you to work with DATE types in the format that you want till you close the session. the DATE type will be a String
The error is from set #LastTime = ... not from your select convert(...)
The error is because your current dateformat is dmy (implicitly set by default language)
If you would rather just change your dateformat then set dateformat mdy; will allow you to set #LastTime with the string 2016-06-13 (June 13th 2016).
If you don't want to mess with changing your dateformat you can specify the style of the string you use with convert when you are setting #LastTime, in this case that would be 121 (120 works as well).
rextester: http://rextester.com/CWZP33091
try this:
declare #LastTime datetime;
set #LastTime = convert(datetime,'2015-06-12 12:08:18.690',121);
select convert(varchar, #LastTime, 101);
have a look at the MSDN documentation for cast and convert at https://msdn.microsoft.com/en-us/library/ms187928.aspx
I'd guess that style 103 is the right one for you.
Give it a try with
select convert(varchar, #LastTime, 103)
I don't have the opportunity to check it out right now, as I don't have a MS SQL Server at hand.
As style 101 converts 'mm/dd/yyyy' it's clear why dd>12 causes an error, as convert is trying to convert 'mm' as a month with valid values from 1 - 12 and you provide 'dd' from 1-31.
Hope that helps
Take care
Andreas

Error Converting NVARCHAR to DATETIME

I create a temp table
CREATE TABLE #Test(theDate DATETIME)
I use this query to insert data into the temp table
INSERT INTO #Test VALUES(CONVERT(NVARCHAR, '2016-09-30' 21))
It works on one database (SQL Server 2005), but gives error on another (SQL Server 2008). I can't remember the exact error, but it has something to do with 'Error converting NVARCHAR TO DATETIME'
Why is working on one database, but not another? Is there a special property to enforce error on mismatched type? I can't find information anywhere.
There are several problems:
You use CONVERT to convert a string to a string while you want a DATE
You use NVARCHAR without a length (which is 1 by default) Bad habit to kick
There is a comma missing after the date literal
You are using 21 which is the format with a 2-digit-year Details here
Better was this
SELECT CONVERT(DATETIME, '2016-09-30', 121)
But even better was to avoid conversions at all.
In your case use Details here
unseparated: INSERT INTO #Test VALUES('20160930')
ODBC (my favorite): INSERT INTO #Test VALUES({d'2016-09-30'})
UPDATE
I cannot check this, no such versions installed, but I'm quite sure, that different default culture / language and implicit error correction leads to this behaviour...
On my SQL Server 2012 instance all of them work fine. The obvious format error (21 instead of 121) is corrected implicitly. The second obvious error (conversion to a 1-char-string) is corrected as well. Lower versions deal with this differently (probably).
If possible, try these lines on your servers...
SET LANGUAGE US_ENGLISH; --MDY
SELECT CONVERT(NVARCHAR, '2016-09-30', 21)
SET LANGUAGE GERMAN; --DMY
SELECT CONVERT(NVARCHAR, '2016-09-30', 21)
SET LANGUAGE JAPANESE; --YMD
SELECT CONVERT(NVARCHAR, '2016-09-30', 21)
The literal date format YYYY-MM-DD, even as this is short ISO8601, is not sure in all situations. That's why one should use culture independant formats always...
You can try this if it is a problem related to language
declare #language as varchar(100)
SELECT #language= ##LANGUAGE
SET LANGUAGE us_english
--... your code...
SET LANGUAGE #language

Sql Server string to date conversion

I want to convert a string like this:
'10/15/2008 10:06:32 PM'
into the equivalent DATETIME value in Sql Server.
In Oracle, I would say this:
TO_DATE('10/15/2008 10:06:32 PM','MM/DD/YYYY HH:MI:SS AM')
This question implies that I must parse the string into one of the standard formats, and then convert using one of those codes. That seems ludicrous for such a mundane operation. Is there an easier way?
Try this
Cast('7/7/2011' as datetime)
and
Convert(DATETIME, '7/7/2011', 101)
See CAST and CONVERT (Transact-SQL) for more details.
Run this through your query processor. It formats dates and/or times like so and one of these should give you what you're looking for. It wont be hard to adapt:
Declare #d datetime
select #d = getdate()
select #d as OriginalDate,
convert(varchar,#d,100) as ConvertedDate,
100 as FormatValue,
'mon dd yyyy hh:miAM (or PM)' as OutputFormat
union all
select #d,convert(varchar,#d,101),101,'mm/dd/yy'
union all
select #d,convert(varchar,#d,102),102,'yy.mm.dd'
union all
select #d,convert(varchar,#d,103),103,'dd/mm/yy'
union all
select #d,convert(varchar,#d,104),104,'dd.mm.yy'
union all
select #d,convert(varchar,#d,105),105,'dd-mm-yy'
union all
select #d,convert(varchar,#d,106),106,'dd mon yy'
union all
select #d,convert(varchar,#d,107),107,'Mon dd, yy'
union all
select #d,convert(varchar,#d,108),108,'hh:mm:ss'
union all
select #d,convert(varchar,#d,109),109,'mon dd yyyy hh:mi:ss:mmmAM (or PM)'
union all
select #d,convert(varchar,#d,110),110,'mm-dd-yy'
union all
select #d,convert(varchar,#d,111),111,'yy/mm/dd'
union all
select #d,convert(varchar,#d,12),12,'yymmdd'
union all
select #d,convert(varchar,#d,112),112,'yyyymmdd'
union all
select #d,convert(varchar,#d,113),113,'dd mon yyyy hh:mm:ss:mmm(24h)'
union all
select #d,convert(varchar,#d,114),114,'hh:mi:ss:mmm(24h)'
union all
select #d,convert(varchar,#d,120),120,'yyyy-mm-dd hh:mi:ss(24h)'
union all
select #d,convert(varchar,#d,121),121,'yyyy-mm-dd hh:mi:ss.mmm(24h)'
union all
select #d,convert(varchar,#d,126),126,'yyyy-mm-dd Thh:mm:ss:mmm(no spaces)'
In SQL Server Denali, you will be able to do something that approaches what you're looking for. But you still can't just pass any arbitrarily defined wacky date string and expect SQL Server to accommodate. Here is one example using something you posted in your own answer. The FORMAT() function and can also accept locales as an optional argument - it is based on .Net's format, so most if not all of the token formats you'd expect to see will be there.
DECLARE #d DATETIME = '2008-10-13 18:45:19';
-- returns Oct-13/2008 18:45:19:
SELECT FORMAT(#d, N'MMM-dd/yyyy HH:mm:ss');
-- returns NULL if the conversion fails:
SELECT TRY_PARSE(FORMAT(#d, N'MMM-dd/yyyy HH:mm:ss') AS DATETIME);
-- returns an error if the conversion fails:
SELECT PARSE(FORMAT(#d, N'MMM-dd/yyyy HH:mm:ss') AS DATETIME);
I strongly encourage you to take more control and sanitize your date inputs. The days of letting people type dates using whatever format they want into a freetext form field should be way behind us by now. If someone enters 8/9/2011 is that August 9th or September 8th? If you make them pick a date on a calendar control, then the app can control the format. No matter how much you try to predict your users' behavior, they'll always figure out a dumber way to enter a date that you didn't plan for.
Until Denali, though, I think that #Ovidiu has the best advice so far... this can be made fairly trivial by implementing your own CLR function. Then you can write a case/switch for as many wacky non-standard formats as you want.
UPDATE for #dhergert:
SELECT TRY_PARSE('10/15/2008 10:06:32 PM' AS DATETIME USING 'en-us');
SELECT TRY_PARSE('15/10/2008 10:06:32 PM' AS DATETIME USING 'en-gb');
Results:
2008-10-15 22:06:32.000
2008-10-15 22:06:32.000
You still need to have that other crucial piece of information first. You can't use native T-SQL to determine whether 6/9/2012 is June 9th or September 6th.
SQL Server (2005, 2000, 7.0) does not have any flexible, or even non-flexible, way of taking an arbitrarily structured datetime in string format and converting it to the datetime data type.
By "arbitrarily", I mean "a form that the person who wrote it, though perhaps not you or I or someone on the other side of the planet, would consider to be intuitive and completely obvious." Frankly, I'm not sure there is any such algorithm.
Use this:
SELECT convert(datetime, '2018-10-25 20:44:11.500', 121) -- yyyy-mm-dd hh:mm:ss.mmm
And refer to the table in the official documentation for the conversion codes.
For this problem the best solution I use is to have a CLR function in Sql Server 2005 that uses one of DateTime.Parse or ParseExact function to return the DateTime value with a specified format.
Short answer:
SELECT convert(date, '10/15/2011 00:00:00', 101) as [MM/dd/YYYY]
Other date formats can be found at SQL Server Helper > SQL Server Date Formats
Took me a minute to figure this out so here it is in case it might help someone:
In SQL Server 2012 and better you can use this function:
SELECT DATEFROMPARTS(2013, 8, 19);
Here's how I ended up extracting the parts of the date to put into this function:
select
DATEFROMPARTS(right(cms.projectedInstallDate,4),left(cms.ProjectedInstallDate,2),right( left(cms.ProjectedInstallDate,5),2)) as 'dateFromParts'
from MyTable
The most upvoted answer here are guravg's and Taptronic's. However, there's one contribution I'd like to make.
The specific format number they showed from 0 to 131 may vary depending on your use-case (see full number list here), the input number can be a nondeterministic one, which means that the expected result date isn't consistent from one SQL SERVER INSTANCE to another, avoid using the cast a string approach for the same reason.
Starting with SQL Server 2005 and its compatibility level of 90,
implicit date conversions became nondeterministic. Date conversions
became dependent on SET LANGUAGE and SET DATEFORMAT starting with
level 90.
Non deterministic values are 0-100, 106, 107, 109, 113, 130. which may result in errors.
The best option is to stick to a deterministic setting, my current preference are ISO formats (12, 112, 23, 126), as they seem to be the most standard for IT people use cases.
Convert(varchar(30), '210510', 12) -- yymmdd
Convert(varchar(30), '20210510', 112) -- yyyymmdd
Convert(varchar(30), '2021-05-10', 23) -- yyyy-mm-dd
Convert(varchar(30), '2021-05-10T17:01:33.777', 126) -- yyyy-mm-ddThh:mi:ss.mmm (no spaces)
This page has some references for all of the specified datetime conversions available to the CONVERT function. If your values don't fall into one of the acceptable patterns, then I think the best thing is to go the ParseExact route.
Personally if your dealing with arbitrary or totally off the wall formats, provided you know what they are ahead of time or are going to be then simply use regexp to pull the sections of the date you want and form a valid date/datetime component.
If you want SQL Server to try and figure it out, just use CAST
CAST('whatever' AS datetime)
However that is a bad idea in general. There are issues with international dates that would come up. So as you've found, to avoid those issues, you want to use the ODBC canonical format of the date. That is format number 120, 20 is the format for just two digit years.
I don't think SQL Server has a built-in function that allows you to provide a user given format. You can write your own and might even find one if you search online.
convert string to datetime in MSSQL implicitly
create table tmp
(
ENTRYDATETIME datetime
);
insert into tmp (ENTRYDATETIME) values (getdate());
insert into tmp (ENTRYDATETIME) values ('20190101'); --convert string 'yyyymmdd' to datetime
select * from tmp where ENTRYDATETIME > '20190925' --yyyymmdd
select * from tmp where ENTRYDATETIME > '20190925 12:11:09.555'--yyyymmdd HH:MIN:SS:MS
You can easily achieve this by using this code.
SELECT Convert(datetime, Convert(varchar(30),'10/15/2008 10:06:32 PM',102),102)
This code solve my problem :
convert(date,YOUR_DATE,104)
If you are using timestamp you can you the below code :
convert(datetime,YOUR_DATE,104)
dateadd(day,0,'10/15/2008 10:06:32 PM')

Resources