一尘不染

如何在Javascript中以mm-dd-hh格式获取两个日期的差

javascript

我可以使用moment.js或纯js获得两个日期之间的差异。

在moment.js中

var a = moment(timestamp1);
var b = moment(timestamp2);
var month =a.diff(b, 'month');
var day =a.diff(b, 'day') - month;
var year =a.diff(b, 'hours');

一个月返回一个月,多个天返回以天为单位的差异。但是我想要答案

MM-DD-hh格式例如2个月12天5小时。我不能直接转换日期,因为还有leap年等其他问题。还有什么其他办法可以全力以赴并计算一切吗?我在angular
js中这样做,如果有帮助


阅读 213

收藏
2020-05-01

共1个答案

一尘不染

要获得两个日期之间的精确差并不容易,因为年,月和日的长度是不同的。另外,加法不一定与减法对称,例如4月30日加1月是5月30日,但是5月31日加1月30日还是7月1日吗?与2月29日前后1年相似。

以下内容尝试解决这些问题,因此,如果添加月份超过一个月,则日期将返回到上个月的最后一天。希望评论是足够的,如果不是,请澄清。

则DateDiff 函数返回值的年,月,日,等一个数组来获取MM-DD-
HH,只拿到并格式化你想要的任何方式。我提供了一个小的格式化功能,该功能仅打印出非零分量。

// Simple calculation of days between two dates based on time value

function getDaysDiff(start, end) {

  return ((parseStringUTC(end) - parseStringUTC(start))/8.64e7).toFixed(2);

}



// Expects input in ISO8601 format: yyyy-mm-ddThh:mm:ss.sssZ

// Always expects UTC

function parseStringUTC(s) {

  s = s.split(/\D/);

  s[6] = s[6]? ('0.'+ s[6]) * 1000 : 0;

  return new Date(Date.UTC(s[0],--s[1],s[2],s[3]||0,s[4]||0,s[5]||0,s[6]||0));

}



/*  Get the difference between two dates in years, months, days,

**  hours, minutes and seconds.

**

**  Difference is values to add to earlier date to reach later date.

**

**  Does not consider daylight saving changes so may be incorrect by offset

**  difference over daylight saving boundaries, so use UTC values (pass

**  values as date.toISOString() or format like ISO 8601 UTC)

**

**  @param {string} d0 - earlier date in format y-m-d h:m:s, can also be

**                       yyyy-mm-ddThh:mm:ssZ, the timezone offset is ignored

**                       the string is not validated

**  @param {string} d1 - later date in same format as above. If d1 is earlier

**                       than d0, results are unreliable.

**  @returns {Array}     values for years, months, days, hours, minutes and

**                       seconds (milliseconds as decimal part of seconds)

*/

function dateDiff(d0,d1) {

  var s = d0.split(/\D/);

  var e = d1.split(/\D/);

  // Calculate initial values for components,

  // Time component is optional, missing values treated as zero

  var ms  = (e[6]||0) - (s[6]||0);

  var sec = (e[5]||0) - (s[5]||0);

  var min = (e[4]||0) - (s[4]||0);

  var hr  = (e[3]||0) - (s[3]||0);

  var day = e[2] - s[2];

  var mon = e[1] - s[1];

  var yr  = e[0] - s[0];



  // Borrowing to resolve -ve values.

  if (ms < 0) {  // ms borrow from sec

    ms  += 1000;

    --sec;

  }

  if (sec < 0) { // sec borrows from min

    sec += 60;

    --min;

  }

  if (min < 0) { // min borrows from hr

    min += 60;

    --hr;

  }

  if (hr < 0) { // hr borrows from day

    hr  += 24;

    --day;

  }



  // Day borrows from month, a little complex but not too hard

  if (day < 0) {

    var prevMonLen = new Date(e[0], e[1]-1, 0).getDate();

    // If the start date is less than the number of days in the previous month,

    // set days to previous month length + current diff days value

    // Note that current diff days may have had a day borrowed, so don't use end date - start date

    // Otherwise, if the start date is equal to or greater than the number of

    // days in the previous month, just set to end date. That's because adding

    // 1 month to 30 Jan should be last day in Feb (i.e. 28 or 29), not 2 or 1 March

    // respectively, which is what happens if adding 1 month to a Date object for 30 Jan.

    // Similarly, 31 May + 1 month should be 30 June, not 1 July.

    day = s[2] < prevMonLen? prevMonLen + day : +e[2];

    --mon;

  }



  if (mon < 0) { // mon borrows from yr

    mon += 12;

    --yr;

  }



  // If days >= number of days in end month and end date is last day

  // of month, zero mon and add one to month

  // If then months = 12, zero and add one to years

  var endMonLen = new Date(e[0], e[1], 0).getDate();



  if (day >= endMonLen && s[2] > e[2] && e[2] == endMonLen) {

    day = 0;

    ++mon;

    if (mon == 12) {

      mon = 0;

      ++yr;

    }

  }

  return [yr,mon,day,hr,min,+(sec + '.' + ('00'+ms).slice(-3))];

}



