Travail correct avec date et heure. Suppression du décalage horaire de DateTimeOffset Serveur SQL de décalage de date nul

Presque tous les projets rencontrent des problèmes causés par une mauvaise manipulation et un mauvais stockage de la date et de l'heure. Même si le projet est utilisé dans le même fuseau horaire, vous pouvez toujours avoir de mauvaises surprises après le passage à l'heure d'hiver/d'été. Dans le même temps, peu de gens sont intrigués par la mise en œuvre du mécanisme correct dès le départ, car il semble que cela ne puisse poser aucun problème, puisque tout est trivial. Malheureusement, la réalité ultérieure montre que ce n’est pas le cas.

Logiquement, on peut distinguer les types de valeurs suivants liés à la date et à l'heure :


Considérons chaque point séparément, sans l'oublier.

Date et heure

Disons que le laboratoire qui a collecté le matériel à analyser est situé dans le fuseau horaire +2 et que la branche centrale, qui surveille l'achèvement des tests dans les délais, se trouve dans le fuseau +1. Les horaires donnés dans l'exemple ont été notés lors de la collecte du matériel par le premier laboratoire. La question se pose : quelle heure le bureau central doit-il voir ? Évidemment, le logiciel bureau central devrait afficher le 15 janvier 2014 12:17:15 - une heure de moins, puisque selon leur montre, l'événement s'est produit à ce moment précis.

Considérons l'une des chaînes d'actions possibles par lesquelles les données passent du client au serveur et inversement, ce qui permet de toujours afficher correctement la date/heure en fonction du fuseau horaire actuel du client :

  1. La valeur est créée sur le client, par exemple le 2 mars 2016 15 :13:36, le client est dans le fuseau horaire +2.
  2. La valeur est convertie en représentation sous forme de chaîne pour transmission au serveur - "2016-03-02T 15 :13:36+02:00”.
  3. Les données sérialisées sont envoyées au serveur.
  4. Le serveur désérialise l'heure en un objet date/heure, l'amenant à son fuseau horaire actuel. Par exemple, si le serveur fonctionne à +1, alors l'objet contiendra le 2 mars 2016 14 :13:36.
  5. Le serveur enregistre les données dans la base de données, mais elles ne contiennent aucune information sur le fuseau horaire - les types date/heure les plus couramment utilisés n'en savent tout simplement rien. Ainsi, le 2 mars 2016 sera enregistré dans la base de données 14 :13:36 dans un fuseau horaire "inconnu".
  6. Le serveur lit les données de la base de données et crée un objet correspondant avec la valeur 2 mars 2016 14 :13:36. Et comme le serveur fonctionne dans le fuseau horaire +1, cette valeur sera également interprétée dans le même fuseau horaire.
  7. La valeur est convertie en représentation sous forme de chaîne pour transmission au client - "2016-03-02T 14 :13:36+01:00”.
  8. Les données sérialisées sont envoyées au client.
  9. Le client désérialise la valeur reçue en un objet date/heure, en la convertissant dans son fuseau horaire actuel. Par exemple, s'il est -5, alors la valeur affichée devrait être le 2 mars 2016. 09 :13:36.
Tout semble intact, mais réfléchissons à ce qui pourrait mal se passer dans ce processus. En fait, des problèmes peuvent survenir à presque chaque étape.
  • L'heure sur le client peut être générée sans aucun fuseau horaire - par exemple, le type DateTime dans .NET avec DateTimeKind.Unspecified.
  • Le moteur de sérialisation peut utiliser un format qui n'inclut pas de décalage horaire.
  • Lors de la désérialisation en objet, le décalage horaire peut être ignoré, en particulier dans les désérialiseurs "faits maison" - à la fois sur le serveur et sur le client.
  • Lors de la lecture à partir d'une base de données, un objet date/heure peut être généré sans aucun fuseau horaire - par exemple, le type DateTime dans .NET avec DateTimeKind.Unspecified. De plus, avec DateTime dans .NET, en pratique, c'est exactement ce qui se produit si vous ne spécifiez pas explicitement un autre DateTimeKind immédiatement après la relecture.
  • Si les serveurs d'applications travaillant avec une base de données commune sont situés dans des fuseaux horaires différents, il y aura une sérieuse confusion dans les décalages horaires. La valeur date/heure écrite dans la base de données par le serveur A et lue par le serveur B sera sensiblement différente de la même valeur originale écrite par le serveur B et lue par le serveur A.
  • Le transfert de serveurs d'applications d'une zone à une autre entraînera une interprétation incorrecte des valeurs de date/heure déjà stockées.
