Quali formati letterali di data / ora sono sicuri LANGUAGE e DATEFORMAT?


24

È facile dimostrare che molti formati di data / ora diversi dai seguenti sono vulnerabili a interpretazioni errate a causa di SET LANGUAGE, SET DATEFORMAT o della lingua predefinita di un accesso:

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

Anche questo formato, senza la T, può sembrare un formato ISO 8601 valido, ma fallisce in diverse lingue:

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

risultati:

Die Spracheneinstellung wurde auf Deutsch geändert.

Messaggio 242, livello 16, stato 3
Bei der Konvertierung eines varchar-Datentyps in einen datetime-Datentyp liegt der Wert außerhalb des gültigen Bereichs.

Il parametro di lingua è passato in Francia.

Messaggio 242, livello 16, stato 3
La conversione di un tipo di donné varchar in un tipo di donnatio datetime a créé une valeur hors limites.

Ora, questi falliscono come se, in inglese, avessi trasposto il mese e il giorno, per formulare un componente data di yyyy-dd-mm:

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

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

Risultato:

Messaggio 242, livello 16, stato 3
La conversione di un tipo di dati varchar in un tipo di dati datetime ha prodotto un valore fuori intervallo.

(Questo non è Microsoft Access, che è "carino" per te e risolve la trasposizione per te. Inoltre, in alcuni casi possono verificarsi errori simili con SET DATEFORMAT ydm;- non è solo una cosa di lingua, è solo lo scenario più comune in cui questi si verificano rotture - e non sempre si notano perché a volte non sono errori, è solo che il 7 agosto è diventato l'8 luglio e nessuno se ne è accorto.)

Quindi, la domanda:

Ora che so che ci sono un sacco di formati non sicuri, ci sono altri formati che saranno sicuri data una combinazione di lingua e formato data?

Risposte:


26

Nella documentazione , si afferma esplicitamente che gli unici formati sicuri sono quelli che ho dimostrato all'inizio della domanda:

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

Tuttavia, recentemente è stato portato alla mia attenzione che esiste un terzo formato che è ugualmente immune da qualsiasi lingua o impostazione del formato della data:

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

TL; DR: questo è vero. Per datetimee smalldatetime.

Continua a leggere per la versione più lunga e circa tutte le prove che otterrai.


C'è una scappatoia che spiega questo - mentre il corpo del testo principale non riesce a riconoscere yyyyMMdd hh:...come un formato sicuro dalla lingua trasposta o dalle interpretazioni del formato della data, c'è un po 'di confusione che dice che la parte della data di tale stringa non è convalidata a seconda delle impostazioni del formato della data:

inserisci qui la descrizione dell'immagine

È piuttosto diverso da me prendere la documentazione alla sua parola, di solito. Puoi dire che sono un po 'scettico. E anche qui il linguaggio è ambiguo - afferma semplicemente che si tratta della combinazione di data e ora, non di chiamare esplicitamente lo spazio (che potrebbe essere un ritorno a capo, per quanto ne so). Dice anche che non è multi-lingua, il che significa che potrebbe fallire in alcune lingue, ma scopriremo tra poco anche che non è corretto.

Quindi ho deciso di dimostrare che nessuna combinazione di lingua / formato della data poteva far fallire questo formato specifico.

Innanzitutto, ho creato un piccolo blocco di SQL dinamico per ogni lingua:

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

Ciò ha prodotto 34 righe di output in questo modo:

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

Ho copiato quell'output in una nuova finestra di query e, al di sopra di esso, ho generato questo codice, che si spera proverebbe a convertire quella stessa data (13 marzo) al 3 ° giorno del 13 ° mese in almeno un caso:

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

No, tutte le lingue lavorate sono semplicemente disponibili ydm. Ho provato anche ogni altro formato e anche ogni tipo di dati data / ora. 34 conversioni di successo al 13 marzo, ogni volta.

Quindi, concedo a @AndriyM e @ErikE che, in effetti, esiste un terzo formato sicuro. Lo terrò a mente per i post futuri, ma ho battuto la batteria sugli altri due in così tanti posti, non li darò la caccia tutti e li correggerò ora.


Per estensione, penseresti che questo sarebbe sicuro, ma no:

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

Penso che in ogni lingua, questo produrrà l'equivalente di:

Messaggio 241, livello 16, stato 1, riga 8
Conversione non riuscita durante la conversione di data e / o ora dalla stringa di caratteri.


Per completezza, c'è un quarto formato di sicuro, ma è sicuro solo per le conversioni ai tipi data / ora più recenti ( date, datetime2, datetimeoffset). In questi casi le impostazioni della lingua non possono interferire:

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

Tuttavia, consiglio vivamente di non utilizzarlo perché funziona solo per i tipi più recenti, e quelli vecchi sono ancora in una grande quantità di utilizzo, nella mia esperienza. Perché avere i trattini lì quando altrove (o di fatto nello stesso codice, se il tipo di dati cambia) devi rimuoverli?

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

Anche con la stessa stringa di origine, le conversioni hanno prodotto risultati abbastanza diversi:

März    Juli    März

Il formato che funziona per datetime ( yyyyMMdd) funzionerà sempre anche per la data e gli altri nuovi tipi. Quindi, IMHO, usalo sempre. E dato il terzo formato per i tipi con data / ora ( yyyyMMdd hh:...), questo ti permetterà in realtà di essere più coerente, anche se il componente data è sempre un po 'meno leggibile.


Adesso mi ci vorranno solo alcuni anni, dare o prendere, per prendere l'abitudine di dimostrare i tre formati sicuri quando parlo della rappresentazione di stringhe di date.


Non è che il terzo formato può diventare pericoloso quando viene aggiunto un nuovo linguaggio a SQL Server in qualche versione futura?
Kuba Wyrostek,

@Kuba Sono abbastanza sicuro che Microsoft abbia imparato la lezione su questo. Qualcuno ha preso una decisione piuttosto scadente di far interpretare tutte queste lingue yyyy-dd-MM, un formato che non credo che nessuno sulla terra abbia mai usato apposta.
Aaron Bertrand
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.