Takket være mangfoldigheden af kulturer på Jorden har vi en række forskellige datoformater. For numeriske datoer har vi måned-dag-år, dag-måned-år og år-måned-dag. Vi har også korte og lange formater. Datoer kan blandes med tid, hvilket er en anden historie. Denne virkelighed følger os på arbejdet. Derfor er SQL-datoformatet ikke noget, vi kan tage med ro.
I Filippinerne bruger vi 2 formater:måned-dag-år og dag-måned-år. Måned-dag-år er det generelle format for numeriske datoer. Men med længere datoformater bruger vi i flæng dag-måned-år og måned-dag-år.
På arbejdet stødte jeg aldrig på SQL Server-datoformatindstilling ud over måned-dag-år. Det varierer dog i rapporter og filer til dataudveksling og Extract-Transform-Load (ETL) projekt. Endsige brugere, der ændrer det i deres stationer for personlige præferencer! Med alle disse scenarier er det ikke at håndtere dem korrekt en opskrift at bryde.
Omhandler dine apps forskellige kulturer? Det er et andet niveau af kompleksitet og et problem, når man håndterer disse forskellige SQL-datoformater. I denne artikel vil vi udforske standarden for at håndtere alle disse varianter og eksempler. Læs videre til slutningen! Men før vi fortsætter, lad os tage en kort omvej om, hvordan SQL Server gemmer datoer.
Hvordan SQL Server gemmer datoer
Gæt. Gemmer SQL Server 29/03/2021 21:35, som det er i databasen? Hvad med 2021-03-29? Er de gemt som formaterede strenge? Baseret på denne dokumentation fra Microsoft er det ikke sådan her.
Lad os for eksempel tage datatypen DATE. Det er gemt som et 3-byte heltal.
Hvordan ved vi det?
Nå, Microsoft siger, at det er sådan, men giver ikke flere oplysninger. Det betyder ikke, at vi ikke kan vide det med sikkerhed. For det første er minimumsværdien for DATE-data 01/01/0001 eller 1. januar, 1 CE eller Common Era. For at konvertere dette til et heltal, konverterer vi denne dato til VARBINARY først, sådan her:
SELECT CAST(CAST('01/01/0001' AS DATE) AS VARBINARY(3))
Resultatet er 0x000000 i hexadecimalt format. Ud fra denne værdi kan vi se, at heltalsværdien for 1. januar 1 CE er 0. Det er logisk, fordi det er den mindste DATO-værdi.
Nu går vi 1 dag frem.
SELECT CAST(CAST('01/02/0001' AS DATE) AS VARBINARY(3)) -- January 2, 1 CE
Resultatet er 0x010000 . Dette er lidt tricky, men dette indlæg gav os en idé. Vi kan ikke behandle det, som vi ser det. Bytene er omvendt, og den faktiske hex-værdi er 0x000001 . Hvis du ved lidt om hex-tal, ved du, at dette er lig med 1 - det er 1 dag fra startpunktet, 1. januar 1 CE.
Lad os nu prøve en nylig dato:29/03/2021.
SELECT CAST(CAST('03/29/2021' AS DATE) AS VARBINARY(3))
Resultatet er 0x55420B . Når vi vender det om, bliver det 0x0B4255 . Denne gang kan vi ikke kende værdien ved at se på den. Så vi ganger det med 1 som et heltal.
SELECT 0x0B4255 * CAST(1 AS INT)
Resultatet er 737.877 . Dette er antallet af dage fra 1. januar 1 e.Kr. Lad os bekræfte det med DATEDIFF.
SELECT DATEDIFF(DAY,CAST('01/01/0001' AS DATE),CAST('03/29/2021' AS DATE))
Resultatet er det samme:737.877 dage. Meget fedt!
Bundlinje:Den formaterede dato er kun til præsentationsformål
Det er sådan SQL Server gemmer DATE-datatyper. Det er anderledes for DATETIME, SMALLDATETIME og DATETIME2, men stadig gemt som heltal. SQL Server beregner tidsvarigheden fra udgangspunktet og viser den dato, vi alle kan forstå.
Uanset om du ser på det i SQL Server Management Studio, dbForge Studio til SQL Server eller din app, er 29/03/2021 kun en præsentation. Hvis du ændrer region eller sprog, vil den lagrede værdi 0x55420B forbliver den samme.
Nu ved vi, at datoer ikke gemmes som strenge. Vi kan glemme at gemme datoer i et bestemt format . Sådan fungerer det i hvert fald ikke. Lad os i stedet se på forskellige måder i SQL til at formatere datoer, som dine apps har brug for.
De 4 nemme måder at formatere datoer på
Lad os undersøge følgende SQL-datofunktioner:
- Konverteringsfunktion
- INDSTIL SPROG
- INDSTIL DATOFORMAT
- FORMAT funktion
Du kan også bruge en SQL-forespørgselsformatering.
1. KONVERTER-funktion
KONVERT er en af datakonverteringsfunktionerne, der også kan tjene til datoformatering. Figur 1 viser et eksempel.
De første to argumenter for CONVERT er måldatatypen og datoværdien. Den tredje er valgfri, men den gælder også for datoer. De numeriske værdier er de SQL-datoformater, der skal bruges under konvertering fra dato til streng.
I figur 1 bruger Japan et år-måned-dag-format med en skråstreg som separator. Tyskland bruger dag-måned-år med prikker som skilletegn. Storbritannien og Frankrig bruger samme rækkefølge som Tyskland, men med en skråstreg som adskiller. Kun USA bruger måned-dag-år med en bindestreg som skilletegn.
I SQL kan du også konvertere DATETIME-udtrykket til DATE.
Se denne reference fra Microsoft for at få en komplet liste over KONVERT-datoformater i SQL.
2. INDSTIL SPROG
Denne indstilling angiver det sprog, der anvendes på sessionen. Det påvirker datoformater og systemmeddelelser. Når du indstiller et sprog, anvender du også implicit indstillingerne SET DATEFORMAT (vi finder ud af det senere).
Lad os indtil videre se eksemplerne i figur 2. Jeg ændrer sprogindstillingerne til litauisk og derefter tilbage til engelsk.
Se figur 2. Det litauiske datoformat er år-måned-dag. Når jeg prøver forskellige datoer, inkluderer den lange dato altid 'm.' før måneden og 'd.' efter dagen. Det første bogstav i måneden og navnet på ugedagen er heller ikke stort. Det er anderledes for andre sprogindstillinger, men du forstår pointen.
For mere information om SET LANGUAGE, se denne reference fra Microsoft.
3. INDSTIL DATOFORMAT
Denne indstilling placerer rækkefølgen af måned, dag og år til fortolkning af datotegnstrenge. Det vil tilsidesætte de implicitte datoformatindstillinger udført af SET LANGUAGE. Her er et eksempel i figur 3.
I figur 3 bruger koden DMY eller dag-måned-år-format. Under disse indstillinger skal enhver datoværdi indstillet til en datovariabel følge dette mønster. 30/03/2021 matcher dette format, men 31/03/2021 udløser en fejl, fordi 31 ikke er en gyldig måned.
Ændringer i datoformatet kan således ødelægge din app, hvis logikken fungerer på grundlag af et andet format.
For mere information om SET DATEFORMAT, se denne reference fra Microsoft.
4. FORMAT-funktion
Af alle tilgængelige formateringsmuligheder er denne den mest fleksible. Det svarer til datoformatering i .Net, da FORMAT er afhængig af tilstedeværelsen af .Net Framework på serveren, hvor SQL Server er installeret. Det er imidlertid ulempen ved denne mulighed.
Ligesom at gøre det i C#, tager FORMAT en datoværdi og en formatstreng. Lad os få et par eksempler i figur 4.
Ligesom i .Net kan du i SQL Server formatere datoer ved hjælp af forskellige separatorer. Du kan også placere måneden, dagen og året hvor som helst. Derefter kan du få den kulturelle information og bruge dens datoformat.
For mere information og eksempler på FORMAT, se denne reference fra Microsoft.
Nu har vi identificeret 4 måder at formatere datoer på og de mange forskellige datoformater. Er der et standardformat, der altid virker, når strenge konverteres til datoer?
ISO 8601 – Det mest bærbare, fejlfrie SQL-datoformat, du kan bruge til konvertering
ISO 8601 har været der siden 1988. Det er den internationale standard for udveksling af data vedrørende datoer og tidspunkter .
Den er også tilgængelig i KONVERTER-funktionen som en af datoformatstilene:stilene 126 og 127 er ISO 8601-kompatible.
Hvad gør den bærbar og fejlfri?
Problemet med ikke-ISO 8601-formater
Lad os demonstrere problemerne for ikke-ISO 8601 med et eksempel:
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')
Afhængigt af din datolokalitet kan du fortolke @d som 9. marts 2021 eller 3. september 2021. Du kan ikke være sikker på, hvad der er hvilken. Det er der, problemet ligger.
Tjek resultatet i figur 5 nedenfor:
Selv SQL Server ved det ikke med sikkerhed!
Det værste er, at denne form for scenarie kan ødelægge din app ligesom det, der skete i figur 3 tidligere. Denne situation er problematisk for apps, der beskæftiger sig med multikulturelle brugere.
Kan ISO 8601 hjælpe med dette?
Brug af ISO 8601 til at håndtere formateringsproblemer
Bemærk, når ISO 8601-formatet ååååMMdd bruges i stedet for MM/dd/åååå og kontroller resultatet i figur 6:
Det vil altid være den 9. marts, uanset hvilket sprog og datoformat der bruges. Dette er fantastisk til dataudveksling og systemintegration. Hvis din bruger har et andet datoformat i stationen, er det heller ikke ligegyldigt.
Hvis du har brug for at omdanne dine datoer til strenge og tilbage, skal du bruge ISO 8601.
ISO 8601 i SQL Server kommer i 2 varianter:
- ÅÅÅÅMMDD er kun for datoer.
- ÅÅÅÅ-MM-DDTHH:MM:SS for en blanding af dato og klokkeslæt, hvor T er afgrænsningen mellem dato og klokkeslæt.
Andre måder at håndtere datoformater på fra apps til SQL Server
1. Brug datobevidste kontroller i appen
Når du beder en bruger om at indtaste datoer i en formular, så lad dem ikke bruge fritekst. Brug datokontrol, der kun tillader valg mellem gyldige værdier.
2. Konverter kun til et andet format, når det er nødvendigt
Bliv ikke ved med at transformere datoer til strenge og omvendt. Brug de oprindelige Dato- eller DateTime-datatyper i opkaldsappen. Hvis du har brug for at transformere dem af en eller anden grund, skal du bruge ISO 8601.
3. Indstil dato/tid, tidszone og kultur i din appstart, hvis relevant
Hvis din app bruger et fast datoformat, og dine brugere elsker at justere datoformaterne, kan du rette begge dele ved opstart af applikationen. Indstil eksplicit, hvad din app har brug for. Det vil være meget bedre, hvis din netværksadministrator kan låse disse indstillinger.
Takeaways
Så er håndteringen af forskellige SQL-datoformater overvældende? Jeg kan ikke bebrejde dig, hvis du stadig finder det sådan, men vi har fundet ud af, at det ikke er umuligt.
Her er, hvad vi har dækket:
- SQL-datoer gemmes som heltal . Det, vi ser med vores øjne, er allerede formateret baseret på SQL Server-indstillinger. Uanset hvor mange gange vi ændrer sprog og datoformater, vil den gemte værdi forblive den samme. Det er nytteløst at tænke på at gemme datoer i et bestemt format.
- Der er 4 måder at formatere datoer på:KONVERT , INDSTIL SPROG , INDSTIL DATOFORMAT og FORMAT .
- Hvis du har brug for at transformere datoer til strenge og omvendt, brug ISO 8601-format .
- Der er 3 andre måder at håndtere datoformater på:
- brug af datobevidste kontroller i din app;
- omdannelse af datoer til strenge kun, når det er nødvendigt;
- indstilling af den påkrævede tidszone, kultur, serverdato/tid ved opstart, når det er relevant .
Tror du, at dette vil være nyttigt for dig og andre? Så del venligst denne artikel på dine foretrukne sociale medieplatforme.