Mais l'inconvénient le plus sérieux de la chaîne décrite ci-dessus est l'utilisation d'un fuseau horaire local sur le serveur. S'il n'y a pas de passage à l'heure d'été/d'hiver, il n'y aura pas de problèmes supplémentaires. Mais sinon, vous risquez d’avoir bien des surprises désagréables.

Les règles de passage à l'heure d'été/d'hiver sont à proprement parler variables. Différents pays peuvent modifier leurs règles de temps à autre, et ces modifications doivent être intégrées aux mises à jour du système bien à l'avance. En pratique, nous avons rencontré à plusieurs reprises des situations dans lesquelles ce mécanisme ne fonctionnait pas correctement, qui ont finalement été résolues en installant des correctifs ou des correctifs. système opérateur, ou utilisé des bibliothèques tierces. La probabilité que les mêmes problèmes se reproduisent n’est pas nulle, il est donc préférable de disposer d’un moyen de garantir qu’ils soient évités.

En tenant compte des considérations décrites ci-dessus, nous formulerons l'approche la plus fiable et la plus simple pour transmettre et stocker l'heure : sur le serveur et dans la base de données, toutes les valeurs doivent être converties au fuseau horaire UTC.

Regardons ce que nous donne cette règle :

  • Lors de l'envoi de données au serveur, le client doit transmettre le décalage horaire afin que le serveur puisse convertir correctement l'heure en UTC. Une option alternative consiste à forcer le client à effectuer cette conversion, mais la première option est plus flexible. Lors de la réception des données du serveur, le client convertira la date et l'heure dans son fuseau horaire local, sachant que dans tous les cas, il recevra l'heure en UTC.
  • Il n'y a pas de transitions entre l'heure d'été et l'heure d'hiver en UTC, les problèmes associés à cela ne seront donc pas pertinents.
  • Sur le serveur, lors de la lecture de la base de données, vous n'avez pas besoin de convertir les valeurs temporelles ; il vous suffit d'indiquer explicitement qu'elles correspondent à l'UTC. Dans .NET, par exemple, cela peut être réalisé en définissant DateTimeKind sur l'objet time sur DateTimeKind.Utc.
  • La différence de fuseaux horaires entre les serveurs travaillant avec une base de données commune, ainsi que le transfert des serveurs d'une zone à une autre, n'affecteront en rien l'exactitude des données reçues.
Pour mettre en œuvre une telle règle, il suffit de veiller à trois choses :
  1. Créez le mécanisme de sérialisation et de désérialisation de telle sorte que les valeurs de date/heure soient correctement traduites de UTC vers le fuseau horaire local et inversement.
  2. Assurez-vous que le désérialiseur côté serveur crée des objets date/heure au format UTC.
  3. Assurez-vous que lors de la lecture à partir de la base de données, les objets date/heure sont créés en UTC. Cet élément est parfois fourni sans modification du code - simplement le fuseau horaire du système sur tous les serveurs est défini sur UTC.
Les considérations et recommandations ci-dessus fonctionnent très bien quand deux conditions sont réunies:
  • La configuration système requise n'exige pas que l'heure locale et/ou le décalage horaire soient affichés exactement tels qu'ils ont été stockés. Par exemple, les billets d’avion doivent imprimer les heures de départ et d’arrivée dans le fuseau horaire correspondant à l’emplacement de l’aéroport. Ou si le serveur imprime des factures créées dans différents pays, chacune doit se retrouver avec l'heure locale et non convertie dans le fuseau horaire du serveur.
  • Toutes les valeurs de date et d'heure dans le système sont "absolues" - c'est-à-dire décrire un moment dans le futur ou dans le passé qui a une seule valeur en UTC. Par exemple, « le lanceur a eu lieu à 23h00, heure de Kiev » ou « la réunion aura lieu de 13h30 à 14h30, heure de Minsk ». Les numéros de ces événements seront différents selon les fuseaux horaires, mais ils décriront le même moment dans le temps. Mais il peut arriver que les conditions requises pour logiciel impliquent l'heure locale "relative" dans certains cas. Par exemple, « ce programme télévisé sera diffusé de 9h00 à 10h00 dans tous les pays où il existe une chaîne de télévision affiliée ». Il s'avère que la diffusion d'un programme n'est pas un événement, mais plusieurs, et potentiellement tous peuvent se produire à différentes périodes de temps à une échelle « absolue ».
