如何在ADO.NET + SQL Server DateTime列的生命周期中处理时区?

Jon*_*vis 4 sql-server ado.net datetime utc sql-server-2008

使用SQL Server 2008.这是一个非常初级的问题,我可以真正使用一些精心设计的信息,但谷歌上的信息似乎相当多地围绕这个主题跳舞,如果有一些详细的说明如何工作将会很好. ..

假设我有一个datetime列,在ADO.NET中我将它设置为DateTime.UtcNow.

1)SQL Server是否相应地存储DateTime.UtcNow,还是根据服务器安装位置的时区再次对其进行偏移,然后在查询时将其返回偏移?我想我知道答案是"当然它存储它而不会再次抵消它"但是想要确定.

然后我查询它并将它从对象转换为DateTime,然后从IDataReader列获取它.据我所知,System.DateTime具有内部跟踪它是UTC日期时间还是偏移的DateTime的元数据,这可能会也可能不会导致.ToLocalTime()和.ToUniversalTime()具有不同的行为,具体取决于此状态.所以,

2)这个转换的System.DateTime对象是否已经知道它是一个UTC DateTime实例,还是假设它已经被偏移?


现在让我说我不使用UtcNow,我在执行ADO.NET INSERT或UPDATE时使用DateTime.Now.

3)ADO.NET是否将偏移量传递给SQL Server,SQL Server是否存储了带偏移量元数据的DateTime.Now?

然后我查询它并将其从IDataReader列转换为DateTime.

4)这个铸造的System.DateTime对象是否已经知道它是一个偏移时间,还是假设它是UTC?

Jon*_*vis 8

进行了一些单元测试,以回答我在所有四个部分中的问题.

1:SQL Server是否相应地存储DateTime.UtcNow,还是根据服务器安装位置的时区再次对其进行偏移,然后在查询时将其返回偏移?

执行此)):

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());
Run Code Online (Sandbox Code Playgroud)

当地时间下午1:30(-7h,或UTC时间晚上8:30)的结果是:

Jun  3 2010 8:30PM
Run Code Online (Sandbox Code Playgroud)

然后我尝试了这个:

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");
Run Code Online (Sandbox Code Playgroud)

在UTC时间晚上9点25分执行,它返回

Jun  3 2010 9:25PM
Run Code Online (Sandbox Code Playgroud)

将此与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");
Run Code Online (Sandbox Code Playgroud)

下午3:55(当地; -7h)执行,返回:

Jun  3 2010  3:55PM
Run Code Online (Sandbox Code Playgroud)

2:然后我查询它并在从IDataReader列中获取它之后将其从对象转换为DateTime.这个已转换的System.DateTime对象是否已知道它是一个UTC DateTime实例,还是假设它已被偏移?

都不是.

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());
Run Code Online (Sandbox Code Playgroud)

结果(在当地时间下午1点58分执行)是:

Kind: Unspecified
UTC: 6/4/2010 3:58:42 AM
Local: 6/3/2010 1:58:42 PM
Run Code Online (Sandbox Code Playgroud)

也就是说,.ToUniversalTime()最终从本地时间偏移到UTC时间不是一次而是两次(??),.ToLocalTime()最终完全没有偏移.

3:ADO.NET是否将偏移量传递给SQL Server,SQL Server是否存储了带偏移量元数据的DateTime.Now?

在不执行任何单元测试的情况下,已知答案是"仅使用DateTimeOffset"SQL类型.SQL datetime不做偏移.

4:这个铸造的System.DateTime对象是否已经知道它是一个偏移时间,还是假设它是UTC?

都不是.SQL的DateTimeOffset类型作为.NET DateTimeOffset结构返回.

以下在本地时间下午3:31执行以下列,其中column offval是datetimeoffset SQL类型,

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());
Run Code Online (Sandbox Code Playgroud)

这导致:

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
Run Code Online (Sandbox Code Playgroud)

令人惊讶的差距.


回过头来使用DateTime.Now而不是DateTime.UtcNow执行上面问题#1的测试,我验证了ADO.NET在存储到数据库之前不会转换为通用时间.

也就是说,这是在当地时间下午3:27(-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");
Run Code Online (Sandbox Code Playgroud)

.. 回 ..

Jun  3 2010  3:27PM
Run Code Online (Sandbox Code Playgroud)

在当地时间下午3:17执行此操作:

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());
Run Code Online (Sandbox Code Playgroud)

导致:

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
Run Code Online (Sandbox Code Playgroud)

将此与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());
Run Code Online (Sandbox Code Playgroud)

下午3:58(当地,-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
Run Code Online (Sandbox Code Playgroud)