如何计算两个日期之间的天数?

IT技术 javascript date
2021-01-07 16:39:59

例如,在输入框中给定两个日期:

<input id="first" value="1/1/2000"/>
<input id="second" value="1/1/2001"/>

<script>
  alert(datediff("day", first, second)); // what goes here?
</script>

如何在 JavaScript 中获取两个日期之间的天数?

6个回答

这是一个快速而肮脏的实现datediff,作为解决问题中提出的问题的概念证明。它依赖于这样一个事实,即您可以通过减去它们来获得两个日期之间经过的毫秒数,这会将它们强制转换为原始数值(自 1970 年初以来的毫秒数)。

// new Date("dateString") is browser-dependent and discouraged, so we'll write
// a simple parse function for U.S. date format (which does no error checking)
function parseDate(str) {
    var mdy = str.split('/');
    return new Date(mdy[2], mdy[0]-1, mdy[1]);
}

function datediff(first, second) {
    // Take the difference between the dates and divide by milliseconds per day.
    // Round to nearest whole number to deal with DST.
    return Math.round((second-first)/(1000*60*60*24));
}

alert(datediff(parseDate(first.value), parseDate(second.value)));
<input id="first" value="1/1/2000"/>
<input id="second" value="1/1/2001"/>

您应该知道“正常”日期 API(名称中没有“UTC”)在用户浏览器的本地时区中运行,因此通常您可能会遇到问题,如果您的用户位于您不知道的时区期望,并且您的代码将不得不处理夏令时转换。您应该仔细阅读 Date 对象及其方法的文档,对于任何更复杂的情况,请强烈考虑使用为日期操作提供更安全、更强大的 API 的库。

此外,为了便于说明,为了简洁起见,该代码段使用window对象的命名访问,但在生产中,您应该使用标准化的 API,例如getElementById,或者更有可能是某些 UI 框架。

我认为 Math.trunc() 更合适。以防万一有人使用更精确的日期对象(即包括小时、分钟和秒)
2021-02-21 16:39:59
作为标记为正确且投票最高的答案(截至目前),值得评论的是该答案没有正确处理夏令时。请参阅 Michael Liu 的回答。
2021-02-28 16:39:59
@osullic——这个答案使用Math.round处理夏令时,但是它确实希望只传递日期字符串,而不是日期和时间。
2021-03-02 16:39:59
parseDate 究竟做了什么?
2021-03-06 16:39:59
请记住,在此示例中日期采用美国格式,即 MM/DD/YYYY。如果您需要英国版本,那么您需要将parseDate方法更改为:return new Date(mdy[2], mdy[1], mdy[0]-1);
2021-03-09 16:39:59

在撰写本文时,只有其他答案中的一个正确处理了 DST(夏令时)转换。以下是位于加利福尼亚的系统的结果:

                                        1/1/2013- 3/10/2013- 11/3/2013-
User       Formula                      2/1/2013  3/11/2013  11/4/2013  Result
---------  ---------------------------  --------  ---------  ---------  ---------
Miles                   (d2 - d1) / N   31        0.9583333  1.0416666  Incorrect
some         Math.floor((d2 - d1) / N)  31        0          1          Incorrect
fuentesjr    Math.round((d2 - d1) / N)  31        1          1          Correct
toloco     Math.ceiling((d2 - d1) / N)  31        1          2          Incorrect

N = 86400000

虽然Math.round返回了正确的结果,但我认为它有点笨拙。相反,通过在 DST 开始或结束时明确说明 UTC 偏移量的变化,我们可以使用精确算法:

function treatAsUTC(date) {
    var result = new Date(date);
    result.setMinutes(result.getMinutes() - result.getTimezoneOffset());
    return result;
}

function daysBetween(startDate, endDate) {
    var millisecondsPerDay = 24 * 60 * 60 * 1000;
    return (treatAsUTC(endDate) - treatAsUTC(startDate)) / millisecondsPerDay;
}

alert(daysBetween($('#first').val(), $('#second').val()));

解释

JavaScript 日期计算很棘手,因为Date对象在内部以 UTC 时间存储时间,而不是本地时间。例如,3/10/2013 12:00 AM 太平洋标准时间 (UTC-08:00) 存储为 3/10/2013 8:00 AM UTC,和 3/11/2013 12:00 AM 太平洋夏令时间 ( UTC-07:00) 存储为 3/11/2013 7:00 AM UTC。这一天,当地时间午夜至午夜,UTC 时间仅为 23 小时!

尽管当地时间中的一天可能多于或少于 24 小时,但 UTC 中的一天始终恰好是 24 小时。1daysBetween上面显示方法利用了这一事实,首先调用treatAsUTC将两个本地时间调整为 UTC 午夜,然后再进行减法和除法。

1. JavaScript 忽略闰秒。

@RobG:正如我在回答中所说:“虽然 Math.round 返回了正确的结果,但我认为它有点笨拙。相反,通过在 DST 开始或结束时明确说明 UTC 偏移量的变化,我们可以使用精确算术。”
2021-02-07 16:39:59
实际上,从技术上讲,您表示为“不正确”的时间是正确的。您没有将 DateTimes 更改为 UTC ...您只是更改每个 DateTime 的时间以获得一个人工整数。四舍五入并不“笨拙”......这正是这里的正确方法,因为无论如何这就是你在头脑中所做的:你正在舍入时间部分(小时,分钟,秒)并试图获得一个整体天数。
2021-02-19 16:39:59
无需使用 UTC,只需将本地时间设置为午夜(或两个日期的相同值)即可。夏令时引入的小数天最多只有 ±0.04(在某些地方更少),因此它以Math.round 结束;-) 依靠 Date 构造函数来解析字符串不是一个好主意。如果您真的想使用 UTC 值,请首先解析使用的字符串Date.UTC(...)
2021-03-05 16:39:59

