【问题标题】:How to get the difference of two dates in mm-dd-hh format in Javascript如何在Javascript中以mm-dd-hh格式获取两个日期的差异
【发布时间】:2023-04-02 16:38:01
【问题描述】:

我可以使用 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');

month 返回月份, days 返回天数的差值。但我想要答案在

MM-DD-hh 格式,例如 2 个月 12 天 5 小时。我无法直接转换日期,因为还有其他问题,例如闰年。有没有其他方法可以全力以赴并计算一切?如果有任何帮助,我会在 Angular js 中执行此操作

【问题讨论】:

  • 日期相隔两个月是什么意思?例如,3 月 3 日至 5 月 3 日,即使这与 2 月 3 日至 4 月 3 日的天数不同?
  • 您是按日历月还是银行月来定义月份? (月总是 30 天)
  • 是的,如果我计算 2 月 3 日和 4 月 4 日之间的差异,它应该给我 2 个月 1 天与 3 月 3 日 5 月 4 日 2 个月 1 天相同。虽然日期不同,但它们都应该返回 2 个月 1 天 @nnnnnn
  • 不,如果是 30 天,那会很容易@PeterS 这就是问题所在
  • 为什么是“mm-dd-hh”格式?这太奇怪了。

标签: javascript angularjs date datetime


【解决方案1】:

获得两个日期之间的精确差异并不简单,因为年、月和日的长度不同。此外,加法不一定与减法对称,例如是 4 月 30 日加一个月是 5 月 30 日,但 5 月 31 日加一个月是 6 月 30 日还是 7 月 1 日?类似于 2 月 29 日正负 1 年。

以下内容尝试处理这些问题,因此如果添加一个月会超过一个月,则日期会返回到上个月的最后一天。希望cmets足够,如果没有,请要求澄清。

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. 在闰年,1 月 31 日至 2 月 29 日为 1 个月,2016 年 2 月 28 日为 28 天。
  3. 非闰年,1 月 31 日至 2 月 28 日为 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 行代码。

【讨论】:

    【解决方案2】:
     var date1 = new Date("4/14/2016");
     var date2 = new Date("10/16/2016");
     var timeDiff = Math.abs(date2.getTime() - date1.getTime());
    
     var newDate = new Date(timeDiff);
     alert(newDate);
     alert("Month " + (newDate.getUTCMonth() + 1));
     alert("Date " + newDate.getUTCDate());
     alert("Hour " + newDate.getUTCHours());
    

    【讨论】:

    • 这种方法不起作用,因为它假设从开始到结束的天数和月数的进展与从 1970 年 1 月 1 日开始的毫秒数完全相同,例如2 月 1 日和 3 月 1 日之间的差应该是 1 个月,但这个算法只会返回 28 天。
    【解决方案3】:

    您不能将差异(差异结果是一个数字)转换为 MM-DD-hh 格式,因为有些月份是 30 天,其他月份是 31 天。

    它可以从开始和结束日期时间计算。

    您正在寻找类似的功能,但不准确

    moment("2010-01-01").from("2010-12-01")
    

    结果:一年前

    moment("2010-01-01").from("2010-12-01", true)
    

    结果:一年

    【讨论】:

    • 是的,它可以完成将需要很多代码:(如果直接完成
    猜你喜欢
    • 2019-03-11
    • 1970-01-01
    • 2017-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-24
    • 2016-04-26
    相关资源
    最近更新 更多