Dankzij de diversiteit aan culturen op aarde hebben we verschillende datumformaten. Voor numerieke datums hebben we maand-dag-jaar, dag-maand-jaar en jaar-maand-dag. We hebben ook korte en lange formaten. Datums kunnen worden gemengd met tijd, wat een ander verhaal is. Deze realiteit volgt ons op het werk. Daarom is het SQL-datumformaat niet iets dat we gemakkelijk kunnen nemen.
In de Filipijnen gebruiken we 2 formaten:maand-dag-jaar en dag-maand-jaar. Maand-dag-jaar is het algemene formaat voor numerieke datums. Maar bij langere datumnotaties gebruiken we door elkaar dag-maand-jaar en maand-dag-jaar.
Op het werk ben ik nooit een andere datumnotatie-instelling van SQL Server tegengekomen dan maand-dag-jaar. Het varieert echter in rapporten en bestanden voor gegevensuitwisseling en het Extract-Transform-Load (ETL) -project. Laat staan gebruikers die het in hun stations wijzigen voor persoonlijke voorkeuren! Met al deze scenario's is het een recept om er niet op de juiste manier mee om te gaan.
Hebben uw apps te maken met verschillende culturen? Dat is een ander niveau van complexiteit en een probleem bij het omgaan met deze verschillende SQL-datumformaten. In dit artikel zullen we de standaard onderzoeken om met al deze variëteiten en voorbeelden om te gaan. Lees verder tot het einde! Maar laten we, voordat we verder gaan, een korte omweg maken over hoe SQL Server datums opslaat.
Hoe SQL Server datums opslaat
Doe een gok. Slaat SQL Server 29-03-2021 21:35 op zoals in de database? Hoe zit het met 2021-03-29? Worden ze opgeslagen als geformatteerde strings? Op basis van deze documentatie van Microsoft is het niet zo.
Laten we bijvoorbeeld het gegevenstype DATE nemen. Het wordt opgeslagen als een geheel getal van 3 bytes.
Hoe weten we dat?
Welnu, Microsoft zegt van wel, maar geeft niet meer informatie. Het betekent niet dat we het niet zeker kunnen weten. Ten eerste is de minimumwaarde van het gegevenstype DATE 01/01/0001 of 1 januari 1 CE of Common Era. Om dit naar een geheel getal te converteren, converteren we die datum eerst naar VARBINARY, als volgt:
SELECT CAST(CAST('01/01/0001' AS DATE) AS VARBINARY(3))
Het resultaat is 0x000000 in hexadecimaal formaat. Uit deze waarde kunnen we zien dat de integerwaarde van 1 januari 1 CE 0 is. Het is logisch, want dat is de minimale DATE-waarde.
Nu gaan we 1 dag vooruit.
SELECT CAST(CAST('01/02/0001' AS DATE) AS VARBINARY(3)) -- January 2, 1 CE
Het resultaat is 0x010000 . Dit is een beetje lastig, maar dit bericht gaf ons een idee. We kunnen het niet behandelen zoals we het zien. De bytes zijn omgekeerd en de werkelijke hexadecimale waarde is 0x000001 . Als je een beetje weet over hexadecimale getallen, weet je dat dit gelijk is aan 1 - het is 1 dag vanaf het startpunt, 1 januari 1 CE.
Laten we nu een recente datum proberen:29-03-2021.
SELECT CAST(CAST('03/29/2021' AS DATE) AS VARBINARY(3))
Het resultaat is 0x55420B . Als we het omkeren, wordt het 0x0B4255 . Deze keer kunnen we de waarde niet weten door ernaar te kijken. We vermenigvuldigen het dus met 1 als een geheel getal.
SELECT 0x0B4255 * CAST(1 AS INT)
Het resultaat is 737.877 . Dit is het aantal dagen vanaf 1 januari 1 CE. Laten we het verifiëren met DATEDIFF.
SELECT DATEDIFF(DAY,CAST('01/01/0001' AS DATE),CAST('03/29/2021' AS DATE))
Het resultaat is hetzelfde:737.877 dagen. Heel gaaf!
Kortom:de geformatteerde datum is alleen voor presentatiedoeleinden
Dat is hoe SQL Server DATE-gegevenstypen opslaat. Het is anders voor DATETIME, SMALLDATETIME en DATETIME2, maar nog steeds opgeslagen als gehele getallen. SQL Server berekent de tijdsduur vanaf het startpunt en geeft de datum weer die we allemaal kunnen begrijpen.
Of je het nu bekijkt in SQL Server Management Studio, dbForge Studio voor SQL Server of je app, 29-03-2021 is slechts een presentatie. Als u de regio of taal wijzigt, wordt de opgeslagen waarde 0x55420B zal hetzelfde blijven.
Nu weten we dat datums niet als strings worden opgeslagen. We kunnen het opslaan van datums in een specifiek formaat vergeten . Zo werkt het sowieso niet. Laten we in plaats daarvan kijken naar verschillende manieren in SQL om datums op te maken die uw apps nodig hebben.
De 4 eenvoudige manieren om datums op te maken
Laten we eens kijken naar de volgende SQL-datumfuncties:
- CONVERT-functie
- TAAL INSTELLEN
- DATUMFORMAAT INSTELLEN
- FORMAT-functie
U kunt ook een SQL-queryformatter gebruiken.
1. CONVERT-functie
CONVERT is een van de gegevensconversiefuncties die ook kan dienen voor datumopmaak. Afbeelding 1 toont een voorbeeld.
De eerste twee argumenten van CONVERT zijn het doelgegevenstype en de datumwaarde. De derde is optioneel, maar geldt ook voor datums. De numerieke waarden zijn de SQL-datumnotatiestijlen die moeten worden gebruikt tijdens de conversie van datum naar tekenreeks.
In figuur 1 gebruikt Japan een jaar-maand-dagnotatie met een schuine streep als scheidingsteken. Duitsland gebruikt dag-maand-jaar met punten als scheidingstekens. Groot-Brittannië en Frankrijk gebruiken dezelfde volgorde als Duitsland, maar met een schuine streep als scheidingsteken. Alleen de Verenigde Staten gebruiken maand-dag-jaar met een koppelteken als scheidingsteken.
In SQL kunt u de DATETIME-expressie ook converteren naar DATE.
Raadpleeg deze referentie van Microsoft voor een volledige lijst van CONVERT-datumnotatiestijlen in SQL.
2. TAAL INSTELLEN
Deze instelling specificeert de taal die op de sessie wordt toegepast. Het heeft invloed op de datumnotaties en systeemberichten. Wanneer je een taal instelt, pas je ook impliciet de SET DATEFORMAT-instellingen toe (we werken het later uit).
Laten we voor nu de voorbeelden in figuur 2 eens bekijken. Ik verander de taalinstellingen naar Litouws en dan terug naar Engels.
Kijk naar figuur 2. Het Litouwse datumformaat is jaar-maand-dag. Telkens wanneer ik verschillende datums probeer, bevat de lange datum altijd 'm.' vóór de maand en 'd.' na de dag. De eerste letter van de maand en de naam van de weekdag worden ook niet met een hoofdletter geschreven. Het is anders voor andere taalinstellingen, maar je snapt het punt.
Raadpleeg deze referentie van Microsoft voor meer informatie over SET LANGUAGE.
3. DATUMFORMAAT INSTELLEN
Deze instelling plaatst de volgorde van de maand, de dag en het jaar voor het interpreteren van tekenreeksen voor datums. Het zal de impliciete datumnotatie-instellingen overschrijven die zijn gedaan door SET LANGUAGE. Hier is een voorbeeld in figuur 3.
In figuur 3 gebruikt de code de indeling DMY of dag-maand-jaar. Onder deze instellingen moet elke datumwaarde die is ingesteld op een datumvariabele dit patroon volgen. 30/03/2021 komt overeen met dit formaat, maar 31/03/2021 activeert een fout omdat 31 geen geldige maand is.
Wijzigingen in de datumnotatie kunnen uw app dus breken als de logica werkt op basis van een andere notatie.
Raadpleeg deze referentie van Microsoft voor meer informatie over SET DATEFORMAT.
4. FORMAT Functie
Van alle beschikbare opmaakopties is deze de meest flexibele. Het is vergelijkbaar voor datumopmaak in .Net, aangezien FORMAT afhankelijk is van de aanwezigheid van het .Net Framework op de server waarop SQL Server is geïnstalleerd. Dat is echter het nadeel van deze optie.
Net zoals het in C# doet, neemt FORMAT een datumwaarde en een opmaakreeks. Laten we een paar voorbeelden geven in figuur 4.
Net als in .Net kun je in SQL Server datums opmaken met verschillende scheidingstekens. Ook kunt u de maand, de dag en het jaar overal plaatsen. Vervolgens kun je de culturele informatie krijgen en het datumformaat gebruiken.
Bekijk deze referentie van Microsoft voor meer informatie en voorbeelden over FORMAT.
Nu hebben we 4 manieren geïdentificeerd om datums op te maken en de verscheidenheid aan datumnotaties. Is er een standaardindeling die altijd werkt bij het converteren van tekenreeksen naar datums?
ISO 8601 – Het meest draagbare, foutloze SQL-datumformaat dat u kunt gebruiken voor conversie
ISO 8601 bestaat sinds 1988. Het is de internationale standaard in de uitwisseling van gegevens met betrekking tot datums en tijden .
Het is ook beschikbaar in de CONVERT-functie als een van de datumnotatiestijlen:stijlen 126 en 127 voldoen aan ISO 8601.
Wat maakt het draagbaar en foutloos?
Het probleem met niet-ISO 8601-indelingen
Laten we het probleem voor niet-ISO 8601 demonstreren met een voorbeeld:
DECLARE @d VARCHAR(10) = '03/09/2021';
SET LANGUAGE Italian;
SELECT FORMAT(CONVERT(DATETIME, @d),'D')
SET LANGUAGE English
SELECT FORMAT(CONVERT(DATETIME, @d),'D')
SET DATEFORMAT DMY
SELECT FORMAT(CONVERT(DATETIME, @d),'D')
SET DATEFORMAT MDY
SELECT FORMAT(CONVERT(DATETIME, @d),'D')
Afhankelijk van de landinstelling van je datum, kun je @d interpreteren als 9 maart 2021 of 3 september 2021. Je weet niet zeker welke welke is. Dat is waar het probleem ligt.
Controleer het resultaat in Afbeelding 5 hieronder:
Zelfs SQL Server weet het niet zeker!
In het ergste geval kan dit soort scenario je app kapot maken, net als wat er eerder in figuur 3 is gebeurd. Deze situatie is problematisch voor apps die te maken hebben met multiculturele gebruikers.
Kan ISO 8601 hierbij helpen?
ISO 8601 gebruiken om opmaakproblemen op te lossen
Let op wanneer de ISO 8601-indeling jjjjMMdd wordt gebruikt in plaats van MM/dd/jjjj en controleer het resultaat in Afbeelding 6:
Het zal altijd 9 maart zijn, ongeacht de taal- en datumnotatie-instellingen die worden gebruikt. Dit is geweldig voor gegevensuitwisseling en systeemintegraties. Als uw gebruiker een ander datumformaat in het station heeft, maakt het ook niet uit.
Als je je datums moet omzetten naar tekenreeksen en terug, gebruik dan ISO 8601.
ISO 8601 in SQL Server is er in 2 smaken:
- JJJJMMDD is alleen voor datums.
- JJJJ-MM-DDTHH:MM:SS voor een mix van datum en tijd, waarbij T het scheidingsteken is tussen datum en tijd.
Andere manieren om datumnotaties van apps naar SQL Server te verwerken
1. Gebruik datumbewuste bedieningselementen in de app
Wanneer u een gebruiker vraagt om datums in een formulier in te voeren, laat hem dan geen vrije tekst gebruiken. Gebruik datumbesturingselementen waarmee u alleen uit geldige waarden kunt kiezen.
2. Alleen indien nodig converteren naar een ander formaat
Blijf datums niet omzetten in strings en vice versa. Gebruik de native datum- of DateTime-gegevenstypen van de aanroepende app. Als je ze om wat voor reden dan ook moet transformeren, gebruik dan ISO 8601.
3. Stel de datum/tijd, tijdzone en cultuur in bij het opstarten van uw app, indien van toepassing
Als uw app een vaste datumnotatie gebruikt en uw gebruikers graag de datumnotaties aanpassen, kunt u beide corrigeren bij het opstarten van de toepassing. Stel expliciet in wat uw app nodig heeft. Het is veel beter als uw netwerkbeheerder deze instellingen kan vergrendelen.
Afhaalmaaltijden
Dus, is het omgaan met variëteiten van SQL-datumnotatie overweldigend? Ik kan het je niet kwalijk nemen als je het nog steeds zo vindt, maar we hebben ontdekt dat het niet onmogelijk is.
Dit is wat we hebben behandeld:
- SQL-datums worden opgeslagen als gehele getallen . Wat we met onze ogen zien, is al geformatteerd op basis van SQL Server-instellingen. Het maakt niet uit hoe vaak we de taal en datumnotaties wijzigen, de opgeslagen waarde blijft hetzelfde. Het heeft geen zin om datums in een specifiek formaat op te slaan.
- Er zijn 4 manieren om datums op te maken:CONVERTEREN , TAAL INSTELLEN , DATUMFORMAAT INSTELLEN , en FORMAAT .
- Als u datums moet omzetten in tekenreeksen en omgekeerd, gebruik dan de ISO 8601-indeling .
- Er zijn 3 andere manieren om met datumnotaties om te gaan:
- datumbewuste bedieningselementen gebruiken in je app;
- datums alleen omzetten in tekenreeksen als dat nodig is;
- de vereiste tijdzone, cultuur, serverdatum/-tijd instellen bij opstarten indien van toepassing .
Denk je dat dit nuttig zal zijn voor jou en voor anderen? Deel dit artikel dan op uw favoriete sociale mediaplatforms.