获取两个日期之间差异的最简单方法:

var diff = Math.floor((Date.parse(str2) - Date.parse(str1)) / 86400000);

您会得到差异天数(如果无法解析其中一个或两者,则为 NaN)。解析日期以毫秒为单位给出结果,要按天得到它,你必须将它除以24 * 60 * 60 * 1000

如果你想把它除以天、小时、分钟、秒和毫秒:

function dateDiff( str1, str2 ) {
    var diff = Date.parse( str2 ) - Date.parse( str1 ); 
    return isNaN( diff ) ? NaN : {
        diff : diff,
        ms : Math.floor( diff            % 1000 ),
        s  : Math.floor( diff /     1000 %   60 ),
        m  : Math.floor( diff /    60000 %   60 ),
        h  : Math.floor( diff /  3600000 %   24 ),
        d  : Math.floor( diff / 86400000        )
    };
}

这是我重构的 James 版本:

function mydiff(date1,date2,interval) {
    var second=1000, minute=second*60, hour=minute*60, day=hour*24, week=day*7;
    date1 = new Date(date1);
    date2 = new Date(date2);
    var timediff = date2 - date1;
    if (isNaN(timediff)) return NaN;
    switch (interval) {
        case "years": return date2.getFullYear() - date1.getFullYear();
        case "months": return (
            ( date2.getFullYear() * 12 + date2.getMonth() )
            -
            ( date1.getFullYear() * 12 + date1.getMonth() )
        );
        case "weeks"  : return Math.floor(timediff / week);
        case "days"   : return Math.floor(timediff / day); 
        case "hours"  : return Math.floor(timediff / hour); 
        case "minutes": return Math.floor(timediff / minute);
        case "seconds": return Math.floor(timediff / second);
        default: return undefined;
    }
}
@stomy 看起来那个版本被删除了-
2021-02-09 16:39:59
不好回答。当时钟在夏令时前进时,Math.floor() 会让你失去一天。Math.round() 在大多数情况下都会给出正确的答案,但在其他答案中也有更好的选择。
2021-02-10 16:39:59
@some 你在哪里看到詹姆斯版本詹姆斯是谁?
2021-02-14 16:39:59
使用Math.abs可能会满足您的需求。
2021-03-07 16:39:59

我建议使用 moment.js 库 ( http://momentjs.com/docs/#/displaying/difference/ )。它可以正确处理夏令时,总的来说非常适合使用。

例子:

var start = moment("2013-11-03");
var end = moment("2013-11-04");
end.diff(start, "days")
1
end.diff(start,"days")虽然编写的代码可以工作,但确实应该如此,因为开始日期在结束日期之后!
2021-02-27 16:39:59
就像一个警告,而 momentjs 很棒,它是一个相当大的依赖项
2021-03-01 16:39:59

脚步

  1. 设置开始日期
  2. 设置结束日期
  3. 计算差异
  4. 将毫秒转换为天

原生JS

const startDate  = '2020-01-01';
const endDate    = '2020-03-15';

const diffInMs   = new Date(endDate) - new Date(startDate)
const diffInDays = diffInMs / (1000 * 60 * 60 * 24);

评论

我知道这不是您的问题的一部分,但总的来说,由于许多边缘情况,我不建议在 vanilla JavaScript 中进行任何日期计算或操作,而是使用诸如date- fnsLuxonmoment.js 之类的库


使用库

日期-fns

https://date-fns.org/v2.16.1/docs/differenceInDays

const differenceInDays = require('date-fns/differenceInDays');

const startDate  = '2020-01-01';
const endDate    = '2020-03-15';

const diffInDays = differenceInDays(new Date(endDate), new Date(startDate));

卢克森

https://moment.github.io/luxon/docs/class/src/datetime.js~DateTime.html#instance-method-diff

const { DateTime } = require('luxon');

const startDate  = '2020-01-01';
const endDate    = '2020-03-15';

const diffInDays = DateTime.fromISO(endDate).diff(DateTime.fromISO(startDate), 'days').toObject().days;

时刻.js

https://momentjs.com/docs/#/displaying/difference/

const moment = require('moment');

const startDate  = '2020-01-01';
const endDate    = '2020-03-15';

const diffInDays = moment(endDate).diff(moment(startDate), 'days');

RunKit 上的示例

https://runkit.com/marcobiedermann/5f573e211e85e7001a37ab00

是否只有我timeDiff以否定形式返回?timeDiff应该(new Date(endDate)) - (new Date(startDate));吗?
2021-02-12 16:39:59
const timeDiff = +(new Date(start)) - +(new Date(end));
2021-02-16 16:39:59
@feyisayo-sonubi 取决于您传入开始和结束日期的顺序。在这个例子中(new Date('2017-11-08')) - (new Date('2017-10-01')) // 3283200000
2021-02-19 16:39:59
我很欣赏这样的答案:简短、简单且不需要随机依赖!干杯。
2021-02-25 16:39:59
伟大的。虽然我更喜欢return (Date.UTC(yr2, mo2-1, dy2) - Date.UTC(yr1, mo1-1, dy1)) / 86400000;
2021-03-01 16:39:59