Dans les cas où la première condition n'est pas respectée, le problème peut être résolu en utilisant des types de données contenant le fuseau horaire - à la fois sur le serveur et dans la base de données. Vous trouverez ci-dessous une petite liste d'exemples pour différentes plates-formes et SGBD.
.FILET DateHeureDécalage
Java org.joda.time.DateTime, java.time.ZonedDateTime
MSSQL décalage date/heure
Oracle, PostgreSQL HORODATAGE AVEC FUSEAU HORAIRE
MySQL

La violation de la deuxième condition est un cas plus complexe. Si cette heure « relative » doit être stockée simplement pour l'affichage et qu'il n'existe aucune tâche pour déterminer le moment « absolu » où l'événement s'est produit ou se produira pour un fuseau horaire donné, il suffit de désactiver simplement la conversion de l'heure. Par exemple, l'utilisateur a saisi le début du programme pour toutes les branches de la société de télévision le 25 mars 2016 à 9h00, et celui-ci sera transmis, stocké et affiché sous cette forme. Mais il peut arriver que certains programmateurs effectuent automatiquement des actions spéciales une heure avant le début de chaque programme (envoyer des notifications ou vérifier la présence de certaines données dans la base de données de la société de télévision). Implémenter un tel planificateur de manière fiable n’est pas une tâche triviale. Disons que le planificateur sait dans quel fuseau horaire se trouve chaque succursale. Et l'un des pays où se trouve une succursale décide de changer de fuseau horaire après un certain temps. Le cas n'est pas aussi rare qu'il y paraît - au cours de cette année et des deux années précédentes, j'ai dénombré plus de 10 événements similaires (http://www.timeanddate.com/news/time/). Il s'avère que soit les utilisateurs doivent maintenir les liaisons de fuseau horaire à jour, soit le planificateur doit automatiquement extraire ces informations de sources globales telles que Google Cartes API de fuseau horaire. Je ne m'engage pas à proposer une solution universelle à de tels cas, je noterai simplement que de telles situations nécessitent une étude sérieuse.

Comme on peut le voir ci-dessus, il n’existe pas d’approche unique qui couvre 100 % des cas. Par conséquent, vous devez d'abord comprendre clairement, à partir des exigences, laquelle des situations mentionnées ci-dessus se produira dans votre système. Très probablement, tout se limitera à la première approche proposée avec stockage en UTC. Eh bien, les situations exceptionnelles décrites ne l’annulent pas, mais ajoutent simplement d’autres solutions pour des cas particuliers.

Date sans heure

Disons que nous avons réglé l’affichage correct de la date et de l’heure en tenant compte du fuseau horaire du client. Passons aux dates sans heure et à l'exemple donné pour ce cas au début - "le nouveau contrat entre en vigueur le 2 février 2016". Que se passera-t-il si les mêmes types et le même mécanisme sont utilisés pour des valeurs telles que pour les dates et heures « normales » ?

Toutes les plates-formes, langages et SGBD n'ont pas de types de date uniquement. Par exemple, dans .NET, il n’existe que le type DateTime, il n’existe pas de type « juste Date » distinct. Même si seule une date a été spécifiée lors de la création d'un tel objet, l'heure est toujours présente et elle est égale à 00:00:00. Si l'on transfère la valeur « 2 février 2016 00:00:00 » d'une zone avec un décalage de +2 à +1, nous obtenons « 1er février 2016 23:00:00 ». Pour l'exemple ci-dessus, cela équivaudrait au nouveau contrat débutant le 2 février dans un fuseau horaire et le 1er février dans l'autre. D’un point de vue juridique, c’est absurde et, bien entendu, cela ne devrait pas être le cas. Règle générale pour les dates « pures », c'est extrêmement simple - de telles valeurs ne doivent être converties à aucune étape de sauvegarde et de lecture.

