Які формати прямої дати / часу безпечні для мови та мови 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.

Msg 242, рівень 16, штат 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, рівень 16, стан 3
La конверсія d'un type de données varchar en type de données datetime a créé une valeur hors limit.

Тепер вони не спрацьовують так, ніби англійською мовою я переніс місяць і день, щоб сформулювати компонент дати yyyy-dd-mm:

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

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

Результат:

Msg 242, Рівень 16, стан 3
Перетворення типу даних varchar у тип даних дати дату призвело до значення поза діапазоном.

(Це не 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: Це правда. Для datetimeі smalldatetime.

Читайте про довшу версію та про стільки доказів, скільки ви збираєтеся отримати.


Існує лазівка, яка пояснює це - хоча основний текст тексту не вдається визнати 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'');';

Ні, кожну мову працювали просто знайдіть ydm. Я також пробував будь-який інший формат, а також кожен тип даних / дати. 34 успішні перетворення до 13 березня, кожного разу.

Отже, я погоджуюся @AndriyM та @ErikE, що, дійсно, існує 3-й безпечний формат. Я буду пам’ятати про це для майбутніх постів, але я забивав барабани про інші два у стільки місцях, я не збираюсь їх полювати і виправляти зараз.


З іншого боку, ви можете подумати, що це буде безпечно, але ні:

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

Формат, який працює для datetime ( yyyyMMdd), також завжди працюватиме для дати та інших нових типів. Отже, ІМХО, просто завжди використовуйте це. І з огляду на третій формат для типів з датою / часом ( yyyyMMdd hh:...), це фактично дозволить вам бути більш послідовними - навіть якщо компонент дати завжди трохи читабельніший.


Тепер мені знадобиться лише кілька років, я хочу взяти, щоб я звик демонструвати три безпечні формати, коли я говорю про строкове представлення дат.


Хіба не те, що третій формат може стати небезпечним, коли нова мова буде додана до SQL Server в якомусь майбутньому випуску?
Kuba Wyrostek

@Kuba Я досить впевнений, що Microsoft навчився їх уроку з цього приводу. Хтось прийняв досить погане рішення, щоб усі ці мови інтерпретували yyyy-dd-MM, формат, який я не думаю, що ніхто на землі ніколи не використовував спеціально.
Аарон Бертран
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.