Moment: Le calcul de l'âge minimal avec des valeurs d'anniversaire limites entraîne un comportement inattendu

Créé le 27 févr. 2017  ·  3Commentaires  ·  Source: moment/moment

Description du problème et étapes de reproduction

Je veux vérifier si une personne a exactement ou plus de 18 ans. J'ai donc utilisé les fonctions diff() et duration() pour calculer l'âge de la personne. J'ai écrit des tests avec des valeurs limites pour tester ma logique. Je voulais tester si j'ai exactement un jour de moins que 18 ans et exactement 18 ans. J'ai donc choisi deux anniversaires pour le test (aujourd'hui nous sommes le 27.02.2017) :

  • exactement 18 : 27.02.1999
  • un jour moins de 18 ans : 28.02.1999

Reproduire

// today date is: 27.02.2017

// exactly age 18    
var birthday = moment("27.02.1999", "DD.MM.YYYY"),
    age = moment.duration(moment().diff(birthday)).asYears();

      console.log(age); // output: 18.00325100985672; expected: == 18

// one day under age 18
var birthday = moment("28.02.1999", "DD.MM.YYYY"),
    age = moment.duration(moment().diff(birthday)).asYears();

    console.log(age); // output: 18.00053005036735; expected: < 18

Je pense que le calcul est correct, à cause des années bissextiles. Mais en termes d'anniversaire, la personne ne devrait pas avoir 18 ans. Peut-être qu'il y a une autre façon de le faire avec momentjs et je le fais mal. Si c'est le cas, ce serait bien d'être mentionné dans la documentation de momentjs.

Code de fabrication

<form id="newsletter-form" data-parsley-validate="">
  <input id="birthday" type="text" class="form-control" required="" data-parsley-minage="18">
  <input type="submit" class="btn btn-default" value="subscribe">
</form>

<script type="text/javascript">
    $(function () {
        window.Parsley.addValidator('minage', {
            validateString: function(value, minAge) {
                var birthday = moment(value, "DD.MM.YYYY"),
                    age = moment.duration(moment().diff(birthday)).asYears();

                return (age >= minAge);
            },
            requirementType: 'integer',
            messages: {
                en: 'You must be mature.'
            }
        });
    });
</script>
<script type="text/javascript">
    $(function () {
        $('#newsletter-form').parsley()
            // field validation
            .on('form:submit', function() {
                return false; // do not submit
            });
    });
</script>

Environnement

Chrome version 56.0.2924.87 (64 bits) sur Windows 7

Autres informations qui peuvent être utiles

  • Fuseau horaire de l'appareil : "(UTC + 00:00) Amsterdam, Berlin, Berne, Rom, Stockholm, Wien"
  • L'heure et la date d'exécution du code : "Lun 27 février 2017 13:38:04 GMT+0100"
  • Autres bibliothèques utilisées : jquery-3.1.1, bootstrap-3.3.7, bootstrap-datepicker-1.6.4, parselyjs-2.6.2
console.log( (new Date()).toString()) // Mon Feb 27 2017 13:38:04 GMT+0100 (Mitteleuropäische Zeit)
console.log((new Date()).toLocaleString()) // 27.2.2017, 13:38:04
console.log( (new Date()).getTimezoneOffset()) // -60
console.log( navigator.userAgent) // Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
console.log(moment.version) // 2.17.1

Commentaire le plus utile

Est-ce que cela fonctionne mieux si vous utilisez simplement

age = moment().diff(birthday, 'years');

Tous les 3 commentaires

Je pense que le calcul est faux, mais c'est super proche. Peut-être à cause d'erreurs d'arrondi ?

Par exemple:

a = moment("27.02.1999", "DD.MM.YYYY");
b = moment("28.02.1999", "DD.MM.YYYY");
c = moment("27.02.2017", "DD.MM.YYYY");
moment.duration(c.diff(a)).asYears(); // 18.00173857094944
moment.duration(c.diff(b)).asYears(); // 17.99900066394245

Ainsi, dans ce cas, moment.duration(end.diff(start)).asYears() renverra 18 environ 16 heures avant qu'il ne le devrait.

Si vous essayez de vérifier si un âge est supérieur ou égal à 18 ans, vous pouvez comparer les années, puis (si 18 ans séparent) les mois, puis les jours.

// pseudocode, correctness not guaranteed
function is18(start, end) {
  if (end.year() - start.year() == 18) {
    if (end.month() == start.month()) {
      return end.date() >= start.date();
    }
    return end.month() > start.month();
  }
  return end.year() - 18 > start.year();
}

Est-ce que cela fonctionne mieux si vous utilisez simplement

age = moment().diff(birthday, 'years');

@mj1856

Ouais, ça marche très bien.

// today date is: 03.03.2017

// exactly age 18    
var birthday = moment("03.03.1999", "DD.MM.YYYY"),
    age = moment().diff(birthday, 'years');

      console.log(age); // output: 18; expected: == 18

// one day under age 18
var birthday = moment("04.03.1999", "DD.MM.YYYY"),
    age = moment().diff(birthday, 'years');

    console.log(age); // output: 17; expected: < 18
Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

tanepiper picture tanepiper  ·  3Commentaires

nikocraft picture nikocraft  ·  3Commentaires

dogukankotan picture dogukankotan  ·  3Commentaires

alvarotrigo picture alvarotrigo  ·  3Commentaires

slavafomin picture slavafomin  ·  3Commentaires