Java:为什么日期构造函数弃用,我用什么代替?

我来自c#世界,所以对Java还不是很有经验。Eclipse刚刚告诉我Date已弃用:

Person p = new Person();
p.setDateOfBirth(new Date(1985, 1, 1));

为什么?应该用什么(尤其是在上述情况下)来代替呢?

269616 次浏览

特定的Date构造函数已弃用,应该使用Calendar代替。 日期JavaDoc描述了哪些构造函数已弃用,以及如何使用Calendar替换它们

Date本身没有弃用。它的很多方法都是。详情请看这里

请改用java.util.Calendar

java.util.Date类实际上并没有被弃用,只是该构造函数以及其他几个构造函数/方法被弃用了。它被弃用是因为这种用法与国际化不兼容。应该改为使用Calendar类:

Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 1988);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
Date dateRepresentation = cal.getTime();

看看Javadoc的日期:

http://download.oracle.com/javase/6/docs/api/java/util/Date.html

类似于binnyb的建议,您可以考虑使用较新的Calendar > GregorianCalendar方法。请看这些最新的文档:

http://download.oracle.com/javase/6/docs/api/java/util/GregorianCalendar.html

不推荐使用构造函数的一个原因是year形参的含义不是您所期望的。javadoc说:

从JDK 1.1版开始,由Calendar.set(year + 1900, month, date)取代。

请注意,year字段是自1900以来的年数,因此您的示例代码很可能不会执行您期望它执行的操作。这就是重点。

通常,Date API只支持现代西方日历,具有特殊指定的组件,并且在设置字段时行为不一致。

CalendarGregorianCalendar api比Date更好,而第三方joda time api通常被认为是最好的。在Java 8中,他们引入了java.time包,这些现在是推荐的替代方案。

请注意,Calendar.getTime()是不确定的,因为一天的时间部分默认为当前时间。

为了重现,试着运行下面的代码几次:

Calendar c = Calendar.getInstance();
c.set(2010, 2, 7); // NB: 2 means March, not February!
System.err.println(c.getTime());

如输出。

Sun Mar 07 10:46:21 CET 2010

几分钟后运行完全相同的代码会得到:

Sun Mar 07 10:57:51 CET 2010

因此,虽然set()强制对应的字段为正确的值,但它泄漏了其他字段的系统时间。(以上使用Sun jdk6 &进行测试;jdk7)

大多数Java开发人员目前使用第三方包Joda-Time。它被广泛认为是一个更好的实现。

然而,Java 8将有一个新的java.time。*包。参见本文,为JDK 8引入新的日期和时间API

博士tl;

LocalDate.of( 1985 , 1 , 1 )

…或…

LocalDate.of( 1985 , Month.JANUARY , 1 )

细节

在Java最初发布和发展时,java.util.Datejava.util.Calendarjava.text.SimpleDateFormat类的开发速度太快了。这些类没有很好地设计或实现。我们尝试了改进,因此出现了您所发现的问题。不幸的是,改进的尝试基本上失败了。你应该避免这些类一起。它们在Java 8中被新的类所取代。

代码中的问题

date包含日期部分和时间部分。您忽略了代码中的时间部分。因此,Date类将接受由JVM默认时区定义的一天的开始,并将该时间应用于Date对象。因此,代码的结果将取决于它运行的机器或设置的时区。可能不是你想要的。

如果你只想要日期,而不需要时间部分,比如出生日期,你可能不想使用Date对象。你可能想只存储日期的字符串,格式为YYYY-MM-DDISO 8601。或者使用Joda-Time中的LocalDate对象(见下文)。

Joda-Time

在Java中首先要学习的是:与Java绑定的避免使用臭名昭著的麻烦java.util.Date &java.util.Calendar类

正如user3277382的回答中正确指出的那样,在Java 8中使用Joda-Time或新的java.time。*包

Joda-Time 2.3中的示例代码

DateTimeZone timeZoneNorway = DateTimeZone.forID( "Europe/Oslo" );
DateTime birthDateTime_InNorway = new DateTime( 1985, 1, 1, 3, 2, 1, timeZoneNorway );


DateTimeZone timeZoneNewYork = DateTimeZone.forID( "America/New_York" );
DateTime birthDateTime_InNewYork = birthDateTime_InNorway.toDateTime( timeZoneNewYork );


DateTime birthDateTime_UtcGmt = birthDateTime_InNorway.toDateTime( DateTimeZone.UTC );


LocalDate birthDate = new LocalDate( 1985, 1, 1 );

转储到控制台…

System.out.println( "birthDateTime_InNorway: " + birthDateTime_InNorway );
System.out.println( "birthDateTime_InNewYork: " + birthDateTime_InNewYork );
System.out.println( "birthDateTime_UtcGmt: " + birthDateTime_UtcGmt );
System.out.println( "birthDate: " + birthDate );

运行时……

birthDateTime_InNorway: 1985-01-01T03:02:01.000+01:00
birthDateTime_InNewYork: 1984-12-31T21:02:01.000-05:00
birthDateTime_UtcGmt: 1985-01-01T02:02:01.000Z
birthDate: 1985-01-01

