Welche Datums- / Uhrzeit-Literalformate sind SPRACHE und DATUMFORMAT sicher?

24

Es ist leicht zu demonstrieren, dass viele andere Datums- / Zeitformate als die folgenden zwei aufgrund von SET LANGUAGE, SET DATEFORMAT oder der Standardsprache eines Logins für eine Fehlinterpretation anfällig sind:

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

Auch dieses Format ohne T sieht möglicherweise wie ein gültiges ISO 8601-Format aus, schlägt jedoch in mehreren Sprachen fehl:

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

Ergebnisse:

Die Spracheneinstellung wurde in Deutsch geändert.

Meldung 242, Stufe 16,
Status 3 Bei der Konvertierung eines varchar-Datentyps in einen datetime-Datentyp liegt der Wert außerhalb des tatsächlichen Bereichs.

Le parameter de langue est passé à Français.

Nachricht 242, Ebene 16,
Status 3 Die Konvertierung erfolgt zu einem Zeitpunkt, an dem die Grenzen überschritten wurden.

Nun schlagen diese fehl, als hätte ich auf Englisch den Monat und den Tag vertauscht, um eine Datumskomponente zu formulieren von yyyy-dd-mm:

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

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

Ergebnis:

Meldung 242, Ebene 16,
Status 3 Die Konvertierung eines varchar-Datentyps in einen datetime-Datentyp führte zu einem Wert außerhalb des zulässigen Bereichs.

(Dies ist nicht Microsoft Access, das für Sie "nett" ist und die Umsetzung für Sie behebt. In einigen Fällen können ähnliche Fehler auch mit auftreten. SET DATEFORMAT ydm;Es ist nicht nur eine Sprachsache, das ist nur das üblichere Szenario, in dem diese auftreten Brüche passieren - und werden nicht immer bemerkt, weil sie manchmal keine Fehler sind. Es ist nur so, dass der 7. August zum 8. Juli wurde und niemand etwas bemerkt hat.)

Also die Frage:

Nachdem ich weiß, dass es eine Reihe von unsicheren Formaten gibt, gibt es andere Formate, die bei jeder Kombination aus Sprache und Datumsformat sicher sind?

Aaron Bertrand
quelle

Antworten:

26

In der Dokumentation wird ausdrücklich darauf hingewiesen, dass die einzigen sicheren Formate diejenigen sind, die ich zu Beginn der Frage demonstriert habe:

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

Vor kurzem wurde ich jedoch darauf aufmerksam gemacht, dass es ein drittes Format gibt, das für alle Sprach- und Datumsformateinstellungen gleichermaßen immun ist:

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

TL; DR: Das ist wahr. Für datetimeund smalldatetime.

Lesen Sie weiter für die längere Version und über so viele Beweise, wie Sie bekommen werden.


Es gibt eine Lücke, die dies erklärt - während der Haupttextkörper das yyyyMMdd hh:...Format nicht als sicher für transponierte Sprach- oder Datumsformatinterpretationen anerkennt , gibt es einen kleinen Klappentext, der besagt, dass der Datumsanteil einer solchen Zeichenfolge abhängig von den Datumsformateinstellungen nicht validiert wird:

Bildbeschreibung hier eingeben

Es ist mir eher unähnlich, die Dokumentation nur beim Wort zu nehmen. Man kann sagen, ich bin ein bisschen skeptisch. Und auch hier ist die Sprache nicht eindeutig - es geht nur um die Kombination von Datum und Uhrzeit, ohne den Raum explizit auszulesen (das könnte, soweit ich weiß, ein Wagenrücklauf sein). Es heißt auch, dass es nicht mehrsprachig ist, was bedeutet, dass es in bestimmten Sprachen fehlschlagen kann, aber wir werden in Kürze herausfinden, dass dies auch falsch ist.

Ich wollte also beweisen, dass keine Kombination von Sprache / Datumsformat dazu führen kann, dass dieses spezielle Format fehlschlägt.

Zuerst habe ich für jede Sprache einen kleinen Block mit dynamischem SQL erstellt:

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

Dies erzeugte 34 Zeilen der Ausgabe wie folgt:

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

Ich habe diese Ausgabe in ein neues Abfragefenster kopiert und darüber den folgenden Code generiert, mit dem hoffentlich in mindestens einem Fall versucht wird, dasselbe Datum (den 13. März) auf den 3. Tag des 13. Monats umzustellen:

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

Nein, jede Sprache funktioniert nur in ydm. Ich habe auch jedes andere Format ausprobiert und auch jeden Datums- / Zeitdatentyp. Jedes Mal 34 erfolgreiche Umbauten bis zum 13. März.

Also gebe ich @AndriyM und @ErikE zu, dass es tatsächlich ein drittes sicheres Format gibt. Ich werde dies für zukünftige Posts im Hinterkopf behalten, aber ich habe die Trommeln an so vielen Stellen auf die anderen beiden geschlagen, dass ich sie jetzt nicht alle aufspüren und korrigieren werde.


Im weiteren Sinne würden Sie denken, dies wäre sicher, aber nein:

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

Ich denke, in jeder Sprache ergibt dies das Äquivalent von:

Meldung 241, Ebene 16, Status 1, Zeile 8 Die
Konvertierung ist fehlgeschlagen, wenn Datum und / oder Uhrzeit aus der Zeichenfolge konvertiert wurden.


Die Vollständigkeit halber gibt es ein viertes sicheres Format, aber es ist nur sicher für Conversions zu den neueren Datum / Zeit - Typen ( date, datetime2, datetimeoffset). In diesen Fällen können die Spracheinstellungen nicht stören:

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

Ich rate jedoch dringend von seiner Verwendung ab, da es nur für die neueren Typen funktioniert und die alten nach meiner Erfahrung immer noch in großem Umfang verwendet werden. Warum müssen die Bindestriche an anderen Stellen (oder tatsächlich im selben Code, wenn sich der Datentyp ändert) entfernt werden?

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

Sogar bei der gleichen Quellzeichenfolge ergaben die Konvertierungen ganz unterschiedliche Ergebnisse:

März    Juli    März

Das Format, das für datetime ( yyyyMMdd) funktioniert, funktioniert auch immer für date und die anderen neuen Typen. Also, IMHO, benutze das einfach immer. Mit dem dritten Format für Typen mit Datum / Uhrzeit ( yyyyMMdd hh:...) können Sie tatsächlich konsistenter sein - auch wenn die Datumskomponente immer etwas weniger lesbar ist.


Jetzt brauche ich nur ein paar Jahre, um mir die Gewohnheit zu geben, die drei sicheren Formate zu demonstrieren , wenn ich über die Zeichenfolgendarstellung von Datumsangaben spreche.

Aaron Bertrand
quelle
Ist es nicht so, dass das dritte Format unsicher wird, wenn SQL Server in einer zukünftigen Version um eine neue Sprache erweitert wird?
Kuba Wyrostek
@Kuba Ich bin ziemlich zuversichtlich, dass Microsoft ihre Lektion dazu gelernt hat. Jemand traf eine ziemlich schlechte Entscheidung, all diese Sprachen JJJJ-TT-MM interpretieren zu lassen, ein Format, von dem ich glaube, dass es noch nie jemand auf der Welt mit Absicht verwendet hat.
Aaron Bertrand