58 votes

Les valeurs DateTime de Linq to SQL sont locales (Kind=Unspecified) - Comment les rendre UTC?

N'existe-t-il pas un moyen (simple) de dire aux classes Linq To SQL qu'une propriété DateTime particulière doit être considérée comme UTC (c'est-à-dire que la propriété Kind du type DateTime doit être Utc par défaut), ou existe-t-il une solution de contournement 'propre' ?

Le fuseau horaire de mon serveur d'application n'est pas le même que celui du serveur SQL 2005 (je ne peux pas les changer), et aucun des deux n'est UTC. Lorsque je persiste une propriété de type DateTime dans la base de données, j'utilise la valeur UTC (donc la valeur dans la colonne de la base de données est UTC), mais lorsque je lis les valeurs (en utilisant Linq To SQL), je vois que la propriété .Kind de la valeur DateTime est 'Non spécifiée'.

Le problème est que lorsque je la 'convertis' en UTC, elle affiche un décalage de 4 heures. Cela signifie également que lorsqu'elle est sérialisée, elle se retrouve du côté client avec un décalage de 4 heures (car elle est sérialisée en utilisant l'UTC).

37voto

RobSiklos Points 3324

Le code LinqToSql généré fournit des points d'extensibilité, vous permettant ainsi de définir des valeurs lorsque les objets sont chargés.

La clé est de créer une classe partielle qui étend la classe générée, puis d'implémenter la méthode partielle OnLoaded.

Par exemple, disons que votre classe est Personne, vous avez donc une classe Personne partielle générée dans Blah.designer.cs.

Étendez la classe partielle en créant une nouvelle classe (doit être dans un fichier différent), comme suit:

public partial class Personne {

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

28voto

Joe Points 60749

SQL Server DateTime ne contient aucune information sur le fuseau horaire ou le DateTimeKind, par conséquent les valeurs DateTime récupérées de la base de données ont correctement Kind = DateTimeKind.Unspecified.

Si vous voulez convertir ces horaires en UTC, vous devriez les 'convertir' comme suit :

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

ou l'équivalent :

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

Je suppose que votre problème est que vous essayez de les convertir comme suit :

DateTime utcDateTime = databaseDateTime.ToUniversalTime();

Cela peut sembler raisonnable à première vue, mais selon la documentation MSDN pour DateTime.ToUniversalTime, lors de la conversion d'un DateTime dont Kind est Unspecified :

L'objet DateTime actuel est supposé être un temps local, et la conversion est effectuée comme si Kind était Local.

Ce comportement est nécessaire pour assurer la compatibilité descendante avec .NET 1.x, qui n'avait pas de propriété DateTime.Kind.

5voto

Marc Gravell Points 482669

La seule façon à laquelle je peux penser pour faire cela serait d'ajouter une propriété de calage dans une classe partielle qui fait la traduction...

5voto

aarondcoleman Points 416

Pour notre cas, il était peu pratique de toujours spécifier DateTimeKind comme indiqué précédemment :

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

Nous utilisons Entity Framework, mais cela devrait être similaire à Linq-to-SQL

Si vous souhaitez forcer tous les objets DateTime sortant de la base de données à être spécifiés en tant qu'UTC, vous devrez ajouter un fichier de transformation T4 et ajouter une logique supplémentaire pour tous les objets DateTime et les objets DateTime nuls de telle sorte qu'ils soient initialisés en DateTimeKind.Utc

J'ai un article de blog qui explique cela étape par étape: http://www.aaroncoleman.net/post/2011/06/16/Forcing-Entity-Framework-to-mark-DateTime-fields-at-UTC.aspx

En bref :

1) Créez le fichier .tt pour votre modèle .edmx (ou .dbml pour Linq-to-SQL)

2) Ouvrez le fichier .tt et trouvez la méthode "WritePrimitiveTypeProperty".

3) Remplacez le code setter existant. C'est tout ce qui se trouve entre les appels de méthode ReportPropertyChanging et ReportPropertyChanged par ce qui suit :

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

3voto

goths Points 2412

@rob263 a fourni une méthode excellente.

Ceci est seulement une aide supplémentaire que je souhaite fournir si vous utilisez Entity Framework au lieu de Linq To Sql.

Entity Framework ne prend pas en charge l'événement OnLoaded.

A la place, vous pouvez faire ce qui suit :

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

            base.OnPropertyChanged(property);
        }

    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X