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

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


当前回答

因为用月份计算要简单得多。

12月之后的一个月就是1月,但是要算出这个,通常你需要用月份的数字来计算

12 + 1 = 13 // What month is 13?

我知道!我可以通过使用模量12来快速解决这个问题。

(12 + 1) % 12 = 1

在11月之前的11个月里,这一切都很好……

(11 + 1) % 12 = 0 // What month is 0?

你可以在加上月份之前再减去1,然后做模数,最后再加1……也就是解决潜在的问题。

((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!

现在让我们考虑0 - 11月的问题。

(0 + 1) % 12 = 1 // February
(1 + 1) % 12 = 2 // March
(2 + 1) % 12 = 3 // April
(3 + 1) % 12 = 4 // May
(4 + 1) % 12 = 5 // June
(5 + 1) % 12 = 6 // July
(6 + 1) % 12 = 7 // August
(7 + 1) % 12 = 8 // September
(8 + 1) % 12 = 9 // October
(9 + 1) % 12 = 10 // November
(10 + 1) % 12 = 11 // December
(11 + 1) % 12 = 0 // January

所有月份的工作都是一样的,没有必要进行变通。

其他回答

博士tl;

Month.FEBRUARY.getValue()  // February → 2.

2

细节

乔恩·斯基特的答案是正确的。

现在我们有了这些麻烦的旧遗留日期-时间类的现代替代品:java。时间类。

java.time.Month

在这些类中有Month枚举。枚举携带一个或多个预定义对象,这些对象在装入类时自动实例化。在Month中,我们有十几个这样的对象,每个对象都有一个名称:JANUARY, FEBRUARY, MARCH,等等。每一个都是一个静态的最终公共类常量。您可以在代码中的任何地方使用和传递这些对象。示例:someMethod(月。8月)

幸运的是,它们有正确的编号,1-12,其中1是一月,12是十二月。

获取特定月份号(1-12)的Month对象。

Month month = Month.of( 2 );  // 2 → February.

向另一个方向,询问Month对象的月号。

int monthNumber = Month.FEBRUARY.getValue();  // February → 2.

这个类上还有很多方便的方法,比如知道每个月有多少天。该类甚至可以生成月份的本地化名称。

您可以获得以各种长度或缩写形式表示的月份的本地化名称。

String output = 
    Month.FEBRUARY.getDisplayName( 
        TextStyle.FULL , 
        Locale.CANADA_FRENCH 
    );

2

此外,您应该围绕代码库传递此枚举的对象,而不仅仅是整数。这样做可以提供类型安全,确保值的有效范围,并使代码更具自文档性。如果不熟悉Java中令人惊讶的强大枚举功能,请参阅Oracle教程。

Year和YearMonth类也很有用。


关于java.time

java。时间框架内置于Java 8及更高版本中。这些类取代了麻烦的旧遗留日期-时间类,如java.util。Date, . calendar, & java.text.SimpleDateFormat。

Joda-Time项目现在处于维护模式,建议迁移到java.time。

要了解更多,请参阅Oracle教程。搜索Stack Overflow可以找到很多例子和解释。规范是JSR 310。

从哪里获取java。时间类?

Java SE 8和SE 9及更高版本 内置的。 带有捆绑实现的标准Java API的一部分。 Java 9增加了一些小特性并进行了修复。 Java SE 6和SE 7 大部分的java。时间功能在ThreeTen-Backport中向后移植到Java 6和7。 安卓 ThreeTenABP项目将ThreeTen-Backport(上文提到过)专门用于Android。 参见如何使用....

ThreeTen-Extra项目扩展了java。额外的课程时间。这个项目是未来可能添加到java.time的一个试验场。你可以在这里找到一些有用的类,比如Interval、YearWeek、YearQuarter等等。

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

真正的原因

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。

在Java 8中,有一个新的日期/时间API JSR 310,它更加合理。规范负责人与JodaTime的主要作者相同,他们共享许多相似的概念和模式。

设置月份为“日历”。MARCH,或者比较,看看它是否== Calendar。比如六月。

Date和Calendar类可以追溯到Java的早期,当时人们还在摸索,它们被广泛认为设计得不是很好。

如果今天用相同的设计创建Calendar,而不是int类型的Calendar。JUNE等等,他们会用枚举。