有人能建议当前的“最佳实践”围绕日期和日历类型。

在编写新代码时,最好总是使用Calendar而不是Date,还是在某些情况下Date是更合适的数据类型?


当前回答

Date最适合存储日期对象。它是持久化的,序列化的…

日历是操作日期的最佳工具。

注意:我们有时也更喜欢java.lang.Long而不是Date,因为Date是可变的,因此不是线程安全的。在Date对象上,使用setTime()和getTime()在两者之间切换。例如,应用程序中的一个常量Date(例如:0 1970/01/01,或应用程序的END_OF_TIME,您设置为2099/12/31;这些对于替换null值作为开始时间和结束时间非常有用,特别是当您在数据库中持久化它们时,因为SQL对null非常特殊)。

其他回答

对于新代码(如果您的策略允许第三方代码),最好的方法是使用Joda Time库。

日期和日历都有很多设计问题,它们都不是新代码的好解决方案。

Date最适合存储日期对象。它是持久化的,序列化的…

日历是操作日期的最佳工具。

注意:我们有时也更喜欢java.lang.Long而不是Date,因为Date是可变的,因此不是线程安全的。在Date对象上,使用setTime()和getTime()在两者之间切换。例如,应用程序中的一个常量Date(例如:0 1970/01/01,或应用程序的END_OF_TIME,您设置为2099/12/31;这些对于替换null值作为开始时间和结束时间非常有用,特别是当您在数据库中持久化它们时,因为SQL对null非常特殊)。

Date and Calendar are really the same fundamental concept (both represent an instant in time and are wrappers around an underlying long value). One could argue that Calendar is actually even more broken than Date is, as it seems to offer concrete facts about things like day of the week and time of day, whereas if you change its timeZone property, the concrete turns into blancmange! Neither objects are really useful as a store of year-month-day or time-of-day for this reason. Use Calendar only as a calculator which, when given Date and TimeZone objects, will do calculations for you. Avoid its use for property typing in an application. Use SimpleDateFormat together with TimeZone and Date to generate display Strings. If you're feeling adventurous use Joda-Time, although it is unnecessarily complicated IMHO and is soon to be superceded by the JSR-310 date API in any event. I have answered before that it is not difficult to roll your own YearMonthDay class, which uses Calendar under the hood for date calculations. I was downvoted for the suggestion but I still believe it is a valid one because Joda-Time (and JSR-310) are really so over-complicated for most use-cases.

博士tl;

建议当前的“最佳实践”围绕日期和日历 日历比日期更好吗

完全避免这些遗留类。使用java。而是时间类。

在UTC中,暂时使用Instant(现代版的Date) 在特定的时区,使用ZonedDateTime(现代版的GregorianCalendar) 在特定的offset-from-UTC中,使用OffsetDateTime(在遗留类中没有等效内容) 对于时区或偏移量未知的date-time(不是moment),使用LocalDateTime(在遗留类中没有等效内容)

细节

Ortomala Lokni的回答是正确的,建议使用现代java。时间类,而不是麻烦的旧遗留日期-时间类(日期、日历等)。但这个答案暗示了一个错误的等价类(见我对那个答案的评论)。

使用java.time

java。时间类是对传统日期-时间类的巨大改进。旧的类设计很差,令人困惑,而且很麻烦。您应该尽可能避免使用旧的类。但是,当您需要从旧类/新类转换或从旧类/新类转换时,可以通过调用添加到旧类的新方法来实现。

有关转换的更多信息,请参阅我的回答和另一个问题的漂亮图表,将java.util. date转换为什么“java. util. date”。时间”类型?。

搜索Stack Overflow提供了数百个关于使用java.time的示例问题和答案。但这里有一个快速的概要。

即时

用即时获取当前时刻。Instant类表示UTC时间轴上的一个时刻,其分辨率为纳秒(最多为十进制分数的九(9)位)。

Instant instant = Instant.now();

