Un type pour Date uniquement en C # - pourquoi n'y a-t-il pas de type Date?

108

Dans notre projet C #, nous avons besoin de représenter une date sans heure. Je connais l'existence du DateTime, cependant, il intègre également une heure de la journée. Je veux préciser que certaines variables et certains arguments de méthode sont basés sur la date . Par conséquent, je ne peux pas utiliser la DateTime.Datepropriété

Quelles sont les approches standard de ce problème? Je ne suis sûrement pas le premier à rencontrer cela? Pourquoi n'y a-t-il pas de Dateclasse en C #?

Quelqu'un a-t-il une belle implémentation en utilisant une structure et peut-être des méthodes d'extension sur DateTime et peut-être implémentant certains opérateurs tels que == et <,>?

Carlo V. Dango
la source
1
Bien que je comprenne vouloir une sémantique explicite et claire, quels problèmes spécifiques cela DateTimecrée-t-il?
Jeff Sternal
15
1 Je dois me rappeler de supprimer les heures au début de la méthode. 2 il ne communique pas bien qu'il fonctionne uniquement sur des dates. Ceci est important par exemple lors du stockage et du chargement à partir de Db où un type étroit suffira. La programmation est une communion pour les gens et non pour les ordinateurs
Carlo V. Dango
6
Je voulais juste dire que l'absence de classe de date est un gros problème et que l'utilisation de DateTime n'est pas du tout bonne. Dès que vous stockez vos "dates" en tant que date-heure, vous êtes otage des problèmes d'économie de jour / fuseau horaire. Jeter la partie horaire peut renvoyer toutes vos dates un jour où les horloges changent (!). Et les utilisateurs de différents fuseaux horaires verront des dates différentes lorsqu'ils essaieront de convertir les dates-heures. Les dates-heures sont parfaites pour représenter des moments précis dans le temps (jiffies d'un point ou autre), mais elles sont très inappropriées pour représenter une date abstraite.
TheMathemagician
3
Une question similaire ultérieure stackoverflow.com/questions/7167710/… , et Jon Skeet dit qu'il devrait y avoir une date.
revoir
9
Un type de données date uniquement est à DateTime comme un type de données entier est à une décimale. Ceux qui prétendent que nous n'avons pas besoin d'une date parce que vous pouvez simplement jeter la partie du temps revient à dire que nous n'avons pas besoin d'entiers, car nous pouvons jeter la partie décimale. Notre monde a un concept de date qui n'inclut pas d'heure. Le 5 mars n'est pas le 5 mars 00:00:00.
Vague

Réponses:

55

Permettez-moi d'ajouter une mise à jour à cette question classique:

  • La bibliothèque Noda Time de Jon Skeet est maintenant assez mature et a un type de date uniquement appelé LocalDate. (Local dans ce cas signifie simplement local pour quelqu'un , pas nécessairement local pour l'ordinateur sur lequel le code est exécuté.)

  • Un type de date uniquement appelé Dateest un ajout proposé au .NET Core, via le projet corefxlab . Vous le trouverez dans le System.Timepackage, avec un TimeOfDaytype et plusieurs méthodes d'extension aux types existants.