java.time

在这种情况下,java.time的代码与Joda-Time的代码几乎相同。

我们得到一个时区(ZoneId),并构造一个赋值给该时区(ZonedDateTime)的date-time对象。然后使用不可变对象模式,基于旧对象相同的瞬间(自时代以来的纳秒计数)创建新的日期时间,但分配了其他时区。最后,我们得到一个LocalDate,它没有时间,也没有时区,但请注意在确定日期时应用了时区(例如,在奥斯陆中,新的一天比在纽约中更早开始)。

ZoneId zoneId_Norway = ZoneId.of( "Europe/Oslo" );
ZonedDateTime zdt_Norway = ZonedDateTime.of( 1985 , 1 , 1 , 3 , 2 , 1 , 0 , zoneId_Norway );


ZoneId zoneId_NewYork = ZonedId.of( "America/New_York" );
ZonedDateTime zdt_NewYork = zdt_Norway.withZoneSameInstant( zoneId_NewYork );


ZonedDateTime zdt_Utc = zdt_Norway.withZoneSameInstant( ZoneOffset.UTC );  // Or, next line is similar.
Instant instant = zdt_Norway.toInstant();  // Instant is always in UTC.


LocalDate localDate_Norway = zdt_Norway.toLocalDate();

关于java.time

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

要了解更多信息,请参见< < em >甲骨文教程/ em >。搜索Stack Overflow可以找到很多例子和解释。规格是JSR 310

现在在维护模式中的< em > Joda-Time < / em >项目建议迁移到java.time类。

你可以直接与你的数据库交换java.time对象。使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。不需要字符串,不需要java.sql.*类。Hibernate 5 &JPA 2.2支持java.time

从哪里获取java。时间类?

表的java。“/></a> . time library with哪个版本的Java或Android</p></div>
                                                                            </div>
                                </div>
                            </div>
                        </div>
                                                <div class=

我偶然发现这个问题是一个新的问题的副本,它询问在特定的年、月和日获得Date的非弃用方法是什么。

到目前为止,这里的答案是使用Calendar类,这在Java 8出现之前是正确的。但是从Java 8开始,这样做的标准方法是:

LocalDate localDate = LocalDate.of(1985, 1, 1);

然后,如果你真的真的需要java.util.Date,你可以使用这个问题中的建议。

有关更多信息,请查看Java 8的API教程

你可以通过使用Calendar类在你的代码中创建一个类似new Date(year,month,date)的方法。

private Date getDate(int year,int month,int date){
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month-1);
cal.set(Calendar.DAY_OF_MONTH, day);
return cal.getTime();
}

它将像已弃用的Date构造函数一样工作

new GregorianCalendar(1985, Calendar.JANUARY, 1).getTime();

(java -8之前的方式)

Date构造函数期望年份的格式为1900年以来的年份、从零开始的月份、从一开始的日期,并将小时/分钟/秒/毫秒设置为零。

Date result = new Date(year, month, day);

因此,使用Calendar替换(从零开始的年,从零开始的月,从一开始的日)来替换已弃用的Date构造函数,我们需要这样的东西:

Calendar calendar = Calendar.getInstance();
calendar.clear(); // Sets hours/minutes/seconds/milliseconds to zero
calendar.set(year + 1900, month, day);
Date result = calendar.getTime();

或者使用Java 1.8(年为0,月和日为1):

Date result = Date.from(LocalDate.of(year + 1900, month + 1, day).atStartOfDay(ZoneId.systemDefault()).toInstant());

以下是Date、Calendar和Java 1.8的相同版本:

int year = 1985; // 1985
int month = 1; // January
int day = 1; // 1st


// Original, 1900-based year, zero-based month, one-based day
Date date1 = new Date(year - 1900, month - 1, day);


// Calendar, zero-based year, zero-based month, one-based day
Calendar calendar = Calendar.getInstance();
calendar.clear(); // Sets hours/minutes/seconds/milliseconds to zero
calendar.set(year, month - 1, day);
Date date2 = calendar.getTime();


// Java-time back to Date, zero-based year, one-based month, one-based day
Date date3 = Date.from(LocalDate.of(year, month, day).atStartOfDay(ZoneId.systemDefault()).toInstant());


SimpleDateFormat format = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss.SSS");


// All 3 print "1985-Jan-01 00:00:00.000"
System.out.println(format.format(date1));
System.out.println(format.format(date2));
System.out.println(format.format(date3));

由于Date构造函数已弃用,您可以尝试此代码。

import java.util.Calendar;


Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 6);// for 6 hour
calendar.set(Calendar.MINUTE, 0);// for 0 min
calendar.set(Calendar.SECOND, 0);// for 0 sec
calendar.set(1996,0,26);// for Date [year,month(0 to 11), date]


Date date = new Date(calendar.getTimeInMillis());// calendar gives long value


String mConvertedDate = date.toString();// Fri Jan 26 06:00:00 GMT+05:30 1996

我从Java安全代码指南得到这个

本节中的示例广泛使用java.util.Date 一个可变API类的例子。在应用程序中是这样的 最好使用新的Java日期和时间API (Java . Time .*)