7

我需要能够存储没有时间成分的日期(年/月/日)。这是日期的抽象概念,例如生日 - 我需要表示一年中的日期,而不是特定的时刻。

我正在使用 Java 从一些输入文本中解析日期,并且需要存储在 MySQL 数据库中。无论数据库、应用程序或任何客户端位于哪个时区,它们都应该看到相同的年/月/日。

我的应用程序将在与数据库服务器具有不同系统时区的机器上运行,我也无法控制。有没有人有一个优雅的解决方案来确保我正确存储日期?

我能想到这些解决方案,但都不是很好:

  • 查询我的 MySQL 连接的时区并解析该时区的输入日期
  • 将日期完全处理为字符串 yyyy-MM-dd
4

6 回答 6

5

java.time

在 JSR-310 被合并到 Java SE 8 之前,这确实是java.util日期时间 API 的一个问题。该java.util.Date对象不像现代日期时间类型那样是真正的日期时间对象;相反,它表示自称为“纪元” January 1, 1970, 00:00:00 GMT(或 UTC)的标准基准时间以来的毫秒数。当您打印 的对象时java.util.Date,其toString方法会返回 JVM 时区中的日期时间,根据此毫秒值计算得出。

您所描述的问题已通过现代日期时间 API *得到解决,我们拥有LocalDate的只是一个日期。在下面的句子中,Oracle 教程很好地描述了它的目的:

例如,您可以使用 LocalDate 对象来表示出生日期,因为大多数人在同一天庆祝他们的生日,无论他们是在他们的出生城市还是在国际日期变更线的另一边的全球。

几页后,它再次描述如下:

LocalDate 表示 ISO 日历中的年月日,可用于表示没有时间的日期。您可以使用 LocalDate 来跟踪重要事件,例如出生日期或结婚日期。

我应该使用哪种数据类型LocalDate

它映射为DATEANSI SQL 类型。ANSI SQL 类型与类型的映射在Oracle 的文章java.time中描述如下:

ANSI SQL Java SE 8
日期 本地日期
时间 当地时间
时间戳 本地日期时间
时区时间 偏移时间
带有时区的时间戳 偏移日期时间

如何在 JDBC 中使用它?

LocalDate下面给出了一个插入a 的示例代码columnfoo(它是DATE类型):

LocalDate localDate = LocalDate.now();
PreparedStatement st = conn.prepareStatement("INSERT INTO mytable (columnfoo) VALUES (?)");
st.setObject(1, localDate);
st.executeUpdate();
st.close();

LocalDate下面给出了从检索 a 的示例代码columnfoo

Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM mytable WHERE <some condition>");
while (rs.next()) {
    // Assuming the column index of columnfoo is 1
    LocalDate localDate = rs.getObject(1, LocalDate.class));
    System.out.println(localDate);
}
rs.close();
st.close();

Trail: Date Time了解有关现代日期时间 API *的更多信息。


* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,则可以使用ThreeTen-Backport,它将大部分java.time功能向后移植到 Java 6 和 7。如果您正在为 Android 项目和 Android API 工作level 仍然不符合 Java-8,请检查Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

于 2021-05-12T14:01:14.820 回答
3

您可以将所有时间/时区的内容归零:

public static Date truncateDate(Date date)
    {
        GregorianCalendar cal = getGregorianCalendar();
        cal.set(Calendar.ZONE_OFFSET, 0); // UTC
        cal.set(Calendar.DST_OFFSET, 0); // We don't want DST to get in the way.

        cal.setTime(date);
        cal.set(Calendar.MILLISECOND, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.HOUR, 0);
        cal.set(Calendar.AM_PM, Calendar.AM);

        return cal.getTime();
    }
于 2008-11-12T22:39:23.003 回答
2

我得出的结论是,在我当前的应用程序(一个直接使用 jdbc 的简单实用程序)中,最好的方法是直接作为字符串插入。对于更大的 Hibernate 应用程序,我可能会费心编写自己的用户类型。不敢相信有人还没有在一些公开可用的代码中解决这个问题......

于 2008-11-14T21:42:40.023 回答
0

难道你不能只DATE在你的表中使用 MySQL 类型,然后在你的插入语句中使用格式化的字符串吗?我认为这样的事情可以避免任何时区调整。

INSERT INTO time_table(dt) VALUES('2008-12-31')
于 2008-11-12T22:50:43.370 回答
0

由于您指定您的服务器和数据库位于不同的时区,因此在我看来您的解释有问题。如果您将日期归零,或者存储为 MM-dd-YYYY,这相当于同一件事,您是存储服务器日期还是数据库日期?如果服务器上是晚上 11:00,数据库上是第二天凌晨 1 点,那么哪个日期是“正确的”?当您查看一年后的日期时,您会记得日期所依赖的机器时区吗?

这些考虑可能有点矫枉过正。但是,为什么不将日期存储在 GMT 中(如果您愿意,可以将秒数归零)?

于 2008-11-15T01:07:22.200 回答
0

该类java.sql.Date正是出于这个原因而存在,不幸的是它使用起来非常不方便,因为它只是扩展java.util.Date并且您必须手动将时间部分设置为零。

于 2009-01-08T15:11:06.450 回答