Tack vare mångfalden av kulturer på jorden har vi en mängd olika datumformat. För numeriska datum har vi månad-dag-år, dag-månad-år och år-månad-dag. Vi har även korta och långa format. Datum kan blandas med tid, vilket är en annan historia. Denna verklighet följer oss på jobbet. Det är därför SQL-datumformatet inte är något vi kan ta lätt på.
I Filippinerna använder vi två format:månad-dag-år och dag-månad-år. Månad-dag-år är det allmänna formatet för numeriska datum. Men med längre datumformat använder vi omväxlande dag-månad-år och månad-dag-år.
På jobbet har jag aldrig stött på SQL Server-datumformatinställning annat än månad-dag-år. Det varierar dock i rapporter och filer för datautbyte och ETL-projekt (Extract-Transform-Load). Än mindre användare som ändrar det i sina stationer för personliga preferenser! Med alla dessa scenarier är att inte hantera dem på rätt sätt ett recept att bryta.
Handlar dina appar om olika kulturer? Det är en annan nivå av komplexitet och ett problem när man hanterar dessa olika SQL-datumformat. I den här artikeln kommer vi att utforska standarden för att hantera alla dessa sorter och exempel. Läs vidare till slutet! Men innan vi fortsätter, låt oss ta en kort omväg om hur SQL Server lagrar datum.
Hur SQL Server lagrar datum
Ta en gissning. Lagrar SQL Server 2021-03-29 21:35 som i databasen? Vad sägs om 2021-03-29? Lagras de som formaterade strängar? Baserat på denna dokumentation från Microsoft är det inte så här.
Låt oss ta datatypen DATE, till exempel. Det lagras som ett 3-byte heltal.
Hur vet vi det?
Nåväl, Microsoft säger att det är så men ger inte mer information. Det betyder inte att vi inte kan veta det säkert. För det första är minimivärdet för datatypen DATE 01/01/0001 eller 1 januari, 1 CE eller Common Era. För att konvertera detta till ett heltal, konverterar vi det datumet till VARBINARY först, så här:
SELECT CAST(CAST('01/01/0001' AS DATE) AS VARBINARY(3))
Resultatet är 0x000000 i hexadecimalt format. Från detta värde kan vi se att heltalsvärdet för 1 januari 1 CE är 0. Det är logiskt eftersom det är det lägsta DATE-värdet.
Nu avancerar vi 1 dag.
SELECT CAST(CAST('01/02/0001' AS DATE) AS VARBINARY(3)) -- January 2, 1 CE
Resultatet är 0x010000 . Det här är lite knepigt, men det här inlägget gav oss en idé. Vi kan inte behandla det som vi ser det. Byten är omvända och det faktiska hexadecimala värdet är 0x000001 . Om du kan lite om hexadecimala tal vet du att detta är lika med 1 – det är 1 dag från startpunkten, 1 januari 1 CE.
Nu ska vi prova ett nytt datum:2021-03-29.
SELECT CAST(CAST('03/29/2021' AS DATE) AS VARBINARY(3))
Resultatet är 0x55420B . När vi vänder på det blir det 0x0B4255 . Den här gången kan vi inte veta värdet genom att titta på det. Så vi multiplicerar det med 1 som ett heltal.
SELECT 0x0B4255 * CAST(1 AS INT)
Resultatet är 737 877 . Detta är antalet dagar från 1 januari 1 e.Kr. Låt oss verifiera det med DATEDIFF.
SELECT DATEDIFF(DAY,CAST('01/01/0001' AS DATE),CAST('03/29/2021' AS DATE))
Resultatet är detsamma:737 877 dagar. Mycket coolt!
Bottom-line:Det formaterade datumet är endast för presentationsändamål
Det är så SQL Server lagrar datatyperna DATE. Det är annorlunda för DATETIME, SMALLDATETIME och DATETIME2, men lagras fortfarande som heltal. SQL Server beräknar tidslängden från startpunkten och visar det datum som vi alla kan förstå.
Oavsett om du tittar på det i SQL Server Management Studio, dbForge Studio för SQL Server eller din app, är 2021-03-29 bara en presentation. Om du ändrar region eller språk kommer det lagrade värdet 0x55420B kommer att förbli densamma.
Nu vet vi att datum inte lagras som strängar. Vi kan glömma att lagra datum i ett specifikt format . Det är inte så det fungerar i alla fall. Låt oss istället titta på olika sätt i SQL för att formatera datum som dina appar behöver.
De fyra enkla sätten att formatera datum
Låt oss undersöka följande SQL-datumfunktioner:
- Konvertera funktion
- STÄLL IN SPRÅK
- STÄLL IN DATUMFORMAT
- FORMAT-funktionen
Du kan också använda en SQL-frågeformaterare.
1. KONVERTERA-funktion
CONVERT är en av datakonverteringsfunktionerna som också kan användas för datumformatering. Figur 1 visar ett exempel.
De två första argumenten för CONVERT är måldatatypen och datumvärdet. Den tredje är valfri men den gäller även för datum. De numeriska värdena är de SQL-datumformatstilar som ska användas under konvertering från datum till sträng.
I figur 1 använder Japan ett år-månad-dag-format med ett snedstreck som avgränsare. Tyskland använder dag-månad-år med punkter som avgränsare. Storbritannien och Frankrike använder samma sekvens som Tyskland, men med ett snedstreck som avgränsare. Endast USA använder månad-dag-år med bindestreck som avgränsare.
I SQL kan du även konvertera uttrycket DATETIME till DATE.
För en fullständig lista över formatmallar för CONVERT datumformat i SQL, kolla denna referens från Microsoft.
2. STÄLL IN SPRÅK
Den här inställningen anger språket som används för sessionen. Det påverkar datumformat och systemmeddelanden. När du ställer in ett språk tillämpar du också implicit inställningarna SET DATEFORMAT (vi löser det senare).
Låt oss nu titta på exemplen i figur 2. Jag ändrar språkinställningarna till litauiska och sedan tillbaka till engelska.
Titta på figur 2. Det litauiska datumformatet är år-månad-dag. När jag provar olika datum inkluderar det långa datumet alltid 'm.' före månaden och 'd.' efter dagen. Månadens första bokstav och veckodagens namn är inte heller versaler. Det är annorlunda för andra språkinställningar men du förstår poängen.
För mer information om SET LANGUAGE, kolla in den här referensen från Microsoft.
3. STÄLL IN DATUMFORMAT
Den här inställningen placerar ordningen på månad, dag och år för tolkning av datumteckensträngar. Det kommer att åsidosätta de implicita datumformatinställningarna som gjorts av SET LANGUAGE. Här är ett exempel i figur 3.
I figur 3 använder koden DMY eller dag-månad-år-format. Under dessa inställningar bör alla datumvärden som är inställda på en datumvariabel följa detta mönster. 30/03/2021 matchar detta format, men 31/03/2021 utlöser ett fel eftersom 31 inte är en giltig månad.
Således kan ändringar i datumformatet bryta din app om logiken fungerar på premissen för ett annat format.
För mer information om SET DATEFORMAT, kolla in den här referensen från Microsoft.
4. FORMAT-funktion
Av alla tillgängliga formateringsalternativ är det här det mest flexibla. Det är liknande för datumformatering i .Net, eftersom FORMAT förlitar sig på närvaron av .Net Framework på servern där SQL Server är installerad. Det är dock nackdelen med det här alternativet.
Precis som att göra det i C# tar FORMAT ett datumvärde och en formatsträng. Låt oss ta några exempel i figur 4.
Som i .Net, i SQL Server, kan du formatera datum med olika separatorer. Du kan också placera månad, dag och år var som helst. Sedan kan du få kulturinformationen och använda dess datumformat.
För mer information och exempel på FORMAT, kolla in den här referensen från Microsoft.
Nu har vi identifierat fyra sätt att formatera datum och olika datumformat. Finns det ett standardformat som alltid fungerar när man konverterar strängar till datum?
ISO 8601 – Det mest portabla, felfria SQL-datumformatet du kan använda för konvertering
ISO 8601 har funnits sedan 1988. Det är den internationella standarden för utbyte av data som rör datum och tider .
Den är också tillgänglig i funktionen KONVERTERA som en av datumformatstilarna:stilarna 126 och 127 är ISO 8601-kompatibla.
Vad gör den bärbar och felfri?
Problemet med icke-ISO 8601-format
Låt oss visa problemet för icke-ISO 8601 med ett exempel:
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')
Beroende på din datumlokal kan du tolka @d som 9 mars 2021 eller 3 september 2021. Du kan inte vara säker på vilket som är vilket. Det är där problemet ligger.
Kontrollera resultatet i figur 5 nedan:
Inte ens SQL Server vet det säkert!
Det värsta är att den här typen av scenario kan bryta din app precis som det som hände i figur 3 tidigare. Denna situation är problematisk för appar som hanterar mångkulturella användare.
Kan ISO 8601 hjälpa till med detta?
Använda ISO 8601 för att hantera formateringsproblem
Lägg märke till när ISO 8601-formatet ååååMMdd används istället för MM/dd/åååå och kontrollera resultatet i figur 6:
Det kommer alltid att vara 9 mars, oavsett vilket språk och datumformat som används. Detta är bra för datautbyte och systemintegrationer. Om din användare har ett annat datumformat i stationen spelar det ingen roll heller.
Om du behöver förvandla dina datum till strängar och tillbaka, använd ISO 8601.
ISO 8601 i SQL Server finns i två varianter:
- ÅÅÅÅMMDD är endast för datum.
- ÅÅÅÅ-MM-DDTHH:MM:SS för en blandning av datum och tid, där T är avgränsaren mellan datum och tid.
Andra sätt att hantera datumformat från appar till SQL Server
1. Använd datummedvetna kontroller i appen
När du ber en användare att ange datum i ett formulär, låt dem inte använda fritext. Använd datumkontroller som endast tillåter att välja mellan giltiga värden.
2. Konvertera till ett annat format endast när det behövs
Fortsätt inte att förvandla datum till strängar och vice versa. Använd de inbyggda datatyperna Date eller DateTime i den uppringande appen. Om du behöver förvandla dem av någon anledning, använd ISO 8601.
3. Ställ in datum/tid, tidszon och kultur i din appstart, om tillämpligt
Om din app använder ett fast datumformat, och dina användare älskar att justera datumformaten, kan du fixa båda vid start av applikationen. Ange uttryckligen vad din app behöver. Det blir mycket bättre om din nätverksadministratör kan låsa dessa inställningar.
Hämtmat
Så, är det överväldigande att hantera olika SQL-datumformat? Jag kan inte klandra dig om du fortfarande tycker att det är så, men vi har upptäckt att det inte är omöjligt.
Här är vad vi har täckt:
- SQL-datum lagras som heltal . Det vi ser med våra ögon är redan formaterat baserat på SQL Server-inställningar. Oavsett hur många gånger vi ändrar språk och datumformat kommer det lagrade värdet att förbli detsamma. Det är meningslöst att tänka på att lagra datum i ett specifikt format.
- Det finns fyra sätt att formatera datum:KONVERTERA , STÄLL IN SPRÅK , STÄLL IN DATUMFORMAT och FORMAT .
- Om du behöver omvandla datum till strängar och vice versa, använd ISO 8601-format .
- Det finns tre andra sätt att hantera datumformat:
- använda datummedvetna kontroller i din app;
- omvandla datum till strängar endast när det behövs;
- ställ in önskad tidszon, kultur, serverdatum/tid vid start när så är tillämpligt .
Tror du att detta kommer att vara användbart för dig och andra? Dela sedan den här artikeln på dina favoritplattformar för sociala medier.