JavaScript 中以年、月、日为单位的两个日期之间的差异

IT技术 javascript date datediff
2021-01-18 19:49:43

我已经搜索了 4 个小时,但没有找到解决方案来获取 JavaScript 中两个日期之间的年、月和日差异,例如:2010 年 4 月 10 日是 3 年 x 月和 y 天前。

有很多解决方案,但它们仅提供天数或月数或年格式的差异,或者它们不正确(意味着不考虑一个月或闰年等中的实际天数等)。做到这一点真的有那么难吗?

我看过:

在 php 中很容易,但不幸的是我只能在该项目中使用客户端脚本。任何可以做到这一点的库或框架也很好。

以下是日期差异的预期输出列表:

//Expected output should be: "1 year, 5 months".
diffDate(new Date('2014-05-10'), new Date('2015-10-10'));

//Expected output should be: "1 year, 4 months, 29 days".
diffDate(new Date('2014-05-10'), new Date('2015-10-09'));

//Expected output should be: "1 year, 3 months, 30 days".
diffDate(new Date('2014-05-10'), new Date('2015-09-09'));

//Expected output should be: "9 months, 27 days".
diffDate(new Date('2014-05-10'), new Date('2015-03-09'));

//Expected output should be: "1 year, 9 months, 28 days".
diffDate(new Date('2014-05-10'), new Date('2016-03-09'));

//Expected output should be: "1 year, 10 months, 1 days".
diffDate(new Date('2014-05-10'), new Date('2016-03-11'));
6个回答

你需要多精确?如果您确实需要考虑普通年份和闰年,以及月份之间天数的确切差异,那么您将不得不编写一些更高级的东西,但对于基本和粗略的计算,这应该可以解决问题:

today = new Date()
past = new Date(2010,05,01) // remember this is equivalent to 06 01 2010
//dates in js are counted from 0, so 05 is june

function calcDate(date1,date2) {
    var diff = Math.floor(date1.getTime() - date2.getTime());
    var day = 1000 * 60 * 60 * 24;

    var days = Math.floor(diff/day);
    var months = Math.floor(days/31);
    var years = Math.floor(months/12);

    var message = date2.toDateString();
    message += " was "
    message += days + " days " 
    message += months + " months "
    message += years + " years ago \n"

    return message
    }


a = calcDate(today,past)
console.log(a) // returns Tue Jun 01 2010 was 1143 days 36 months 3 years ago

请记住,这是不精确的,为了完全精确地计算日期,必须有一个日历并知道一年是否是闰年,而且我计算月数的方式也只是近似值.

但是你可以很容易地改进它。

感谢您的回答。但我修改了一点,下面给出了链接:jsfiddle.net/PUSQU/8
2021-03-16 19:49:43
谢谢您的回答。这很棒,但恐怕正是我所说的我不是在寻找。如前所述,您写的内容很简单-考虑到一个月中的确切天数、闰年……这是困难的事情。
2021-03-25 19:49:43

实际上,有一个带有 moment.js 插件的解决方案,而且非常简单。

你可能会使用moment.js

不要再重新发明轮子了。

只需插入Moment.js 日期范围插件


例子:

var starts = moment('2014-02-03 12:53:12');
var ends   = moment();

var duration = moment.duration(ends.diff(starts));

// with ###moment precise date range plugin###
// it will tell you the difference in human terms

var diff = moment.preciseDiff(starts, ends, true); 
// example: { "years": 2, "months": 7, "days": 0, "hours": 6, "minutes": 29, "seconds": 17, "firstDateWasLater":  false }


// or as string:
var diffHuman = moment.preciseDiff(starts, ends);
// example: 2 years 7 months 6 hours 29 minutes 17 seconds

document.getElementById('output1').innerHTML = JSON.stringify(diff)
document.getElementById('output2').innerHTML = diffHuman
<html>
<head>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.14.1/moment.min.js"></script>

  <script src="https://raw.githubusercontent.com/codebox/moment-precise-range/master/moment-precise-range.js"></script>

</head>
<body>
  
  <h2>Difference between "NOW and 2014-02-03 12:53:12"</h2>
  <span id="output1"></span>
  <br />
  <span id="output2"></span>
  
</body>
</html>

答案是不公平的。我已经找到了图书馆 - 但如果答案在顶部,我会节省几个小时
2021-03-10 19:49:43
缩小版只有 51KB。~20KB 如果服务器托管启用 gzip 压缩(cloudflare 托管启用)。更不用说浏览器可能已经从您访问过的另一个使用相同 CDN 引用的站点缓存了。考虑到这个库还如何处理日期的许多其他有用的东西,考虑到在 JavaScript 中处理日期的难度,我认为这是一个很好的权衡。
2021-03-18 19:49:43
不要只为计算机优化,为程序员优化。您的 2 行功能可以及时变为 20-30。在我的书中,使用经过测试且一致的库会更好。
2021-03-24 19:49:43
当您只需要 2 行功能时,您将支付超过 100k 的巨额罚款
2021-03-25 19:49:43
那么不要在这些情况下使用它。
2021-04-03 19:49:43

