Using SQL Server 2008. This is a really junior question and I could really use some elaborate information, but the information on Google seems to dance around the topic quite a bit and it would be nice if there was some detailed elaboration on how this works...
Let's say I have a datetime column and in ADO.NET I set it to DateTime.UtcNow.
1) Does SQL Server store DateTime.UtcNow accordingly, or does it offset it again based on the timezone of where the server is installed, and then return it offset-reversed when queried? I think I know that the answer is "of course it stores it without offsetting it again" but want to be certain.
So then I query for it and cast it from object to a DateTime after getting it from, say, an IDataReader column. As far as I know, System.DateTime has metadata that internally tracks whether it is a UTC DateTime or it is an offsetted DateTime, which may or may not cause .ToLocalTime() and .ToUniversalTime() to have different behavior depending on this state. So,
2) Does this casted System.DateTime object already know that it is a UTC DateTime instance, or does it assume that it has been offset?
Now let's say I don't use UtcNow, I use DateTime.Now, when performing an ADO.NET INSERT or UPDATE.
3) Does ADO.NET pass the offset to SQL Server and does SQL Server store DateTime.Now with the offset metadata?
So then I query for it and cast it from, say, an IDataReader column to a DateTime.
4) Does this casted System.DateTime object already know that it is an offset time, or does it assume that it is UTC?
The TIMEZONE defaults to +00:00. The DATE part defaults to 1900-1-1. The TIMEZONE will default to +00:00.
DATETIME is a date/time that does not store timezone.
For SQL Database, the time zone is always set to UTC and CURRENT_TIMEZONE returns the name of the UTC time zone.
Performed some unit tests to answer my own question in all four parts.
###1: Does SQL Server store DateTime.UtcNow accordingly, or does it offset it again based on the timezone of where the server is installed, and then return it offset-reversed when queried? Executed this):
cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.UtcNow));
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT CAST(val as varchar) value FROM testtbl";
Console.WriteLine(cmd.ExecuteScalar());
The result of this at 1:30 PM local time (-7h, or 8:30 PM UTC) was:
Jun 3 2010 8:30PM
Then I tried this:
cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.UtcNow));
cmd.ExecuteNonQuery();
Console.WriteLine("change time zone to utc");
Console.ReadLine();
cmd.CommandText = "SELECT CAST(val as varchar) value FROM testtbl";
Console.WriteLine(cmd.ExecuteScalar());
Console.WriteLine("change time zone back to local");
Executed at 9:25 PM UTC, it returned
Jun 3 2010 9:25PM
Compare this to DateTime.Now:
cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.Now));
cmd.ExecuteNonQuery();
Console.WriteLine("change time zone to utc");
Console.ReadLine();
cmd.CommandText = "SELECT CAST(val as varchar) value FROM testtbl";
Console.WriteLine(cmd.ExecuteScalar());
Console.WriteLine("change time zone back to local");
Executed at 3:55 PM (local; -7h), returned:
Jun 3 2010 3:55PM
###2: So then I query for it and cast it from object to a DateTime after getting it from, say, an IDataReader column. Does this casted System.DateTime object already know that it is a UTC DateTime instance, or does it assume that it has been offset?
Neither.
cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.UtcNow));
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT val value FROM testtbl";
var retval = (DateTime)cmd.ExecuteScalar();
Console.WriteLine("Kind: " + retval.Kind);
Console.WriteLine("UTC: " + retval.ToUniversalTime().ToString());
Console.WriteLine("Local: " + retval.ToLocalTime().ToString());
The result of this (executed at 1:58 PM local time) was:
Kind: Unspecified
UTC: 6/4/2010 3:58:42 AM
Local: 6/3/2010 1:58:42 PM
That is, .ToUniversalTime()
ended up offsetting from local time to UTC time not once but twice (??), and .ToLocalTime()
ended up not offsetting at all.
###3: Does ADO.NET pass the offset to SQL Server and does SQL Server store DateTime.Now with the offset metadata?
Without performing any unit tests, the answer is already known to be "only with DateTimeOffset" SQL type. SQL's datetime
does not do offsets.
###4: Does this casted System.DateTime object already know that it is an offset time, or does it assume that it is UTC?
Neither. SQL's DateTimeOffset type is returned as a .NET DateTimeOffset struct.
The following executed at 3:31 PM local time where column offval
is a datetimeoffset SQL type,
cmd.CommandText = "INSERT INTO testtbl (offval) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.Now));
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT offval value FROM testtbl";
object retvalobj = cmd.ExecuteScalar();
Console.WriteLine("Type: " + retvalobj.GetType().Name);
var retval = (DateTimeOffset)retvalobj;
Console.WriteLine("ToString(): " + retval.ToString());
Console.WriteLine("UTC: " + retval.ToUniversalTime().ToString());
Console.WriteLine("Local: " + retval.ToLocalTime().ToString());
This resulted in:
Type: DateTimeOffset
ToString(): 6/3/2010 3:31:47 PM +00:00
UTC: 6/3/2010 3:31:47 PM +00:00
Local: 6/3/2010 8:31:47 AM -07:00
A surprising disparity.
Going back and executing the test for question #1 above using DateTime.Now instead of DateTime.UtcNow, I validated that ADO.NET does NOT convert to universal time before storing to the database.
That is, this executed at 3:27 PM local time (-7h):
cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.Now));
cmd.ExecuteNonQuery();
Console.WriteLine("change time zone to utc");
Console.ReadLine();
cmd.CommandText = "SELECT CAST(val as varchar) value FROM testtbl";
Console.WriteLine(cmd.ExecuteScalar());
Console.WriteLine("change time zone back to local");
.. returned ..
Jun 3 2010 3:27PM
Executing this at 3:17 PM local time:
cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.UtcNow));
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT val FROM testtbl";
var result = (DateTime)cmd.ExecuteScalar();
Console.WriteLine("Kind: " + result.Kind);
Console.WriteLine("ToString(): " + result.ToString());
Console.WriteLine("Add 1 minute, is greater than UtcNow? "
+ (result.AddMinutes(1) > DateTime.UtcNow).ToString());
Console.WriteLine("Add 1 minute, is greater than Now? "
+ (result.AddMinutes(1) > DateTime.Now).ToString());
Console.WriteLine("Add 1 minute, is less than UtcNow? "
+ (result.AddMinutes(1) < DateTime.UtcNow).ToString());
Console.WriteLine("Add 1 minute, is less than Now? "
+ (result.AddMinutes(1) < DateTime.Now).ToString());
Console.WriteLine("Subtract 1 minute, is greater than UtcNow? "
+ (result.AddMinutes(-1) > DateTime.UtcNow).ToString());
Console.WriteLine("Subtract 1 minute, is greater than Now? "
+ (result.AddMinutes(-1) > DateTime.Now).ToString());
Console.WriteLine("Subtract 1 minute, is less than UtcNow? "
+ (result.AddMinutes(-1) < DateTime.UtcNow).ToString());
Console.WriteLine("Subtract 1 minute, is less than Now? "
+ (result.AddMinutes(-1) < DateTime.Now).ToString());
Resulted in:
Kind: Unspecified
ToString(): 6/3/2010 10:17:05 PM
Add 1 minute, is greater than UtcNow? True
Add 1 minute, is greater than Now? True
Add 1 minute, is less than UtcNow? False
Add 1 minute, is less than Now? False
Subtract 1 minute, is greater than UtcNow? False
Subtract 1 minute, is greater than Now? True
Subtract 1 minute, is less than UtcNow? True
Subtract 1 minute, is less than Now? False
Compare this to DateTime.Now:
cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.Now));
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT val FROM testtbl";
var result = (DateTime)cmd.ExecuteScalar();
Console.WriteLine("Kind: " + result.Kind);
Console.WriteLine("ToString(): " + result.ToString());
Console.WriteLine("Add 1 minute, is greater than UtcNow? "
+ (result.AddMinutes(1) > DateTime.UtcNow).ToString());
Console.WriteLine("Add 1 minute, is greater than Now? "
+ (result.AddMinutes(1) > DateTime.Now).ToString());
Console.WriteLine("Add 1 minute, is less than UtcNow? "
+ (result.AddMinutes(1) < DateTime.UtcNow).ToString());
Console.WriteLine("Add 1 minute, is less than Now? "
+ (result.AddMinutes(1) < DateTime.Now).ToString());
Console.WriteLine("Subtract 1 minute, is greater than UtcNow? "
+ (result.AddMinutes(-1) > DateTime.UtcNow).ToString());
Console.WriteLine("Subtract 1 minute, is greater than Now? "
+ (result.AddMinutes(-1) > DateTime.Now).ToString());
Console.WriteLine("Subtract 1 minute, is less than UtcNow? "
+ (result.AddMinutes(-1) < DateTime.UtcNow).ToString());
Console.WriteLine("Subtract 1 minute, is less than Now? "
+ (result.AddMinutes(-1) < DateTime.Now).ToString());
Executed at 3:58 PM (local, -7h):
Kind: Unspecified
ToString(): 6/3/2010 3:59:26 PM
Add 1 minute, is greater than UtcNow? False
Add 1 minute, is greater than Now? True
Add 1 minute, is less than UtcNow? True
Add 1 minute, is less than Now? False
Subtract 1 minute, is greater than UtcNow? False
Subtract 1 minute, is greater than Now? False
Subtract 1 minute, is less than UtcNow? True
Subtract 1 minute, is less than Now? True
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With