分区日期时间

要通过某些特定地区的挂钟时间来查看同一时刻,可以应用一个时区(ZoneId)来获得一个zoneeddatetime。

时区

请指定合适的时区名称,如“America/Montreal”、“Africa/Casablanca”或“Pacific/Auckland”。不要使用3-4个字母的缩写,如EST或IST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone();

抵消

时区是一个区域相对于utc偏移量的变化历史。但有时你只得到一个偏移量而没有完整的区域。在这种情况下,使用OffsetDateTime类。

ZoneOffset offset = ZoneOffset.parse( "+05:30" );
OffsetDateTime odt = instant.atOffset( offset );

使用时区比仅仅使用偏移量更可取。

LocalDateTime

Local…类中的“Local”表示任何位置,而不是特定的位置。所以这个名字可能是反直觉的。

LocalDateTime、LocalDate和LocalTime故意缺乏任何关于偏移量或时区的信息。所以它们不代表实际的时刻,它们不是时间轴上的点。当有疑问或困惑时,使用zoneeddatetime而不是LocalDateTime。更多讨论请搜索Stack Overflow。

字符串

不要将日期-时间对象与表示其值的字符串合并。可以解析字符串以获得日期-时间对象,也可以从日期-时间对象生成字符串。但是字符串从来不是日期-时间本身。

了解在java中默认使用的标准ISO 8601格式。时间类。


关于java.time

java。时间框架内置于Java 8及更高版本中。这些类取代了麻烦的旧遗留日期-时间类,如java.util。日期,日历和简单日期格式。

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

要了解更多,请参阅Oracle教程。搜索Stack Overflow可以找到很多例子和解释。规范是JSR 310。

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

从哪里获取java。时间类?

Java SE 8、Java SE 9以及更高版本 内置的。 带有捆绑实现的标准Java API的一部分。 Java 9增加了一些小特性并进行了修复。 Java SE 6和Java SE 7 大部分的java。时间功能在ThreeTen-Backport中向后移植到Java 6和7。 安卓 后续版本的Android捆绑实现的java。时间类。 对于早期的Android, ThreeTenABP项目适应了ThreeTen-Backport(如上所述)。参见如何使用ThreeTenABP....

ThreeTen-Extra项目扩展了java。额外的课程时间。这个项目是未来可能添加到java.time的一个试验场。你可以在这里找到一些有用的类,比如Interval、YearWeek、YearQuarter等等。

如果可能的话,我通常使用Date。虽然它是可变的,但实际上不推荐使用突变器。最后,它基本上包装了一个表示日期/时间的long。相反,如果我必须操作这些值,我会使用calendar。

您可以这样想:只在需要可以轻松操作的字符串时使用StringBuffer,然后使用toString()方法将它们转换为字符串。同样,我只在需要操作时态数据时使用Calendar。

为了实现最佳实践,我倾向于在域模型之外尽可能多地使用不可变对象。它极大地降低了任何副作用的可能性,并且它是由编译器代替JUnit测试完成的。可以通过在类中创建私有final字段来使用这种技术。

回到StringBuffer的类比。下面是一些代码,向您展示如何在日历和日期之间进行转换

String s = "someString"; // immutable string
StringBuffer buf = new StringBuffer(s); // mutable "string" via StringBuffer
buf.append("x");
assertEquals("someStringx", buf.toString()); // convert to immutable String

// immutable date with hard coded format.  If you are hard
// coding the format, best practice is to hard code the locale
// of the format string, otherwise people in some parts of Europe
// are going to be mad at you.
Date date =     new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).parse("2001-01-02");

// Convert Date to a Calendar
Calendar cal = Calendar.getInstance();
cal.setTime(date);

// mutate the value
cal.add(Calendar.YEAR, 1);

// convert back to Date
Date newDate = cal.getTime();

// 
assertEquals(new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).parse("2002-01-02"), newDate);