考虑闰年的日期准确时间

时间:2016-03-23 17:44:19

标签: javascript date

我想以这种格式准确地显示用户的年龄。似乎闰年使输出有点出乎意料(我23岁,而我的输出是22.9)。有什么我可以做的不同来解释这个吗?

var t= (new Date()) - (new Date(1993,3,1));
t/= (1000*60*60*24*365);

http://codepen.io/kylebillings/pen/RaVLBM

提前感谢您解决问题的帮助!

1 个答案:

答案 0 :(得分:0)

在两个日期之间表达时间的问题是,有很多关于如何处理不同长度的年,月和日的决定,例如

  1. 如果有人在2月29日出生,他们会在2月28日或次年3月1日结束吗?主管部门(和人民)的意见不同。

  2. 5月31日加6月30日或7月1日?

  3. 夏令时结束时,一段时间会发生两次。对于那个时期的时间,是否应该使用更早或更晚的时间?

  4. 确定两个日期之间的年,月,日等的特定算法是否正确取决于您对上述内容的回答。如果您选择使用库,您应该知道它如何处理上述所有内容并确保它适合使用它的人。

    下面是一个计算两个日期之间差异的函数,基本算法是计算出必须添加到较早日期的值才能到达更晚的日期。将截至3月31日的一个月添加到6月30日,因此没有月份滚动。同样,2月29日加一年是2月28日,而不是3月1日。夏令时可能会有一些怪癖,它依赖浏览器来做到这一点(最好是变化,在某些情况下是完全错误的),所以避免DST转换期。我有一个算法可以修复那些发布的算法,但我还没有为它开发代码。

    因此,以下内容可能适用与否,但它以一致的方式处理问题,并显示比我遇到的其他算法更准确的通用算法。它使用自己的ISO 8601扩展格式日期解析器来删除前面的浏览器错误,但它无法全部删除它们。 Safari在夏令时转换方面存在问题。这只是完成工作的一种方式,不一定是明确的。有一套广泛的测试,我想我已经涵盖了所有测试,但只有进一步的测试才会证实这一点。

    代码在每个浏览器中运行回IE 5,但运行测试需要 .every .forEach .toISOString

    
    
    // Ensure consistent parsing of ISO 8601 date strings. Some browsers will still parse
    // ISO dates without a timezone as UTC (looking at you, Safari). Also, some may parse
    // ISO dates without a timezone as local but most us UTC (inconsistent with ISO 8601)
    // Certain versions of Chrome, allow out of bounds values in ISO 8601 strings.
    // IE 8 and lower will not parse ISO 8601 strings at all.
    //
    // Date (and time) only treated as local: 2016-02-29, 2016-02-29T12:23:57
    //
    // Date with timezone applies the offset: 2016-02-29Z, 2016-02-29+10:00, 2016-02-29T12:23:57+10:00
    function parseISO (s) {
    
      // Get offset if s ends in Z/z or +/-dd:dd 
      var zone = /z$/i.test(s)? ['+00:00'] : s.match(/[+-]\d\d:\d\d$/);
      
      // Trim offset from string before getting other parts
      // Otherwise valid strings like 2016T-04:30 will break (2016 with offset -04:30)
      var b = (zone? s.replace(/z$|[+-]\d\d:\d\d$/i,'') : s).split(/\D/);
    
      // Resolve zone to ISO 8601 sign orientation and minutes, or set to null if missing
      if (zone) {
        var sign = /^-/.test(zone[0])? -1 : 1;
        var z = zone[0].match(/\d+/g);
        zone = sign * (z[0]*60 + Number(z[1]));
      }
      
      // Create date. If zone available, use UTC and zone, otherwise use local and hope 
      // that the Date constructor gets it right for DST
      if (zone !== null) {
                                // y      m                  d        h         m                s        ms
        return new Date(Date.UTC(b[0], (b[1]? b[1]-1 : 0), b[2]||1, b[3]||0, (b[4]||0) - zone, b[5]||0, ((b[6]||'0')+'00').slice(0,3)));
      } else {
        return new Date(         b[0], (b[1]? b[1]-1 : 0), b[2]||1, b[3]||0, b[4]||0,          b[5]||0, ((b[6]||'0')+'00').slice(0,3));
      }
    }
    
    // If precise is true, then time is used, otherwise only the
    // date difference is returned
    function dateDiff(startDate, endDate, precise) {
    
      // If start is after end, swap dates
      if (startDate > endDate) {
        var t = startDate;
        startDate = endDate;
        endDate = t;
      }
      
      var years = months = days = hrs = mins = secs = ms = 0;
      var d0 = new Date(+startDate), d1 = new Date(+endDate);
      var dx;
     
      // Add years to a date. Only tricky one is Feb 29. 
      function addYears(date, years) {
        var startMonth = date.getMonth();
        date.setFullYear(date.getFullYear() + years);
        // Catch Feb 29 -> March 1 
        if (date.getMonth() != startMonth) {
          date.setDate(0);
        }
        return date;
      }
    
      // Add months to a date. If goes beyond end of month when it shouldn't, trim
      // to last day of epxected month (e.g. 31 May + 1 => 31 June => 1 July => 30 June)
      function addMonths(date, months){
        var t = new Date(+date);
        var startDate = date.getDate();
        date.setMonth(date.getMonth() + +months);
        // If gone past end of month, set to last day of previous month
        if (date.getDate() != startDate) {
          date.setDate(0);
        }
        return date;
      }
      
      // Return number of days in month for date
      function daysInMonth(date) {
        var d = new Date(+date);
        d.setMonth(d.getMonth() + 1, 0);
        return d.getDate();
      }
      
      // If precision not required, set hours to start of day
      if (!precise) {
        d0.setHours(0,0,0,0);
        d1.setHours(0,0,0,0);
      }
      
      // Temp date for doing initial estimate
      dx = new Date(+d0);
      
      // Get year difference, add to early date (use temp date)
      years = d1.getFullYear() - d0.getFullYear();
      addYears(dx, years);
    
      // If gone past end, subtract 1 and update real early date
      if (dx > d1) years -= 1;
      addYears(d0, years);
      
      // Reset temp date
      dx = new Date(+d0);
      
      // Do months as for years
      months = ((d1.getMonth() + 12) - d0.getMonth()) % 12;
      // If month difference is zero but days are more than 360, must be next year so make months 11
      months = (d1 - d0) / 8.64e7 > 330? 11 : months;
      
      addMonths(dx, months);
      
      if (dx > d1) months -= 1;
      addMonths(d0, months);
      dx = new Date(+d0);
      
      // From here everything is predictable (even daylight saving, as long as the UA gets it right)
      var diff = d1 - d0;
      days  =  diff / 8.64e7 | 0;
      hrs   = (diff % 8.64e7) / 3.6e6 | 0;
      mins  = (diff % 3.6e6)  / 6e4 | 0;
      secs  = (diff % 6e4)    / 1e3 | 0;
      ms    =  diff % 1e3;
    
      return {y:years, m:months, d:days, hr:hrs, min:mins, sec:secs, ms:ms};  
    }
    
    
    // Run date tests
    function sameAs(a0, a1) {
      return a0.every(function (v, i){return a1[i] == v});
    }
    var resultHTML = ['<table><tr><th>#<th>Start<th>End<th>Precise<th>Expect<th>Got'];
    [
    //*
      {start:'2016-01-31T00:00:00', end:'2016-01-31T00:00:00', precise: true, expect:  [0,0,0,0,0,0,0]},
      {start:'2016-01-31T00:00:00', end:'2016-01-31T00:00:00', precise: false, expect: [0,0,0,0,0,0,0]},
    
      {start:'2016-01-31T00:00:00', end:'2017-03-01T00:00:00', precise: true, expect:  [1,1,1,0,0,0,0]},
      {start:'2016-01-31T00:00:00', end:'2017-03-01T00:00:00', precise: false, expect: [1,1,1,0,0,0,0]},
    
      {start:'2016-01-29T00:00:00', end:'2017-03-01T00:00:00', precise: true, expect:  [1,1,1,0,0,0,0]},
      {start:'2016-01-29T00:00:00', end:'2017-03-01T00:00:00', precise: false, expect: [1,1,1,0,0,0,0]},
    
      {start:'2016-02-29T00:00:00', end:'2017-02-28T00:00:00', precise: true, expect:  [1,0,0,0,0,0,0]},
      {start:'2016-02-29T00:00:00', end:'2017-02-28T00:00:00', precise: false, expect: [1,0,0,0,0,0,0]},
    //*/
      {start:'2016-02-29T00:00:01.1Z', end:'2017-02-28T00:00:00Z', precise: true, expect:  [0,11,29,23,59,58,900]},
      {start:'2016-02-29T00:00:01.1Z', end:'2017-02-28T00:00:00Z', precise: false, expect: [1,0,0,0,0,0,0]}, //*,
    
      {start:'2016-02-28T00:00:01.1', end:'2017-02-28T00:00:00', precise: true, expect:  [0,11,30,23,59,58,900]},
      {start:'2016-02-28T00:00:01.1', end:'2017-02-28T00:00:00', precise: false, expect: [1,0,0,0,0,0,0]},
    
      {start:'2016-02-27T00:00:01.1', end:'2017-02-28T00:00:00', precise: true, expect:  [1,0,0,23,59,58,900]},
      {start:'2016-02-27T00:00:01.1', end:'2017-02-28T00:00:00', precise: false, expect: [1,0,1,0,0,0,0]},
    
      {start:'2016-01-30T00:00:00', end:'2017-03-01T00:00:00', precise: true, expect:  [1,1,1,0,0,0,0]},
      {start:'2016-01-30T00:00:00', end:'2017-03-01T00:00:00', precise: false, expect: [1,1,1,0,0,0,0]},
      
      {start:'2016-01-30T00:00:01.001', end:'2017-03-01T00:00:00', precise: true, expect:  [1,1,0,23,59,58,999]},
      {start:'2016-01-30T00:00:01.001', end:'2017-03-01T00:00:00', precise: false, expect: [1,1,1,0,0,0,0]},
      
      {start:'2016-01-31T00:00:01.001', end:'2017-03-01T00:00:00', precise: true, expect:  [1,1,0,23,59,58,999]},
      {start:'2016-01-31T00:00:01.001', end:'2017-03-01T00:00:00', precise: false, expect: [1,1,1,0,0,0,0]},
      
      {start:'2016-01-30T00:00:01.001', end:'2017-03-02T00:00:00', precise: true, expect:  [1,1,1,23,59,58,999]},
      {start:'2016-01-30T00:00:01.001', end:'2017-03-02T00:00:00', precise: false, expect: [1,1,2,0,0,0,0]},
      
      {start:'2016-10-30T00:00:00', end:'2016-12-31T00:00:00', precise: true, expect:  [0,2,1,0,0,0,0]},
      {start:'2016-10-30T00:00:00', end:'2016-12-31T00:00:00', precise: false, expect: [0,2,1,0,0,0,0]},
    
      {start:'2016-10-30T00:00:00.1', end:'2016-12-31T00:00:00', precise: true, expect:  [0,2,0,23,59,59,900]},
      {start:'2016-10-30T00:00:00.1', end:'2016-12-31T00:00:00', precise: false, expect: [0,2,1,0,0,0,0]},
    
      {start:'2016-10-31T00:00:00', end:'2016-12-30T00:00:00', precise: true, expect:  [0,1,30,0,0,0,0]},
      {start:'2016-10-31T00:00:00', end:'2016-12-30T00:00:00', precise: false, expect: [0,1,30,0,0,0,0]},
     
      {start:'2016-01-31T00:00:00Z', end:'2016-01-31T00:00:00Z', precise: true, expect: [0,0,0,0,0,0,0]},   // 0
      {start:'2016-01-31T00:00:00Z', end:'2016-01-31T00:00:00Z', precise: false, expect: [0,0,0,0,0,0,0]},  // 1
    
      {start:'2016-01-31T00:00:00Z', end:'2017-03-01T00:00:00Z', precise: true,  expect: [1,1,1,0,0,0,0]},  // 2
      {start:'2016-01-31T00:00:00Z', end:'2017-03-01T00:00:00Z', precise: false, expect: [1,1,1,0,0,0,0]},  // 3
     
      {start:'2016-01-29T00:00:00Z', end:'2017-03-01T00:00:00Z', precise: true,  expect: [1,1,1,0,0,0,0]},  // 4
      {start:'2016-01-29T00:00:00Z', end:'2017-03-01T00:00:00Z', precise: false, expect: [1,1,1,0,0,0,0]},  // 5
    
      {start:'2016-01-30T00:00:00Z', end:'2017-03-01T00:00:00Z', precise: true,  expect: [1,1,1,0,0,0,0]},  // 6
      {start:'2016-01-30T00:00:00Z', end:'2017-03-01T00:00:00Z', precise: false, expect: [1,1,1,0,0,0,0]},  // 7
     
      {start:'2016-01-30T00:00:01Z', end:'2017-03-01T00:00:00Z', precise: true,  expect: [1,1,0,23,59,59,0]},  // 8
      {start:'2016-01-30T00:00:01Z', end:'2017-03-01T00:00:00Z', precise: false, expect: [1,1,1,0,0,0,0]},  // 9
     
      {start:'2016-10-30T00:00:00Z', end:'2016-12-31T00:00:00Z', precise: true,  expect: [0,2,1,0,0,0,0]},  // 10
      {start:'2016-10-30T00:00:00Z', end:'2016-12-31T00:00:00Z', precise: false, expect: [0,2,1,0,0,0,0]},  // 11
    
      {start:'2016-10-31T00:00:00Z', end:'2016-12-30T00:00:00Z', precise: true,  expect: [0,1,30,0,0,0,0]},  // 12
      {start:'2016-10-31T00:00:00Z', end:'2016-12-30T00:00:00Z', precise: false, expect: [0,1,30,0,0,0,0]},  // 13
     
      {start:'2016-10-31T00:00:01Z', end:'2016-12-30T00:00:00Z', precise: true,  expect: [0,1,29,23,59,59,0]},  // 14
      {start:'2016-10-31T00:00:01Z', end:'2016-12-30T00:00:00Z', precise: false,  expect: [0,1,30,0,0,0,0]},  // 15
     
      {start:'2016-10-31T00:00:00Z', end:'2016-12-31T00:00:00Z', precise: true,  expect: [0,2,0,0,0,0,0]},  // 16
      {start:'2016-10-31T00:00:00Z', end:'2016-12-31T00:00:00Z', precise: false, expect: [0,2,0,0,0,0,0]},  // 17
     
      {start:'2016-10-31T00:00:00Z', end:'2016-12-01T00:00:00Z', precise: true, expect: [0,1,1,0,0,0,0]},  // 18
      {start:'2016-10-31T00:00:00Z', end:'2016-12-01T00:00:00Z', precise: false,  expect: [0,1,1,0,0,0,0]},  // 19
     
      {start:'2016-10-31T00:00:01Z', end:'2016-12-01T00:00:00Z', precise: true,  expect: [0,1,0,23,59,59,0]},  // 20
      {start:'2016-01-31T12:00:01Z', end:'2016-03-02T12:00:00Z', precise: false, expect: [0,1,2,0,0,0,0]},  // 21
    
      {start:'2016-01-31T12:00:01Z', end:'2016-03-02T12:00:00Z', precise: true,  expect: [0,1,1,23,59,59,0]},  // 22
      {start:'2016-01-31T12:00:01Z', end:'2016-03-01T12:00:00Z', precise: false, expect: [0,1,1,0,0,0,0]},  // 23
    
      {start:'2016-01-31T12:00:01Z', end:'2016-03-01T12:00:00Z', precise: true,  expect: [0,1,0,23,59,59,0]},  // 24
      {start:'2016-01-31T12:00:01Z', end:'2016-03-01T12:00:00Z', precise: false,  expect: [0,1,1,0,0,0,0]},  // 25
    
      {start:'2016-01-30T12:00:01Z', end:'2016-03-01T12:00:00Z', precise: true,  expect: [0,1,0,23,59,59,0]},   // 26
      {start:'2016-01-30T12:00:01Z', end:'2016-03-01T12:00:00Z', precise: false, expect: [0,1,1,0,0,0,0]},  // 27
    
      {start:'2016-01-31T12:00:01Z', end:'2016-03-02T12:00:00Z', precise: true,  expect: [0,1,1,23,59,59,0]},  // 28
      {start:'2016-01-31T12:00:01Z', end:'2016-03-02T12:00:00Z', precise: false, expect: [0,1,2,0,0,0,0]},  // 29
    
      {start:'2016-01-30T12:00:01Z', end:'2016-03-02T12:00:00Z', precise: true,  expect: [0,1,1,23,59,59,0]},  // 30
      {start:'2016-01-30T12:00:01Z', end:'2016-03-02T12:00:00Z', precise: false, expect: [0,1,2,0,0,0,0]},     // 31
     
      {start:'2016-03-15T12:00:00Z', end:'2016-04-14T12:00:01Z', precise: true,  expect: [0,0,30,0,0,1,0]},   // 32
      {start:'2016-03-15T12:00:00Z', end:'2016-04-14T12:00:01Z', precise: false,  expect: [0,0,30,0,0,0,0]},  // 33
    
      {start:'2016-07-23T12:00:01Z', end:'2016-09-24T12:00:00Z', precise: true,  expect: [0,2,0,23,59,59,0]},  // 34
      {start:'2016-07-23T12:00:01Z', end:'2016-09-24T12:00:00Z', precise: false,  expect: [0,2,1,0,0,0,0]},    // 35
    
      {start:'2016-07-31T12:00:01Z', end:'2018-07-01T12:00:00Z', precise: true,  expect: [1,11,0,23,59,59,0]},
      {start:'2016-07-31T12:00:01Z', end:'2018-07-01T12:00:00Z', precise: false,  expect: [1,11,1,0,0,0,0]},
    
      {start:'2016-07-01T12:00:01Z', end:'2018-07-31T12:00:00Z', precise: true,  expect: [2,0,29,23,59,59,0]},
      {start:'2016-07-01T12:00:01Z', end:'2018-07-31T12:00:00Z', precise: false,  expect: [2,0,30,0,0,0,0]}
    //*/
      ].forEach(function(obj, i) {
      var x = dateDiff(parseISO(obj.start), parseISO(obj.end), obj.precise)
      var r = [x.y, x.m, x.d, x.hr, x.min, x.sec, x.ms];
      resultHTML.push('<tr><td>'+i+'<td>' + obj.start + '<td>' + obj.end + '<td>' + (obj.precise? 'Y':'') +
                      '<td>' + (obj.precise? obj.expect : obj.expect.slice(0,3)) + '<td class="' + (sameAs(r, obj.expect)? '':'fail') + '">' +
                      (obj.precise? r : r.slice(0,3)));
    });
    document.write(resultHTML.join('') + '</table>');
    &#13;
    .fail {
      background-color: #ffbbbb;
    }
    table {
      font-family: courier, mono-spaced;
      font-size: 90%;
      border-collapse: collapse;
      border-top: 1px solid #bbbbbb;
      border-left: 1px solid #bbbbbb;
    }
    td, th {
      border-bottom: 1px solid #bbbbbb;
      border-right: 1px solid #bbbbbb;
    }
    td {
      padding: 2px  5px 2px 5px;
    }
    td:nth-Child(3) {
      text-align: center;
    }
    &#13;
    &#13;
    &#13;