How do I insert a datetime value into a SQL database table where the type of the column is datetime?
This is an older question with a proper answer (please use parameterized queries) which I'd like to extend with some timezone discussion. For my current project I was interested in how do the datetime columns handle timezones and this question is the one I found.
Turns out, they do not, at all.
datetime column stores the given DateTime as is, without any conversion. It does not matter if the given datetime is UTC or local.
You can see for yourself:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM (VALUES (@a, @b, @c)) example(a, b, c);";
var local = DateTime.Now;
var utc = local.ToUniversalTime();
command.Parameters.AddWithValue("@a", utc);
command.Parameters.AddWithValue("@b", local);
command.Parameters.AddWithValue("@c", utc.ToLocalTime());
using (var reader = command.ExecuteReader())
{
reader.Read();
var localRendered = local.ToString("o");
Console.WriteLine($"a = {utc.ToString("o").PadRight(localRendered.Length, ' ')} read = {reader.GetDateTime(0):o}, {reader.GetDateTime(0).Kind}");
Console.WriteLine($"b = {local:o} read = {reader.GetDateTime(1):o}, {reader.GetDateTime(1).Kind}");
Console.WriteLine($"{"".PadRight(localRendered.Length + 4, ' ')} read = {reader.GetDateTime(2):o}, {reader.GetDateTime(2).Kind}");
}
}
}
What this will print will of course depend on your time zone but most importantly the read values will all have Kind = Unspecified. The first and second output line will be different by your timezone offset. Second and third will be the same. Using the "o" format string (roundtrip) will not show any timezone specifiers for the read values.
Example output from GMT+02:00:
a = 2018-11-20T10:17:56.8710881Z read = 2018-11-20T10:17:56.8700000, Unspecified
b = 2018-11-20T12:17:56.8710881+02:00 read = 2018-11-20T12:17:56.8700000, Unspecified
read = 2018-11-20T12:17:56.8700000, Unspecified
Also note of how the data gets truncated (or rounded) to what seems like 10ms.