รูปแบบตัวอักษรวันที่ / เวลา LANGUAGE และ DATEFORMAT ปลอดภัยอย่างไร


24

มันเป็นเรื่องง่ายที่จะแสดงให้เห็นว่าวันที่ / เวลารูปแบบหลายอื่น ๆกว่าสองต่อไปนี้เป็นความเสี่ยงที่จะเข้าใจผิดเนื่องจาก 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 Deutsch geändert.

ข่าวสารเกี่ยวกับ 242 ระดับ 16 สถานะ 3
Bei der Konvertierung eines varchar-Datentyps ใน einen datetime-Datentyp liegt der Wert außerhalb des gültigen Bereichs

เลอปาแลร์เดอลัวร์เอสปาเซ่Français

ข้อความเกี่ยวกับ 242 ระดับ 16 สถานะ 3
การแปลงเป็นชนิดเดอdonnées varchar en พิมพ์ de donnéesวันที่และเวลา จำกัด

ตอนนี้สิ่งเหล่านี้ล้มเหลวราวกับว่าในภาษาอังกฤษฉันได้ทำการเปลี่ยนเดือนและวันเพื่อกำหนดส่วนประกอบของวันที่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;- มันไม่ได้เป็นเพียงภาษาเท่านั้น การแตกเกิดขึ้น - และไม่ได้สังเกตเสมอเพราะบางครั้งพวกเขาไม่ผิดพลาดก็แค่วันที่ 7 สิงหาคมกลายเป็น 8 กรกฎาคมและไม่มีใครสังเกตเห็น)

ดังนั้นคำถาม:

ตอนนี้ฉันรู้แล้วว่ามีหลายรูปแบบที่ไม่ปลอดภัยมีรูปแบบอื่น ๆ ที่จะปลอดภัยเมื่อมีการรวมภาษาและรูปแบบวันที่หรือไม่

คำตอบ:


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

ฉันคัดลอกผลลัพธ์นั้นไปยังหน้าต่างแบบสอบถามใหม่และเหนือกว่านั้นฉันสร้างรหัสนี้ซึ่งหวังว่าจะพยายามแปลงวันที่เดียวกันนั้น (วันที่ 13 มีนาคม) เป็นวันที่ 3 ของเดือนที่ 13 อย่างน้อยหนึ่งกรณี:

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

Nope ydmทุกภาษาทำงานเพียงแค่หาใน ฉันลองทุกรูปแบบอื่นด้วยและทุกประเภทวันที่ / เวลา 34 Conversion ที่ประสบความสำเร็จถึง 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) จะยังมักจะทำงานให้กับวันที่และชนิดใหม่ ๆ ดังนั้น IMHO ใช้มันเสมอ และเมื่อกำหนดรูปแบบที่สามสำหรับประเภทที่มีวันที่ / เวลา ( yyyyMMdd hh:...) สิ่งนี้จะช่วยให้คุณมีความสอดคล้องมากยิ่งขึ้น - แม้ว่าองค์ประกอบของวันที่จะสามารถอ่านได้น้อยลง


ตอนนี้จะใช้เวลาไม่กี่ปีที่ฉันจะให้หรือใช้เพื่อให้เป็นนิสัยของการสาธิตรูปแบบที่ปลอดภัยทั้งสามเมื่อฉันพูดคุยเกี่ยวกับการเป็นตัวแทนสตริงของวันที่


ไม่ใช่ว่ารูปแบบที่สามอาจกลายเป็นที่ไม่ปลอดภัยเมื่อภาษาใหม่จะถูกเพิ่มไปยัง SQL Server ในการเปิดตัวในอนาคตบางอย่าง?
Kuba Wyrostek

@Kuba ฉันค่อนข้างมั่นใจว่า Microsoft ได้เรียนรู้บทเรียนเกี่ยวกับเรื่องนี้แล้ว บางคนตัดสินใจไม่ดีพอที่จะแปลภาษาเหล่านี้ให้ yyyy-dd-MM ซึ่งเป็นรูปแบบที่ฉันไม่คิดว่าจะมีใครใช้บนโลกนี้ได้อย่างตั้งใจ
Aaron Bertrand
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.