Travail correct avec date et heure. Fonctionne correctement avec la date et l'heure Décalage zéro des dates du serveur SQL

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

L'essence du problème est que .. 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 survenait lorsqu'un attribut de type TIME est rencontré dans la base de données, c'est-à-dire 01.01.001 10 :30:00 ou la date est définie dans cet attribut signé vide 01.01.001 00:00:00. Lors de l'enregistrement d'un tel attribut, il n'est pas enregistré.
Sur Internet, ils proposent de créer une nouvelle base 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, et où est cette valeur stockée dans SQL. Trouvé et changé en 2000 et tout était ok ..
Et maintenant, étape par étape, où changer.

Expulsez tous les utilisateurs.

ATTENTION!!!

1. Faites d'abord sauvegarde au moyen de 1C c'est-à-dire déchargez-le dans * .dt
Cela doit être fait avant de changer le "décalage"
Si cela n'est pas fait, alors dans toute votre base de données, références, doc, etc.
où il y a un accessoire, la date sera par exemple 02.10.0009
CE QUI N'EST PAS PERMIS….
Vous avez donc effectué un téléchargement vers * .dt

2. Accédez à SQL Server Management Studio
Trouvez votre base dans la liste, cliquez sur le signe plus.
Trouvez le papa "Tables" là-bas et ouvrez-le.
Un tas de tables s'ouvrira, va tout en bas, trouve la table
_YearOffset, nous nous tenons dessus et sélectionnons l'élément "Ouvrir la table" avec la touche droite, voir Fig. 1
Changer la valeur 0 à 2000
Fermer SQL Server Management Studio

3. Allez dans le 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.
Après le chargement de la base de données... Vous pouvez aller à 1C et vous assurer que les dates sont normales.
Nous avons changé le résultat "décalage de date de 0 à 2000"

Parfois, il arrive 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 afficher le lien]):

Déclarer le curseur TablesAndFields pour

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

ouvrir TablesEtChamps