Il existe plusieurs façons d'éviter la conversion des dates :

  • Si la plateforme prend en charge un type qui représente une date sans heure, alors cela doit être utilisé.
  • Ajoutez un attribut spécial aux métadonnées de l'objet qui indiquera au sérialiseur que le fuseau horaire doit être ignoré pour une valeur donnée.
  • Transmettez la date du client et inversement sous forme de chaîne et stockez-la sous forme de date. Cette approche est peu pratique si vous devez non seulement afficher la date sur le client, mais également y effectuer certaines opérations : comparaison, soustraction, etc.
  • Transmettez et stockez sous forme de chaîne, puis convertissez-le en date uniquement pour le formatage en fonction des paramètres régionaux du client. Elle présente encore plus d'inconvénients que l'option précédente - par exemple, si des parties de la date dans la chaîne stockée ne sont pas dans l'ordre « année, mois, jour », il sera alors impossible d'effectuer une recherche indexée efficace par plage de dates.
Vous pouvez bien sûr essayer de donner un contre-exemple et dire que le contrat n'a de sens que dans le pays dans lequel il est conclu ; le pays est dans le même fuseau horaire, et donc le moment où il entre en vigueur peut être déterminé sans ambiguïté. Mais même dans ce cas, les utilisateurs d'autres fuseaux horaires ne seront pas intéressés par le moment où cet événement se produira dans leur heure locale. Et même s'il était nécessaire d'afficher ce moment dans le temps, il faudrait alors afficher non seulement la date, mais aussi l'heure, ce qui contredit l'état d'origine.

Intervalle de temps

Avec le stockage et le traitement des intervalles de temps, tout est simple : leur valeur ne dépend pas du fuseau horaire, il n'y a donc pas de recommandations particulières ici. Ils peuvent être stockés et transmis sous forme d'un nombre d'unités de temps (entier ou virgule flottante, selon la précision recherchée). Si la précision à la seconde est importante, alors en nombre de secondes, si la précision à la milliseconde est importante, alors en nombre de millisecondes, etc.

Mais le calcul de l’intervalle peut comporter des pièges. Disons que nous avons un exemple de code C# qui calcule l'intervalle de temps entre deux événements :

DateTime start = DateTime.Now ; //... DateTime end = DateTime.Now; heures doubles = (fin - début).TotalHours;
À première vue, il n'y a aucun problème ici, mais ce n'est pas le cas. Premièrement, il peut y avoir des problèmes avec les tests unitaires d'un tel code, mais nous en reparlerons un peu plus tard. Deuxièmement, imaginons que le moment initial tombe à l'heure d'hiver et que le moment final tombe à l'heure d'été (par exemple, c'est ainsi que le nombre d'heures de travail est mesuré et les travailleurs ont un quart de nuit).

Supposons que le code s'exécute dans un fuseau horaire dans lequel l'heure d'été en 2016 a lieu dans la nuit du 27 mars, et simulons la situation décrite ci-dessus :

DateTime start = DateTime.Parse("2016-03-26T20:00:15+02"); DateTime end = DateTime.Parse("2016-03-27T05:00:15+03"); heures doubles = (fin - début).TotalHours;
Ce code donnera lieu à 9 heures, même si en réalité 8 heures se sont écoulées entre ces instants. Vous pouvez facilement le vérifier en modifiant le code comme ceci :

DateTime start = DateTime.Parse("2016-03-26T20:00:15+02").ToUniversalTime(); DateTime end = DateTime.Parse("2016-03-27T05:00:15+03").ToUniversalTime(); heures doubles = (fin - début).TotalHours;
D'où la conclusion : toute opération arithmétique avec la date et l'heure doit être effectuée en utilisant soit des valeurs UTC, soit des types qui stockent les informations de fuseau horaire. Et puis revenez au local si nécessaire. De ce point de vue, l'exemple original peut être facilement corrigé en remplaçant DateTime.Now par DateTime.UtcNow.

Cette nuance ne dépend pas d’une plateforme ou d’un langage spécifique. Voici un code similaire en Java qui pose le même problème :

LocalDateTime start = LocalDateTime.now(); //... LocalDateTime end = LocalDateTime.now(); longues heures = ChronoUnit.HOURS.between(start, end);
Cela peut également être facilement corrigé, par exemple en utilisant ZonedDateTime au lieu de LocalDateTime.

