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
Related
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'
I am currently debugging a scenario in which dates need to be read from a string in a stored procedure.
The code that we have used is a simple convert statement.
e.g.
SELECT CONVERT(DATETIME, #dateInput)
The problem comes when I need to test the dates coming from the Chinese date format (yyyy.mm.dd) which is ISO 102.
What settings in Windows 10 / SQL Server must be set so that running SELECT CONVERT(DATETIME, '2020.05.16') will not return
"The conversion of a varchar data type to a datetime data type resulted in an out-of-range value."
I understand that using SELECT CONVERT(DATETIME, '2020.05.16', 102) would work but that would make the universal stored procedure incorrect when running with different windows date formats
Along with changing my windows settings to reflect the same date format I also changed:
My default language of my SQLEXPRESS server in Properties > Advanced > Default Language
My default language of my SQL User Login Properties > General > Default Language
This meant that it could read the direct string value without needed the ISO 102 identifier.
If the input value is always in the format yyyy.MM.dd then use the appropriate style code (don't rely on the language setting of the LOGIN):
DECLARE #StringInput varchar(10) = '2020.05.16';
SELECT CONVERT(datetime,#StringInput,102);
Ideally, however, instead of defining your parameter as a varchar define it as a datetime in the first place and have the application pass the correct data type; not a string. But that is a different question for the language your application is written in.
When I insert a date like this '01.03.2020 21:35:12' it changes into '2020-01-03 21:35:12.000'.
I want to insert the date with DOT as the Date separator.
NOTE: I'm not using a stored procedure, just insert query.
This is an inferior choice in format, because nobody reading that code can be certain whether you meant January 3rd or March 1st. You can get there this way, but it is ugly, unintuitive, and equally non-self-documenting:
DECLARE #d varchar(30) = '01.03.2020 21:35:12';
SELECT CONVERT(datetime, #d, 104);
Much better to use a standard, unambiguous date format for literals. These are the only two formats not subject to misinterpretation by language, dateformat, or regional settings, and therefore don't need to be accompanied by cryptic style numbers:
DECLARE #d1 varchar(30) = '20200301 21:35:12',
#d2 varchar(30) = '2020-03-01T21:35:12';
SELECT CONVERT(datetime, #d1), CONVERT(datetime, #d2);
Background:
Recommended SQL Server Date Formats
Bad Habits to Kick : Mis-handling date / range queries
Dating Responsibly
I don't think you can change the display in SSMS from the YYYY-MM-DD TIME format. If you want to change the way you get the date back when selected, you can use the CONVERT or FORMAT functions.
CONVERT function: https://www.mssqltips.com/sqlservertip/1145/date-and-time-conversions-using-sql-server/
FORMAT function: https://learn.microsoft.com/en-us/sql/t-sql/functions/format-transact-sql
You may need to select your datetime twice with the CONVERT. Once for the date and once for the time in order to get the combination of formats you want.
-John
I have a query with hard coded dates, in this format
startdate >= '2012-11-03' AND enddate <= '2012-11-30 23:59'
My database date format is 'mdy', however I'm sure it will accept yyyy-mm-dd as its the universal date structure.
When I try run this query in SSMS on my target DB connected with a specific database user (userX) I get an error about the date formats
Msg 242, Level 16, State 3, Line 1
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
However, when I run the exact query connected as the SA user, the query executes..
Why is this? I have given userX full dbo permissions (sysadmin etc) and still get the error?
If you need to specify datetimes using strings, you should use a safe, language-independent format.
In SQL Server, that's the ISO-8601 format (slightly adapted), and it supports basically two safe formats for DATETIME that always work - regardless of your language, regional and dateformat settings:
YYYYMMDD (e.g. 20121231 for 31st of December 2012) if you need date only
YYYY-MM-DDTHH:mm:ss (e.g. 2012-12-31T21:05:00 for 31st of December 2012, 9:05pm)
Note:
the first date-only format has no dashes or delimiters!
the second format has dashes for the date (can be omitted, too), and there's a fixed T as delimiter between the date and the time portion of the string
Update: as per your last comment (on the different default languages for the two users) - try this:
-- this is how your `SA` interprets the string as datetime....
SET LANGUAGE english
SELECT CAST('2012-11-30 23:59' AS DATETIME)
Works just fine...
-- this is how your British user interprets teh string as datetime
SET LANGUAGE british
SELECT CAST('2012-11-30 23:59' AS DATETIME)
Msg 242, Level 16, State 3, Line 7
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
This tries to interpret the string as 11th of the 30th month of 2012 and obviously, that fails....
consider using
startdate >= '2012-11-03' AND enddate < '2012-12-01'
instead
I am writing some T-SQL which needs to enforce a minimum date value onto some null fields:
DECLARE #epoch DATETIME;
set #epoch='1900-01-01';
select min = ISNULL(ValidFromDate,#epoch)
Is the string '1900-01-01' always going to return a datetime of Jan 1 1900 in any environment or will SQL server try to parse the string according to local culture rules?
If that's not good enough, what is the recommended way of specifying a particular date/time in T-SQL?
The best format for string-based dates is the ISO-8601 standard format.
For DATETIME variables and columns, this is either YYYYMMDD (for dates without time; without any dashes!) or YYYY-MM-DDTHH:MM:SS (date + time).
Contrary to popular belief, YYYY-MM-DD for DATETIME variables is NOT language-/dateformat-independent! If you try this, the second CAST will result in an error:
SET LANGUAGE us_english
SELECT CAST('2011-07-20' AS DATETIME)
SET LANGUAGE british
SELECT CAST('2011-07-20' AS DATETIME)
but this will work:
SET LANGUAGE british
SELECT CAST('20110720' AS DATETIME)
This is the best format since it's indepdendent of your language and dateformat settings in SQL Server.
For SQL Server 2008 and columns of type DATE (just date - no time), the format can also be YYYY-MM-DD (with the dashes) and that works for all settings, too.
Why there is such a difference between DATE and DATETIME is beyond me - that's just the way it is for now!
See Tibor Karaszi's excellent The Ultimate Guide to the DateTime data types for even more details and examples.