Linq to SQL DateTime值是本地的(Kind = Unspecified) - 如何使其成为UTC?

时间:2009-05-05 04:16:29

标签: c# sql-server linq-to-sql datetime

是否有一种(简单)方法告诉Linq To SQL类特定的DateTime属性应该被视为UTC(即默认情况下将DateTime类型的Kind属性设置为Utc),或者是否存在'干净'解决方法?

我的app-server上的时区与SQL 2005 Server不同(不能更改任何),没有一个是UTC。当我将类型为DateTime的属性保留为dB时,我使用UTC值(因此db列中的值为UTC),但是当我读回值时(使用Linq To SQL),我得到DateTime的.Kind属性值为'未指定'。

问题在于,当我将它'转换为UTC时,它是4小时关闭。这也意味着当它被序列化时,它最终在客户端以4小时的错误偏移(因为它使用UTC序列化)。

8 个答案:

答案 0 :(得分:37)

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

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

例如,假设您的课程为Person,因此您在Person中生成了部分Blah.designer.cs课程。

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

public partial class Person {

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

答案 1 :(得分:28)

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();

乍一看这似乎是合理的,但根据the MSDN documentation for DateTime.ToUniversalTime,转换未指定种类的DateTime时:

  

假定当前的DateTime对象   成为当地时间和转换   表现就好像是本地的。

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

答案 2 :(得分:5)

我能想到的唯一方法就是在部分类中添加一个shim属性来进行翻译......

答案 3 :(得分:5)

对于我们的情况,总是如前所述指定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模型创建.tt文件(或者为Linq-to-SQL创建.dbml)

2)打开.tt文件,找到“WritePrimitiveTypeProperty”方法。

3)替换现有的setter代码。这是ReportPropertyChangingReportPropertyChanged方法回调之间的所有内容,包含以下内容:

<#+ 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)#>);
<#+ 
        }
#>

答案 4 :(得分:3)

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

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

实体框架不支持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);
        }

    }

答案 5 :(得分:0)

我用这种方式动态指定 DateTimeKind

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

答案 6 :(得分:-1)

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

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

可能有更简洁的方法可以做到这一点,但我手头有这个可以快速测试!

我不确定是否有办法让它透明地使用LINQ to SQL类 - 您可能希望查看部分类,看看是否可以挂钩读取/写入值的位置。

答案 7 :(得分:-4)

我想要UTC,TimeZone类可以为你做,如果你想在不同的时区之间进行转换,比TimeZoneInfo适合你。以我的代码为例,使用TimeZoneInfo:

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