Java日期与日历


364

有人可以建议当前有关“最佳做法”的内容DateCalendar类型。

在编写新代码时,最好始终青睐CalendarDate,还是在某些情况下Date更合适的数据类型?



2
仅供参考,麻烦的旧日期,时间类,如java.util.Datejava.util.Calendarjava.text.SimpleDateFormat现在的遗产,由取代java.time类。在ThreeTen-Backport项目中,许多java.time功能都被反向移植到Java 6和Java 7 。在ThreeTenABP项目中进一步适用于早期的Android 。请参阅如何使用ThreeTenABP…
罗勒·布尔克

2
仅供参考,Joda-Time项目(在另一条评论中提到)现在处于维护模式,团队建议迁移到java.time类。请参见Oracle教程
罗勒·布尔克

Answers:


377

Date是一个简单的类,主要由于向后兼容的原因而存在。如果需要设置特定日期或进行日期算术,请使用日历。日历还处理本地化。此后不推荐使用Date的以前的日期操作功能。

就个人而言,我倾向于在可能的情况下使用毫秒(如果合适)或“日历”(Long)。

日期和日历都是可变的,因此在API中使用它们时往往会出现问题。


5
仅供参考,麻烦的旧日期时间类(如java.util.Datejava.util.Calendarjava.text.SimpleDateFormat现在已成为旧版)被Java 8及更高版本中内置的java.time类取代。请参见Oracle 教程
罗勒·布尔克

67

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

两者,日期日历,有这么多的设计问题,无论是对新代码良好的解决方案。


7
我赞同使用乔达时间的建议。它更易于使用和理解,并提供更多可使用的功能。
Jeroen van Bergen

30
有关是使用Joda Time还是坚持使用标准JDK类的讨论,请参见stackoverflow.com/questions/589870/…–
Jonik

3
我不知道它是否值得投票,但不能回答问题。作为评论可能会更好。
IcedD​​ante 2012

7
因为问题是关于使用日期和日历而不是使用第三方库,这会给项目增加单一供应商依赖性风险。
阿基米德·特拉哈诺

3
这是“最佳方法”,直到Java 8可以使用java.time包为止。–
DaBlick

58
  • Date并且Calendar实际上是相同的基本概念(都代表时间瞬间,并且都是围绕基础long值的包装)。

  • 有人可能会说这Calendar实际上比实际Date情况timeZone更糟,因为它似乎提供了有关星期几和一天中的时间之类的具体事实,而如果您更改其属性,则具体内容会变得白白!由于这个原因,两个对象都不能真正用作存储年月日一天中的时间

  • 使用Calendar仅作为一个计算器其中,由于在DateTimeZone对象,会为你做计算。避免将其用于在应用程序中键入属性。

  • 使用SimpleDateFormat连同TimeZoneDate生成显示字符串。

  • 如果您喜欢冒险,可以使用Joda-Time,尽管它不必要地复杂,恕我直言,并且无论如何很快就会被JSR-310 date API取代。

  • 在此之前,我已经回答过,推出自己的YearMonthDay课程并不难,该课程Calendar在后台进行日期计算。我对这个建议不满意,但我仍然认为它是一个有效的建议,因为Joda-Time(和JSR-310)对于大多数用例而言确实过于复杂。


1
JSR310是否有时间表?它将用Java 7编写,但我认为现在并非如此。
Brian Agnew

@Brian-在该邮件列表中,它肯定变得非常安静!
oxbow_lakes

