【问题标题】:Calendar returns wrong month [duplicate]日历返回错误的月份[重复]
【发布时间】:2010-12-17 19:58:43
【问题描述】:
Calendar rightNow = Calendar.getInstance();
String month = String.valueOf(rightNow.get(Calendar.MONTH));

上面的sn-p执行后,month得到的值是10而不是11,怎么会呢?

【问题讨论】:

  • +1 表示基于不一致的 java api 的完全合法的问题。
  • 仅供参考,这个问题使用了麻烦的旧日期时间类,现在已被 java.time 类取代。
  • Calendar 使用月份(0-11) 而 LocalDate 使用 (1-12) 为什么?最让 Java 开发人员感到困惑的是,API 遵循不同的约定,完全令人失望。

标签: java calendar


【解决方案1】:

月份从 0 而非 1 开始编入索引,因此 10 是 11 月,11 是 12 月。

【讨论】:

  • 这有什么意义?它应该是一个日历而不是一个谜题。天数通常被索引,年份也是如此。你不认为Android开发者犯了一个错误。是的,他们当然做到了,但他们无法纠正它,因为它与数百万的旧应用程序不兼容。
  • 这不是 Android 问题,而是 Java 问题。
  • 正确!月份从 0 开始,到 11 结束。
【解决方案2】:

它们从 0 开始 - 检查 the docs

【讨论】:

    【解决方案3】:

    许多答案都很清楚:月份从 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
    

    注意:输出可能会有所不同,它是特定于语言环境的。

    【讨论】:

      【解决方案4】:

      正如一些人所指出的,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)中仍然存在这种不一致和相关的混淆。

      【讨论】:

        【解决方案5】:

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

        因此一月 = 0,二月 = 1,等等

        【讨论】:

          【解决方案6】:

          来自 API:

          一年的第一个月是一月 这是0;最后取决于 一年中的月数。

          http://java.sun.com/j2se/1.5.0/docs/api/java/util/Calendar.html

          【讨论】:

            【解决方案7】:

            tl;博士

            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

            现在在maintenance modeJoda-Time 项目也建议迁移到 java.time。

            要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。

            大部分 java.time 功能在ThreeTen-Backport 中向后移植到 Java 6 和 7,并在 ThreeTenABP 中进一步适应 Android。

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

            1-12 个月

            在 java.time 中,月份数字确实是 1-12 月的预期 1-12。

            LocalDate 类表示没有时间和时区的仅日期值。

            时区

            时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因区域而异。例如,Paris France 午夜后几分钟是新的一天,而 Montréal Québec 仍然是“昨天”。

            continent/region 的格式指定proper time zone name,例如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。更多转化信息见Convert java.util.Date to what “java.time” type?

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

            Month枚举

            顺便说一下,java.time 类包括方便的Month enum。在您的代码中使用此类的实例而不是仅仅使用整数,以使您的代码更具自文档性,提供type-safety,并确保有效值。

            Month month = today.getMonth();  // Returns an instant of `Month` rather than integer.
            

            Month 枚举提供了有用的方法,例如使用localized name of the month 生成字符串。


            关于java.time

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

            Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

            要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

            从哪里获得 java.time 类?

            ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

            【讨论】:

              【解决方案8】:
              cal.get(Calendar.MONTH) + 1;
              

              上面的语句给出了确切的月份数。由于get(Calendar.Month) 从 0 开始返回月份,因此将结果加 1 将给出正确的输出。设置月份时记得减1。

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

              或者使用提供的常量变量。

              cal.set(Calendar.MONTH, Calendar.AUGUST);
              

              【讨论】:

                【解决方案9】:

                使用会更好

                Calendar.JANUARY
                

                这是零……

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-08-16
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-07-16
                  相关资源
                  最近更新 更多