对此进行了修改,使其更加准确。它将日期转换为 'YYYY-MM-DD' 格式,忽略 HH:MM:SS,并采用可选的 endDate 或使用当前日期,并且不关心值的顺序。

function dateDiff(startingDate, endingDate) {
    var startDate = new Date(new Date(startingDate).toISOString().substr(0, 10));
    if (!endingDate) {
        endingDate = new Date().toISOString().substr(0, 10);    // need date in YYYY-MM-DD format
    }
    var endDate = new Date(endingDate);
    if (startDate > endDate) {
        var swap = startDate;
        startDate = endDate;
        endDate = swap;
    }
    var startYear = startDate.getFullYear();
    var february = (startYear % 4 === 0 && startYear % 100 !== 0) || startYear % 400 === 0 ? 29 : 28;
    var daysInMonth = [31, february, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

    var yearDiff = endDate.getFullYear() - startYear;
    var monthDiff = endDate.getMonth() - startDate.getMonth();
    if (monthDiff < 0) {
        yearDiff--;
        monthDiff += 12;
    }
    var dayDiff = endDate.getDate() - startDate.getDate();
    if (dayDiff < 0) {
        if (monthDiff > 0) {
            monthDiff--;
        } else {
            yearDiff--;
            monthDiff = 11;
        }
        dayDiff += daysInMonth[startDate.getMonth()];
    }

    return yearDiff + 'Y ' + monthDiff + 'M ' + dayDiff + 'D';
}

然后你可以像这样使用它:

// based on a current date of 2019-05-10
dateDiff('2019-05-10'); // 0Y 0M 0D
dateDiff('2019-05-09'); // 0Y 0M 1D
dateDiff('2018-05-09'); // 1Y 0M 1D
dateDiff('2018-05-18'); // 0Y 11M 23D
dateDiff('2019-01-09'); // 0Y 4M 1D
dateDiff('2019-02-10'); // 0Y 3M 0D
dateDiff('2019-02-11'); // 0Y 2M 27D
dateDiff('2016-02-11'); // 3Y 2M 28D - leap year
dateDiff('1972-11-30'); // 46Y 5M 10D
dateDiff('2016-02-11', '2017-02-11'); // 1Y 0M 0D
dateDiff('2016-02-11', '2016-03-10'); // 0Y 0M 28D - leap year
dateDiff('2100-02-11', '2100-03-10'); // 0Y 0M 27D - not a leap year
dateDiff('2017-02-11', '2016-02-11'); // 1Y 0M 0D - swapped dates to return correct result
dateDiff(new Date() - 1000 * 60 * 60 * 24); // 0Y 0M 1D

较旧的不太准确但更简单的版本

@RajeevPNadig 的回答正是我要找的,但他的代码返回的值不正确。这不是很准确,因为它假设从 1970 年 1 月 1 日开始的日期序列与具有相同天数的任何其他序列相同。例如,它将 7 月 1 日到 9 月 1 日(62 天)的差计算为 0Y 2M 3D 而不是 0Y 2M 0D,因为 1970 年 1 月 1 日加上 62 天是 3 月 3 日。

// startDate must be a date string
function dateAgo(date) {
    var startDate = new Date(date);
    var diffDate = new Date(new Date() - startDate);
    return ((diffDate.toISOString().slice(0, 4) - 1970) + "Y " +
        diffDate.getMonth() + "M " + (diffDate.getDate()-1) + "D");
}

然后你可以像这样使用它:

// based on a current date of 2018-03-09
dateAgo('1972-11-30'); // "45Y 3M 9D"
dateAgo('2017-03-09'); // "1Y 0M 0D"
dateAgo('2018-01-09'); // "0Y 2M 0D"
dateAgo('2018-02-09'); // "0Y 0M 28D" -- a little odd, but not wrong
dateAgo('2018-02-01'); // "0Y 1M 5D" -- definitely "feels" wrong
dateAgo('2018-03-09'); // "0Y 0M 0D"

如果您的用例只是日期字符串,那么如果您只想要一个快速而肮脏的 4 班轮,这可以正常工作。

这个答案对于不同的日期都失败了,因为它假设从 1970 年 1 月 1 日开始的日期序列与任何其他相同天数的序列相同,这是无效的。例如,它将 7 月 1 日到 9 月 1 日(62 天)的差计算为 0Y 2M 3D 而不是 0Y 2M 0D,因为 1970 年 1 月 1 日加上 62 天是 3 月 3 日。
2021-03-20 19:49:43
@RobG 我想我清楚地说明了答案中的局限性,并将您的评论作为解释。鉴于这里的许多其他答案都存在完全相同的问题,因此不完全确定为什么会投反对票。这是为了简单快捷,但以牺牲完美的准确性为代价。
2021-03-27 19:49:43
这正是我要找的,谢谢!
2021-04-04 19:49:43
这需要更多的赞成票。它解决了上面的答案所遇到的问题。做得好!
2021-04-06 19:49:43
@RobG 也为您添加了一个更长但准确的版本。
2021-04-08 19:49:43

我使用这个简单的代码来获得年、月、日与当前日期的差异。

var sdt = new Date('1972-11-30');
var difdt = new Date(new Date() - sdt);
alert((difdt.toISOString().slice(0, 4) - 1970) + "Y " + (difdt.getMonth()+1) + "M " + difdt.getDate() + "D");
编写的此代码返回不正确的结果。假设今天的日期是 2018-03-09:sdt = new Date("2017-03-09")会提示“1Y 1M 1D”, sdt = new Date("2018-02-09")会提示“0Y 1M 29D”, sdt = new Date("2018-03-09")会提示“0Y 1M 1D”。我对此答案的编辑被拒绝,因此我添加了更正后的代码作为单独的答案。
2021-03-22 19:49:43

我认为您正在寻找与我想要的相同的东西。我尝试使用 javascript 提供的毫秒差异来做到这一点,但这些结果在日期的现实世界中不起作用。如果您想要 2016 年 2 月 1 日和 2017 年 1 月 31 日之间的差异,我想要的结果是 1 年 0 个月和 0 天。整整一年(假设您将最后一天算作一整天,就像在公寓租约中一样)。但是,毫秒方法将为您提供 1 年 0 个月和 1 天,因为日期范围包括闰年。所以这是我在 javascript 中为我的 adobe 表单使用的代码(您可以命名字段):(已编辑,我更正了一个错误)

var f1 = this.getField("LeaseExpiration");
var g1 = this.getField("LeaseStart");


var end = f1.value
var begin = g1.value
var e = new Date(end);
var b = new Date(begin);
var bMonth = b.getMonth();
var bYear = b.getFullYear();
var eYear = e.getFullYear();
var eMonth = e.getMonth();
var bDay = b.getDate();
var eDay = e.getDate() + 1;

if ((eMonth == 0)||(eMonth == 2)||(eMonth == 4)|| (eMonth == 6) || (eMonth == 7) ||(eMonth == 9)||(eMonth == 11))

{
var eDays =  31;
}

if ((eMonth == 3)||(eMonth == 5)||(eMonth == 8)|| (eMonth == 10))

{
var eDays = 30;
}

if (eMonth == 1&&((eYear % 4 == 0) && (eYear % 100 != 0)) || (eYear % 400 == 0))
{
var eDays = 29;
}

if (eMonth == 1&&((eYear % 4 != 0) || (eYear % 100 == 0)))
{
var eDays = 28;
}


if ((bMonth == 0)||(bMonth == 2)||(bMonth == 4)|| (bMonth == 6) || (bMonth == 7) ||(bMonth == 9)||(bMonth == 11))

{
var bDays =  31;
}

if ((bMonth == 3)||(bMonth == 5)||(bMonth == 8)|| (bMonth == 10))

{
var bDays = 30;
}

if (bMonth == 1&&((bYear % 4 == 0) && (bYear % 100 != 0)) || (bYear % 400 == 0))
{
var bDays = 29;
}

if (bMonth == 1&&((bYear % 4 != 0) || (bYear % 100 == 0)))
{
var bDays = 28;
}


var FirstMonthDiff = bDays - bDay + 1;


if (eDay - bDay < 0)
{

eMonth = eMonth - 1;
eDay = eDay + eDays;

}

var daysDiff = eDay - bDay;

if(eMonth - bMonth < 0)
{
eYear = eYear - 1;
eMonth = eMonth + 12;
}

var monthDiff = eMonth - bMonth;

var yearDiff = eYear - bYear;

if (daysDiff == eDays)
{
daysDiff = 0;
monthDiff = monthDiff + 1;

if (monthDiff == 12)
{
monthDiff = 0;
yearDiff = yearDiff + 1;
}

}

if ((FirstMonthDiff != bDays)&&(eDay - 1 == eDays))

{
daysDiff = FirstMonthDiff;

}
event.value = yearDiff + " Year(s)" + " " + monthDiff + " month(s) " + daysDiff + " days(s)"
非常感谢,解决方案包括一切。
2021-03-12 19:49:43
就像一个魅力,但我不得不做一些调整......如果 ((FirstMonthDiff != bDays)&&(eDay == eDays)) 和 Lasitha Benaragama 在上面的评论中提到的变化。
2021-03-30 19:49:43
if (eDay - bDay < 0) { eMonth = eMonth - 1; eDay = eDay + bDays;}
2021-04-02 19:49:43
对于 2020 年 3 月 4 日和 2020 年 2 月 20 日,这将为 16 天。但不是16天。
2021-04-06 19:49:43