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

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


当前回答

日期应该重新开发。它不应该是一个长整数,而应该包含年、月、日、小时、分钟、秒作为单独的字段。甚至还可以存储这个日期所关联的日历和时区。

在我们平常的谈话中,如果你安排的约会时间是纽约时间2013年11月1日下午1点,这就是“约会时间”。它不是日历。所以我们在Java中也应该能够像这样交谈。

当Date存储为一个长整数(自1970年1月1日以来的毫秒数)时,计算其当前日期取决于日历。不同的日历会给出不同的日期。这是从给出绝对时间(如大爆炸后1万亿秒)的角度出发的。但通常我们也需要一种方便的对话方式,比如一个对象可以封装年、月等。

我想知道Java中是否有新的进展来协调这两个目标。也许我的java知识太老了。

其他回答

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

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

Date是一个更简单的类,主要是为了向后兼容。如果需要设置特定的日期或进行日期计算,请使用Calendar。日历也处理本地化。date之前的日期操作函数已弃用。

就我个人而言,如果有选择的话,我倾向于使用以毫秒为单位的时间作为long(或long,视情况而定)或Calendar。

Date和Calendar都是可变的,这在API中使用时往往会出现问题。

如果可能的话,我通常使用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);

日期应该重新开发。它不应该是一个长整数,而应该包含年、月、日、小时、分钟、秒作为单独的字段。甚至还可以存储这个日期所关联的日历和时区。

在我们平常的谈话中,如果你安排的约会时间是纽约时间2013年11月1日下午1点,这就是“约会时间”。它不是日历。所以我们在Java中也应该能够像这样交谈。

当Date存储为一个长整数(自1970年1月1日以来的毫秒数)时,计算其当前日期取决于日历。不同的日历会给出不同的日期。这是从给出绝对时间(如大爆炸后1万亿秒)的角度出发的。但通常我们也需要一种方便的对话方式,比如一个对象可以封装年、月等。

我想知道Java中是否有新的进展来协调这两个目标。也许我的java知识太老了。

博士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等等。