Những định dạng ngày / giờ theo nghĩa đen là NGÔN NGỮ và DATEFORMAT an toàn?


24

Thật dễ dàng để chứng minh rằng nhiều định dạng ngày / giờ khác với hai định dạng sau dễ bị giải thích sai do TẠO NGÔN NGỮ, TẬP DATEFORMAT hoặc ngôn ngữ mặc định của thông tin đăng nhập:

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

Ngay cả định dạng này, không có T, có thể trông giống như định dạng ISO 8601 hợp lệ, nhưng nó bị lỗi ở một số ngôn ngữ:

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

Các kết quả:

Die Spracheneinstellung wurde auf Đức geändert.

Msg 242, Cấp 16, Bang 3
Bei der Konvertierung eines varchar-Datentyps in einen datetime-Datentyp liegt der Wert außerhalb des gültigen Bereichs.

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

Msg 242, Level 16, State 3
La convert d'un type de données varchar en type de données datetime a créé une valeur hors limites.

Bây giờ, những điều này thất bại như thể, bằng tiếng Anh, tôi đã hoán đổi tháng và ngày, để tạo thành một thành phần ngày của yyyy-dd-mm:

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

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

Kết quả:

Msg 242, Cấp 16, Trạng thái 3
Việc chuyển đổi loại dữ liệu varchar thành loại dữ liệu datetime dẫn đến giá trị ngoài phạm vi.

(Đây không phải là Microsoft Access, đó là "đẹp" cho bạn và sửa chữa các chuyển vị cho bạn Ngoài ra, các lỗi tương tự có thể xảy ra trong một số trường hợp với. SET DATEFORMAT ydm;- nó không phải là chỉ một điều về ngôn ngữ, đó chỉ là kịch bản phổ biến hơn nơi những xảy ra sự cố - và không phải lúc nào cũng nhận thấy vì đôi khi chúng không có lỗi, chỉ là ngày 7 tháng 8 trở thành ngày 8 tháng 7 và không ai để ý.)

Vì vậy, câu hỏi:

Bây giờ tôi biết có một loạt các định dạng không an toàn, có bất kỳ định dạng nào khác sẽ an toàn với bất kỳ kết hợp ngôn ngữ và định dạng ngày nào không?

Câu trả lời:


26

Trong tài liệu , có tuyên bố rất rõ ràng rằng các định dạng an toàn duy nhất là những định dạng tôi đã trình bày ở phần đầu của câu hỏi:

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

Tuy nhiên, gần đây tôi đã nhận thấy rằng có một định dạng thứ ba miễn nhiễm như nhau đối với bất kỳ cài đặt ngôn ngữ hoặc định dạng ngày nào:

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

TL; DR: Đây là sự thật.Cho datetimesmalldatetime.

Đọc tiếp cho phiên bản dài hơn và về nhiều bằng chứng bạn sẽ nhận được.


Có một lỗ hổng giải thích điều này - trong khi phần thân văn bản chính không thừa nhận yyyyMMdd hh:...là một định dạng an toàn khỏi các diễn giải định dạng ngôn ngữ hoặc ngày chuyển đổi, có một chút lúng túng nói rằng phần ngày của chuỗi đó không được xác thực tùy thuộc vào cài đặt định dạng ngày:

nhập mô tả hình ảnh ở đây

Nó thường không giống như tôi chỉ lấy tài liệu theo từ của nó, thường. Bạn có thể nói tôi hơi hoài nghi. Và ngôn ngữ cũng mơ hồ ở đây - nó chỉ nói rằng đây là sự kết hợp giữa ngày và thời gian, không gọi ra không gian một cách rõ ràng (đó có thể là sự trở lại của xe ngựa, đối với tất cả những gì tôi biết). Nó cũng nói rằng nó không phải là đa ngôn ngữ, điều đó có nghĩa là nó có thể bị lỗi ở một số ngôn ngữ nhất định, nhưng chúng tôi sẽ sớm phát hiện ra rằng điều đó cũng không chính xác.