WHILE @@ FETCH_STATUS = 0
COMMENCER Exec (" mettre à 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.
FETCH NEXT FROM TablesAndFields dans @TableName, @ColumnName
FINIR

fermer TablesEtChamps
désallouer TablesEtChamps
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 épinglé peu de temps après ce code :

TestDateAndTime = testDateAndTime.DateTime.Date;

Décomposons ceci :

  • Vous avez commencé avec la valeur DateTimeOffset 2008-05-01T08 : 06 : 32 + 01 : 00
  • Ensuite, vous avez appelé DateTime, ce qui a donné une valeur DateTime 2008-05-01T08: 06: 32 avec DateTimeKind.Unspecified.
  • Ensuite, vous avez appelé Date, ce qui a donné une valeur DateTime 2008-05-01T00 : 00 : 00 avec DateTimeKind.Unspecified.
  • Vous affectez le résultat à testDateAndTime, qui est de type DateTimeOffset. Cela appelle un transtypage implicite de DateTime vers DateTimeOffset - qui s'applique local Fuseau horaire... Dans votre cas, le décalage pour cette valeur dans votre fuseau horaire local semble être -04 : 00, donc la valeur résultante est le DateTimeOffset de 2008-05-01T00 : 00 : 00-04 : 00 comme vous l'avez décrit.

Vous avez dit:

L'objectif final est d'avoir simplement 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 y a un type Date pur dans Le temps du système package dans corefxlab, mais il n'est pas encore tout à fait prêt pour une application de production typique. Il y a LocalDate dans la bibliothèque Noda Time, que vous pouvez utiliser aujourd'hui, mais vous devez toujours reconvertir en un type natif avant d'enregistrer dans la base de données. Donc, en même temps, la meilleure chose que vous puissiez faire est de :

  • Modifiez votre serveur SQL pour utiliser le type de date sur le champ.
  • Dans votre code .NET, utilisez DateTime avec 00:00:00 et DateTimeKind.Unspecified. Vous devez vous rappeler d'ignorer la partie horaire (car il existe en effet des dates sans minuit local dans certains fuseaux horaires).
  • Modifiez le test de prop en DateTime et non en DateTimeOffset.

En général, alors que DateTimeOffset convient à un grand nombre de scénarios (par ex. horodatageévénements), cela ne fonctionne pas bien pour les valeurs de date uniquement.

je veux date actuelle avec décalage de zéro.

Si vous vraiment envie c'est comme DateTimeOffset, vous pouvez faire :

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

Cependant, je ne recommande pas de faire cela. Ce faisant, vous prenez local date de la valeur d'origine et prétendant être en UTC. Si le décalage d'origine est autre que zéro, il s'agit d'une fausse déclaration. Cela conduira à différentes erreurs par la suite, car vous parlez en fait d'un moment différent (avec une date potentiellement différente) de celui que vous avez créé.

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

Prenons un exemple commençant à 2016-12-31T22 : 00 : 00-04 : 00. Selon votre approche, vous devez stocker dans la base de données 2016-12-31T00 : 00 : 00 + 00 : 00. 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 fuseau horaire différent, serait 2016-12-30T20 : 00 : 00-04 : 00 . Notez le changement de dates dans la conversion. Ce n'est probablement pas le comportement que vous voudriez utiliser dans votre application.

Presque tous les projets sont confrontés à des problèmes causés par une manipulation et un stockage incorrects 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 perplexes quant à la mise en œuvre du bon mécanisme dès le départ, car il semble qu'il ne puisse y avoir aucun problème avec cela, car tout est trivial. Malheureusement, la réalité ultérieure montre que ce n'est pas le cas.

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


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

date et l'heure

Supposons que le laboratoire qui a collecté le matériel à analyser se trouve dans le fuseau horaire +2 et que la branche centrale, qui surveille l'exécution en temps voulu des analyses, se trouve dans le fuseau horaire +1. L'heure indiquée dans l'exemple a été notée lors de la collecte du matériel par le premier laboratoire. La question se pose - quel chiffre de temps le bureau central devrait-il voir ? Évidemment, le logiciel du bureau central devrait afficher le 15 janvier 2014 12:17:15 - une heure de moins, puisque selon leur horloge l'événement s'est produit à ce moment-là.

Considérez l'une des chaînes d'actions possibles par lesquelles les données passent du client au serveur et vice versa, ce qui vous permet de toujours afficher correctement la date/l'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 une représentation sous forme de chaîne pour la 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, la convertissant dans son fuseau horaire actuel. Par exemple, si le serveur s'exécute à +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, alors qu'il ne contient aucune information sur le fuseau horaire - les types de date / heure les plus couramment utilisés n'en savent tout simplement rien. Ainsi, la base de données sera sauvegardée le 2 mars 2016 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 opère dans le fuseau horaire +1, cette valeur sera également interprétée dans le même fuseau horaire.
  7. La valeur est convertie en une représentation sous forme de chaîne pour la 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, la convertissant dans son fuseau horaire actuel. Par exemple, s'il s'agit de -5, la valeur affichée doit être le 2 mars 2016 09 :13:36.
Tout semble être holistique, mais réfléchissons à ce qui peut mal tourner dans ce processus. En fait, des problèmes ici 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 le décalage de fuseau horaire.
  • Lors de la désérialisation vers un objet, le décalage de fuseau horaire peut être ignoré, en particulier dans les désérialiseurs "à faire soi-même" - à 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 formé sans aucun fuseau horaire - par exemple, le type DateTime dans .NET avec DateTimeKind.Unspecified. De plus, en pratique, c'est exactement ce qui se passe avec DateTime dans .NET si vous ne spécifiez pas explicitement un autre DateTimeKind immédiatement après la relecture.
  • Si les serveurs d'applications fonctionnant avec la base de données partagée se trouvent dans des fuseaux horaires différents, il y aura une grave confusion sur les décalages temporels. La valeur date/heure écrite dans la base de données par le serveur A et lue par le serveur B sera très différente de la même valeur d'origine écrite par le serveur B et lue par le serveur A.
  • Le déplacement des serveurs d'applications d'une zone à une autre entraînera une mauvaise interprétation des valeurs de date/heure déjà stockées.
Mais le défaut le plus grave de la chaîne décrite ci-dessus est l'utilisation du fuseau horaire local sur le serveur. S'il n'y a pas de transition vers l'heure d'été, il n'y aura pas de problèmes supplémentaires. Mais sinon, vous pouvez avoir beaucoup de mauvaises surprises.

Les règles de passage à l'heure d'été / d'hiver sont à proprement parler variables. Différents pays peuvent parfois modifier leurs règles, et ces changements doivent être intégrés à l'avance dans les mises à jour du système. Dans la pratique, nous avons rencontré à plusieurs reprises des situations de mauvais fonctionnement de ce mécanisme, qui, de ce fait, ont été résolues par l'installation de correctifs ou système opérateur, ou utilisé des bibliothèques tierces. La probabilité de reproduire les mêmes problèmes n'est pas nulle, il vaut donc mieux avoir un moyen garanti de les éviter.

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

Considérez ce que cette règle nous donne :

  • Lors de l'envoi de données au serveur, le client doit transmettre le décalage de fuseau horaire afin que le serveur puisse convertir correctement l'heure en UTC. Une option alternative consiste à obliger 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 traduira la date et l'heure dans son fuseau horaire local, sachant qu'il viendra de toute façon à l'heure en UTC.
  • En UTC, il n'y a pas de transition entre l'heure d'été et l'heure d'hiver, par conséquent, les problèmes associés à cela ne seront pas pertinents.
  • Sur le serveur, lors de la lecture de la base de données, vous n'avez pas besoin de convertir les valeurs de temps, il vous suffit d'indiquer explicitement qu'il correspond à l'UTC. Dans NET, par exemple, cela peut être réalisé en définissant le DateTimeKind pour l'objet de temps sur DateTimeKind.Utc.
  • La différence de fuseaux horaires entre les serveurs fonctionnant avec une base de données commune, ainsi que le transfert de serveurs d'une zone à une autre, n'affecteront en aucune manière l'exactitude des données reçues.
Pour mettre en place une telle règle, il suffit de s'occuper de trois choses :
  1. Faites en sorte que le mécanisme de sérialisation et de désérialisation soit tel que les valeurs date/heure soient correctement traduites de l'UTC vers le fuseau horaire local et vice versa.
  2. Assurez-vous que le désérialiseur côté serveur crée des objets date/heure en UTC.
  3. Assurez-vous que lors de la lecture de la base de données, les objets date/heure sont créés en UTC. Cette clause est parfois fournie sans modifications de code - c'est simplement que le fuseau horaire du système sur tous les serveurs est défini sur UTC.
Les considérations et directives ci-dessus fonctionnent très bien avec une combinaison de deux conditions:
  • La configuration système requise n'a pas besoin d'être affichée heure locale et/ou le décalage de fuseau horaire exactement tel qu'il a été stocké. Par exemple, dans les billets d'avion, les heures de départ et d'arrivée doivent être imprimées dans le fuseau horaire correspondant à l'emplacement de l'aéroport. Ou si le serveur envoie pour imprimer des factures créées dans différents pays, chacun doit se terminer par une heure locale, non convertie dans le fuseau horaire du serveur.
  • Toutes les valeurs de date et d'heure du système sont "absolues", c'est-à-dire. décrire un moment dans le futur ou le passé qui correspond à une valeur unique en UTC. Par exemple, "le lancement du lanceur a eu lieu à 23h00, heure de Kiev", ou "la réunion se tiendra de 13h30 à 14h30, heure de Minsk". Dans différents fuseaux horaires, les numéros de ces événements seront différents, mais ils décriront le même moment dans le temps. Mais il peut arriver que les exigences de Logiciel impliquent l'heure locale "relative" dans certains cas. Par exemple, "cette émission de télévision se déroulera de 9h00 à 10h00 dans tous les pays où il existe une branche de la chaîne de télévision". Il s'avère que le spectacle du programme n'est pas un événement, mais plusieurs, et potentiellement tous peuvent se produire à différents intervalles de temps sur une échelle "absolue".
Pour les cas où la première condition est violé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.
.RAPPORTER Décalage DateHeure
Java org.joda.time.DateTime, java.time.ZonedDateTime
MS SQL décalage dateheure
Oracle, PostgreSQL horodatage avec fuseau horaire
MySQL

Violation de la deuxième condition - plus cas difficile... Si cette heure "relative" doit être stockée simplement pour l'affichage, et qu'il n'y a pas de tâche pour déterminer le moment "absolu" dans le temps où un événement s'est produit ou se produira pour un fuseau horaire donné, il suffit juste de désactiver la conversion de l'heure . Par exemple, l'utilisateur a entré le début de la diffusion pour toutes les succursales de la société de télévision le 25 mars 2016 à 9h00, et il sera transmis, stocké et affiché sous cette forme. Mais il peut arriver que certains programmateurs doivent effectuer 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). La mise en œuvre fiable d'un tel ordonnanceur 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ù il y a une succursale, après un certain temps, décide de changer le fuseau horaire. Le cas n'est pas aussi rare qu'il y paraît - pour cette année et les deux années précédentes, j'ai compté plus de 10 événements de ce type (http://www.timeanddate.com/news/time/). Il s'avère que soit les utilisateurs doivent maintenir leurs liaisons de fuseau horaire à jour, soit le planificateur doit automatiquement prendre ces informations à partir de sources globales telles que Google Maps API de fuseau horaire. Je ne m'engage pas à proposer une solution universelle à de tels cas, je note simplement que de telles situations nécessitent une étude sérieuse.

Comme vous pouvez le voir ci-dessus, il n'y a pas d'approche unique qui couvre 100% des cas... Par conséquent, vous devez d'abord comprendre clairement à partir des exigences lesquelles des situations ci-dessus seront dans votre système. Très probablement, tout se limitera à la première approche proposée avec un 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 trouvé le bon affichage de la date et de l'heure, en tenant compte du fuseau horaire du client. Passons aux dates sans heure et à l'exemple indiqué pour ce cas au début - "le nouveau contrat entre en vigueur le 2 février 2016". Que se passe-t-il si vous utilisez les mêmes types et le même mécanisme pour des valeurs telles que pour des dates « régulières » avec des heures ?

Toutes les plates-formes, langues et SGBD n'ont pas de types qui stockent uniquement la date. Par exemple, dans .NET, il n'y a que le type DateTime, il n'y a pas de "juste Date" séparé. Même si seule la 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 nous traduisons la valeur "2 février 2016 00:00:00" de la 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 fait que dans un fuseau horaire le nouveau contrat entrera en vigueur le 2 février et dans un autre - le 1er février. D'un point de vue juridique, c'est absurde et, bien sûr, cela ne devrait pas être le cas. La règle générale pour les dates "pures" est extrêmement simple - de telles valeurs ne doivent être converties à aucune étape de la sauvegarde et de la lecture.

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

  • Si la plate-forme prend en charge un type qui représente une date sans heure, alors cela doit être utilisé.
  • Ajoutez une fonctionnalité spéciale aux métadonnées des objets qui indiqueront au sérialiseur que pour valeur donnée le fuseau horaire doit être ignoré.
  • 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 le client a besoin non seulement d'afficher la date, mais également d'effectuer certaines opérations sur celle-ci : comparaison, soustraction, etc.
  • Transmettez et stockez sous forme de chaîne et convertissez en une date uniquement pour le formatage des paramètres régionaux du client. Elle a encore plus d'inconvénients que l'option précédente - par exemple, si les parties de la date dans la chaîne stockée ne sont pas dans l'ordre "année, mois, jour", alors il sera impossible de faire une recherche indexée efficace sur la date intervalle.
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 situé dans le même fuseau horaire, et il est donc possible de déterminer sans ambiguïté le moment de sa entrée en vigueur. Mais même dans ce cas, les utilisateurs d'autres fuseaux horaires ne seront pas intéressés par le moment de leur heure locale où cet événement se produira. Et même s'il était nécessaire d'afficher ce moment dans le temps, non seulement la date mais aussi l'heure devraient être affichées, ce qui contredit la condition initiale.

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 la forme d'un nombre d'unités de temps (entier ou virgule flottante, selon la précision recherchée). Si la seconde précision est importante - alors en nombre de secondes, si milliseconde - alors en nombre de millisecondes, etc.

Mais le calcul de l'intervalle peut avoir des écueils. Supposons que nous ayons un exemple de code C # qui compte 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 pas de problèmes ici, mais ce n'est pas le cas. Tout d'abord, il peut y avoir des problèmes avec les tests unitaires de ce 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, le nombre d'heures de travail est mesuré de cette manière et les travailleurs ont une équipe de nuit).