/*  Format output from dateDiff function, e.g. 3years, 2 days, 23.12 seconds

**

**  @param {Array} v - values array in order years, months, days, hours, minutes

**                     seconds (milliseconds as decimal part of seconds)

**  @returns {string} Values with their names appended. Adds "s" to values other

**                    than 1, zero values omitted, e.g. "0 months" not returned.

*/

function formatOutput(v) {

  var values = ['year','month','day','hour','minute','second']

  return v.reduce(function (s, x, i) {

    s += x? (s.length? ' ' : '') +

         (i == 5? x.toFixed(3) : x) + ' ' + values[i] + (x==1?'':'s'):'';

    return s;

  }, '');

}



// Tests, focus on February

var dates = [

  ['2016-01-31','2016-03-01'], //  1 month   1 day  - 31 Jan + 1 month = 29 Feb

  ['2016-01-29','2016-03-01'], //  1 month   1 day  - 29 Jan + 1 month = 29 Feb

  ['2016-01-27','2016-03-01'], //  1 month   3 days - 27 Jan + 1 month = 27 Feb

  ['2016-01-27','2016-03-29'], //  2 months  2 days - 27 Jan + 2 month = 27 Mar

  ['2016-01-29','2016-03-27'], //  1 month  27 days - 29 Jan + 1 month = 29 Feb

  ['2015-12-31','2016-01-30'], // 30 days           - 31 Dec + 30 days = 30 Jan

  ['2015-12-27','2016-01-30'], //  1 month   3 days - 27 Dec + 1 month = 27 Jan

  ['2016-02-29','2017-02-28'], //  1 year could also be 11 months 30 days

                               // since 29 Feb + 11 months = 28 Feb, but 28 Feb is last day of month

                               // so roll over to full year

                               // Both work, but 1 year is more logical

  ['1957-12-04','2016-02-20'], // 58 years   2 months 16 days

  ['2000-02-29','2016-02-28'], // 15 years  11 months 30 days

                               // Not full year as Feb 2016 has 29 days

  ['2000-02-28','2016-02-28'], // 16 years

  ['2000-02-28','2016-02-29'], // 16 years  1 day

  ['2016-02-28T23:52:19.212Z','2016-12-02T01:48:57.102Z'] // 9 months 3 days 1 hour 56 minutes 37.899 seconds

];



var arr = [];

dates.forEach(function(a) {

  arr.push(a[0] + ' to ' + a[1] + '<br>' + formatOutput(dateDiff(a[0], a[1])));

});

document.write(arr.join('<br>'));


  table {

    border-collapse:collapse;

    border-left: 1px solid #bbbbbb;

    border-top: 1px solid #bbbbbb;

  }

  input {

    width: 12em;

  }

  input.bigGuy {

    width: 32em;

  }

  td {

    border-right: 1px solid #bbbbbb;

    border-bottom: 1px solid #bbbbbb;

  }

  td:nth-child(1) { text-align: right; }


<form onsubmit="this.doCalc.onclick(); return false;">

  <table>

    <tr>

      <td width="250"><label for="startDate">Start date (yyyy-mm-dd)</label>

      <td><input name="startDate" id="startDate" value="2012-08-09T22:15:03.22" size="25">

    <tr>

      <td><label for="endDate">End date (yyyy-mm-dd)</label>

      <td><input name="endDate" id="endDate" value="2013-08-13T12:10:03.22" size="25">

    <tr>

      <td><label for="dateDifference">Date difference: </label>

      <td><input name="dateDifference" readonly class="bigGuy">

    <tr>

      <td><label for="daysDifference">Days difference: </label>

      <td><input name="daysDifference" readonly>

    <tr>

      <td>

      <input type="button" value="Calc date difference" name="doCalc2" onclick="

        this.form.dateDifference.value = formatOutput(dateDiff(this.form.startDate.value, this.form.endDate.value));

        this.form.daysDifference.value = getDaysDiff(this.form.startDate.value, this.form.endDate.value) + ' days';

      ">

      <td><input type="reset">

  </table>

</form>

注意事项:

  1. 5月31日至6月30日为1个月。7月1日是没有意义的。
  2. 在a年中,1月31日至2月29日为1个月,至2016年2月28日为28天。
  3. 1月31日至2月28日不是a年,则为1个月。
  4. 2016年2月29日至2017年2月28日为1年,因为2月28日是该月的最后一天
  5. 2016年2月29日至2020年2月28日是3年11个月零30天,因为2月28日不是2020年每月的最后一天。
  6. 无需使用Date对象就可以完全实现此解决方案,为了方便起见,我只是使用它来获取一个月中的日子,但是不使用Date的替代方法大约需要4行代码。
2020-05-01