在java.util。日历中,一月被定义为第0个月,而不是第1个月。这有什么具体原因吗?

我看到很多人对此感到困惑……


当前回答

基于C的语言在某种程度上复制了C。tm结构(在time.h中定义)有一个整数字段tm_mon,(注释的)范围为0-11。

基于C的语言从索引0开始数组。因此,这对于以tm_mon作为索引输出月份名称数组中的字符串非常方便。

其他回答

因为程序员痴迷于基于0的索引。好吧,实际情况要比这复杂一些:当您使用基于0的索引来处理底层逻辑时,它更有意义。但总的来说,我还是会坚持我的第一句话。

我想说是懒惰。数组从0开始(每个人都知道);一年中的月份是一个数组,这让我相信Sun的一些工程师只是懒得在Java代码中加入这个小细节。

这只是Java日期/时间API这一可怕的混乱的一部分。列出它的问题将花费很长时间(而且我确信我不知道一半的问题)。不可否认,处理日期和时间是很棘手的,但不管怎样。

帮自己一个忙,使用Joda Time代替,或者可能使用JSR-310。

编辑:至于原因——正如在其他回答中提到的,这很可能是由于旧的C api,或者只是一种从0开始的感觉……当然,除了一天从1开始。我怀疑原始实现团队之外的任何人都不能真正说出原因——但是,我再次敦促读者不要过于担心为什么做出了糟糕的决定,而是要看看java.util.Calendar中所有糟糕的事情,并找到更好的东西。

支持使用基于0的索引的一点是,它使“名称数组”之类的事情更容易:

// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];

当然,一旦你有了13个月的日历,这种方法就行不通了……但至少指定的大小是您期望的月数。

这不是一个很好的理由,但它是一个理由……

编辑:作为一个评论,请求一些关于我认为日期/日历错误的想法:

Surprising bases (1900 as the year base in Date, admittedly for deprecated constructors; 0 as the month base in both) Mutability - using immutable types makes it much simpler to work with what are really effectively values An insufficient set of types: it's nice to have Date and Calendar as different things, but the separation of "local" vs "zoned" values is missing, as is date/time vs date vs time An API which leads to ugly code with magic constants, instead of clearly named methods An API which is very hard to reason about - all the business about when things are recomputed etc The use of parameterless constructors to default to "now", which leads to hard-to-test code The Date.toString() implementation which always uses the system local time zone (that's confused many Stack Overflow users before now)

真正的原因

You would think that when we deprecated most of Date and added the new Calendar class, we would have fixed Date's biggest annoyance: the fact that January is month 0. We certainly should have, but unfortunately we didn't. We were afraid that programmers would be confused if Date used zero-based months and Calendar used one-based months. And a few programmers probably would have been. But in hindsight, the fact that Calendar is still zero-based has caused an enormous amount of confusion, and it was probably the biggest single mistake in the Java international API's.

引用自Laura Werner的Java国际日历,链接在底部。

更好的选择:java.time

这可能只是重复其他人所说的,抛弃旧的和设计糟糕的Calendar类,使用java。时间,现代Java日期和时间API。从1月的1到12月的12,月份的编号始终是合理的。

如果您从尚未升级到java的遗留API中获取Calendar。时,要做的第一件事是转换为现代的ZonedDateTime。根据你的需要,你可以从那里做进一步的转换。在大多数情况下,您获得的Calendar对象实际上总是GregorianCalendar子类的一个实例(因为Calendar类本身是抽象的)。demonstreate:

    Calendar oldfashionedCalendarObject = Calendar.getInstance();
    ZonedDateTime zdt
            = ((GregorianCalendar) oldfashionedCalendarObject).toZonedDateTime();
    
    System.out.println(zdt);
    System.out.format("Month is %d or %s%n", zdt.getMonthValue(), zdt.getMonth());

当我刚刚在我的时区运行时输出:

2021 - 03 - 17 - t23:18:47.761 + 01:00[欧洲/哥本哈根] 月是3号或MARCH

链接

Laura Werner编写的Java国际日历 Oracle教程:Date Time解释如何使用java.time。

基于C的语言在某种程度上复制了C。tm结构(在time.h中定义)有一个整数字段tm_mon,(注释的)范围为0-11。

基于C的语言从索引0开始数组。因此,这对于以tm_mon作为索引输出月份名称数组中的字符串非常方便。