Vì vậy, tôi đặt ra để chứng minh rằng không có sự kết hợp nào giữa ngôn ngữ / dateformat có thể làm cho định dạng cụ thể này thất bại.

Đầu tiên, tôi tạo một khối SQL động nhỏ cho mỗi ngôn ngữ:

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

Điều này tạo ra 34 hàng đầu ra như thế này:

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)';    

Tôi đã sao chép đầu ra đó sang một cửa sổ truy vấn mới và trên nó, tôi đã tạo mã này, hy vọng sẽ cố gắng chuyển đổi cùng ngày đó (ngày 13 tháng 3) sang ngày thứ 3 của tháng thứ 13 trong ít nhất một trường hợp:

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

Không, mọi ngôn ngữ làm việc chỉ cần tìm trong ydm. Tôi cũng đã thử mọi định dạng khác và cả kiểu dữ liệu ngày / giờ. 34 chuyển đổi thành công đến ngày 13 tháng 3, mọi lúc.

Vì vậy, tôi thừa nhận với @AndriyM và @ErikE rằng, thực sự, có một định dạng an toàn thứ 3. Tôi sẽ ghi nhớ điều này cho các bài viết trong tương lai, nhưng tôi đã đánh trống cho hai người kia ở rất nhiều nơi, tôi sẽ không săn lùng tất cả và sửa chúng ngay bây giờ.


Về phần mở rộng, bạn sẽ nghĩ cái này sẽ an toàn, nhưng không:

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

Tôi nghĩ trong mọi ngôn ngữ, điều này sẽ mang lại tương đương với:

Msg 241, Cấp 16, Trạng thái 1,
Chuyển đổi Dòng 8 không thành công khi chuyển đổi ngày và / hoặc thời gian từ chuỗi ký tự.


Để hoàn chỉnh, có một định dạng an toàn thứ tư, nhưng nó chỉ là an toàn cho các chuyển đổi cho các loại ngày / giờ mới hơn ( date, datetime2, datetimeoffset). Trong những trường hợp này, cài đặt ngôn ngữ không thể can thiệp:

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

Tuy nhiên, tôi đánh giá cao việc sử dụng nó vì nó chỉ hoạt động cho các loại mới hơn và những loại cũ vẫn còn sử dụng rất nhiều, theo kinh nghiệm của tôi. Tại sao có dấu gạch ngang ở đó khi mọi nơi khác (hoặc trên thực tế trong cùng một mã, nếu kiểu dữ liệu thay đổi) bạn phải xóa chúng?

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

Ngay cả với cùng một chuỗi nguồn, các chuyển đổi mang lại kết quả khá khác nhau:

März    Juli    März

Định dạng hoạt động cho datetime ( yyyyMMdd) cũng sẽ luôn hoạt động cho ngày và các loại mới khác. Vì vậy, IMHO, chỉ luôn luôn sử dụng đó. Và được đưa ra định dạng thứ ba cho các loại có ngày / giờ ( yyyyMMdd hh:...), điều này thực sự sẽ cho phép bạn nhất quán hơn - ngay cả khi thành phần ngày luôn dễ đọc hơn một chút.


Bây giờ tôi sẽ chỉ mất vài năm, cho hoặc nhận, để có thói quen thể hiện ba định dạng an toàn khi tôi nói về biểu diễn chuỗi ngày.


Không phải là định dạng thứ ba có thể trở nên không an toàn khi một ngôn ngữ mới được thêm vào SQL Server trong một số bản phát hành trong tương lai?
Kuba Wyrostek

@Kuba Tôi khá tự tin rằng Microsoft đã học được bài học của họ về điều này. Ai đó đã đưa ra một quyết định khá tồi tệ khi tất cả các ngôn ngữ này diễn giải yyyy-dd-MM, một định dạng mà tôi không nghĩ rằng bất kỳ ai trên trái đất đã từng sử dụng trên mục đích.
Aaron Bertrand
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.