Supposons que le code fonctionne dans un fuseau horaire dans lequel l'heure d'été en 2016 se produit 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 se traduira par 9 heures, bien qu'en fait 8 heures se soient écoulées entre ces moments. 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 - toutes les opérations arithmétiques avec la date et l'heure doivent être effectuées en utilisant soit des valeurs UTC, soit des types qui stockent des informations sur le fuseau horaire. Et puis traduisez en local si nécessaire. De ce point de vue, l'exemple d'origine peut être facilement corrigé en changeant DateTime.Now en DateTime.UtcNow.

Cette nuance ne dépend pas d'une plate-forme ou d'une langue spécifique. Voici un code Java similaire avec le même inconvénient :

LocalDateTime start = LocalDateTime.now (); // ... LocalDateTime end = LocalDateTime.now (); longues heures = ChronoUnit.HOURS.entre (début, fin);
Il est également facilement corrigé - par exemple, en utilisant ZonedDateTime au lieu de LocalDateTime.

Programmer des événements programmés

La planification d'événements programmés est une situation plus complexe. Un type universel qui vous permet de stocker des horaires dans bibliothèques standards non. Mais une telle tâche ne se pose pas si rarement, donc des solutions toutes faites peuvent être trouvées sans problème. Un bon exemple est un format de planificateur cron qui est utilisé sous une forme ou une autre par d'autres solutions, par exemple Quartz : http://quartz-scheduler.org/api/2.2.0/org/quartz/CronExpression.html. Il couvre presque tous les besoins de planification, y compris les options du 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é à cron.

