当使用下面的调用在JavaScript中初始化一个新的Date对象时,我发现month参数从0开始计数。

new Date(2010, 3, 1);  // that's the 1st April 2010!

为什么month参数从0开始?另一方面,月份的日期参数(最后一个)是1到31之间的数字。有什么好的理由吗?


当前回答

一年总是有12个月,所以早期的C实现可能使用一个静态的固定宽度数组,索引为0..11。

其他回答

这是编程世界中一个古老的(可能是不幸的,可能会消亡的)传统,请参阅旧标准(POSIX) localtime C函数http://linux.die.net/man/3/localtime

除了月份的第一天之外,其他都是以0为基础的,请参阅这里的完整列表,包括范围:)

实际上,这里以1为基础的天数才是奇怪的……奇怪的是。为什么要这么做?我不知道……但可能发生在同一次会议上,他们喝得烂醉,决定分号是可选的。

这个问题的真正答案是从java.util复制的。日期,也有这个怪癖。在Twitter上可以找到Brendan Eich的证明——他最初实现了JavaScript:

https://twitter.com/BrendanEich/status/481939099138654209

https://twitter.com/BrendanEich/status/771006397886533632

Brendan还指出Netscape的Ken Smith从Java进行了移植。

https://twitter.com/BrendanEich/status/771006208949891072

这件事发生在1995年,当时JDK 1.0正处于测试阶段。它于1996年推出。1997年,JDK 1.1问世,它弃用了java.util上的绝大多数函数。日期,将它们移到java.util。日历,但即便如此,仍然有零位月。开发人员受够了这一点,创建了Joda-Time库,最终导致了java。Java 8(2014)内置的时间包。

简而言之,Java花了18年的时间才得到一个正确设计的内置日期/时间API,但JavaScript仍然停留在黑暗时代。我们确实有优秀的库,如Luxon Moment.js、date-fns、js-joda等。但是到目前为止,该语言只内置了Date。希望这种情况在不久的将来会随着TC39 Temporal提案而改变。

他们可能认为月份是一个枚举(第一个索引为0),而日期不是,因为它们没有关联的名称。

或者更确切地说,他们认为这一天的数字就是这一天的实际表示形式(就像在12/31这样的日期中用数字表示月份一样),就好像你可以用数字作为变量进行枚举,但实际上是以0为基础的。

所以实际上,对于月份,也许他们认为正确的枚举表示应该是使用月份的名称,而不是数字,如果日期有名称表示,他们也会这样做。想象一下,如果我们说1月5日、1月6日,而不是1月5日、1月6日等等,那么他们可能也会做一个以0为基础的天数枚举……

也许在潜意识里,他们想到了月份的枚举{一月,二月,……}和天数为{1,2,3,…},除非你以数字而不是名称的形式访问日期,比如1代表1,等等,所以不可能从0开始…

我知道这不是最初问题的答案,但我只是想向你们展示我对这个问题的首选解决方案,我似乎从来没有记住过,因为它不时地出现。

小函数zerofill可以在需要的地方填充零,月份只是加了+1:

function zerofill(i) {
    return (i < 10 ? '0' : '') + i;
}

function getDateString() {
    const date = new Date();
    const year = date.getFullYear();
    const month = zerofill(date.getMonth()+1);
    const day = zerofill(date.getDate());
    return year + '-' + month + '-' + day;
}

但是,是的,Date有一个相当不直观的API,当我读Brendan Eich的Twitter时,我笑了。