Dato SQL roundtrip

by Jesper september 22, 2009 11:28

Jeg var måske lidt forvirret da jeg skrev min post om datoer i går, så jeg kommer lige med en specificering af mine undersøgelser. Jeg tager udgangspunkt i følgende lille konsol app:

var sel = new SqlCommand("SELECT TOP 1 * FROM DatoTabel order by id desc", forbindelse);
var insert = new SqlCommand("INSERT INTO DatoTabel (Dato) VALUES (@dato)", forbindelse);

var dato = new DateTime(2009, 9, 21, 12, 0, 0);

Console.WriteLine("DateTime instans");
Console.WriteLine("Værdi: {0:o}", dato);
Console.WriteLine("Kind: {0}", dato.Kind);
Console.WriteLine("====================");

insert.Parameters.AddWithValue("@dato", dato);
forbindelse.Open();
insert.ExecuteNonQuery();

using (var r = sel.ExecuteReader())
    if (r.Read())
        dato = r.GetDateTime(1);

forbindelse.Close();

Console.WriteLine("SQL roundtripped instans");
Console.WriteLine("Værdi: {0:o}", dato);
Console.WriteLine("Kind: {0}", dato.Kind);

Den giver følgende output:

image

Her skal man bemærke at variablen: dato, bliver instantieret til et specifik dato-klokkeslæt. Kind er som default Unspecified, og SQL roundtrippet returnerer også Unspecified. Helt som man kunne forvente (men var det det man forventede?).

Hvis vi prøver med den overload, hvor man kan specificerer Kind:

var dato = new DateTime(2009, 9, 21, 12, 0, 0, DateTimeKind.Local);
// DateTime.Now har også Kind=Local

Så får man følgende overraskende resultat:

image

Vi gemmer en dato med tids-zone, men får en “DK-UTC” værdi tilbage – altså det samme klokkeslæt, men man har mistet zone informationen. Nu er man altså overladt til konventioner, når man hiver datoer ud af databasen. Det viser sig at vi helt upåagtet har en  konvention der siger:

Når vi gemmer en DateTime i databasen, så gemmer vi den med dansk zone-offset som udgangspunkt.

Man bør derfor rette sin udlæsningskode til på følgende måde, når man har den konvention:

dato = DateTime.SpecifyKind(r.GetDateTime(1), DateTimeKind.Local);

Det vil give følgende output:

image

Så er der styr på zonen… så længe klienten har dansk som lokal indstilling… Man kan altså få overraskende resultater, hvis man f.eks. har en webserver der står i USA, og man bruger ovenstående konvention, for så bliver man offsat med 6 timer.

Hvis man starter på en ny database, vil den rigtige løsning være at vedtage en konvention, der siger at alle DateTime værdier der gemmes i databasen, skal tolkes som UTC. På den måde vil man ikke blive snydt af klienter/servere med forskellige zoner. Man skal så sørge for at konverterer DateTime objektet inden man gemmer, og så skal man specificerer Utc når man henter:

Console.WriteLine("DateTime instans");
Console.WriteLine("Værdi: {0:o}", dato);
Console.WriteLine("Kind: {0}", dato.Kind);
Console.WriteLine("====================");

insert.Parameters.AddWithValue("@dato", dato.ToUniversalTime());
forbindelse.Open();
insert.ExecuteNonQuery();

using (var r = sel.ExecuteReader())
    if (r.Read())
        dato = DateTime.SpecifyKind(r.GetDateTime(1), DateTimeKind.Utc);

forbindelse.Close();

Det giver følgende output:

image

Nu kan ma så lade klienten tage stilling til hvilken tids-zone der skal vises, og man kan kalde ToLocalTime metoden, hvis man vil udlæse i den aktuelle tids-zone.

Alternativt, kan man vælge en af de nye felttyper i SQL Server 2008, hvor man har DateTimeOffset, som jo gemmer zone info. Husk bare at en konvertering fra DateTime, skal tage højde for den implicitte konvention på de eksisterende felter.

Tags:

C#

Kommentarerne er lukkede

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen | Modified by Mooglegiant

About

Mit navn er Jesper Jensen, og jeg arbejder til dagligt som web-udvikler hos DGI, hvor mit speciale er klientside applikationer. Før det var jeg nogle år i robotbranchen, hvor jeg arbejdede med 3D simulering og system koordinering. Jeg elsker webudvikling, og specielt JavaScript har min interesse. Jeg har blogget om mine oplevelser med udvikling siden 2004

Calendar

<<  juli 2010  >>
mationtofr
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar

RecentComments

Comment RSS