En plus des recommandations décrites ci-dessus sur le stockage et le traitement de différents types de valeurs temporelles, il y en a plusieurs autres dont j'aimerais également parler.

Tout d'abord, à propos de l'utilisation de membres de classe statiques pour obtenir l'heure actuelle - DateTime.UtcNow, ZonedDateTime.now (), etc. Comme mentionné, les utiliser directement dans le code peut sérieusement compliquer les tests unitaires, car sans cadres fictifs spéciaux, il ne sera pas possible de changer 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 y a au moins deux façons de résoudre ce problème :

  • Mettez en surbrillance l'interface IDateTimeProvider avec une méthode unique qui renvoie l'heure actuelle. Ajoutez ensuite une dépendance à 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, ce 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 en termes de test.
  • 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 depuis l'extérieur. Par exemple, dans le cas du code C#, cette classe peut exposer la propriété UtcNow et le SetImplementation (Func impl). Usage propriété statique ou la méthode pour obtenir l'heure actuelle élimine le besoin d'écrire explicitement la dépendance de l'interface supplémentaire partout, mais du point de vue des principes de la POO, ce n'est pas solution idéale... Cependant, si pour une raison quelconque l'option précédente ne convient pas, vous pouvez l'utiliser.
Un problème supplémentaire qui devrait être résolu lors du passage à votre implémentation du fournisseur d'heure actuelle est de vous assurer que personne "à l'ancienne" ne continue à utiliser les classes standard. Cette tâche est facile à accomplir dans la plupart des systèmes de contrôle de la qualité du code. Essentiellement, cela se résume à rechercher une sous-chaîne "indésirable" dans tous les fichiers, à l'exception de celui où l'implémentation "par défaut" est déclarée.

La deuxième nuance avec l'obtention de l'heure actuelle est que le client n'est pas digne de confiance... 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 de fuseau horaire.

Et une autre chose que je voudrais 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 les problèmes de compatibilité potentiels. En pratique, il est extrêmement rare que vous deviez implémenter vous-même le formatage, la norme elle-même peut donc être utile principalement à des fins d'information.

Balises : ajouter des balises

Partagez ceci