Moment: 具有边界生日值的最小年龄计算会导致意外行为

创建于 2017-02-27  ·  3评论  ·  资料来源: moment/moment

问题描述和重现步骤

我想检查一个人是否已满 18 岁。 所以我使用diff()duration()函数来计算人的年龄。 我写了一些带有边界值的测试来测试我的逻辑。 我想测试我是否正好比 18 岁小一天和正好 18 岁。所以我选择了两个生日进行测试(今天是 27.02.2017):

  • 正好 18: 27.02.1999
  • 18 岁以下的某一天:28.02.1999

复制

// 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

我认为计算是正确的,因为有闰年。 但就生日而言,这个人不应该是18岁。 也许还有其他方法可以用 momentjs 来做到这一点,但我做错了。 如果是这样,那么在 momentjs 的文档中提到会很好。

生产代码

<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>

环境

Windows 7 上的 Chrome 版本 56.0.2924.87(64 位)

其他可能有用的信息

  • 机器时区:“(UTC+01:00)阿姆斯特丹、柏林、伯尔尼、罗马、斯德哥尔摩、维也纳”
  • 代码运行的时间和日期:“Mon Feb 27 2017 13:38:04 GMT+0100”
  • 其他正在使用的库: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

最有用的评论

如果你只是使用它会更好吗

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

所有3条评论

我认为计算是错误的,但它非常接近。 也许是由于舍入错误?

例如:

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

因此,在这种情况下, moment.duration(end.diff(start)).asYears()将在它应该返回的大约 16 小时之前返回18

如果您要检查年龄是否大于或等于 18,您可以比较年份,然后(如果相隔 18 年)月份,然后是天。

// 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();
}

如果你只是使用它会更好吗

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

@mj1856

是的,这很好用。

// 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
此页面是否有帮助?
0 / 5 - 0 等级

相关问题

fernandoacorreia picture fernandoacorreia  ·  31评论

mau21mau picture mau21mau  ·  69评论

dushkostanoeski picture dushkostanoeski  ·  23评论

GautierT picture GautierT  ·  149评论

llacroix picture llacroix  ·  186评论