Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
164 views
in Technique[技术] by (71.8m points)

angularjs - How to get the difference of two dates in mm-dd-hh format in Javascript

I can get the difference between two dates using moment.js or plain js.

in 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');

month returns month , days return difference in days . But I want the answer in

MM-DD-hh format for example 2 months 12 days 5 hours . I can not convert the day directly cause there is other issues like leap year . Is there any other way then going all out and calculating everything ? I am doing this in angular js if that is of any help

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Getting the precise difference between two dates is not simple as years, months, and days have different lengths. Also, adding is not necessarily symmetric with subtraction, e.g. is 30 April plus one month is 30 May, but is 31 May plus one month 30 June or 1 July? Similar with 29 Feb plus or minus 1 year.

The following tries to deal with those issues, so that if adding a month rolls over an extra month, the date is returned to the last day of the previous month. Hopefully the comments are sufficient, if not, ask for clarification.

The dateDiff function returns an array of the values for years, months, days, etc. To get MM-DD-hh, just get that and format it any way you want. I've included a small formatting function that just prints out the non–zero components.

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...