Calendrier des événements programmés

La planification d'événements planifiés est une situation plus complexe. Il n'existe pas de type universel permettant de stocker les horaires dans des bibliothèques standard. Mais une telle tâche n’est pas très rare, des solutions toutes faites peuvent donc être trouvées sans problème. Un bon exemple est le format du planificateur cron, qui est utilisé sous une forme ou une autre par d'autres solutions, comme Quartz : http://quartz-scheduler.org/api/2.2.0/org/quartz/CronExpression.html. Il couvre presque tous les besoins de planification, y compris des options telles que le « deuxième vendredi du mois ».

Dans la plupart des cas, cela n'a aucun sens d'écrire votre propre planificateur, car il existe des solutions flexibles et éprouvées, mais si, pour une raison quelconque, il est nécessaire de créer votre propre mécanisme, alors au moins le format de planification peut être emprunté. de cron.

En plus des recommandations décrites ci-dessus concernant le stockage et le traitement des différents types de valeurs temporelles, il y en a plusieurs autres que je voudrais également mentionner.

Premièrement, concernant l'utilisation de membres de classe statiques pour obtenir l'heure actuelle - DateTime.UtcNow, ZonedDateTime.now(), etc. Comme cela a été dit, leur utilisation directement dans le code peut sérieusement compliquer les tests unitaires, car sans cadres moqueurs spéciaux, il ne sera pas possible de remplacer l'heure actuelle. Par conséquent, si vous envisagez d'écrire des tests unitaires, vous devez vous assurer que l'implémentation de telles méthodes peut être remplacée. Il existe au moins deux manières de résoudre ce problème :

  • Fournissez une interface IDateTimeProvider avec une méthode unique qui renvoie l'heure actuelle. Ajoutez ensuite une dépendance sur cette interface dans toutes les unités de code où vous devez obtenir l'heure actuelle. Lors de l'exécution normale du programme, l'implémentation « par défaut » sera injectée à tous ces endroits, qui renvoie l'heure actuelle réelle, et dans les tests unitaires - toute autre implémentation nécessaire. Cette méthode est la plus flexible du point de vue des tests.
  • Créez votre propre classe statique avec une méthode pour obtenir l'heure actuelle et la possibilité d'installer toute implémentation de cette méthode de l'extérieur. Par exemple, dans le cas du code C#, cette classe peut exposer la propriété UtcNow et la méthode SetImplementation(Func) implicite). L'utilisation d'une propriété ou d'une méthode statique pour obtenir l'heure actuelle élimine le besoin de spécifier explicitement une dépendance sur une interface supplémentaire partout, mais du point de vue des principes de la POO, ce n'est pas une solution idéale. Cependant, si pour une raison quelconque l'option précédente ne convient pas, vous pouvez utiliser celle-ci.
Un problème supplémentaire qui devrait être résolu lors du passage à votre implémentation du fournisseur de temps actuel est de s'assurer que personne ne continue à utiliser les classes standard à l'ancienne. Cette tâche est facile à résoudre dans la plupart des systèmes de contrôle qualité du code. Essentiellement, cela revient à rechercher une sous-chaîne « indésirable » dans tous les fichiers à l'exception de celui dans lequel l'implémentation « par défaut » est déclarée.

La deuxième mise en garde concernant l'obtention de l'heure actuelle est que on ne peut pas faire confiance au client. L'heure actuelle sur les ordinateurs des utilisateurs peut être très différente de l'heure réelle, et s'il y a une logique qui y est liée, alors cette différence peut tout gâcher. Tous les endroits où il est nécessaire d'obtenir l'heure actuelle doivent, si possible, être effectués côté serveur. Et, comme mentionné précédemment, toutes les opérations arithmétiques avec le temps doivent être effectuées soit en valeurs UTC, soit en utilisant des types qui stockent le décalage horaire.

Et une autre chose que je voulais mentionner est la norme ISO 8601, qui décrit le format de date et d'heure pour l'échange d'informations. En particulier, la représentation sous forme de chaîne de la date et de l'heure utilisée dans la sérialisation doit être conforme à cette norme pour éviter d'éventuels problèmes de compatibilité. Dans la pratique, il est extrêmement rare que vous deviez implémenter le formatage vous-même, la norme elle-même peut donc être utile principalement à des fins d'information.

