【不知为不知】为什么Java Calendar类的月份与实际月份少1

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/amoscn/article/details/86623591

问题:为什么Java Calendar类的月份与实际月份差1?

这个答案搜了很多,好像也没有一个明确的回答。

最初猜想

Calendar类当初设计时的一个bug,由于已经使用开来,没法贸然的修改

解决问题,先猜再查未偿不是一件好事。

查源码解释

下面是Calendar类中属性MONTH的注释以及定义:

 /**
     * Field number for <code>get</code> and <code>set</code> indicating the
     * month. This is a calendar-specific value. The first month of
     * the year in the Gregorian and Julian calendars is
     * <code>JANUARY</code> which is 0; the last depends on the number
     * of months in a year.
     *
     * @see #JANUARY
     * @see #FEBRUARY
     * @see #MARCH
     * @see #APRIL
     * @see #MAY
     * @see #JUNE
     * @see #JULY
     * @see #AUGUST
     * @see #SEPTEMBER
     * @see #OCTOBER
     * @see #NOVEMBER
     * @see #DECEMBER
     * @see #UNDECIMBER
     */
    public final static int MONTH = 2;

注释大致的意思是:

字段编号指示月份以用于get/set方法,这是特定于日历的值。 格列高利历(Gregorian) /朱利安历(Julian)的一年中的第一个月是JANUARY,即0; 最后一个月份取决于一年中的月数。

最后一句话是什么意思呢? 下面再说。 我们一起看一下上述13个月份的定义,请注意,不是12个月,而是13个月。

    /**
     * Value of the {@link #MONTH} field indicating the
     * first month of the year in the Gregorian and Julian calendars.
     */
    public final static int JANUARY = 0;

    /**
     * Value of the {@link #MONTH} field indicating the
     * second month of the year in the Gregorian and Julian calendars.
     */
    public final static int FEBRUARY = 1;

    /**
     * Value of the {@link #MONTH} field indicating the
     * third month of the year in the Gregorian and Julian calendars.
     */
    public final static int MARCH = 2;

    /**
     * Value of the {@link #MONTH} field indicating the
     * fourth month of the year in the Gregorian and Julian calendars.
     */
    public final static int APRIL = 3;

    /**
     * Value of the {@link #MONTH} field indicating the
     * fifth month of the year in the Gregorian and Julian calendars.
     */
    public final static int MAY = 4;

    /**
     * Value of the {@link #MONTH} field indicating the
     * sixth month of the year in the Gregorian and Julian calendars.
     */
    public final static int JUNE = 5;

    /**
     * Value of the {@link #MONTH} field indicating the
     * seventh month of the year in the Gregorian and Julian calendars.
     */
    public final static int JULY = 6;

    /**
     * Value of the {@link #MONTH} field indicating the
     * eighth month of the year in the Gregorian and Julian calendars.
     */
    public final static int AUGUST = 7;

    /**
     * Value of the {@link #MONTH} field indicating the
     * ninth month of the year in the Gregorian and Julian calendars.
     */
    public final static int SEPTEMBER = 8;

    /**
     * Value of the {@link #MONTH} field indicating the
     * tenth month of the year in the Gregorian and Julian calendars.
     */
    public final static int OCTOBER = 9;

    /**
     * Value of the {@link #MONTH} field indicating the
     * eleventh month of the year in the Gregorian and Julian calendars.
     */
    public final static int NOVEMBER = 10;

    /**
     * Value of the {@link #MONTH} field indicating the
     * twelfth month of the year in the Gregorian and Julian calendars.
     */
    public final static int DECEMBER = 11;

    /**
     * Value of the {@link #MONTH} field indicating the
     * thirteenth month of the year. Although <code>GregorianCalendar</code>
     * does not use this value, lunar calendars do.
     */
    public final static int UNDECIMBER = 12;

把重点放在第一个月以及最后一个月的注释上面:

