SQL Server如何确定隐式日期时间转换的格式?

Shw*_*ain 18 sql-server datetime implicit implicit-conversion

declare @str_datetime varchar(50)
set @str_datetime='30-04-2012 19:01:45' -- 30th April 2012
declare @dt_datetime datetime
select @dt_datetime=@str_datetime
Run Code Online (Sandbox Code Playgroud)

这会产生以下错误:

消息242,级别16,状态3,行4
将varchar数据类型转换为日期时间数据类型导致超出范围的值.

我的问题是SQL Server如何决定使用哪种格式进行隐式日期时间转换?

Aar*_*and 27

这可能取决于多种因素 - 操作系统的区域设置,当前用户的语言和日期格式设置.默认情况下,Windows使用US English,用户的设置为US EnglishMDY.

但是这里有一些例子来说明这是如何改变的.

用户正在使用BRITISH语言设置:

-- works:
SET LANGUAGE BRITISH;
SELECT CONVERT(DATETIME, '30-04-2012 19:01:45');

-- fails:
SELECT CONVERT(DATETIME, '04/13/2012');
GO
Run Code Online (Sandbox Code Playgroud)

(错误)

消息242,级别16,状态3,行5
将varchar数据类型转换为日期时间数据类型导致超出范围的值.

用户正在使用Français:

-- works:
SET LANGUAGE FRENCH;
SELECT CONVERT(DATETIME, '30-04-2012 19:01:45');

-- fails:
SELECT CONVERT(DATETIME, '04/13/2012');
GO
Run Code Online (Sandbox Code Playgroud)

(错误)

Msg 242,Level 16,State 3,Line 1
La conversion d'un typededonnéesvarcharen typededonnéesdatetimeacrééunevaleur hors limites.

用户再次使用Français:

SET LANGUAGE FRENCH;

-- fails (proving that, contrary to popular belief, YYYY-MM-DD is not always safe):
SELECT CONVERT(DATETIME, '2012-04-30');
GO
Run Code Online (Sandbox Code Playgroud)

(错误)

Msg 242,Level 16,State 3,Line 1
La conversion d'un typededonnéesvarcharen typededonnéesdatetimeacrééunevaleur hors limites.

用户正在使用DMY而不是MDY:

SET LANGUAGE ENGLISH;
SET DATEFORMAT DMY;

-- works:
SELECT CONVERT(DATETIME, '30-04-2012 19:01:45');

-- fails:
SELECT CONVERT(DATETIME, '04-30-2012');
GO
Run Code Online (Sandbox Code Playgroud)

(错误)

消息242,级别16,状态3,行2
将varchar数据类型转换为日期时间数据类型导致超出范围的值.

您最好的选择是使用ISO标准,非区域,安全,明确的日期格式.我通常推荐的两个是:

YYYYMMDD                  - for date only.
YYYY-MM-DDTHH:MM:SS[.mmm] - for date + time, and yes that T is important.
Run Code Online (Sandbox Code Playgroud)

这些都没有失败:

SET DATEFORMAT MDY;
SET LANGUAGE ENGLISH;
SELECT CONVERT(DATETIME, '20120430');
SELECT CONVERT(DATETIME, '2012-04-30T19:01:45');
SET LANGUAGE FRENCH;
SELECT CONVERT(DATETIME, '20120430');
SELECT CONVERT(DATETIME, '2012-04-30T19:01:45');
SET LANGUAGE BRITISH;
SELECT CONVERT(DATETIME, '20120430');
SELECT CONVERT(DATETIME, '2012-04-30T19:01:45');
SET DATEFORMAT DMY;
SELECT CONVERT(DATETIME, '20120430');
SELECT CONVERT(DATETIME, '2012-04-30T19:01:45');
Run Code Online (Sandbox Code Playgroud)

因此,我强烈建议您不要让用户输入自由文本日期格式(或者您自己使用不可靠的格式),而是控制输入字符串并确保它们遵循这些安全格式之一.然后,无论用户具有什么设置或基础区域设置是什么,您的日期将始终被解释为它们的预期日期.如果您当前正在让用户在表单上的文本字段中输入日期,请​​停止执行该操作并实现日历控件或至少一个选择列表,以便最终控制传递回SQL Server的字符串格式.

在某些背景下,请阅读Tibor Karaszi的"日期时间数据类型的终极指南"和我的帖子"Kick Bad Babits :错误处理日期/范围查询".

  • @Eli我认为你错过了我想要的观点.如果一个人在英格兰并通过字符串'09/04/2014'并且另一个人在美国并通过'09/04/2014',那么你的功能如何神奇地知道它们中的哪一个意味着4月9日以及哪一个意味着9月4日? (2认同)