Balises : Ajouter des balises

L'option humaine ([vous devez vous inscrire pour voir le lien]).

L'essence du problème est la suivante.. Si vous avez accidentellement déployé une base de données sur un serveur SQL avec un « décalage de date » de 0, alors un problème survient lorsque la base de données contient un attribut de type TIME, c'est-à-dire cet attribut est défini sur 01. /01/0001 10:30:00 ou date enregistrée vide 01.01.0001 00:00:00. Lors de l'enregistrement de tels détails, ils ne sont pas enregistrés.
Sur Internet, ils suggèrent de créer une nouvelle base de données avec un décalage de 2000.
Mais je n’avais pas vraiment envie de créer une nouvelle base. Et modifiez le chemin d'accès à la base de données pour tous les utilisateurs.
Ensuite, j'ai suivi le chemin pour savoir où cette valeur est stockée dans SQL. Je l'ai trouvé et je l'ai changé en 2000 et tout allait bien.
Et maintenant, étape par étape, où changer.

Expulsez tous les utilisateurs.

ATTENTION!!!

1. Faites-le d'abord copie de sauvegarde au moyen de 1C c'est-à-dire téléchargez-le sur *.dt
Cela doit être fait avant de modifier le « décalage »
Si cela n'est pas fait, alors dans toute votre base de données il y aura des références, des documents, etc.
où y a-t-il une condition requise, la date sera disons 02.10.0009
CE QUI N'EST PAS AUTORISÉ...
Vous avez donc téléchargé sur *.dt

2. Accédez à SQL Server Management Studio
Trouvez votre base dans la liste et appuyez sur le signe plus.
Recherchez-y le dossier « Tables » et ouvrez-le.
Un tas de tables s'ouvriront, allez tout en bas, trouvez la table
_YearOffset, placez-vous dessus et sélectionnez l'élément « Ouvrir la table » avec le bouton droit, voir Fig. 1
Changez la valeur 0 en 2000
Fermer SQL Server Management Studio

3. Accédez au configurateur et chargez la base de données précédemment enregistrée.

Si cela n’est pas fait, alors toutes les dates seront avec l’année 0009.
Une fois la base de données chargée... Vous pouvez accéder à 1C et vous assurer que les dates sont normales.
Le résultat est que nous avons changé le « décalage de date de 0 à 2000 »

Il arrive parfois que cette option ne puisse pas être utilisée pour une raison ou une autre. Ensuite, il existe une option plus hardcore ([vous devez vous inscrire pour voir le lien]) :

Déclarez le curseur TablesAndFields pour

SELECT objets.nom comme nom de table, colonnes.nom comme nom de colonne
DE dbo.sysobjects comme objets
rejoindre à gauche dbo.syscolumns en tant que colonnes sur objects.id = columns.id
où objets.xtype = "U" et colonnes.xtype = 61

ouvrir TablesEtFields

