为啥我不应该使用 date4j 而不是 joda java.util.Calendar 或 jsr 310?

Posted

技术标签:

【中文标题】为啥我不应该使用 date4j 而不是 joda java.util.Calendar 或 jsr 310?【英文标题】:Why shouldn't I use date4j instead of joda java.util.Calendar or jsr 310?为什么我不应该使用 date4j 而不是 joda java.util.Calendar 或 jsr 310? 【发布时间】:2011-11-03 08:30:40 【问题描述】:

我最近遇到了date4j,这是一个非常简单的库(本质上是一个类),用于在 Java 中处理日期。从概念上讲,我真的很喜欢 date4j 的“想法”。事实上,在阅读了整个主站点和 javadoc 中的文档之后,我非常同意所陈述的一切。

现在,可能我不应该使用 date4j 的几个原因 - 错误、性能、缺乏用户等。我不是在问这些事情。从概念上讲,我在问 date4j 的想法有什么问题(对于那里的大多数应用程序)?当然,可能有一些应用程序需要 joda 或threeten 之类的东西 - 但我相信这些应用程序属于少数。

人们对处理日期/时间的用户(几乎每个编写 Java 应用程序的人)给出的一般建议是这样的:

使用 joda-time 代替 java.util.Calendar 将您的网络服务器设置为 UTC 将数据库服务器设置为 UTC 以 UTC 格式存储您的日期时间

事实上,最后三个要点说明了人们在处理日期时存在的当前心理模型存在的问题。人们试图在应用程序和数据库级别管理时区(更不用说 ORM 框架添加了另一层抽象,这使事情进一步复杂化)。

你不应该做那些事情。例如,如果您使用的是 java.util.Calendar,并且您正在一些用户定义的时区中操作时间:

Calendar c = Calendar.getInstance(TimeZone.getTimeZone("America/New_York"));
c.set(Calendar.YEAR, 2011);
c.set(Calendar.MONTH, 0);
c.set(Calendar.DAY_OF_MONTH, 1);
c.set(Calendar.HOUR_OF_DAY, 3);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);

这表示时间上的“瞬间”,与时区无关。您应该能够在这一次与数据库之间进行持久化,而不必担心发生任何类型的“转换”。数据库是否在上海时区和网络服务器在洛杉矶时区无关紧要 - 时刻都是一样的。

一个问题是一些数据库试图为你管理时区(我在看着你,Postgres!grr!)并使behavior at the JDBC driver level is vendor specific变得更糟 - 即 PreparedStatement.setDate/getDate .

date4j 使用的心智模型似乎摆脱了所有的困惑。例如,显式强制使用在调用 now() 时提供时区。网站上有一些非常好的使用图书馆的建议(这些事情我之前已经在我自己的应用程序中做了),例如:

不要使用试图管理时区的数据库类型 将时区存储为单独的列(如果需要)

为什么没有更多的人采用像 date4j 这样的库?

【问题讨论】:

【参考方案1】:

到目前为止,我从未看过 date4j,但我通读了文档,发现了一个具体问题,并对它的问题提出了一种看法。

首先,具体问题。它不在内部维护时区。因此,夏令时存在歧义。考虑周日的过渡,当凌晨 1:59:59 (EDT) 变为凌晨 1:00:00 (EST)。显然,在那一天,说凌晨 1:30 是模棱两可的。那个时间发生了两次。所以下面是模棱两可的

new DateTime("2011-11-06 01:30:00")

第二,我的看法。 Date 的问题在于它没有维护时区。当 Calendar 出现时,损害已经造成。此外,Calendar 本身并没有因为可变而给自己带来任何好处。但从本质上讲,解决方案不仅要包括时刻,还要包括它被感知的地方——时区、文化、地区之类的东西。然后需要在持久化到数据库、序列化、通信等时同时维护这些内容。同样的个人原则使我谦虚地建议即使 xsd:dateTime 也不足,因为它只是允许相对于 UTC 表示日期+时间,它没有规定是否提供例如是否遵守夏令时。

【讨论】:

似乎两种方式都有歧义。例如,如果您正在编写 Google 日历之类的日历应用程序,并且用户在凌晨 1:30 安排了一个活动,那么您将哪个时刻存储在数据库中? 我并不是说现在任何人都做得很好,只是说如果不在 DateTime 实例中保留更多细节,就没有希望解决问题。 顺便说一句,我并不反对你。我相信您的答案是正确的,但会等待其他人发布。如果您正在做一些简单的事情,例如记录某些事件发生的特定瞬间 - 发布了一条消息,删除了一个项目,那么由于您所说的原因,date4j 可能无法正常工作。 这个新的 DateTime("2011-11-06 01:30:00") 没有歧义。相当于 new DateTime("2011-11-06 01A:30:00") ,第二次又是 "01:30" 的时候是 new DateTime("2011-11-06 01B:30:00")但是我不确定 A 和 B 应该放在哪里。 是的,问题的核心——日历和时间很复杂。没有办法绕过它。同时,我们不能指望每个人都成为该主题的专家。因此,API 本质上需要具有规范性——(非侵入性地)强制用户遵循解决底层问题的模式。尤其是像闰秒这样的事情对大多数人来说是无趣的,即使在计算时间间隔时也是如此。但是由于没有偏好配置文件和内部支持复杂性,代码或结果都会受到影响。【参考方案2】:

四年后,我遇到了这个问题。对我来说,date4j 的问题 #1 是错误。我知道你没有特别问这些,但这是一个大问题。

更具体地说,错误本身并不是问题,正是完全没有某种错误报告系统让我对 date4j 感到困扰。甚至没有邮件列表,据我所知,根本没有任何东西。我知道这对于讨论日期/时间 API 应该是什么样子可能不是很有趣,但基础设施仍然很重要。

缺少一个很可能是图书馆没有被广泛采用的一个原因。

【讨论】:

四年后,我们终于可以开始使用新的 Java 8 日期和时间 API... :) 也许再过 4 年,我将能够在我的 Grails 项目中本地使用新类,而无需滚动我自己的用户类型映射或使用第三方映射库... 有关信息,Date4J 现在在 GitHub 上,可以报告问题。 github.com/johanley/date4j

以上是关于为啥我不应该使用 date4j 而不是 joda java.util.Calendar 或 jsr 310?的主要内容,如果未能解决你的问题,请参考以下文章

我应该用threeten代替joda-time吗

在 Hibernate 中持久化 Joda DateTime 而不是 Java Date

为啥我们应该使用 RNN 而不是马尔可夫模型?

为啥我应该更喜欢单个'await Task.WhenAll'而不是多个等待?

为啥我应该在数据库而不是文件系统中插入 blob?

为啥我不应该包含 cpp 文件而使用标头?