仅检查一下,它就变得不活动,这意味着他们在18个月内还没有发布里程碑草案:-(
Brian Agnew

邮件列表上的最新评论是在7月由Stephen
撰写的

同意 如果您知道如何安全地将日期用作一个不变的对象,并知道如何使用日历来操纵日期,那么大多数人应该是安全的。在多线程代码中使用SimpleDateFormat时要小心。
cwash

25

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

日历最适合处理日期。

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


我认为您正在研究不可变的东西java.lang.Long
pjp

17

如果可能,我通常使用Date。尽管可变,但实际上不推荐使用。最后,它基本上包装了一个代表日期/时间的长整数。相反,如果必须操纵值,我将使用日历。

您可以这样想:仅在需要易于操作的String,然后使用toString()方法将其转换为String时,才使用StringBuffer。同样,仅在需要处理时间数据时才使用日历。

为了获得最佳实践,我倾向于在领域模型之外尽可能多地使用不可变对象。它显着减少了产生任何副作用的机会,它是由编译器而不是JUnit测试为您完成的。您可以通过创建私人决赛来使用此技术在类中字段来。

回到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);

是的,不可变的对象对于日期时间工作有意义。取代/ 使用不可变对象模式的java.time类。DateCalendar
罗勒·布尔克

这可能是正确的,但在2010
。– Archimedes Trajano

是。但也有成千上万的人阅读此页现在,超过15万为止。我的评论是对他们的说明,而不是对您的批评。
罗勒·布尔克

我知道,这就是为什么我赞成其他答案。但是,仍然有些人需要使用较旧的JDK,它们也需要上面的答案。
阿基米德·特拉哈诺

1
实际上,对于Java 6和7,我们有ThreeTen-Backport项目。这使几乎所有的java.time功能都具有相同的API。因此,无需使用那些可怕的旧式日期时间类。
罗勒·布尔克

15

Dates应该用作不变的时间点;Calendars是可变的,如果您需要与其他班级合作以得出最终日期,可以将其传递和修改。考虑它们类似于StringStringBuilder,您将了解我认为应如何使用它们。

(是的,我知道Date实际上在技术上并不是一成不变的,但目的是它不应是可变的,如果没有人调用不赞成使用的方法,那么它就是如此。)


是的,不可变的对象对于日期时间工作很有意义。取代/ 使用不可变对象模式的java.time类。具体来说,替换和替换/ 。DateCalendarInstantjava.util.DateZonedDateTimeCalendarGregorianCalendar
罗勒·布尔克

15

tl; dr

建议当前的“最佳做法”,DateCalendar

是它最好的总是青睐Calendar过度Date

完全避免使用这些旧类。请改用java.time类。

Java中所有日期和时间类型的表,包括现代的和传统的

细节

Ortomala Lokni 的Answer是正确的建议,建议使用现代的java.time类,而不是麻烦的旧旧日期时间类(DateCalendar,等)。但是那个答案提示错误的类是等效的(请参阅我对该答案的评论)。

使用java.time

java.time类相对于旧的日期-时间类,昼夜差异有很大的改进。旧的类设计欠佳,令人困惑且麻烦。您应尽可能避免使用旧类。但是,当您需要与旧版本/新版本之间进行转换时,可以通过调用添加到旧版本的新方法来实现类中的。

有关转换的更多信息,请参阅我的答案和精美图表到另一个问题,将java.util.Date转换为“ java.time”类型?

搜索堆栈溢出给出了使用java.time的数百个示例问题与解答。但这是一个简短的摘要。

Instant

使用获取当前时刻Instant。该Instant级表示时间轴上的时刻UTC,分辨率为纳秒(最多小数的9个位数)。

Instant instant = Instant.now();

ZonedDateTime

要通过某个特定区域的挂钟时间的镜头看到同一瞬间,请应用一个时区(ZoneId)以获得ZonedDateTime

时区

指定适当的时区名称,格式continent/region,如America/MontrealAfrica/CasablancaPacific/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…任何地区,而不是特定的地区。因此,该名称可能违反直觉。

LocalDateTime,,LocalDate并且LocalTime故意缺少有关偏移或时区的任何信息。因此,他们不能代表实际的时刻,他们是不是在时间轴上的点。如有疑问或困惑,请使用ZonedDateTime而不是LocalDateTime。搜索堆栈溢出以获取更多讨论。

弦乐

不要将日期时间对象与代表其值的字符串混淆。您可以解析字符串以获取日期时间对象,并且可以从日期时间对象生成字符串。但是字符串永远不是日期时间本身。