J'ai étudié ce problème de manière significative, je vais donc également partager plusieurs raisons de la nécessité de ces types:

  1. Il existe un écart logique entre une valeur de date uniquement et une valeur de date à minuit.

    • Pas tous les jours locale a un minuit dans chaque fuseau horaire. Exemple: la transition de l'heure d'été vers l'avant du Brésil fait passer l'horloge de 11:59:59 à 01:00:00.

    • Une date-heure fait toujours référence à une heure spécifique dans la journée, tandis qu'une date uniquement peut faire référence au début de la journée, à la fin de la journée ou à toute la plage de la journée.

  2. Le fait d'attacher une heure à une date peut entraîner un changement de date lorsque la valeur est transmise d'un environnement à un autre, si les fuseaux horaires ne sont pas surveillés très attentivement. Cela se produit généralement en JavaScript (dont l' Dateobjet est en réalité une date + heure), mais peut également se produire facilement dans .NET, ou dans la sérialisation lorsque les données sont transmises entre JavaScript et .NET.

  3. La sérialisation d'un DateTimeavec XML ou JSON (et autres) inclura toujours l'heure, même si ce n'est pas important. C'est très déroutant, surtout si l'on considère des choses comme les dates de naissance et les anniversaires, où l'heure n'a pas d'importance.

  4. Sur le plan architectural, il DateTimes'agit d'un objet de valeur DDD , mais il enfreint le principe de responsabilité unique de plusieurs manières:

    • Il est conçu comme un type date + heure, mais est souvent utilisé comme date uniquement (en ignorant l'heure), ou en heure uniquement (en ignorant la date). ( TimeSpanest également souvent utilisé pour l'heure de la journée, mais c'est un autre sujet.)

    • La DateTimeKindvaleur attachée à la .Kindpropriété divise le type unique en trois. Le Unspecifiedgenre est vraiment l'intention d'origine de la structure et doit être utilisé de cette façon. Le Utckind aligne la valeur spécifiquement avec UTC et le Localkind aligne la valeur sur le fuseau horaire local de l'environnement.

      Le problème d'avoir un indicateur distinct pour kind est que chaque fois que vous consommez un DateTime, vous êtes censé vérifier .Kindpour décider du comportement à adopter. Les méthodes de cadre font toutes cela, mais d'autres oublient souvent. Il s'agit vraiment d'une violation SRP, car le type a maintenant deux raisons différentes de changer (la valeur et le genre).

    • Les deux conduisent à des utilisations d'API qui se compilent, mais qui sont souvent absurdes, ou ont des cas extrêmes causés par des effets secondaires. Considérer:

      // nonsensical, caused by mixing types
      DateTime dt = DateTime.Today - TimeSpan.FromHours(3);  // when on today??
      
      // strange edge cases, caused by impact of Kind
      var london = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
      var paris = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
      var dt = new DateTime(2016, 3, 27, 2, 0, 0);  // unspecified kind
      var delta = paris.GetUtcOffset(dt) - london.GetUtcOffset(dt);  // side effect!
      Console.WriteLine(delta.TotalHours); // 0, when should be 1 !!!

En résumé, alors que a DateTime peut être utilisé pour une date uniquement, il ne devrait le faire que lorsque chaque lieu qui l'utilise fait très attention d'ignorer l'heure, et est également très prudent de ne pas essayer de convertir vers et depuis UTC ou autre fuseaux horaires.

Matt Johnson-Pint
la source
2
Si seulement System.Time.Datefinirait dans le framework .NET: /
Robert Jørgensgaard Engdahl
1
Vous pouvez l'utiliser aujourd'hui, il vous suffit de vous abonner au flux corefx myget, et vous pouvez y System.Timeaccéder comme n'importe quel autre package. Ce n'est tout simplement pas encore "officiel".
Matt Johnson-Pint
16

Je soupçonne qu'il n'y a pas de Dateclasse pure dédiée car vous avez déjà DateTimequi peut le gérer. Cela Dateentraînerait des doubles emplois et de la confusion.

Si vous voulez l'approche standard, regardez la DateTime.Datepropriété qui donne uniquement la partie date de a DateTimeavec la valeur de l'heure définie sur 12:00:00 minuit (00:00:00).

Robert MacLean
la source
61
Un gros avantage d'une classe Date dédiée est qu'elle ne souffre pas de la complexité des fuseaux horaires et de l'heure d'été.
Dimitri C.
3
@DimitriC. Je ne suis pas d'accord - vous pouvez utiliser DateTime avec UTC et vous ne souffrez pas des problèmes expliqués, plus avec un DateTime, même si vous ne voulez que des dates, vous pouvez toujours faire des calculs qui impliquent du temps (c'est-à-dire donnez-moi la date si je soustrais 20 x 2 heures à partir d'aujourd'hui).
Robert MacLean
@Robert MacLean: Merci d'avoir souligné la commodité d'utiliser UTC DateTimes. J'ai fait quelques tests et il semble que DateTimeKind.Unspecified agit comme UTC concernant les soustractions. Donc en effet, si vous faites attention au "genre" de DateTimes avec lequel vous travaillez, tout se passera bien.
Dimitri C.
10
Avoir à penser à UTC et à tout ce qui a à voir avec le fuseau horaire n'est qu'un gaspillage d'énergie, car il peut facilement être évité par une classe Date distincte. Et je ne vois aucune confusion entre Date et DateTime.
maulik13
6
Convenez que C # devrait vraiment avoir une classe Date. Non seulement la conversion de fuseau horaire est une source constante de bogues sous-marins, mais c'est tout simplement douloureux lorsqu'il s'agit de choses basées sur un jour ouvrable plutôt que sur l'heure.
Julian Birch
12

J'ai envoyé un e-mail à [email protected] et c'est leur réponse

Marcos, ce n'est pas un bon endroit pour poser des questions comme celles-ci. Essayez http://stackoverflow.com La réponse courte est que vous avez besoin d'un modèle pour représenter un point dans le temps, et DateTime le fait, c'est le scénario le plus utile en pratique . Le fait que les humains utilisent deux concepts (date et heure) pour marquer des points dans le temps est arbitraire et inutile à séparer.

Découpez seulement là où c'est justifié, ne faites pas les choses juste pour le plaisir de faire les choses à l'aveuglette. Pensez-y de cette façon: quel problème avez-vous qui est résolu en divisant DateHeure en Date et Heure? Et quels problèmes aurez-vous que vous n'avez pas maintenant? Astuce: si vous regardez les utilisations de DateTime dans le framework .NET: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references Vous verrez que la plupart sont renvoyés par une méthode. Si nous n'avions pas un seul concept comme DateTime, vous devrez utiliser des paramètres out ou des tuples pour renvoyer une paire de date et d'heure.

HTH, Kirill Osenkov

Dans mon e-mail, je me suis demandé si c'était parce que DateTime utilise TimeZoneInfo pour obtenir l'heure de la machine - dans Now propriété. Donc je dirais que c'est parce que "les règles métier" sont "trop ​​couplées", ils me l'ont confirmé.

MVCDS
la source
Cet article donne vraiment un aperçu des idées derrière la décision de conception de ne pas avoir de classe de date intégrée. Quelle est la question que vous leur envoyez? Je ne veux pas dire que je suis d'accord avec cette décision pour les raisons mêmes que @TheMathemagician a énumérées ci-dessus.
Robert Jørgensgaard Engdahl
@ RobertJørgensgaardEngdahl, malheureusement, je n'ai plus accès à ce compte de messagerie. Mais je crois que je leur ai demandé pourquoi ils avaient couplé l'heure et la date dans la structure DateTime. Et je pensais être d'accord avec TheMathemagician, je pense que MS a adopté cette approche de conception parce qu'en tant qu'entreprise internationale, elle paie ses besoins - et il est maintenant trop tard pour les changer - alors que le fractionnement des concepts ne le fait pas.
MVCDS
2
Peut-être que MS pourrait tout simplement mettre en œuvre une SpaceTimeclasse! Hé, selon Einstein, l'espace et le temps sont étroitement liés, nous ne devrions donc pas avoir besoin de les différencier non plus, non? (!!!!!!!!!!!) Je suis un peu nouveau pour C #, mais je dois dire, c'est un champ de mines en provenance de VB.NET où est, tout simplement, date, Today(), now, etc. Pas de DateTimedéchets préfixant, pas dégoûtant. (Et ces points-virgules et cette sensibilité à la casse sont vraiment ennuyeux! Il suffit de me tirer dessus maintenant!)
SteveCinq
2
Et leur propre SQL Server a le Datetype et le résultat doit être de type Date- s'il était de Datetype résultat attendu sous forme de chaîne sans temps. Par exemple, Delphi a également Date comme DateTime, mais typeinfo différent pour Date et DateTime.
user2091150
1
Kirill Osenkov répond à la question "Pourquoi ne pas avoir des classes de date et d'heure séparées par rapport à une classe de date et d'heure ?". Le Q réel était "Pourquoi ne pas avoir également des classes de date et d'heure séparées?". Je comprends que la date et l'heure doivent être couplées en une seule classe pour les nombreux cas d'utilisation accordés du concept date-heure . Cependant, il existe probablement au moins autant sinon plus de cas d'utilisation valides d'un simple concept de date . Et bien sûr, il existe également de nombreux cas d'utilisation valables d'un concept de temps .
Tom
4

Si vous devez exécuter des comparaisons de dates, utilisez

yourdatetime.Date;

Si vous affichez à l'écran, utilisez

yourdatetime.ToShortDateString();
MattP
la source
La partie .Date est ce que je recherchais.
Brendan Vogt
3

Permettez-moi de spéculer: c'est peut-être parce que jusqu'à SQL Server 2008, il n'y avait pas de type de données Date dans SQL, donc il serait difficile de le stocker dans SQL Server ?? Et c'est après tout un produit Microsoft?

Pleun
la source
Une date / heure de base de données est différente d'une heure de date C #. Une date / heure de base de données n'a pas de fuseau horaire et ne fait donc pas référence à un instant particulier. Mais C # sait que l'instant est et stocke les graduations depuis l'époque UTC.
artsrc
2
la discussion porte sur la DATE dédiée, pas tellement sur la partie datetime donc je ne comprends pas le point que vous essayez de faire?
Pleun
Cela ne répond pas à la question. Pour critiquer ou demander des éclaircissements à un auteur, laissez un commentaire sous sa publication.
Barranka
@Barranka - La question contient "Pourquoi n'y a-t-il pas de classe Date en C #?"
STLDev
2

Qui sait pourquoi c'est ainsi. Il y a beaucoup de mauvaises décisions de conception dans le framework .NET. Cependant, je pense que c'est une question assez mineure. Vous pouvez toujours ignorer la partie heure, donc même si un code décide de faire référence à une date / heure plus que juste la date, le code qui se soucie ne devrait jamais regarder que la partie date. Vous pouvez également créer un nouveau type qui ne représente qu'une date et utiliser des fonctions dans DateTime pour effectuer le gros du travail (calculs).

siride
la source
1
Je ne pense vraiment pas que ce soit une mauvaise décision, que vous souhaitiez utiliser uniquement une date ou non. Je ne vous déconseillerai pas mais c'est mon opinion.
JonH
Je ne pense pas que je l'ai bien formulé. Je n'ai pas vraiment de problème avec cela, en soi, bien que je puisse voir comment avoir deux ou trois types serait plus approprié d'un point de vue abstraction / élégance. Mon point était vraiment qu'il y a beaucoup de choses dans le framework .NET qui peuvent vous laisser vous gratter la tête et qu'il ne vaut pas la peine de s'énerver, d'autant plus que ce «problème» est assez mineur par rapport à certaines décisions de conception flagrantes (générique contraintes).
siride
+1 parce que c'est vrai ... était-ce le seul problème (ou le plus gros) de .NET :-) :-) De combien de versions de SQL Server avaient-ils besoin pour ajouter une DATE et un HEURE? Et là, ils étaient BEAUCOUP plus utiles (au moins pour des raisons d'intégrité)
xanatos
Je devrais également ajouter que je pense que "tout commence à -100 points" est un bon moyen de créer un framework pauvre en pisse et c'est peut-être l'une des choses qui se sont retrouvées coincées dans ces ordures.
siride
2
Je viens de me faire mordre par ce problème car une partie du code a négligé d'utiliser la propriété .Date et ne s'est donc pas comparée correctement. Je pense vraiment qu'il est nécessaire d'avoir un type de date qui ne stocke pas l'heure, pour éviter ce type d'erreur
JoelFan
2