PENDANT que @@FETCH_STATUS = 0
COMMENCER Exécuteur(" mise à jour"+ @NomTable + "
ensemble " + @NomColonne + " = ""2000- 01- 01 00:00:00" "
où " + @NomColonne + " > ""3999- 12- 31 23:59:59" "")

Ceci est exécuté tant que la récupération précédente réussit.
RÉCUPÉRER NEXT FROM TablesAndFields dans @TableName, @ColumnName
FIN

fermer TablesEtFields
deallocateTablesAndFields
aller

Avant toute manipulation, n'oubliez pas de faire des copies des bases de données !

Le problème n'a rien à voir avec la base de données. Si vous définissez un point d'arrêt ou entrez une sortie quelque part, vous devriez pouvoir voir le décalage être capturé peu de temps après ce code :

TestDateAndTime = testDateAndTime.DateTime.Date ;

Décomposons-le :

  • Vous avez commencé avec la valeur DateTimeOffset 2008-05-01T08:06:32+01:00
  • Vous avez ensuite appelé .DateTime , ce qui a donné la valeur DateTime 2008-05-01T08:06:32 avec DateTimeKind.Unspecified .
  • Vous avez ensuite appelé .Date , ce qui a abouti à une valeur DateTime de 2008-05-01T00:00:00 avec DateTimeKind.Unspecified .
  • Vous attribuez le résultat à testDateAndTime , qui est de type DateTimeOffset . Cela provoque une conversion implicite de DateTime en DateTimeOffset - , qui s'applique locale fuseau horaire. Dans votre cas, il semble que le décalage de cette valeur dans votre fuseau horaire local soit -04:00 , donc la valeur résultante est DateTimeOffset de 2008-05-01T00:00:00-04:00 comme vous l'avez décrit.

Vous avez dit:

Le but ultime est simplement d’avoir une date sans décalage horaire ni fuseau horaire.

Eh bien, il y a actuellement pas un type de données C# natif, qui est juste une date sans heure. Il existe un type Date pur dans. Système.Heure package dans corefxlab, mais il n'est pas encore tout à fait prêt pour une application de production typique. Il existe un LocalDate dans la bibliothèque Noda Time que vous pouvez utiliser aujourd'hui, mais vous devrez toujours le reconvertir en un type natif avant de l'enregistrer dans la base de données. En attendant, le mieux que vous puissiez faire est de :

  • Modifiez votre serveur SQL pour utiliser le type de date dans le champ.
  • Dans votre code .NET, utilisez DateTime avec l'heure 00:00:00 et DateTimeKind.Unspecified. Il faut penser à ignorer la partie horaire (puisqu'il existe effectivement des dates sans minuit local dans certains fuseaux horaires).
  • Modifiez l'accessoire de test pour qu'il soit un DateTime plutôt qu'un DateTimeOffset.

En général, même si DateTimeOffset convient à un grand nombre de scénarios (par exemple horodatagesévénements), cela ne convient pas bien aux valeurs de date uniquement.

Je veux date actuelle avec décalage nul.

Si tu je veux vraiment c'est comme DateTimeOffset , vous pouvez faire :

TestDateAndTime = new DateTimeOffset(testDateAndTime.Date, TimeSpan.Zero);

Cependant, je ne recommande pas de faire cela. En faisant cela, vous prenez locale la date de la valeur d'origine et affirmez qu'elle est en UTC. Si le décalage d'origine est autre que zéro, ce sera une fausse déclaration. Cela entraînera par la suite d'autres erreurs puisque vous parlez en réalité d'un moment différent (avec potentiellement une date différente) de celui que vous avez créé.

Concernant la question supplémentaire posée à votre conseil d'administration. La spécification de DateTimeKind.Utc modifie le comportement de la distribution implicite. Au lieu d'utiliser le fuseau horaire local, l'heure UTC est utilisée, qui présente toujours un décalage zéro. Le résultat est le même que le point de vue plus explicite que j'ai donné ci-dessus. Je déconseille toujours cela pour les mêmes raisons.

Prenons un exemple commençant par 2016-12-31T22:00:00-04:00. Selon votre approche, vous devez enregistrer 2016-12-31T00:00:00+00:00 dans la base de données. Cependant, ce sont deux moments différents. Le premier, normalisé en UTC, serait 2017-01-01T02:00:00+00:00, et le second, converti dans un autre fuseau horaire, serait 2016-12-30T20:00:00-04:00. Veuillez noter le changement de dates dans la conversion. Ce n'est probablement pas le comportement que vous souhaiteriez dans votre application.

DateTimeOffset testDateAndTime = new DateTimeOffset(2008, 5, 1, 8, 6, 32, new TimeSpan(1, 0, 0));

//NETTOYER L'HEURE ET LA DATE testDateAndTime = testDateAndTime.DateTime.Date;

var datesTableEntry = db.DatesTable.First(dt => dt.Id == someTestId);

datesTableEntry.test= testDateAndTime ;

db.SaveChangesAsync(); RÉSULTAT DANS LA BASE DE DONNÉES : 2008-05-01 00:00:00.0000000 -04:00

Comment activer -4h00 à +00h00 (à partir du code avant de sauvegarder) ?

J'ai essayé:

Tâche publique

SetTimeZoneOffsetToZero(DateTimeOffset dateTimeOffSetObj) ( TimeSpan zeroOffsetTimeSpan = new TimeSpan(0, 0, 0, 0, 0); return dateTimeOffSetObj.ToOffset(zeroOffsetTimeSpan); )

