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

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


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

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

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


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 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.


我一直提倡joda时间。这是为什么。

API是一致的和直观的。不像java.util。日期/日历api 它没有线程问题,不像java.text.SimpleDateFormat等(我见过许多客户端问题,没有意识到标准日期/时间格式不是线程安全的) 它是新的Java日期/时间api (JSR310,计划在Java 8中发布)的基础。因此,您将使用的api将成为核心Java api。

编辑:如果可以迁移到Java 8, Java 8引入的Java日期/时间类现在是首选的解决方案


日期应该用作不可变的时间点;日历是可变的,如果您需要与其他类协作以得出最终日期,则可以传递和修改日历。把它们看作类似于String和StringBuilder,您就会理解我认为应该如何使用它们。

(是的,我知道Date实际上在技术上不是不可变的,但目的是它不应该是不可变的,如果没有调用已弃用的方法,那么它就是不可变的。)


如果可能的话,我通常使用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知识太老了。


有点晚了,但是Java在JDK 8中有一个新的日期时间API。您可能希望升级JDK版本并采用该标准。没有更多凌乱的日期/日历,没有更多第三方罐子。


顺便说一句,“日期”通常被标记为“过时/弃用”(我不知道确切的原因)-关于它的一些东西写在那里 Java:为什么日期构造函数弃用,我用什么代替?

看起来这是一个构造函数的问题-方法通过新日期(int年,int月,int日),推荐的方法是通过日历和设置参数分开。(Calendar cal = Calendar. getinstance (); )


Java 8,新的Java。应该使用时间包。

对象是不可变的,时区和日光节约被考虑在内。

你可以从一个旧的java.util.Date对象中创建一个ZonedDateTime对象,就像这样:

    Date date = new Date();
    ZonedDateTime zonedDateTime = date.toInstant().atZone(ZoneId.systemDefault());

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


当我需要对日期进行一些特定的操作时,我使用日历,但当你需要格式化日期以适应你的需求时,我发现它很有帮助,最近我发现Locale有很多有用的操作和方法。我现在使用Locale !