Pourquoi? Nous ne pouvons que spéculer et cela ne fait pas grand-chose pour résoudre les problèmes d'ingénierie. Une bonne supposition est que DateTimecontient toutes les fonctionnalités qu'une telle structure aurait.

Si cela compte vraiment pour vous, enveloppez simplement DateTimevotre propre structure immuable qui expose uniquement la date (ou regardez la DateTime.Datepropriété).

Jason
la source
2

En plus de la réponse de Robert, vous avez également la DateTime.ToShortDateStringméthode. De plus, si vous vouliez vraiment un objet Date, vous pouvez toujours utiliser le modèle Adapter et envelopper l'objet DateTime en exposant uniquement ce que vous voulez (c'est-à-dire le mois, le jour, l'année).

Mat
la source
2

Il y a toujours la DateTime.Datepropriété qui coupe la partie temporelle du DateTime. Vous pouvez peut-être encapsuler ou encapsuler DateTime dans votre propre type de date.

Et pour savoir pourquoi, eh bien, je suppose que vous devrez demander à Anders Heljsberg.

Mikael Östberg
la source
1

Parce que pour connaître la date, vous devez connaître l'heure du système (en ticks), qui comprend l'heure - alors pourquoi jeter ces informations?

DateTimea une Datepropriété si vous ne vous souciez pas du tout de l'heure.