了解java.time类中默认使用的标准ISO 8601格式。


关于java.time

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

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

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

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

在哪里获取java.time类?

与哪个版本的Java或Android一起使用的哪个java.time库的表

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


10

使用Java 8,新的java.time包应使用。

对象是不可变的,考虑了时区和夏时制。

您可以像这样ZonedDateTime从旧java.util.Date对象创建对象:

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

很好建议java.time类。但是不好建议LocalDateTime。该类有意丢失有关UTC偏移量和时区的任何信息。所以,该类是等同Date是UTC和Calendar具有指定的时区。参见我的答案和精美图表到另一个问题,将java.util.Date转换为什么“ java.time”类型?
罗勒·布尔克

9

我一直提倡乔达时代。这就是为什么。

  1. API一致且直观。与java.util.Date/Calendar API不同
  2. java.text.SimpleDateFormat不同,它没有线程问题等(我已经看到许多客户端问题,这些问题与不意识到标准日期/时间格式不是线程安全的)有关。
  3. 它是新的Java日期/时间API(JSR310,计划用于Java 8)的基础。因此,您将使用将成为核心Java API的API。

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


3
我最后一次看,JODA和JSR-310看上去非常不同,即使它们都写的斯蒂芬Colebourne对。就是说,JODA将向您介绍JSR-310还解决的日期时间问题的复杂性
oxbow_lakes 2009年

2
因为问题是关于使用日期和日历而不是使用第三方库,这会给项目增加单一供应商依赖性风险。
阿基米德·特拉哈诺

2
我认为最佳实践就是根本不使用这些类
Brian Agnew

3
鉴于java.util.Date和Calendar类的已知问题,建议Joda-Time(或JSR 310)对我来说似乎合适且负责。我们不是在谈论口味或美学风格。如果有人问他们应该乘坐红色轿车还是银色轿车,而我知道红色轿车的轮胎flat了,银色轿车的散热器坏了,我应该选车还是建议打车呢?如今,这个问题的答案似乎很明显,甚至连Sun / Oracle都决定抛弃这些骗子,购买一辆新车:JSR 310:日期和时间API。
罗勒·布尔克

1
仅供参考,Joda-Time项目现在处于维护模式,建议迁移到java.time类。请参见Oracle教程
罗勒·布尔克

8

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


1

日期应重新制定。它应该不是年长的整数,而应将年,月,日,时,分,秒作为单独的字段。存储与该日期相关联的日历和时区甚至更好。

在我们的自然对话中,如果在纽约时间2013年11月1日下午1点设置约会,则此日期为DateTime。它不是日历。因此,我们也应该能够在Java中像这样交谈。

当日期存储为长整数(自1970年1月1日以来的毫秒数)时,计算其当前日期取决于日历。不同的日历将给出不同的日期。这是出于给出绝对时间的预期(例如,在“大爆炸”之后1万亿秒)。但是通常我们还需要一种方便的对话方式,例如封装年份,月份等的对象。

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


您可以存储相同的时间,但是根据不同的日历系统报告不同的小时/分钟/天/周/年/年/事实,这是一个优势,而不是劣势。它反映了(复杂的)现实。
ThrawnCA

确实,Date已经重新开发;由java.time.Instant班级取代。和Calendar/ GregorianCalendarjava.time.ZonedDateTime类替换。
罗勒·布尔克


0

当我需要对日期进行某些特定的操作(例如时间上的移动)时,我使用Calendar,但是Date当您需要格式化日期以适应您的需求时,我发现它很有用,最近我发现Locale有很多有用的操作和方法。我正在使用语言环境!


仅供参考,麻烦的CalendarDate类在几年前被java.time类所取代。无需使用DateCalendar。与Locale日期时间对象的含义无关。A Locale仅用于指定在生成表示日期时间对象值的文本时用于本地化的人类语言和文化规范。
罗勒·布尔克
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.