12
votes

Comment nous survivons en utilisant un fuseau horaire local avec Breeze

J'écris ceci pour recueillir des commentaires sur nos approches et aider, espérons-le, aider quelqu'un d'autre (et ma mémoire).

Scénario
  • Toutes nos bases de données utilisent DateTime Types de données sans aucune information sur le fuseau horaire.
  • International, nous savons que toutes les dates / temps de nos bases de données sont dans le temps local (Nouvelle-Zélande), pas UTC. Pour une application Web, ce n'est pas idéal mais nous ne contrôlons pas la conception de toutes ces bases de données car elles prennent en charge les autres systèmes (comptabilité, paie, etc.).
  • Nous utilisons un cadre d'entité (modèle en premier) pour l'accès aux données.

    Notre problème
    • Sans informations sur le fuseau horaire spécifiques La pile de brise / API / entité d'entité semble favoriser l'hypothèse de l'UTC, pas locale, ce qui est probablement pour le mieux mais qui ne convient pas à notre application.
    • Breeze aime passer des dates de retour au serveur au format UTC standard, en particulier dans les chaînes de requête (par exemple, clauses). Imaginez un contrôleur de brise qui expose directement une table de la base de données comme iquéryable. Le client Breeze passera n'importe quel filtre de date (où) clauses sur le serveur au format UTC. Frame-entité utilisera fidèlement ces dates pour créer une requête SQL, tout à fait incarcérateur que les dates de la table de base de données sont dans notre fuseau horaire local. Pour nous, cela signifie que les résultats sont entre 12 et 13 heures de compensation de ceux que nous souhaitons (en fonction des économies de la journée).

      Notre objectif est de vous assurer que notre code côté serveur (et la base de données) utilise systématiquement des dates dans notre fuseau horaire local et que toutes les requêtes renvoient les résultats souhaités.


1 commentaires

Aucun de cela n'est nécessaire si vous utilisez simplement un DateTimeOffset au lieu d'une date d'heure. Alors tout "fonctionne simplement"


3 Réponses :


15
votes

Notre solution Partie 1: Framework d'entité

Lorsque l'entité framework obtient DateTime code> de la base de données, il les définit sur DateTimeKind.unspecified code>. En d'autres termes, ni local ou utc. Nous voulions spécifiquement marquer nos dates comme denttimekind.local code>. P>

Pour atteindre cela, nous avons décidé de modifier le modèle de Framework d'entité qui génère les classes d'entité. Au lieu de nos dates étant une propriété simple, nous avons introduit une date de support de support et utilisait un ensemble de propriétés pour rendre la date local code> s'il était non spécifié code>. P> Dans le modèle (fichier .tt), nous avons remplacé ... p> xxx pré>

... avec ... p> xxx pré> Cela crée un set d'une ligne plutôt laid, mais cela fait du travail. Il utilise une fonction d'assistance pour défaut la date à un local code> qui ressemble à ceci: p> xxx pré>


notre solution Partie 2: filtres iquérissables H2>

Le problème suivant était la brise qui passe des dates UTC lors de l'application de où code> clauses à nos IQuiserisable code> Actions de contrôleur. Après avoir examiné le code de Breeze, Web API et Entity Framework, nous avons décidé que la meilleure option était d'intercepter les appels vers nos actions de contrôleur et d'échanger les dates UTC dans le QueryString code> avec des dates locales. P>

Nous avons choisi de faire cela à l'aide d'un attribut personnalisé que nous pourrions appliquer à nos actions de contrôleur tels que: p> xxx pré>

la classe mise en œuvre de cet attribut est la suivante: p> XXX PRE>


NOTRE SOLUTION PARTIE 3: JSON H2>

Ceci pourrait être plus controversé, mais notre public d'application Web est entièrement local aussi :). P>

Nous voulions que JSON soit envoyé au client contenant des dates / fois dans notre fuseau horaire local par défaut. Nous voulions aussi que les dates de JSON reçoivent du client à convertir à notre fuseau horaire local. Pour ce faire, nous avons créé un jsonlocaldateTimeMeconverter code> et échangé les installations de la brise JSON Converter Breeze. P>

Le convertisseur ressemble à ceci: p>

public class CustomBreezeConfig : Breeze.WebApi.BreezeConfig
{

    protected override JsonSerializerSettings CreateJsonSerializerSettings()
    {
        var baseSettings = base.CreateJsonSerializerSettings();

        // swap out the standard IsoDateTimeConverter that breeze installed with our own
        var timeConverter = baseSettings.Converters.OfType<IsoDateTimeConverter>().SingleOrDefault();
        if (timeConverter != null)
        {
            baseSettings.Converters.Remove(timeConverter);
        }
        baseSettings.Converters.Add(new JsonLocalDateTimeConverter());

        return baseSettings;
    }
}


2 commentaires

Pouah! Je ne peux pas encore penser à une meilleure alternative. Comment les composants Breeze.net pourraient-ils vous aussi faciles pour vous ... sans le faire pour vous? Garder tout ce que UTC haut et bas reste la meilleure défaillance de notre avis ... et comme vous semblez être d'accord. Mais vous ne serez pas les seuls dans cette liaison.


@Ward, merci de lire cela et de votre commentaire. Je pense que Breeze fait ce que cela devrait. Les fuseaux horaires sont juste une douleur. Ce serait génial si le client Breeze avait la possibilité d'envoyer des dates dans le fuseau horaire de la date javaScript, mais vous ne pouviez pas vous échapper avec simplement à l'aide de la date du navigateur.OrisoSostring () et ce serait ajouter beaucoup de code (et être moins standard »). Une option peut être de permettre à l'utilisateur de "brancher" une fonction de sérialisation de date de la clientèle de client comme un remplacement facultatif pour TOISOSOSTRING () . Ils pourraient alors utiliser une bibliothèque comme moment.js pour faire la conversion.



1
votes

J'ai atteint votre article et je voulais passer des informations. Un collègue a mis en œuvre votre solution et il a bien fonctionné pour tout utilisateur du fuseau horaire du serveur. Malheureusement, pour les utilisateurs situés en dehors du Timezone du serveur, il n'a pas fonctionné.

J'ai modifié votre classe de convertisseur pour faire usage de TimezoneInfo. Voici le code: xxx

La clé est: xxx

Cette variable est définie dans notre contexte utilisateur lors de la connexion. Lorsqu'un utilisateur connecte, nous transmettons les résultats de JSTimezondetect sur la demande de connexion et nous mettons cette information dans le contexte de l'utilisateur sur le serveur. Parce que nous avons un serveur Windows et JSTimeZoneDeTECT crachera un fuseau horaire Iana et que nous avons besoin d'une temporisation Windows, j'ai importé NuDA-Time Nuget dans notre solution et avec le code suivant, nous pouvons convertir un fuseau horaire Iana en une fusible Windows Timezone: xxx


0 commentaires

2
votes

Bien que je réalise que vous ne pourrez peut-être pas contrôler cela dans votre scénario, je crois qu'une autre solution à ce problème est d'utiliser le type DateTimeOffset plutôt que DateTime pour représenter la date / l'heure dans votre modèle d'entité.


0 commentaires