Linq到SQLdate时间值是本地(Kind = Unspecified) – 我如何使UTC?

是不是有一种(简单的)方法来告诉Linq To SQL类,一个特定的DateTime属性应该被认为是UTC(也就是说DateTimetypes的Kind属性默认是Utc),还是有一个“干净的”解决方法?

我的应用程序服务器上的时区与SQL 2005 Server不同(不能更改任何),而且没有任何是UTC。 当我将DateTimetypes的属性保存到dB时,我使用UTC值(因此db列中的值为UTC),但是当我读取值(使用Linq To SQL)时,我得到DateTime的.Kind属性价值是“未指定”。

问题是,当我“转换”到UTC是4个小时。 这也意味着当它被序列化的时候,它会在客户端结束,并且有一个4小时的错误偏移量(因为它是使用UTC序列化的)。

生成的LinqToSql代码提供了扩展点,所以您可以在加载对象时设置值。

关键是创build一个扩展生成的类的部分类,然后实现OnLoaded部分方法。

例如,假设你的类是Person ,所以你在Blah.designer.cs有一个生成的部分Person类。

通过创build一个新的类(必须在不同的文件中)来扩展部分类,如下所示:

 public partial class Person { partial void OnLoaded() { this._BirthDate = DateTime.SpecifyKind(this._BirthDate, DateTimeKind.Utc); } } 

SQL Server DateTime不包含任何时区或DateTimeKind信息,因此从数据库正确检索的DateTime值具有Kind = DateTimeKind.Unspecified。

如果你想使这些时间UTC,你应该“转换”他们如下:

 DateTime utcDateTime = new DateTime(databaseDateTime.Ticks, DateTimeKind.Utc); 

或相当的:

 DateTime utcDateTime = DateTime.SpecifyKind(databaseDateTime, DateTimeKind.Utc); 

我假设你的问题是,你正试图将它们转换如下:

 DateTime utcDateTime = databaseDateTime.ToUniversalTime(); 

这乍一看似乎是合理的,但根据DateTime.ToUniversalTime的MSDN文档 ,在转换Datetypes为Unspecified的DateTime时:

当前的DateTime对象被假定为本地时间,并且转换的执行类似Kind是Local。

此行为是与.NET 1.x向后兼容性所必需的,它没有DateTime.Kind属性。

我认为这样做的唯一方法就是在部分类中添加一个shim属性来完成翻译…

对于我们的情况,如前所述总是指定DateTimeKind是不切实际的:

 DateTime utcDateTime = DateTime.SpecifyKind(databaseDateTime, DateTimeKind.Utc); 

我们正在使用entity framework,但这应该类似于Linq-to-SQL

如果要强制所有从数据库中出来的DateTime对象指定为UTC,则需要添加一个T4转换文件,并为所有DateTime和可为空的DateTime对象添加其他逻辑,以使它们初始化为DateTimeKind.Utc

我有一个博客文章,这一步一步解释: http : //www.aaroncoleman.net/post/2011/06/16/Forcing-Entity-Framework-to-mark-DateTime-fields-at-UTC.aspx

简而言之:

1)为你的.edmx模型创build.tt文件(或者.dbml用于Linq-to-SQL)

2)打开.tt文件并find“WritePrimitiveTypeProperty”方法。

3)replace现有的setter代码。 这是ReportPropertyChangingReportPropertyChanged方法callback之间的所有内容,具体如下:

 <#+ if( ((PrimitiveType)primitiveProperty.TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.DateTime) { #> if(<#=code.FieldName(primitiveProperty)#> == new DateTime()) { <#=code.FieldName(primitiveProperty)#> = StructuralObject.SetValidValue(value<#=OptionalNullableParameterForSetValidValue(primitiveProperty, code)#>); <#+ if(ef.IsNullable(primitiveProperty)) { #> if(value != null) <#=code.FieldName(primitiveProperty)#> = DateTime.SpecifyKind(<#=code.FieldName(primitiveProperty)#>.Value, DateTimeKind.Utc); <#+ } else {#> <#=code.FieldName(primitiveProperty)#> = DateTime.SpecifyKind(<#=code.FieldName(primitiveProperty)#>, DateTimeKind.Utc); <#+ } #> } else { <#=code.FieldName(primitiveProperty)#> = StructuralObject.SetValidValue(value<#=OptionalNullableParameterForSetValidValue(primitiveProperty, code)#>); } <#+ } else { #> <#=code.FieldName(primitiveProperty)#> = StructuralObject.SetValidValue(value<#=OptionalNullableParameterForSetValidValue(primitiveProperty, code)#>); <#+ } #> 

@ rob263提供了一个很好的方法。

这只是一个额外的帮助,我希望提供如果您使用entity framework而不是Linq To Sql。

entity framework不支持OnLoaded事件。

相反,您可以执行以下操作:

  public partial class Person { protected override void OnPropertyChanged(string property) { if (property == "BirthDate") { this._BirthDate= DateTime.SpecifyKind(this._BirthDate, DateTimeKind.Utc); } base.OnPropertyChanged(property); } } 

我使用这种方式来即时指定DateTimeKind

 DateTime myDateTime = new DateTime(((DateTime)myUtcValueFromDb).Ticks, DateTimeKind.Utc); 

这个代码片段将允许您将从LINQ到SQL返回的DateTimes(Kind = Unspecified)转换为UTC时间,而不会影响时间。

 TimeZoneInfo UTCTimeZone = TimeZoneInfo.FindSystemTimeZoneById("UTC"); DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(myLinq2SQLTime, UTCTimeZone); 

有可能更干净的方法来做到这一点,但我有这个手,可以快速testing!

我不确定是否有办法让LINQ to SQL类透明地工作 – 您可能希望查看partial类,看看是否可以挂钩读取/写入值。

我希望UTC,TimeZone类可以为你做,如果你想在不同的时区之间转换,比TimeZoneInfo是你的。 使用TimeZoneInfo从我的代码示例:

 TimeZoneInfo cet = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time"); ac.add_datetime = TimeZoneInfo.ConvertTime(DateTime.Now, cet);