Il ne fait rien.

J'étais sûr que le SpecifyKind spécifierait mon dateTimeOffset, comme changer à la fois le décalage de l'heure et du fuseau horaire, mais une fois testé, il semble simplement changer le décalage du fuseau horaire, ce que je veux. Y a-t-il un problème avec ça ?

1 réponse

Le problème n'a rien à voir avec la base de données. Si vous définissez un point d'arrêt ou enregistrez une sortie quelque part, vous devriez voir le décalage être lié peu de temps après ce code :

TestDateAndTime = testDateAndTime.DateTime.Date ;

Décomposons-le :

  • Vous avez commencé avec DateTimeOffset 2008-05-01T08:06:32+01:00
  • Vous avez ensuite appelé .DateTime , ce qui a donné une valeur DateTime de 2008-05-01T08:06:32 avec DateTimeKind.Unspecified .
  • Vous avez ensuite appelé .Date , ce qui a abouti à une valeur DateTime de 2008-05-01T00:00:00 avec DateTimeKind.Unspecified .
  • Vous renvoyez le résultat dans testDateAndTime qui est de type DateTimeOffset . Cela provoque une conversion implicite de DateTime en DateTimeOffset qui applique le fuseau horaire local. Dans votre cas, il semble que le décalage de cette valeur dans votre fuseau horaire local soit -04:00 , donc la valeur résultante est DateTimeOffset 2008-05-01T00:00:00-04:00 comme vous l'avez décrit,

Vous avez dit:

L’objectif final est simplement d’avoir une date sans décalage horaire ni fuseau horaire.

Eh bien, il n’existe actuellement aucun type de données C# natif qui soit simplement une date sans heure. Il existe un type Date pur dans le package System.Time de corefxlab, mais il n'est pas tout à fait prêt pour une application de production typique. Il y a LocalDate dans la bibliothèque de temps de Noda, que vous pouvez utiliser aujourd'hui, mais vous devrez toujours le reconvertir en un type natif avant de le stocker dans la base de données. En attendant, le mieux que vous puissiez faire est de :

  • Modifiez votre serveur SQL pour utiliser le type de date dans ce champ.
  • Dans votre code .NET, utilisez DateTime avec l'heure 00:00:00 et DateTimeKind.Unspecified. Il faut penser à ignorer la partie horaire (puisqu'il existe effectivement des dates sans minuit local dans certains fuseaux horaires).
  • Modifiez l'alerte de test pour qu'elle soit DateTime plutôt que DateTimeOffset.

En général, même si DateTimeOffset convient à grande quantité Dans certains scénarios (tels que les événements d'horodatage), il ne convient pas aux valeurs de date uniquement.

Je veux la date actuelle avec un décalage nul.

Si vous le voulez vraiment comme DateTimeOffset, vous feriez :

TestDateAndTime = new DateTimeOffset(testDateAndTime.Date, TimeSpan.Zero);

Cependant, je déconseille cela. En faisant cela, vous prenez la date locale de la valeur d'origine et affirmez qu'elle est en UTC. Si le décalage d'origine est autre que zéro, ce sera une fausse déclaration. Cela entraînera par la suite d'autres erreurs puisque vous parlez en réalité d'un moment différent (avec potentiellement une date différente) de celui que vous avez créé.

Concernant la question supplémentaire posée dans votre modification, la spécification de DateTimeKind.Utc modifie le comportement de la distribution implicite. Au lieu d'utiliser le fuseau horaire local, l'heure UTC est utilisée, qui présente toujours un décalage zéro. Le résultat est le même que le point de vue plus explicite que j'ai donné ci-dessus. Je déconseille toujours cela pour les mêmes raisons.

Regardons l'exemple du démarrage à partir du 2016-12-31T22:00:00-04:00. Selon votre approche, vous devez enregistrer 2016-12-31T00:00:00+00:00 dans la base de données. Cependant, ce sont deux moments différents. Le premier, normalisé en UTC, serait 2017-01-01T02:00:00+00:00, et le second, converti dans un autre fuseau horaire, serait 2016-12-30T20:00:00-04:00. Veuillez noter le changement de dates dans la conversion. Ce n'est probablement pas le comportement que vous souhaiteriez dans votre application.