Massif
la source
1

Ouais, System.DateTime est également scellé. J'ai vu des gens jouer à des jeux avec cela en créant une classe personnalisée juste pour obtenir la valeur de chaîne de l'heure comme mentionné dans les articles précédents, des trucs comme:

class CustomDate
{
    public DateTime Date { get; set; }
    public bool IsTimeOnly { get; private set; }

    public CustomDate(bool isTimeOnly)
    {
        this.IsTimeOnly = isTimeOnly;
    }

    public string GetValue()
    {
        if (IsTimeOnly)
        {
            return Date.ToShortTimeString();
        }

        else
        {
            return Date.ToString();
        }
    }
}

Ceci est peut-être inutile, car vous pouvez facilement extraire GetShortTimeString d'un ancien type DateTime sans nouvelle classe

Ta01
la source
0

Si vous utilisez les propriétés Date ou Today pour obtenir uniquement la partie date de l'objet DateTime.

DateTime today = DateTime.Today;
DateTime yesterday = DateTime.Now.AddDays(-1).Date;

Ensuite, vous obtiendrez le composant de date uniquement avec le composant d'heure défini sur minuit.

eph_tagh
la source
1
ce n'est certainement pas ce que je voulais
Carlo V. Dango
@Carlo V. Dango: Je ne suis pas d'accord. Je pense que c'est exactement ce que tu voulais.
siride
1
@Carlo V. Dango: Qu'est-ce que vous cherchez précisément à faire que ces propriétés ne vous permettent pas d'accomplir?
eph_tagh
5
C'est assez simple: une empreinte mémoire de Date ne représenterait probablement que la moitié de l'empreinte mémoire d'un DateTime (32 au lieu de 64 bits). Vous seriez sûr que votre stupide collègue n'a pas .AddHours (1) à votre date en le changeant mais en "gardant le même" du PDV de "date seulement". Si (pour une erreur) le DateTime est défini sur DateTimeKind.Local et que l'heure est normalisée à UTC, la Date changera probablement (m'est arrivé grâce à l'utilisation de XmlSerialization et un aller-retour mal fait vers JSON) ... Est-ce suffisant?
xanatos