Java 日期与日历
Posted
技术标签:
【中文标题】Java 日期与日历【英文标题】:Java Date vs Calendar 【发布时间】:2010-11-27 02:44:39 【问题描述】:有人可以就Date
和Calendar
类型的当前“最佳实践”提出建议。
在编写新代码时,最好始终偏爱Calendar
而不是Date
,还是在某些情况下Date
是更合适的数据类型?
【问题讨论】:
乔达时间:joda-time.sourceforge.net 仅供参考,java.util.Date
、java.util.Calendar
和 java.text.SimpleDateFormat
等麻烦的旧日期时间类现在已被 java.time 类所取代。许多 java.time 功能在ThreeTen-Backport 项目中被反向移植到Java 6 和Java 7。在ThreeTenABP 项目中进一步适用于早期的android。见How to use ThreeTenABP…。
仅供参考,Joda-Time 项目(在另一条评论中提到)现在位于maintenance mode,团队建议迁移到java.time 类。见Tutorial by Oracle。
【参考方案1】:
Date 是一个更简单的类,主要是出于向后兼容的原因。如果您需要设置特定日期或进行日期算术,请使用日历。日历也处理本地化。 Date 之前的日期操作函数已被弃用。
我个人倾向于使用以毫秒为单位的时间作为 long(或 Long,视情况而定)或 Calendar(如果有选择)。
日期和日历都是可变的,这在 API 中使用时往往会出现问题。
【讨论】:
仅供参考,非常麻烦的旧日期时间类,如java.util.Date
、java.util.Calendar
和 java.text.SimpleDateFormat
现在是 legacy,被 Java 8 中内置的 java.time 类所取代,之后。见Tutorial by Oracle。【参考方案2】:
新代码(如果您的政策允许第三方代码)的最佳方式是使用Joda Time library。
Date 和 Calendar 都有很多设计问题,对于新代码来说都不是很好的解决方案。
【讨论】:
我同意使用 joda time 的建议。它更易于使用和理解,并提供更多您可以使用的功能。 关于是使用 Joda Time 还是坚持使用标准 JDK 类的讨论,请参阅***.com/questions/589870/… 我不知道它是否值得被否决,但它没有回答这个问题。作为评论可能会更好。 因为问题是关于使用日期和日历而不是使用第三方库,这会给项目增加单一供应商依赖风险。 在 Java 8 提供 java.time package 之前,这是“最佳方式”。【参考方案3】:Date
和Calendar
实际上是相同的基本概念(两者都代表即时,并且是底层long
值的包装器)。
有人可能会争辩说,Calendar
实际上比Date
更糟糕,因为它似乎提供了关于诸如星期几和一天中的时间之类的具体事实,而如果你改变它的timeZone
属性,混凝土就会变成blancmange!由于这个原因,这两个对象都不能真正用作 year-month-day 或 time-of-day 的存储。
仅将Calendar
用作计算器,当给定Date
和TimeZone
对象时,它将为您进行计算。避免将其用于应用程序中的属性输入。
使用SimpleDateFormat
与TimeZone
和Date
一起生成显示字符串。
如果您喜欢冒险,请使用 Joda-Time,尽管恕我直言,它不必要地复杂,并且无论如何很快就会被 JSR-310 日期 API 取代。
我之前已经回答过,创建自己的 YearMonthDay
类并不难,它在底层使用 Calendar
进行日期计算。我对这个建议投了反对票,但我仍然相信这是一个有效的建议,因为Joda-Time(和JSR-310)对于大多数用例来说确实过于复杂了。
【讨论】:
JSR310 有时间框架吗?它将在 Java 7 中使用,但我相信现在情况并非如此。 @Brian - 在那个邮件列表上肯定已经很安静了! 只是检查一下,它已经处于非活动状态,这意味着他们在 18 个月内没有发布里程碑草案 :-( 邮件列表上的最新评论来自 7 月和斯蒂芬,因此该项目可能仍在进行中 同意。如果您知道如何安全地使用 Date 作为不可变对象,并且知道如何使用 Calendar 来操作 Dates,那么大多数人应该是安全的。在多线程代码中使用 SimpleDateFormat 时要小心。【参考方案4】:日期最适合存储日期对象。它是持久化的,序列化的......
日历最适合处理日期。
注意:我们有时也喜欢 java.lang.Long 而不是 Date,因为 Date 是可变的,因此不是线程安全的。在 Date 对象上,使用 setTime() 和 getTime() 在两者之间切换。例如,应用程序中的常量 Date(例如:零 1970/01/01,或您设置为 2099/12/31 的应用 END_OF_TIME ;这些对于将空值替换为开始时间和结束时间非常有用,尤其是当您将它们持久保存在数据库中时,因为 SQL 对空值非常特殊)。
【讨论】:
我认为你在谈论不可变的java.lang.Long
【参考方案5】:
tl;博士
建议围绕
Date
和Calendar
的当前“最佳实践”最好总是支持
Calendar
而不是Date
完全避免使用这些遗留类。请改用java.time 类。
暂时在UTC 中使用Instant
(现代相当于Date
)
在特定的time zone 中,使用ZonedDateTime
(现代相当于GregorianCalendar
)
在特定的offset-from-UTC 中暂时使用OffsetDateTime
(旧类中没有等效项)
对于时区或偏移量未知的日期时间(不是片刻),请使用 LocalDateTime
(旧类中没有等效项)
详情
Answer by Ortomala Lokni 建议使用现代的java.time 类而不是麻烦的旧的旧日期时间类(Date
、Calendar
等)是正确的。但是该答案表明错误的类是等效的(请参阅我对该答案的评论)。
使用 java.time
java.time 类是对传统日期时间类(昼夜差异)的巨大改进。旧的类设计不良、混乱且麻烦。您应该尽可能避免使用旧课程。但是当您需要转换到/从旧/新时,您可以通过调用添加到 old 类的新方法来实现。
有关转换的更多信息,请参阅my Answer and nifty diagram 到另一个问题,Convert java.util.Date to what “java.time” type?。
搜索 Stack Overflow 提供了数百个有关使用 java.time 的示例问题和答案。但这里有一个简短的概要。
Instant
使用Instant
获取当前时刻。 Instant
类代表UTC 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。
Instant instant = Instant.now();
ZonedDateTime
要通过某个特定区域的wall-clock time 的镜头看到同一时刻,请应用时区 (ZoneId
) 以获得ZonedDateTime
。
时区
以continent/region
的格式指定proper time zone name,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用 3-4 个字母的缩写,例如 EST
或 IST
,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone();
偏移
时区是一个地区在其offset-from-UTC 中的更改历史。但有时你只得到一个没有完整区域的偏移量。在这种情况下,请使用 OffsetDateTime
类。
ZoneOffset offset = ZoneOffset.parse( "+05:30" );
OffsetDateTime odt = instant.atOffset( offset );
使用时区优于仅使用偏移量。
LocalDateTime
Local…
类中的“本地”表示任何 位置,而不是特定位置。所以这个名字可能是违反直觉的。
LocalDateTime
、LocalDate
和 LocalTime
故意缺少有关偏移量或时区的任何信息。所以它们确实不代表实际时刻,它们不是时间线上的点。如果有疑问或困惑,请使用ZonedDateTime
而不是LocalDateTime
。搜索 Stack Overflow 以获取更多讨论。
字符串
不要将日期时间对象与表示其值的字符串混为一谈。您可以解析字符串以获取日期时间对象,并且可以从日期时间对象生成字符串。但字符串永远不是日期时间本身。
了解标准 ISO 8601 格式,默认情况下用于 java.time 类。
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date
、Calendar
和 SimpleDateFormat
。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
使用符合JDBC 4.2 或更高版本的JDBC driver,您可以直接与您的数据库交换java.time 对象。不需要字符串也不需要 java.sql.* 类。
从哪里获得 java.time 类?
Java SE 8、Java SE 9 及更高版本 内置。 标准 Java API 的一部分,带有捆绑实现。 Java 9 添加了一些小功能和修复。 Java SE 6 和 Java SE 7 大部分 java.time 功能都在ThreeTen-Backport 中向后移植到 Java 6 和 7。 Android 更高版本的 Android 捆绑包实现 java.time 类。 对于早期的 Android,ThreeTenABP 项目采用 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…。ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval
、YearWeek
、YearQuarter
和more。
【讨论】:
感谢您的详细解释。我是 Java 新手,我将开始使用 java.time。【参考方案6】:如果可能,我通常使用日期。尽管它是可变的,但实际上不推荐使用 mutators。最后,它基本上包装了一个代表日期/时间的 long 。相反,如果我必须操纵这些值,我会使用日历。
您可以这样想:仅当您需要拥有可以轻松操作的字符串然后使用 toString() 方法将它们转换为字符串时才使用 StringBuffer。同样,我只在需要操作时态数据时才使用日历。
为了最佳实践,我倾向于在领域模型之外尽可能多地使用不可变对象。它显着减少了任何副作用的机会,并且它是由编译器为您完成的,而不是 JUnit 测试。您可以通过在类中创建 private 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);
【讨论】:
是的,immutable objects 对日期时间工作有意义。取代Date
/Calendar
的 java.time 类使用不可变对象模式。
这可能是真的,但在 2010 年不会。
是的。但是现在有成千上万的人在阅读这个页面,到目前为止已经超过 150,000 人。我的评论是给他们的便条,而不是对你的批评。
我知道,这就是为什么我赞成另一个答案。但是,仍然有一些人需要忍受旧 JDK 的痛苦,他们也需要上述答案。
实际上,对于 Java 6 和 7,我们有 ThreeTen-Backport 项目。这带来了具有几乎相同 API 的大部分 java.time 功能。所以没有必要使用那些糟糕的遗留日期时间类。【参考方案7】:
Date
s 应该用作不可变的时间点; Calendar
s 是可变的,如果您需要与其他类协作以得出最终日期,可以传递和修改。将它们与String
和StringBuilder
类似,您就会明白我认为应该如何使用它们。
(是的,我知道 Date 在技术上实际上不是不可变的,但目的是它不应该是可变的,如果没有调用过时的方法,那么它就是这样。)
【讨论】:
是的,immutable objects 对日期时间工作有意义。取代Date
/Calendar
的 java.time 类使用不可变对象模式。具体来说,Instant
替换 java.util.Date
,ZonedDateTime
替换 Calendar
/GregorianCalendar
。【参考方案8】:
对于 Java 8,应该使用新的 java.time package。
对象是不可变的,考虑了时区和夏令时。
您可以像这样从旧的java.util.Date
对象创建ZonedDateTime
对象:
Date date = new Date();
ZonedDateTime zonedDateTime = date.toInstant().atZone(ZoneId.systemDefault());
【讨论】:
很好地推荐 java.time 类。但不好建议LocalDateTime
。该类故意丢失有关从 UTC 偏移和时区的任何信息。因此,该类 不 等效于 Date
是 UTC 并且 Calendar
具有指定的时区。请参阅 my Answer and nifty diagram 到另一个问题 Convert java.util.Date to what “java.time” type?。【参考方案9】:
我一直提倡Joda-time。原因如下。
-
API 一致且直观。与 java.util.Date/Calendar API 不同
与java.text.SimpleDateFormat 等不同,它不受线程问题的影响(我见过许多与没有意识到标准日期/时间格式不是线程安全的客户问题有关)
它是新 Java 日期/时间 API(JSR310,计划用于 Java 8)的基础。因此,您将使用将成为核心 Java API 的 API。
编辑:如果您可以迁移到 Java 8,Java 8 引入的 Java 日期/时间类现在是首选解决方案
【讨论】:
我上次查看时,JODA 和 JSR-310 看起来非常不同,即使它们都是由 Stephen Colebourne 编写的。也就是说,JODA 将向您介绍 JSR-310 也解决的日期时间问题的复杂性 因为问题是关于使用日期和日历而不是使用第三方库,这会给项目增加单一供应商依赖风险。 我认为最好的做法就是不使用这些类 鉴于 java.util.Date 和 Calendar 类的已知问题,建议 Joda-Time(或 JSR 310)对我来说似乎合适且负责。我们不是在谈论品味或审美风格的问题。如果有人问红车还是银车,我知道红车轮胎漏气,银车散热器坏了,我应该选车还是建议叫出租车?这个问题的答案现在似乎很明显,因为即使是 Sun/Oracle 也决定抛开这些垃圾并购买一辆新车:JSR 310:日期和时间 API。 仅供参考,Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。见Tutorial by Oracle。【参考方案10】:聚会有点晚了,但是 Java 在 JDK 8 中有一个新的日期时间 API。您可能想要升级您的 JDK 版本并采用该标准。不再有凌乱的日期/日历,不再有第 3 方罐子。
【讨论】:
【参考方案11】:日期应该重新制定。它不应该是一个长整数,而是应该将年、月、日、小时、分钟、秒作为单独的字段保存。存储与此日期相关联的日历和时区可能会更好。
在我们的自然对话中,如果在 2013 年 11 月 1 日下午 1 点纽约时间安排约会,这是一个日期时间。它不是日历。所以我们在 Java 中也应该能够像这样进行对话。
当 Date 存储为长整数(自 1970 年 1 月 1 日或其他时间以来的毫秒数)时,计算其当前日期取决于日历。不同的日历会给出不同的日期。这是从给出一个绝对时间的角度来看(例如大爆炸后的 1 万亿秒)。但通常我们还需要一种方便的对话方式,比如封装年、月等的对象。
我想知道 Java 是否有新的进展来协调这两个目标。也许我的java知识太老了。
【讨论】:
您可以及时存储相同的瞬间,但根据不同的日历系统报告不同的小时/分钟/天/周/年/foo,这是一种优势,而不是劣势。它反映了(复杂的)现实。 确实,Date
已经重新开发了;替换为 java.time.Instant
类。而Calendar
/GregorianCalendar
被java.time.ZonedDateTime
类取代。【参考方案12】:
顺便说一句,“日期”通常被标记为“过时/已弃用”(我不知道确切原因)——那里写了一些关于它的东西 Java: Why is the Date constructor deprecated, and what do I use instead?
看起来这是构造函数的问题-通过 new Date(int year, int month, int day),推荐的方法是通过 Calendar 和 set参数分开 .. (Calendar cal = Calendar.getInstance(); )
【讨论】:
【参考方案13】:当我需要对日期进行一些特定操作(如时间移动)时,我会使用日历,但是当您需要格式化日期以适应您的需求时,我发现它很有帮助,最近我发现 Locale 有很多有用的操作和方法。所以我现在正在使用 Locale!
【讨论】:
仅供参考,麻烦的Calendar
和 Date
类在几年前被 java.time 类所取代。无需使用Date
或Calendar
。而Locale
与日期时间对象的含义无关。 Locale
仅用于指定用于本地化的人类语言和文化规范,同时生成表示日期时间对象值的文本。以上是关于Java 日期与日历的主要内容,如果未能解决你的问题,请参考以下文章