JANUARY初始化int数值 = 0, 指示在格列高利历(Gregorian) /朱利安历(Julian)中的第一个月份

JANUARY = 0 - indicating the first month of the year in the Gregorian and Julian calendars.

UNDECIMBER初始化int数值 = 12,指示第13个月份,明确说明在格列高利历(Gregorian)里不会使用,但是农历(lunar calendars)会用到。

UNDECIMBER = 12 - indicating the thirteenth month of the year. Although Gregorian Calendar does not use this value, lunar calendars do.

但是,找了半天也没有关于lunar calendars的实现,而且也没有看到哪儿有用到UNDECIMBER.

好像有点跑偏了,实际上我是想找到为什么要把第一月份初始化 = 0 ,所以才去找关于UNDECIMBER的信息,可是却没找到关于为何把第一个月份定义为0的解释。

查wiki百科

既然java注释里写明了

The first month of the year in the Gregorian and Julian calendars is JANUARY which is `0`

咱们把视线暂时转移到Gregorian日历的wiki上

月名由来
一月 Januarius 名字来自古罗马神话的神雅努斯。
二月 Februarius 名字来自古罗马的节日Februus(斋戒月)and the god of death。
三月 Martius 名字来自古罗马神话的战神玛尔斯。
四月 Aprilis 名字来自古罗马的词aperire,意思为“开始”,意味着春天开始。
五月 Maius 名字来自古罗马神话的土地女神迈亚,或来自拉丁语词 maiores(意为“较年长者”)。
六月 Junius 名字来自古罗马神话的女神朱诺,或来自拉丁语词 iuniores(意为“较年轻者”)。
七月 原名Quintilis,后改Julius。古罗马历只有10个月,这是第五月,原名是“第五”的意思,因为凯撒是这月出生的,经元老院一致通过,将此月改为凯撒的名字“儒略”。
八月 原名Sextilis,后改Augustus。原名是“第六”的意思,因为后来屋大维是死于此月,元老院将此月改为他的称号“奥古斯都”。
九月 September 拉丁语“第七”的意思。
十月 October 拉丁语“第八”的意思。
十一月 Novembris 拉丁语“第九”的意思。
十二月 December 拉丁语“第十”的意思。

解释不通啊? 如果象wiki这么说的话,第一个月应该初始化 = -1 才对啊!此路不通!!

最终猜想

综合上述内容,并未得到最终结论,咱们暂且猜一下原因(做学术靠猜确实非常不严谨)。

  1. 程序员惯性思维 ,index起始编号 = 0。
  2. 单纯的定义无其他意义,默默付出的Java先辈们,应该也没有考虑那么多,只是单纯的把月份定义从0开始而非1开始。

使用Calendar关于月份的建议

set 月份时 一律 - 1
get 月份时 一律 + 1

    @Test
    public void testCalendar() {
        Calendar cal = Calendar.getInstance();
        // 当前月份是 1月
        // 若不对月份做处理 则打印结果为 0
        System.out.println(cal.get(Calendar.MONTH));
        // 期望打印当前实际月份 则+1 打印 = 1
        System.out.println(cal.get(Calendar.MONTH) + 1);
		
        //  期望设置日历的月份 = 8月  -> 将set与get独立
        cal.set(Calendar.MONTH, 8 - 1);
        //  打印设置是否有效    -> 将set与get独立 
        System.out.println(cal.get(Calendar.MONTH) + 1);
    }

当然在同时存在get和set月份方法的时候,你不加+1-1,数据是没有问题的。但是如果两个方法相隔很远,又或者只有其中一个方法呢 ?

若是某个客户端使用了错误生成日历的方法,找到问题的根源会比较麻烦。尽可能的按照上述要求来做,这样在日历的月份上出错的可能性大大降低。

执行结果:

0
1
8

本章完!

猜你喜欢

转载自blog.csdn.net/amoscn/article/details/86623591