关于java:Date()对象的不一致。 月,日和年都错了

Date() object inconsistency. Month, day and year are all wrong

现在是3/15/11,当我正在调用一个新的日期对象时:

1
Date now = new Date();

我得到了回报

  • 月份为2(getMonth()),
  • 当天为2(getDay())
  • 而年份(getYear())为111。

这个惯例有理由吗?


直接来自班级的文件:

  • A year y is represented by the integer y - 1900.
  • A month is represented by an integer from 0 to 11; 0 is January, 1 is February, and so forth; thus 11 is December.
  • A date (day of month) is represented by an integer from 1 to 31 in the usual manner.

至于getDay()

Returns the day of the week represented by this date. The returned value (0 = Sunday, 1 = Monday, 2 = Tuesday, 3 = Wednesday, 4 = Thursday, 5 = Friday, 6 = Saturday) represents the day of the week that contains or begins with the instant in time represented by this Date object, as interpreted in the local time zone.

2011年3月15日实际上是星期二。


Is there a reason for this convention?

原因是它是Date指定的javadoc; 看@matt b的答案。

Date API是在JDK 1.0时代创建的,众所周知,它们在许多领域都存在问题。 这就是大多数Date方法被标记为Deprecated的原因。 (顺便说一句,这意味着建议您不要在新代码中使用它们!)

Calendar API是对Date的重大改进,但迄今为止用于处理Java中日期/时间值的最佳API是第三方Joda时间API。

如果您想要Joda时间使用的示例,请查看上面的链接。 在GregorianCalendar javadocs中有一个日历用法示例。 可以在此页面上找到更多日历使用示例。


TL;博士

1
2
3
LocalDate                              // Modern class to represent a date-only value, without time-of-day, without time zone or offset-from-UTC.
.now( ZoneId.of("Africa/Tunis" ) )    // Capture the current date as seen in the wall-clock time used by the people of a specific region (a time zone).
.getYear()                             // Get year number, such as 2019 presently.

…和:

1
.getMonthValue()                       // Get month number, 1-12 for January-December.

…和:

1
.getDayOfMonth()                       // Get day-of-month number, 1-31.

细节

显然你正在使用两个可怕的日期时间类java.util.Datejava.sql.Date。在采用JSR 310时,两者都过时了,定义了它们的替代品,即现代java.time类。

LocalDate

LocalDate类表示没有时间且没有时区或从UTC偏移的仅日期值。

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

如果未指定时区,则JVM会隐式应用其当前的默认时区。该默认值可能会在运行时(!)期间随时更改,因此您的结果可能会有所不同。最好明确指定您期望/预期的时区作为参数。如果关键,请与您的用户确认该区域。

Continent/Region的格式指定正确的时区名称,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用2-4字母缩写,例如ESTIST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

1
2
ZoneId z = ZoneId.of("America/Montreal" ) ;  
LocalDate today = LocalDate.now( z ) ;

如果要使用JVM的当前默认时区,请求它并作为参数传递。如果省略,代码变得模糊不清,因为我们不确定您是否打算使用默认值,或者如果您像许多程序员一样,不知道这个问题。

1
ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

或指定日期。您可以将月份设置为一个数字,1月至12月的数字为1-12。

1
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ;  // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.

或者,更好的是,使用预定义的Month枚举对象,一年中的每个月一个。提示:在整个代码库中使用这些Month对象而不仅仅是整数,以使代码更加自我记录,确保有效值并提供类型安全性。同上Year& YearMonth

1
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;

访问部分日期

java.time类使用合理的编号,1-12个月,1-7个星期几,年份编号如2019年是2019年等等。

1
2
3
4
int year = ld.getYear() ;                 // The year, such as 2019 presently.
int monthNumber = ld.getMonthValue() ;    // Number of the month 1-12 for January-December.
Month month = ld.getMonth() ;             // Get the `Month` enum object, one of a dozen predefined objects (one for each month of the year).
int dayOfMonth = ld.getDayOfMonth() ;     // Get the day of the month, 1-31.

关于java.time

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

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

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

您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

  • Java SE 8,Java SE 9,Java SE 10,Java SE 11及更高版本 - 带有捆绑实现的标准Java API的一部分。

    • Java 9增加了一些小功能和修复。
  • Java SE 6和Java SE 7

    • 大多数java.time功能都被反向移植到Java 6& 7在ThreeTen-Backport。
  • Android的

    • 更高版本的Android捆绑java.time类的实现。
    • 对于早期的Android(<26),ThreeTenABP项目采用ThreeTen-Backport(如上所述)。请参见如何使用ThreeTenABP ....

ThreeTen-Extra项目使用其他类扩展了java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的类,例如IntervalYearWeekYearQuarter等。