Java:为什么不赞成使用Date构造函数,我该使用什么呢?


211

我来自C#领域,因此对Java不太熟悉。Eclipse告诉我Date不推荐使用:

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

为什么?而应该使用什么(尤其是在上述情况下)呢?


37
我正在经历类似的学习过程,从C#到Java。令我困扰的另一件事是,一年中的月份是从0开始的系统(0到11,其中Jan. = 0和Dec. = 11),但是月份中的几天是从1开始的(1到31)。抬起头来!
Paul Sasik

2
@Paul Sasik,是的,但是有Calendar.JANUARY常量,每个月都有一个
Diogo

5
@PaulSasik大声笑。是的,愚蠢的Java。不得不从C#切换到Java和OMG的痛苦和痛苦。
cbmeeks


8
来自C#人士的有关Java的尖刻的“ 笑声” 使大笑,因为.Net 从出色的Java库Joda-Time的端口中获得了不错的日期时间库(Noda Time)。
罗勒·布尔克2014年

Answers:


97

Date不赞成使用特定的构造函数,而Calendar应改用它。在JavaDoc描述了构造已被弃用,以及如何使用,以取代他们Calendar


25
日历需要一个额外的对象,并且像其他8行代码一样,要执行相同的操作,即根据我的判断创建一个Date对象。当您只需要一个Date而不是一个时区调整后的变量时,这很令人困惑并且似乎不必要。
G_V

5
仅供参考,非常麻烦旧日期,时间类,如java.util.Datejava.util.Calendarjava.text.SimpleDateFormat现在的遗产,由取代java.time内置到Java 8和更高等级。请参见Oracle 教程
罗勒·布尔克

250

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


2
这应该是最好的答案。谢谢。
jordaniac89

说“在国际化中不能很好地工作”,是否意味着您不能为Date指定TimeZone?谢谢
DiveInto

解析字符串的日期是什么,所以现在我们必须对包含年,月和日的字符串进行子字符串化?似乎在大多数情况下不需要添加如此复杂的逻辑和方法的事情会带来很多麻烦。
G_V

1
只是要添加,这将考虑默认时区。如果我们要指定任何其他时区,可以使用Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(<timezone id>));
Vikas Prasad

1
“应该改用Calendar类” -对于Java 8和更高版本,java.time.*如果要更改代码,则这些类是更好的选择。
斯蒂芬·C

73

tl; dr

LocalDate.of( 1985 , 1 , 1 )

…要么…

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

细节

java.util.Datejava.util.Calendarjava.text.SimpleDateFormat班级被送往太快时的Java首次推出和发展。这些类的设计或实施不正确。已尝试进行改进,因此发现了不推荐使用的地方。不幸的是,改进的尝试在很大程度上失败了。您应该完全避免这些类。在Java 8中,新类取代了它们。

代码中的问题

java.util.Date同时具有日期和时间部分。您忽略了代码中的时间部分。因此,Date类将采用JVM的默认时区所定义的一天的开始时间,并将该时间应用于Date对象。因此,您的代码结果将根据运行的计算机或设置的时区而有所不同。可能不是您想要的。

如果只想要日期,而没有时间部分,例如出生日期,则可能不想使用Date对象。您可能只想以ISO 8601格式存储日期的字符串YYYY-MM-DD。或使用LocalDateJoda-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)的日期时间对象。然后,使用不可变对象模式,我们根据旧对象的同一瞬间(自epoch以来的纳秒数)创建了新的日期时间,但分配了其他时区。最后,我们得到一个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

java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar,和SimpleDateFormat

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

要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

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

在哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多


5
实际提及根本原因并涵盖所有可用替代方案的道具。
mabi

这是正确的答案,应避免使用Calendar类。ThreeTen是最佳选择
ant2009

11

构造函数被弃用的原因之一是year参数的含义与您期望的不一样。Javadoc说:

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

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

通常,DateAPI仅支持现代西方日历,具有特殊指定的组件,并且如果您设置字段,则行为会不一致。

CalendarGregorianCalendarAPI是比好Date,和第三方乔达时间的API被普遍认为是最好的。在Java 8中,他们介绍了java.time软件包,现在推荐使用这些软件包。


此解决方案效果很好,除了它不像新的Date(year,month,day)那样在小时-分钟-秒-毫秒中使用零时间。因此,我们需要类似以下内容:Calendar cal = Calendar.getInstance(); cal.clear(); cal.set(年+ 1900,月,日期); 日期dateRepresentation = cal.getTime();
乔尔·理查德·科特

11

我遇到了这个问题,它是一个较新问题的重复,该问题询问Date在特定的年,月和日获得不折旧的方式是什么。

到目前为止,这里的答案都说要使用Calendar该类,直到Java 8出现,这都是正确的。但是从Java 8开始,执行此操作的标准方法是:

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

然后,如果您确实需要a java.util.Date,则可以使用此问题中的建议。

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


7

请注意,这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测试)




2

由于不推荐使用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

1
这些可怕的类现在已成为历史遗产,几年前已被JSR 310中定义的java.time类所取代。建议DateCalendar在2019年提出的建议不多
罗勒·布尔克

@BasilBourque感谢您的建议,我希望尽快进行更新。
Divyanshu Kumar


1

您可以new Date(year,month,date)使用Calendar类来制作方法,就像在代码中一样。

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


1
这些非常麻烦的类在几年前被现代的java.time类(尤其是Instant和)取代ZonedDateTime
罗勒·布尔克

上面的解决方案应该在年份中加上1900,并且不能从月份中减去1,还应该在Calendar.getInstance()行之后运行cal.clear(),以便将小时/分钟/秒/毫秒设置为零(如它们在Date构造函数中执行)。
乔尔·理查德·科特

1

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

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

因此,对于已过时的Date构造函数使用Calendar替换(从零开始的年,从零开始的月,从一开始的天),我们需要类似以下内容:

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(具有从零开始的年份和从一开始的月份和日期):

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

以下是日期,日历和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));

1
这两个可怕的类被排挤年前由java.time在JSR 310中定义的类
罗勒布尔克

更新了答案以显示日期,日历和Java 1.8版本。
乔尔·理查德·科特

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.