语言和DATEFORMAT安全哪些日期/时间文字格式?


24

很容易证明,由于SET LANGUAGE,SET DATEFORMAT或登录名的默认语言,除了以下两种格式外,许多其他日期/时间格式都容易引起误解:

yyyyMMdd                 -- unseparated, date only
yyyy-MM-ddThh:mm:ss.fff  -- date dash separated, date/time separated by T 

即使没有T的这种格式,也可能看起来像是有效的ISO 8601格式,但是它在几种语言中均失败:

DECLARE @d varchar(32) = '2017-03-13 23:22:21.020';

SET LANGUAGE Deutsch;
SELECT CONVERT(datetime, @d);

SET LANGUAGE Français;
SELECT CONVERT(datetime, @d);

结果:

Die Spracheneinstellung wurde auf Deutschgeändert。

消息242,第16级,状态3国家
地图上的Eines varchar-Datentyps,日期从WereaußerhalbdesgültigenBereichs开始。

法语的最佳语言翻译。

消息242,级别16,状态3
转换日期类型为dénéesvalchar hors limite的转换类型。

现在,这些方法似乎无法用英语将月份和日期换成日期部分yyyy-dd-mm

DECLARE @d varchar(32) = '2017-13-03 23:22:21.020';

SET LANGUAGE us_english;
SELECT CONVERT(datetime, @d);

结果:

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

(这不是Microsoft Access,它对您“很不错”,并且可以为您解决换位问题。此外,在某些情况下,类似的错误也可能发生SET DATEFORMAT ydm;-这不仅仅是语言问题,这是更常见的情况,发生破损-并不总是被注意到,因为有时它们不是错误,只是8月7日变成了7月8日,却没人注意到。)

因此,问题是:

现在我知道有一堆不安全的格式,考虑到任何语言和日期格式的组合,是否还有其他格式可以安全使用?

Answers:


26

文档中,非常明确地指出,唯一安全的格式是我在问题一开始就演示的格式:

yyyyMMdd                 -- unseparated, date only
yyyy-MM-ddThh:mm:ss.fff  -- date dash separated, date/time separated by T 

但是,最近引起我注意的是,第三种格式同样不受任何语言或日期格式设置的影响:

yyyyMMdd hh:mm:ss.fff    -- unseparated date, no T separator

TL; DR:的确如此。对于datetimesmalldatetime

请继续阅读以获取更长的版本,并获得尽可能多的证明。


有一个漏洞可以解释这一点-尽管主要文本主体无法承认yyyyMMdd hh:...其是可以避免转位语言或日期格式解释的格式,但是有一点点模糊,表示根据日期格式设置,此类字符串的日期部分未得到验证:

在此处输入图片说明

通常,仅凭单据就与我完全不同。您可以说我有点怀疑。而且这里的语言也是模棱两可的-它只是说这与日期和时间的组合有关,而不是明确地声明空格(据我所知,这可能是回车符)。它还说它不是多语言的,这意味着它可能在某些语言中失败,但是我们很快就会发现这也是不正确的。

因此,我着手证明语言/日期格式的任何组合都不会使这种特定格式失败。

首先,我为每种语言创建了一个动态SQL块:

EXEC sys.sp_executesql @sql, N'@lang sysname', N'us_english';

这样产生了34行输出,如下所示:

EXEC sys.sp_executesql @sql, N'@lang sysname', N'us_english';
EXEC sys.sp_executesql @sql, N'@lang sysname', N'Deutsch';
EXEC sys.sp_executesql @sql, N'@lang sysname', N'Français';
EXEC sys.sp_executesql @sql, N'@lang sysname', N'日本語';
...
EXEC sys.sp_executesql @sql, N'@lang sysname', N'简体中文';
EXEC sys.sp_executesql @sql, N'@lang sysname', N'Arabic';
EXEC sys.sp_executesql @sql, N'@lang sysname', N'ไทย';
EXEC sys.sp_executesql @sql, N'@lang sysname', N'norsk (bokmål)';    

我将该输出复制到一个新的查询窗口,并在其上方生成了以下代码,该代码有望在至少一种情况下尝试将同一日期(3月13日)转换为第13个月的第3天:

DECLARE @sql nvarchar(max) = N'
SET LANGUAGE @lang;
SET DATEFORMAT ydm;
SELECT @@LANGUAGE, CONVERT(datetime, ''20170313 23:22:21.020'');';

不,每种语言都可以在中找到ydm。我也尝试了所有其他格式,也尝试了每种日期/时间数据类型。到3月13日,每次都有34次成功转换。

因此,我同意@AndriyM和@ErikE的确存在第三种安全格式。在以后的帖子中,我会牢记这一点,但是我在很多地方都在敲打其他两个架子,我现在不打算全力以赴地纠正它们。


通过扩展,您会认为这是安全的,但不能:

yyyyMMddThh:mm:ss.fff    -- unseparated date, T separator

我认为在每种语言中,这将产生以下效果:

消息241,级别16,状态1,行8
从字符串转换日期和/或时间时转换失败。


为了完整起见,还有第四个安全的格式,但它仅用于转换到较新的日期/时间类型安全的(datedatetime2datetimeoffset)。在这些情况下,语言设置不会产生干扰:

yyyy-MM-dd hh:mm:...

但是,我强烈建议您不要使用它,因为根据我的经验,它适用于较新的类型,而旧的仍在大量使用。为什么在其他任何地方(或者实际上在同一代码中,如果数据类型发生更改)必须将其删除时,这些破折号在那里?

SET LANGUAGE Deutsch;
DECLARE @dashes char(10) = '2017-03-07 03:34';
DECLARE @d date = @dashes, @dt datetime = @dashes, @dt2 datetime2 = @dashes;

SELECT DATENAME(MONTH,@d), DATENAME(MONTH,@dt), DATENAME(MONTH,@dt2);

即使给定相同的源字符串,转换也会产生完全不同的结果:

März    Juli    März

对于日期时间(作品格式yyyyMMdd)将始终日期和其他新类型的工作。因此,恕我直言,请始终使用它。给定日期/时间(yyyyMMdd hh:...)类型的第三种格式,这实际上将使您更加一致-即使日期部分的可读性始终较低。


现在,我要花几年的时间来养成在谈论日期的字符串表示形式时演示三种安全格式的习惯。


在将来的版本中向SQL Server添加新语言时,第三种格式是否会变得不安全?
库巴Wyrostek

@Kuba我相当有信心Microsoft已经从中吸取了教训。有人做出了一个非常糟糕的决定,让所有这些语言都解释yyyy-dd-MM,这种格式我认为地球上从来没有人故意使用过。
亚伦·伯特兰
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.