LANGUAGE 和 DATEFORMAT 安全的日期/時間文字格式是什麼?
由於 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:這是真的。**對於
datetime
和smalldatetime
。繼續閱讀更長的版本,以及盡可能多的證據。
有一個漏洞可以解釋這一點——雖然正文未能承認
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:...
) 的類型的第三種格式,這實際上可以讓您更加一致——即使日期組件總是有點不可讀。現在,當我談論日期的字元串表示時,我只需要幾年時間,就可以養成展示三種安全格式的習慣。