日历返回错误的月份。

119
Calendar rightNow = Calendar.getInstance();
String month = String.valueOf(rightNow.get(Calendar.MONTH));

执行上述代码后,月份的值变为10而不是11。为什么会这样呢?


18
基于不一致的java api提出的完全合理的问题值得点赞(+1)。 - Steven Magana-Zook
请注意,此问题使用麻烦的旧日期时间类,现已被 java.time 类所取代。 - Basil Bourque
日历使用月份(0-11),而本地日期使用(1-12),为什么? 这对于Java开发人员来说最令人困惑,各种API都遵循不同的约定,完全令人失望。 - Muhammad Azam
9个回答

154

月份索引从0开始,而不是1,因此10代表十一月,11代表十二月。


32
那有什么意义呢?这应该是一个日历而不是一个谜题。日期和年份都应该按照正常顺序编制索引。你不认为安卓开发者犯了一个错误吗?当然,他们犯了错误,但他们无法进行更正,因为这会导致数百万旧应用程序与其不兼容。 - TomeeNS
8
这不是安卓的问题,而是Java的问题。 - Grrrben
1
正确!月份从0开始,到11结束。 - Erfan

32

它们从0开始 - 在文档中查看。


25

众所周知,许多答案表明月份从0开始计数。

这里有一个提示:您应该使用SimpleDateFormat获取月份的字符串表示形式:

Calendar rightNow = Calendar.getInstance();
java.text.SimpleDateFormat df1 = new java.text.SimpleDateFormat("MM");
java.text.SimpleDateFormat df2 = new java.text.SimpleDateFormat("MMM");
java.text.SimpleDateFormat df3 = new java.text.SimpleDateFormat("MMMM");
System.out.println(df1.format(rightNow.getTime()));
System.out.println(df2.format(rightNow.getTime()));
System.out.println(df3.format(rightNow.getTime()));

输出:

11
Nov
November

注意:输出可能会有所不同,这取决于区域设置。


19

正如一些人指出的那样,Java中CalendarDate类返回的月份是从0开始索引而不是1。因此0代表一月,而当前的月份——十一月——是10。

你可能会想知道为什么会这样。其根源在于POSIX标准函数ctimegmtimelocaltime,它们接受或返回一个带有以下字段的time_t结构体(来自man 3 ctime):

int tm_mday;    /* day of month (1 - 31) */
int tm_mon;     /* month of year (0 - 11) */
int tm_year;    /* year - 1900 */

该API被几乎完全复制到Java 1.0的Java Date类中,然后基本上未经修改地复制到Java 1.1的Calendar类中。Sun在引入Calendar时解决了最突出的问题 - 格里高利日历中的2001年由其Date类中的值101表示。但我不确定为什么他们没有改变日期和月份值,至少使它们在索引上一致,无论是从零还是从一开始。这种不一致性和相关的混淆仍然存在于Java(和C)至今。

12

月份从零开始,就像列表的索引一样。

因此,1月 = 0,2月 = 1,以此类推。


9

简述

LocalDate.now()            // Returns a date-only `LocalDate` object for the current month of the JVM’s current default time zone.
         .getMonthValue()  // Returns 1-12 for January-December.

详情

其他答案是正确的,但已经过时。

令人烦恼的旧日期时间类有许多糟糕的设计选择和缺陷。其中一个是零基数计算月份数字0-11,而不是明显的1-12。

java.time

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

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

要了解更多信息,请参阅 Oracle 教程。并搜索 Stack Overflow 获取许多示例和解释。

Java 6 和 7 中的大部分 java.time 功能都被移植到 ThreeTen-Backport,并进一步适配到 Android 中的 ThreeTenABP

ThreeTen-Extra项目扩展了java.time的附加类,该项目是java.time可能未来添加内容的试验场。

月份1-12

在java.time中,月份数字确实是预期的1-12,分别对应一月到十二月。

LocalDate类表示仅包含日期值而不包括时间和时区的值。

时区

时区对于确定日期至关重要。对于任何给定的时刻,日期因时区而异。例如,在法国巴黎的午夜后几分钟是新的一天,而在魁北克蒙特利尔仍然是“昨天”。

请以大陆/地区的格式指定一个正确的时区名称,例如America/MontrealAfrica/CasablancaPacific/Auckland。千万不要使用3-4个字母的缩写,如ESTIST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );
int month = today.getMonthValue();  // Returns 1-12 as values.

如果您需要一个特定时区的日期时间,请使用ZonedDateTime对象,方法与上述类似。
ZonedDateTime now = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );
int month = now.getMonthValue();  // Returns 1-12 as values.

转换旧类

如果你手头有一个GregorianCalendar对象,请使用旧类中新增的toZonedDateTime方法将其转换为ZonedDateTime。如需更多转换信息,请参见将java.util.Date转换为“java.time”类型?

ZonedDateTime zdt = myGregorianCalendar.toZonedDateTime();
int month = zdt.getMonthValue();  // Returns 1-12 as values.

Month 枚举

顺便提一下,java.time 类中包括方便的 Month 枚举。在您的代码中使用此类的实例而不是仅仅使用整数,可以使您的代码更具自我说明性,提供类型安全性,并确保有效的值。

Month month = today.getMonth();  // Returns an instant of `Month` rather than integer.
< p > Month 枚举提供了有用的方法,例如生成一个 月份本地化名称 的字符串。


关于java.time

Java 8及更高版本内置java.time框架。这些类替代了麻烦的旧式legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

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

要了解更多信息,请参见Oracle教程。在Stack Overflow上搜索许多示例和解释。规范为JSR 310
如何获取java.time类?

ThreeTen-Extra项目通过增加额外的类,扩展了java.time。该项目是java.time未来可能添加的内容的试验场。您可能会在这里找到一些有用的类,例如Interval, YearWeek, YearQuarter更多


9

4
cal.get(Calendar.MONTH) + 1;

上述语句给出了确切的月份数字。由于get(Calendar.Month)返回从0开始计算的月份,因此将结果加1即可得到正确的输出。在设置月份时要记得减去1。

cal.set(Calendar.MONTH, (8 - 1));

或者使用提供的常量变量。
cal.set(Calendar.MONTH, Calendar.AUGUST);

2
最好使用 <code> 标签。
Calendar.JANUARY

which is zero ...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接