Sql-Server

LANGUAGE 和 DATEFORMAT 安全的日期/時間文字格式是什麼?

  • August 14, 2021

由於 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);

結果:

語言設置已更改為德語。

消息 242,級別 16,狀態 3

從 varchar 數據類型轉換為 datetime 數據類型時,值超出範圍。

Le paramètre de langue est passé à Français。

消息 242,級別 16,狀態 3

轉換 d’un type de données varchar en type de données datetime a créé une valeur hors 限制。

現在,這些都失敗了,好像在英語中,我已經調換了月份和日期,以製定日期組件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 數據類型轉換為 datetime 數據類型導致值超出範圍。

(這不是 Microsoft Access,它對您來說“很好”並為您修復了轉置。此外,在某些情況下可能會發生類似的錯誤SET DATEFORMAT ydm;- 這不僅僅是語言問題,這只是更常見的情況,其中這些破損發生 - 並不總是被注意到,因為有時它們不是錯誤,只是 8 月 7 日變成了 7 月 8 日,沒有人注意到。)

所以,問題:

既然我知道有一堆不安全的格式,那麼在給定任何語言和日期格式組合的情況下,還有其他安全的格式嗎?

文件中,非常明確地指出,唯一安全的格式是我在問題開始時展示的格式:

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:...是一種不受轉置語言或日期格式解釋的安全格式,但有一點模糊說,這種字元串的日期部分沒有根據 dateformat 設置進行驗證:

在此處輸入圖像描述

通常情況下,我只是按原樣接受文件,這與我不同。你可以說我有點懷疑。而且這裡的語言也是模棱兩可的——它只是說明這是關於日期和時間的組合,而不是明確地指出空格(據我所知,這可能是輸入)。它還說它不是多語言的,這意味著它可能在某些語言中失敗,但我們很快就會發現這也是不正確的。

所以我開始證明語言/日期格式的組合不會使這種特定格式失敗。

首先,我為每種語言創建了一小塊動態 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. 我也嘗試了所有其他格式,以及每種日期/時間數據類型。34 次成功轉換到 3 月 13 日,每次。

**所以,我確實向@AndriyM 和@ErikE 承認,確實存在第三種安全格式。**我會在以後的文章中記住這一點,但是我在很多地方都對其他兩個人大肆抨擊,我現在不打算將它們全部追捕並糾正它們。


通過擴展,您會認為這個是安全的,但不是:

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

我認為在每種語言中,這將產生相當於:

消息 241,級別 16,狀態 1,第 8 行

從字元串轉換日期和/或時間時轉換失敗。


為完整起見,還有第四種安全格式,但它僅對轉換為較新的日期/時間類型 ( date, datetime2, datetimeoffset) 是安全的。在這些情況下,語言設置不會干擾:

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適用於 datetime ( )的格式將始終適用於 date 和其他新類型。所以,恕我直言,總是使用它。並且給定帶有日期/時間 ( yyyyMMdd hh:...) 的類型的第三種格式,這實際上可以讓您更加一致——即使日期組件總是有點不可讀。


現在,當我談論日期的字元串表示時,我只需要幾年時間,就可以養成展示三種安全格式的習慣。

引用自:https://dba.stackexchange.com/questions/166771