En partie comme une préparation pour essayer de répondre à cette question correctement (et peut-être même définitivement...), en partie à examiner la façon dont beaucoup qui on peut avoir confiance code qui est collé sur de la SORTE, et en partie comme un exercice de trouver des bugs, j'ai créé un tas de tests unitaires pour cette question, et de les appliquer à de nombreuses solutions proposées à partir de cette page et un couple de doublons.
Les résultats sont concluants: pas un seul des contributions de code précision des réponses à la question. Mise à jour: j'ai maintenant quatre bonnes solutions à cette question, y compris mon propre, voir les mises à jour ci-dessous.
Code testé
À partir de cette question, j'ai testé le code par les utilisateurs suivants:
Mohammed Ijas Nasirudeen, ruffin, Malu MN, Dave, pk., Jani, lc.
Ce sont toutes les réponses qui a fourni tous les trois des années, des mois et des jours dans leur code. Noter que deux de ces, Dave et Jani, a donné, le nombre total des jours et des mois, plutôt que le nombre total de mois après le décompte des années, et le nombre total de jours après le décompte des mois. Je pense que les réponses sont mauvaises en termes de ce que l'OP a semblé vouloir, mais les tests unitaires ne pas vous en dire beaucoup dans ces cas. (Notez que dans Jani de cas, cela a été mon erreur et de son code a été fait correct - voir mise à Jour 4 ci-dessous)
Les réponses par Jon Skeet, Aghasoleimani, Mukesh Kumar, Richard, Colin, sheir, juste que j'ai vu, Chalkey et Andy, étaient incomplètes. Cela ne veut pas dire que les réponses n'étaient pas tout bon, en fait, plusieurs d'entre eux sont utiles contributions en faveur d'une solution. Cela signifie simplement qu'il n'y avait pas de code à la prise de deux DateTime
s et le retour à 3 int
s que j'ai pu tester correctement. Quatre de ces cependant, parler à l'aide de TimeSpan
. Comme de nombreuses personnes l'ont mentionné, TimeSpan
ne rend pas compte de quoi que ce soit plus que les jours.
Les autres réponses que j'ai testé étaient de
- question 3054715 - LukeH, ho1 et ce. ___curieux_geek
- question 6260372 - Chuck Rostance et Jani (même réponse que cette question)
- question 9 (!) - Dylan Hayes, Jon et Rajeshwaran S P
c'.___curieux_geek réponse est code sur une page qu'il a lié pour qui je ne pense pas qu'il a écrit. Jani réponse est le seul qui utilise une bibliothèque externe, la Période de la Bibliothèque pour le .Net.
Toutes les autres réponses à toutes ces questions semble être incomplète. Question 9 sur l'âge en années, et les trois réponses sont celles qui ont dépassé la brève et calculé en années, mois et jours. Si quelqu'un trouve encore des doublons de cette question, s'il vous plaît laissez-moi savoir.
Comment je l'ai testé
Tout simplement: j'ai fait une interface
public interface IDateDifference
{
void SetDates(DateTime start, DateTime end);
int GetYears();
int GetMonths();
int GetDays();
}
Pour chaque réponse, j'ai écrit une classe implémentant cette interface, en utilisant le copier-coller code. Bien sûr, j'ai dû adapter les fonctions avec des signatures différentes, etc, mais j'ai essayé de faire le minimum de modifications à faire, en conservant toute la logique du code.
J'ai écrit un tas de tests NUnit dans un résumé à la classe générique
[TestFixture]
public abstract class DateDifferenceTests<DDC> where DDC : IDateDifference, new()
et ajout d'un vide de la classe dérivée
public class Rajeshwaran_S_P_Test : DateDifferenceTests<Rajeshwaran_S_P>
{
}
pour le fichier source pour chaque IDateDifference
classe.
NUnit est assez intelligent pour faire le reste.
Les tests
Un couple de ces derniers ont été écrits à l'avance et le reste a été écrit pour essayer de briser apparemment implémentations de travail.
[TestFixture]
public abstract class DateDifferenceTests<DDC> where DDC : IDateDifference, new()
{
protected IDateDifference ddClass;
[SetUp]
public void Init()
{
ddClass = new DDC();
}
[Test]
public void BasicTest()
{
ddClass.SetDates(new DateTime(2012, 12, 1), new DateTime(2012, 12, 25));
CheckResults(0, 0, 24);
}
[Test]
public void AlmostTwoYearsTest()
{
ddClass.SetDates(new DateTime(2010, 8, 29), new DateTime(2012, 8, 14));
CheckResults(1, 11, 16);
}
[Test]
public void AlmostThreeYearsTest()
{
ddClass.SetDates(new DateTime(2009, 7, 29), new DateTime(2012, 7, 14));
CheckResults(2, 11, 15);
}
[Test]
public void BornOnALeapYearTest()
{
ddClass.SetDates(new DateTime(2008, 2, 29), new DateTime(2009, 2, 28));
CheckControversialResults(0, 11, 30, 1, 0, 0);
}
[Test]
public void BornOnALeapYearTest2()
{
ddClass.SetDates(new DateTime(2008, 2, 29), new DateTime(2009, 3, 1));
CheckControversialResults(1, 0, 0, 1, 0, 1);
}
[Test]
public void LongMonthToLongMonth()
{
ddClass.SetDates(new DateTime(2010, 1, 31), new DateTime(2010, 3, 31));
CheckResults(0, 2, 0);
}
[Test]
public void LongMonthToLongMonthPenultimateDay()
{
ddClass.SetDates(new DateTime(2009, 1, 31), new DateTime(2009, 3, 30));
CheckResults(0, 1, 30);
}
[Test]
public void LongMonthToShortMonth()
{
ddClass.SetDates(new DateTime(2009, 8, 31), new DateTime(2009, 9, 30));
CheckControversialResults(0, 1, 0, 0, 0, 30);
}
[Test]
public void LongMonthToPartWayThruShortMonth()
{
ddClass.SetDates(new DateTime(2009, 8, 31), new DateTime(2009, 9, 10));
CheckResults(0, 0, 10);
}
private void CheckResults(int years, int months, int days)
{
Assert.AreEqual(years, ddClass.GetYears());
Assert.AreEqual(months, ddClass.GetMonths());
Assert.AreEqual(days, ddClass.GetDays());
}
private void CheckControversialResults(int years, int months, int days,
int yearsAlt, int monthsAlt, int daysAlt)
{
// gives the right output but unhelpful messages
bool success = ((ddClass.GetYears() == years
&& ddClass.GetMonths() == months
&& ddClass.GetDays() == days)
||
(ddClass.GetYears() == yearsAlt
&& ddClass.GetMonths() == monthsAlt
&& ddClass.GetDays() == daysAlt));
Assert.IsTrue(success);
}
}
La plupart des noms sont légèrement stupide et n'a pas vraiment expliquer pourquoi le code peut échouer à l'épreuve, mais en regardant les deux dates et la réponse(s) doit être suffisant pour comprendre le test.
Il y a deux fonctions qui font tout l' Assert
s, CheckResults()
et CheckControversialResults()
. Ceux-ci fonctionnent bien pour vous épargner de la saisie et de donner les résultats de la droite, mais malheureusement, il est plus difficile de voir exactement ce qui n'allait pas (parce que l' Assert
en CheckControversialResults()
échouera avec l'Attendue "vrai", plutôt que de vous dire dont la valeur est incorrecte. Si quelqu'un a une meilleure façon de le faire (éviter d'écrire les mêmes contrôles à chaque fois, mais ont plus de messages d'erreur utiles) s'il vous plaît laissez-moi savoir.
CheckControversialResults()
est utilisé pour les quelques cas où il semble y avoir deux opinions différentes sur ce qui est juste. J'ai une opinion de moi-même, mais j'ai pensé que je devrais être libéral dans ce que j'ai accepté ici. L'essentiel, c'est de décider d'un an après le 29 Février est du 28 Février ou mars 1.
Ces tests sont le nœud de la question, et il pourrait très bien être des erreurs, donc s'il vous plaît commentaire si vous en trouvez un qui est mauvais. Il serait également bon d'entendre quelques suggestions pour d'autres tests afin de vérifier les futures itérations de réponses.
Aucun test n'implique du temps de la journée - tous DateTime
s sont à minuit. Y compris les temps, aussi longtemps que il sait comment arrondi vers le haut et vers le bas pour jours fonctionne (je pense que c'est), peut se montrer encore plus de défauts.
Les résultats
Le tableau de bord complet de résultats se présente comme suit:
ChuckRostance_Test 3 failures S S S F S S F S F
Dave_Test 6 failures F F S F F F F S S
Dylan_Hayes_Test 9 failures F F F F F F F F F
ho1_Test 3 failures F F S S S S F S S
Jani_Test 6 failures F F S F F F F S S
Jon_Test 1 failure S S S S S S F S S
lc_Test 2 failures S S S S S F F S S
LukeH_Test 1 failure S S S S S S F S S
Malu_MN_Test 1 failure S S S S S S S F S
Mohammed_Ijas_Nasirudeen_Test 2 failures F S S F S S S S S
pk_Test 6 failures F F F S S F F F S
Rajeshwaran_S_P_Test 7 failures F F S F F S F F F
ruffin_Test 3 failures F S S F S S F S S
this_curious_geek_Test 2 failures F S S F S S S S S
Mais notez que Jani solution a été vraiment bon et a réussi tous les tests - voir mise à jour 4 ci-dessous.
Les colonnes sont dans l'ordre alphabétique du nom du test:
- AlmostThreeYearsTest
- AlmostTwoYearsTest
- BasicTest
- BornOnALeapYearTest
- BornOnALeapYearTest2
- LongMonthToLongMonth
- LongMonthToLongMonthPenultimateDay
- LongMonthToPartWayThruShortMonth
- LongMonthToShortMonth
Trois réponses ont échoué à seulement 1 essai chaque, Jon, LukeH et Manu MN. Gardez à l'esprit ces tests ont probablement été écrites spécifiquement pour résoudre les défauts dans ces réponses.
Chaque test a été adoptée par au moins un morceau de code, qui est un peu rassurant de voir que aucun de ces tests sont erronées.
Quelques réponses a échoué à de nombreux tests. J'espère que personne ne se sent c'est une condamnation de la qui affiche les efforts. Tout d'abord le nombre de succès est assez arbitraire, car les tests ne sont pas de recouvrir uniformément la problématique de la question de l'espace. Deuxièmement ce n'est pas le code de production - les réponses sont affichées afin que les gens peuvent apprendre d'eux, de ne pas les copier exactement dans leurs programmes. Code qui échoue à un grand nombre de tests peut encore avoir de grandes idées. Au moins une pièce qui n'a pas beaucoup de tests ont eu un petit bug que je n'ai pas corrigé. Je suis reconnaissant à toute personne qui a pris le temps de partager leur travail avec tout le monde, pour faire de ce projet très intéressant.
Mes conclusions
Il en existe trois:
Les calendriers sont durs.
J'ai écrit neuf essais, dont trois où deux réponses sont possibles. Certains des tests où je n'ai eu qu'une réponse pourrait pas faire l'unanimité avec. Il suffit de penser au sujet de exactement ce que nous voulons dire quand nous disons '1 mois plus tard" ou "2 ans plus tôt" est difficile dans beaucoup de situations. Et rien de ce code eu à traiter avec toutes les complexités des choses comme le travail quand les années bissextiles sont. Tous il utilise la bibliothèque de code pour gérer les dates. Si vous imaginez le "spec" pour dire le temps en jours, semaines, mois et années écrit, il y a toutes sortes de trucs. Parce que nous savons assez bien depuis l'école primaire, et utilisez tous les jours, nous sommes aveugles à de nombreuses particularités. La question n'est pas académique - les différents types de décomposition des périodes de temps en années, les quarts et les mois sont essentiels dans le logiciel de comptabilité pour les obligations et autres produits financiers.
L'écriture de code correct est difficile.
Il y avait beaucoup de bugs. Dans un peu plus obscur des sujets ou moins populaires de questions que les chances d'un bug existant sans avoir été signalé par un intervenant sont beaucoup, beaucoup plus élevé que pour cette question. Vous devriez vraiment jamais, jamais copier le code de SI dans votre programme, sans comprendre exactement ce qu'il fait. Le revers de la médaille, c'est que vous ne devriez probablement pas écrire de code dans votre réponse, qui est prêt à être copié et collé, mais plutôt intelligente et expressive pseudo-code qui permet à quelqu'un de comprendre la solution et de mettre en œuvre leur propre version (avec leurs propres bugs!)
Les tests unitaires sont utiles.
Je suis encore un sens à poster mon propre solution à cela quand je suis toute à elle (pour quelqu'un d'autre pour trouver ce qui est caché, sur des hypothèses erronées dans le!) Faire cela a été un bel exemple de " sauver les insectes en les transformant en des tests unitaires pour fixer la prochaine version du code.
Mise à jour
L'ensemble du projet est maintenant à https://github.com/jwg4/date-difference
Cela comprend ma propre tentative jwg.cs
, qui passe tous les tests que j'ai actuellement, y compris quelques-uns qui en vérifier le bon moment de la journée de la manipulation. N'hésitez pas à ajouter plus de tests pour briser ce et d'autres implémentations ou un code de meilleure qualité pour répondre à la question.
Mise à jour 2
@MattJohnson a ajouté une mise en œuvre qui utilise Jon Skeet est NodaTime. Elle passe tous les tests actuels.
Mise à jour 3
@KirkWoll la réponse de Différence en nombre de mois entre deux dates a été ajouté au projet sur github. Elle passe tous les tests actuels.
Mise à jour 4
@Jani a souligné dans un commentaire que j'avais utilisé son code à tort. Il n'suggérer des méthodes qui a compté les années, les mois et les jours correctement, (à côté de certains qui permettent de compter le nombre total de jours et de mois, pas les restes) cependant j'ai utilisé à tort la faux dans mon code de test. J'ai corrigé mon wrapper autour de son code et il passe tous les tests. Il y a maintenant quatre bonnes solutions, dont Jani était la première. Deux bibliothèques (Intenso.Laps de temps et NodaTime) et deux sont écrit à partir de zéro.